Skip to content

Commit 6e1c972

Browse files
committed
fix tickets-crud that was introduced in issues 242
-Admins and Coordinators will be able to create tickets -Traineess will be able to see tickets assigned to them -Also Admins and Admins can also perform CRUD operations on Tickets
1 parent ea1e1e3 commit 6e1c972

18 files changed

+1170
-418
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React from 'react';
2+
import ActionDropdown from '../components/ActionDropdown';
3+
4+
interface ActionDropdownCellProps {
5+
row: {
6+
original: any;
7+
};
8+
onView: (ticket: any) => void;
9+
onEdit: (id: string) => void;
10+
onDelete: (id: string) => void;
11+
}
12+
13+
function ActionDropdownCell({
14+
row,
15+
onView,
16+
onEdit,
17+
onDelete,
18+
}: ActionDropdownCellProps) {
19+
return (
20+
<ActionDropdown
21+
onView={() => onView(row.original)}
22+
onEdit={() => onEdit(row.original.id)}
23+
onDelete={() => onDelete(row.original.id)}
24+
/>
25+
);
26+
}
27+
28+
export default ActionDropdownCell;

src/Mutations/help.mutation.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { gql } from '@apollo/client';
22

33
const CREATE_TICKET = gql`
4-
mutation CreateTicket($subject: String!, $message: String!) {
5-
createTicket(subject: $subject, message: $message) {
4+
mutation CreateTicket($subject: String!, $message: String!, $assignee: ID!) {
5+
createTicket(subject: $subject, message: $message, assignee: $assignee) {
66
responseMsg
77
}
88
}

src/components/ActionDropdown.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* eslint-disable react/function-component-definition */
2+
import React, { useState } from 'react';
3+
import { FaEllipsisV, FaEye, FaEdit, FaTrashAlt } from 'react-icons/fa';
4+
5+
interface ActionDropdownProps {
6+
onView: () => void;
7+
onEdit: () => void;
8+
onDelete: () => void;
9+
}
10+
11+
function ActionDropdown({ onView, onEdit, onDelete }: ActionDropdownProps) {
12+
const [isOpen, setIsOpen] = useState(false);
13+
14+
const toggleDropdown = () => setIsOpen(!isOpen);
15+
16+
return (
17+
<div className="relative">
18+
<button
19+
type="button"
20+
aria-label="Open actions menu"
21+
className="text-gray-500 hover:text-gray-700"
22+
onClick={toggleDropdown}
23+
>
24+
<FaEllipsisV className="text-2xl" />
25+
</button>
26+
{isOpen && (
27+
<div className="absolute right-0 z-10 w-48 mt-2 bg-white border border-gray-200 rounded-md shadow-lg">
28+
<button
29+
type="button"
30+
aria-label="View item"
31+
onClick={() => {
32+
onView();
33+
setIsOpen(false);
34+
}}
35+
className="flex items-center w-full px-4 py-2 text-sm text-blue-600 hover:bg-blue-100"
36+
>
37+
<FaEye className="mr-2" />
38+
View
39+
</button>
40+
<button
41+
type="button"
42+
aria-label="Edit item"
43+
onClick={() => {
44+
onEdit();
45+
setIsOpen(false);
46+
}}
47+
className="flex items-center w-full px-4 py-2 text-sm text-yellow-600 hover:bg-yellow-100"
48+
>
49+
<FaEdit className="mr-2" />
50+
Edit
51+
</button>
52+
<button
53+
type="button"
54+
aria-label="Delete item"
55+
onClick={() => {
56+
onDelete();
57+
setIsOpen(false);
58+
}}
59+
className="flex items-center w-full px-4 py-2 text-sm text-red-600 hover:bg-red-100"
60+
>
61+
<FaTrashAlt className="mr-2" />
62+
Delete
63+
</button>
64+
</div>
65+
)}
66+
</div>
67+
);
68+
}
69+
70+
export default ActionDropdown;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* eslint-disable react/function-component-definition */
2+
import React, { useState } from 'react';
3+
import { toast } from 'react-toastify';
4+
5+
interface ConfirmationModalProps {
6+
isOpen: boolean;
7+
onConfirm: () => void;
8+
onCancel: () => void;
9+
message: string;
10+
}
11+
12+
const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
13+
isOpen,
14+
onConfirm,
15+
onCancel,
16+
message,
17+
}) => {
18+
const [loading, setLoading] = useState(false);
19+
20+
if (!isOpen) return null;
21+
22+
const handleConfirm = async () => {
23+
setLoading(true);
24+
try {
25+
onConfirm();
26+
} catch (error) {
27+
toast.error('Failed to confirm action. Please try again.');
28+
} finally {
29+
setLoading(false);
30+
}
31+
};
32+
33+
return (
34+
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
35+
<div className="w-full max-w-sm p-6 bg-white rounded-lg shadow-lg dark:bg-gray-800 dark:shadow-gray-700">
36+
<h2 className="mb-4 text-2xl font-bold text-gray-900 dark:text-gray-100">
37+
Confirm Action
38+
</h2>
39+
<p className="mb-4 text-gray-700 dark:text-gray-300">{message}</p>
40+
<div className="flex justify-end space-x-4">
41+
<button
42+
type="button"
43+
onClick={onCancel}
44+
className="px-4 py-2 text-white bg-gray-500 rounded hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-400 dark:bg-gray-600 dark:hover:bg-gray-700 dark:focus:ring-gray-500"
45+
>
46+
Cancel
47+
</button>
48+
<button
49+
onClick={handleConfirm}
50+
type="button"
51+
className={`px-4 py-2 text-white rounded hover:bg-red-600 focus:outline-none focus:ring-2 ${
52+
loading ? 'bg-red-400 cursor-not-allowed' : 'bg-red-500'
53+
} dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-500`}
54+
disabled={loading}
55+
>
56+
{loading ? 'Processing...' : 'Confirm'}
57+
</button>
58+
</div>
59+
</div>
60+
</div>
61+
);
62+
};
63+
64+
export default ConfirmationModal;

src/components/DataTable.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ interface TableData {
1818
}
1919

2020
function DataTable({ data, columns, title, loading }: TableData) {
21-
// const sortedData = React.useMemo(() => [...data], []);
2221
const sortedColumns = React.useMemo(() => [...columns], [columns]);
2322
const sortedData = data;
24-
// const sortedColumns = columns;
2523
const TableInstance = useTable(
2624
{ data: sortedData, columns: sortedColumns, initialState: { pageSize: 3 } },
2725

@@ -65,7 +63,7 @@ function DataTable({ data, columns, title, loading }: TableData) {
6563
<input
6664
defaultValue={globalFilter || ''}
6765
placeholder="Filter"
68-
className="px-5 py-2 mt-4 font-sans text-xs border border-primary rounded outline-none dark:bg-neutral-600 dark:text-white w-52 md:w-96"
66+
className="px-5 py-2 mt-4 font-sans text-xs border rounded outline-none border-primary dark:bg-neutral-600 dark:text-white w-52 md:w-96"
6967
/* istanbul ignore next */
7068
onChange={(e) => setGlobalFilter(e.target.value)}
7169
/>

0 commit comments

Comments
 (0)