Skip to content

Commit

Permalink
fixing all issues introduced in tickets CRUD
Browse files Browse the repository at this point in the history
  • Loading branch information
amiparadis250 committed Oct 7, 2024
1 parent 1423c1c commit 11373cb
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 82 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 23 additions & 30 deletions src/components/EditTicketModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* eslint-disable react/function-component-definition */
import React, { useState, FC, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
import { useMutation } from '@apollo/client';
import { toast } from 'react-toastify';
import { UPDATE_TICKET } from '../queries/tickets.queries'; // Import the mutation
import { UPDATE_TICKET } from '../queries/tickets.queries';

interface EditTicketModalProps {
isOpen: boolean;
Expand All @@ -12,31 +11,32 @@ interface EditTicketModalProps {
refetchTickets: () => void;
}

const EditTicketModal: FC<EditTicketModalProps> = ({
function EditTicketModal({
isOpen,
onClose,
ticket,
users,
refetchTickets,
}) => {
}: EditTicketModalProps) {
const [formData, setFormData] = useState<any>({});
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState<any>({});
const [hasChanges, setHasChanges] = useState(false); // Track changes
const [hasChanges, setHasChanges] = useState(false);
const authToken = localStorage.getItem('auth_token');

// Update formData when the ticket prop changes
useEffect(() => {
setFormData(ticket || {});
setFormData({
...ticket,
assignee: ticket?.assignee?.id || '',
});
}, [ticket]);

// Check for changes in form data
useEffect(() => {
const isSameData =
formData.subject === ticket?.subject &&
formData.message === ticket?.message &&
formData.status === ticket?.status &&
formData.assignee === ticket?.assignee?.id;
formData.assignee === (ticket?.assignee?.id || '');
setHasChanges(!isSameData);
}, [formData, ticket]);

Expand All @@ -46,7 +46,7 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
Authorization: `Bearer ${authToken}`,
},
},
onCompleted: (data) => {
onCompleted: () => {
setLoading(false);
toast.success('Ticket updated successfully');
refetchTickets();
Expand All @@ -58,7 +58,6 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
},
});

// Form validation
const validateForm = () => {
const newErrors: { subject?: string; status?: string; assignee?: string } =
{};
Expand All @@ -69,7 +68,6 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
return Object.keys(newErrors).length === 0;
};

// Handle form field changes
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>,
) => {
Expand All @@ -80,7 +78,6 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
}));
};

