273 lines
8.0 KiB
TypeScript
273 lines
8.0 KiB
TypeScript
'use client';
|
|
|
|
import { useMemo } from 'react';
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import { toast } from 'sonner';
|
|
|
|
import { ticketService } from '@/services/api';
|
|
import type {
|
|
AssignTicketPayload,
|
|
CloseTicketPayload,
|
|
RequestTicketExtensionPayload,
|
|
ReviewTicketExtensionPayload,
|
|
ReviewRepairPayload,
|
|
SubmitRepairPayload,
|
|
SubmitRepairUploadPayload,
|
|
TicketDetail,
|
|
TicketListParams,
|
|
} from '@/types';
|
|
|
|
import { ticketKeys } from '../queries/ticketKeys';
|
|
|
|
const TICKET_TABLE_REFRESH_MS = 5 * 60 * 1000;
|
|
|
|
interface UseTicketsQueryParams {
|
|
skip: number;
|
|
limit: number;
|
|
chainageId?: string;
|
|
}
|
|
|
|
function buildTicketListParams({
|
|
skip,
|
|
limit,
|
|
chainageId,
|
|
}: UseTicketsQueryParams): TicketListParams {
|
|
return {
|
|
skip,
|
|
limit,
|
|
chainage_id: chainageId || undefined,
|
|
};
|
|
}
|
|
|
|
export function useTicketsQuery(params: UseTicketsQueryParams) {
|
|
const { skip, limit, chainageId } = params;
|
|
const listParams = useMemo(
|
|
() => buildTicketListParams({ skip, limit, chainageId }),
|
|
[chainageId, limit, skip],
|
|
);
|
|
|
|
return useQuery({
|
|
queryKey: ticketKeys.list(listParams),
|
|
queryFn: () => ticketService.getTickets(listParams),
|
|
staleTime: TICKET_TABLE_REFRESH_MS,
|
|
refetchInterval: TICKET_TABLE_REFRESH_MS,
|
|
});
|
|
}
|
|
|
|
export function useTicketOverviewQuery(ticketId: string | undefined) {
|
|
return useQuery({
|
|
queryKey: ticketKeys.overview(ticketId ?? ''),
|
|
queryFn: () => ticketService.getTicketOverview(ticketId as string),
|
|
enabled: Boolean(ticketId),
|
|
});
|
|
}
|
|
|
|
export function useTicketClassDetailQuery(
|
|
ticketId: string | undefined,
|
|
defectClass: string | undefined,
|
|
) {
|
|
return useQuery({
|
|
queryKey: ticketKeys.classDetail(ticketId ?? '', defectClass ?? ''),
|
|
queryFn: () =>
|
|
ticketService.getTicketClassDetail(
|
|
ticketId as string,
|
|
defectClass as string,
|
|
),
|
|
enabled: Boolean(ticketId && defectClass),
|
|
});
|
|
}
|
|
|
|
export function useTicketDefectClassesQuery(ticketId: string | undefined) {
|
|
return useQuery({
|
|
queryKey: ticketKeys.defectClasses(ticketId ?? ''),
|
|
queryFn: () => ticketService.getTicketDefectClasses(ticketId as string),
|
|
enabled: Boolean(ticketId),
|
|
});
|
|
}
|
|
|
|
export function useTicketExtensionRequestQuery(
|
|
ticketId: string | undefined,
|
|
assignmentId: number | undefined,
|
|
) {
|
|
return useQuery({
|
|
queryKey: ticketKeys.extensionRequest(ticketId ?? '', assignmentId),
|
|
queryFn: () =>
|
|
ticketService.getTicketExtensionRequest(
|
|
ticketId as string,
|
|
assignmentId as number,
|
|
),
|
|
enabled: Boolean(ticketId && assignmentId),
|
|
});
|
|
}
|
|
|
|
export function useAssignableTicketUsersQuery(enabled: boolean) {
|
|
return useQuery({
|
|
queryKey: ticketKeys.assignableUsers(),
|
|
queryFn: () => ticketService.getAssignableUsers(),
|
|
enabled,
|
|
});
|
|
}
|
|
|
|
function mergeTicketDetailResponse(
|
|
current: TicketDetail | undefined,
|
|
next: TicketDetail,
|
|
) {
|
|
if (!current) return next;
|
|
|
|
return {
|
|
...current,
|
|
...next,
|
|
history: next.history ?? current.history,
|
|
};
|
|
}
|
|
|
|
export function useAssignTicketMutation(ticketId: string) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: AssignTicketPayload) =>
|
|
ticketService.assignTicket(ticketId, payload),
|
|
onSuccess: (ticket, payload) => {
|
|
toast.success('Ticket assigned');
|
|
queryClient.setQueryData<TicketDetail>(
|
|
ticketKeys.classDetail(ticketId, payload.defect_class),
|
|
(current) => mergeTicketDetailResponse(current, ticket),
|
|
);
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
},
|
|
onError: () => toast.error('Failed to assign ticket'),
|
|
});
|
|
}
|
|
|
|
export function useSubmitRepairMutation(ticketId: string) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: SubmitRepairPayload) =>
|
|
ticketService.submitRepair(ticketId, payload),
|
|
onSuccess: (ticket) => {
|
|
toast.success('Repair submitted');
|
|
queryClient.setQueryData<TicketDetail>(
|
|
ticketKeys.classDetail(ticketId, ticket.defect_class),
|
|
(current) => mergeTicketDetailResponse(current, ticket),
|
|
);
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
},
|
|
onError: () => toast.error('Failed to submit repair'),
|
|
});
|
|
}
|
|
|
|
export function useSubmitRepairUploadMutation(ticketId: string) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: SubmitRepairUploadPayload) =>
|
|
ticketService.submitRepairUpload(ticketId, payload),
|
|
onSuccess: (ticket, payload) => {
|
|
toast.success('Repair evidence uploaded');
|
|
queryClient.setQueryData<TicketDetail>(
|
|
ticketKeys.classDetail(ticketId, payload.defect_class),
|
|
(current) => mergeTicketDetailResponse(current, ticket),
|
|
);
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
},
|
|
onError: () => toast.error('Failed to upload repair evidence'),
|
|
});
|
|
}
|
|
|
|
export function useRequestTicketExtensionMutation(
|
|
ticketId: string,
|
|
assignmentId: number | undefined,
|
|
defectClass: string,
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: RequestTicketExtensionPayload) => {
|
|
if (assignmentId === undefined) {
|
|
throw new Error('No active assignment is available for this ticket.');
|
|
}
|
|
return ticketService.requestTicketExtension(
|
|
ticketId,
|
|
assignmentId,
|
|
payload,
|
|
);
|
|
},
|
|
onSuccess: () => {
|
|
toast.success('Extension request submitted');
|
|
queryClient.invalidateQueries({
|
|
queryKey: ticketKeys.extensionRequest(ticketId, assignmentId),
|
|
});
|
|
queryClient.invalidateQueries({
|
|
queryKey: ticketKeys.classDetail(ticketId, defectClass),
|
|
});
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
},
|
|
onError: () => toast.error('Failed to request an extension'),
|
|
});
|
|
}
|
|
export function useReviewTicketExtensionMutation(
|
|
ticketId: string,
|
|
assignmentId: number,
|
|
defectClass: string,
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: ReviewTicketExtensionPayload) =>
|
|
ticketService.reviewTicketExtension(ticketId, assignmentId, payload),
|
|
onSuccess: (_response, payload) => {
|
|
toast.success(
|
|
payload.action === 'approve'
|
|
? 'Extension request approved'
|
|
: 'Extension request rejected',
|
|
);
|
|
queryClient.invalidateQueries({
|
|
queryKey: ticketKeys.extensionRequest(ticketId, assignmentId),
|
|
});
|
|
queryClient.invalidateQueries({
|
|
queryKey: ticketKeys.classDetail(ticketId, defectClass),
|
|
});
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
},
|
|
onError: () => toast.error('Failed to review extension request'),
|
|
});
|
|
}
|
|
export function useReviewRepairMutation(ticketId: string) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: ReviewRepairPayload) =>
|
|
ticketService.reviewRepair(ticketId, payload),
|
|
onSuccess: (ticket, payload) => {
|
|
toast.success(
|
|
payload.action === 'approve' ? 'Repair approved' : 'Repair rejected',
|
|
);
|
|
queryClient.setQueryData<TicketDetail>(
|
|
ticketKeys.classDetail(ticketId, payload.defect_class),
|
|
(current) => mergeTicketDetailResponse(current, ticket),
|
|
);
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
},
|
|
onError: () => toast.error('Failed to review repair'),
|
|
});
|
|
}
|
|
|
|
export function useCloseTicketMutation(ticketId: string) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: CloseTicketPayload) =>
|
|
ticketService.closeTicket(ticketId, payload),
|
|
onSuccess: (ticket, payload) => {
|
|
toast.success('Ticket closed');
|
|
queryClient.setQueryData<TicketDetail>(
|
|
ticketKeys.classDetail(ticketId, payload.defect_class),
|
|
(current) => mergeTicketDetailResponse(current, ticket),
|
|
);
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
},
|
|
onError: () => toast.error('Failed to close ticket'),
|
|
});
|
|
}
|