// Save the ticket after validation
const handleSave = async () => {
if (!validateForm()) return;

Expand All @@ -93,7 +90,7 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
subject: formData.subject,
message: formData.message,
status: formData.status,
assignee: formData.assignee?.id, // Use only the ID for assignee
assignee: formData.assignee,
},
},
});
Expand Down Expand Up @@ -182,8 +179,8 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
errors.status ? 'border-red-500' : 'border-gray-300'
} rounded-md shadow-sm dark:bg-gray-700 dark:text-gray-100 dark:border-gray-600`}
>
<option value="Open">Open</option>
<option value="Closed">Closed</option>
<option value="open">Open</option>
<option value="closed">Closed</option>
</select>
{errors.status && (
<p className="mt-1 text-sm text-red-500">{errors.status}</p>
Expand All @@ -200,22 +197,18 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
<select
id="assignee"
name="assignee"
value={formData.assignee || ticket.assignee?.id || ''}
value={formData.assignee || ''}
onChange={handleChange}
className={`block w-full px-4 py-2 mt-1 text-gray-900 bg-gray-200 border ${
errors.assignee ? 'border-red-500' : 'border-gray-300'
} rounded-md shadow-sm dark:bg-gray-700 dark:text-gray-100 dark:border-gray-600`}
>
<option value="">
{ticket.assignee?.email || 'Select Assignee'}
</option>
{users
.filter((user) => user.id !== ticket.assignee?.id) // Exclude current assignee
.map((user) => (
<option key={user.id} value={user.id}>
{user.email}
</option>
))}
<option value="">Select Assignee</option>
{users.map((user) => (
<option key={user.id} value={user.id}>
{user.email}
</option>
))}
</select>
{errors.assignee && (
<p className="mt-1 text-sm text-red-500">{errors.assignee}</p>
Expand All @@ -233,7 +226,7 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
<button
type="submit"
className="px-4 py-2 text-white bg-blue-600 rounded-md hover:bg-blue-700"
disabled={loading || !hasChanges} // Disable when loading or no changes
disabled={loading || !hasChanges}
>
Save
</button>
Expand All @@ -242,6 +235,6 @@ const EditTicketModal: FC<EditTicketModalProps> = ({
</div>
</div>
);
};
}

export default EditTicketModal;
64 changes: 57 additions & 7 deletions src/components/NewTicketModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import { useMutation } from '@apollo/client';
import { toast } from 'react-toastify';
import CREATE_TICKET from '../Mutations/help.mutation';
Expand All @@ -18,11 +18,13 @@ function NewTicketModal({
}: NewTicketModalProps) {
const [subject, setSubject] = useState('');
const [message, setMessage] = useState('');
const [selectedUser, setSelectedUser] = useState<string>('');
const [selectedTeam, setSelectedTeam] = useState('');
const [selectedUser, setSelectedUser] = useState('');
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState<{
subject?: string;
message?: string;
team?: string;
user?: string;
}>({});

Expand All @@ -33,19 +35,38 @@ function NewTicketModal({
refetchTickets();
onClose();
},

onError: (error) => {
setLoading(false);
toast.error(`Error creating ticket: ${error.message}`);
},
});

const teams = useMemo(() => {
const uniqueTeams = new Set(users.map((user) => user.team.id));
return Array.from(uniqueTeams).map((teamId) => {
const teamUser = users.find((user) => user.team.id === teamId);
return { id: teamId, name: teamUser.team.name };
});
}, [users]);

const filteredUsers = useMemo(
() =>
selectedTeam ? users.filter((user) => user.team.id === selectedTeam) : [],
[selectedTeam, users],
);

const validateForm = () => {
const newErrors: { subject?: string; message?: string; user?: string } = {};
const newErrors: {
subject?: string;
message?: string;
team?: string;
user?: string;
} = {};
if (!subject.trim()) newErrors.subject = 'Subject is required';
if (subject.length > 100)
newErrors.subject = 'Subject must be less than 100 characters';
if (!message.trim()) newErrors.message = 'Message is required';
if (!selectedTeam) newErrors.team = 'Team must be selected';
if (!selectedUser) newErrors.user = 'User must be selected';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
Expand All @@ -63,8 +84,7 @@ function NewTicketModal({
assignee: selectedUser,
},
});
} catch (error) {
// @ts-ignore
} catch (error: any) {
toast.error(`Error submitting ticket: ${error.message}`);
}
};
Expand Down Expand Up @@ -127,6 +147,35 @@ function NewTicketModal({
<p className="mt-1 text-sm text-red-500">{errors.message}</p>
)}
</div>
<div className="mb-6">
<label
htmlFor="teams"
className="block text-sm font-medium text-gray-700 dark:text-gray-300"
>
Teams
</label>
<select
id="teams"
value={selectedTeam}
onChange={(e) => {
setSelectedTeam(e.target.value);
setSelectedUser('');
}}
className={`block w-full px-4 py-2 mt-1 text-gray-900 bg-gray-200 border ${
errors.team ? 'border-red-500' : 'border-gray-300'
} rounded-md shadow-sm dark:bg-gray-700 dark:text-gray-100 dark:border-gray-600`}
>
<option value="">Select Teams</option>
{teams.map((team) => (
<option key={team.id} value={team.id}>
{team.name}
</option>
))}
</select>
{errors.team && (
<p className="mt-1 text-sm text-red-500">{errors.team}</p>
)}
</div>
<div className="mb-6">
<label
htmlFor="user"
Expand All @@ -141,9 +190,10 @@ function NewTicketModal({
className={`block w-full px-4 py-2 mt-1 text-gray-900 bg-gray-200 border ${
errors.user ? 'border-red-500' : 'border-gray-300'
} rounded-md shadow-sm dark:bg-gray-700 dark:text-gray-100 dark:border-gray-600`}
disabled={!selectedTeam}
>
<option value="">Select assignee</option>
{users.map((user) => (
{filteredUsers.map((user) => (
<option key={user.id} value={user.id}>
{user.email}
</option>
Expand Down
5 changes: 5 additions & 0 deletions src/components/tickets.columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ export const ticketsColumns = [
accessor: 'assignee',
Cell: ({ value }: any) => value?.email || 'N/A',
},
{
Header: 'Team',
accessor: 'assigneeTeam',
Cell: ({ value }: any) => value?.team.name || 'N/A',
},
];
Loading

0 comments on commit 11373cb

Please sign in to comment.