125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
'use client';
|
|
|
|
import { useCallback, useState } from 'react';
|
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { toast } from 'sonner';
|
|
|
|
import { ticketService } from '@/services/api';
|
|
import type { ReviewTicketExtensionPayload } from '@/types';
|
|
|
|
import { ticketKeys } from '../../ticket/queries/ticketKeys';
|
|
import { extensionRequestKeys } from '../queries/extensionRequestKeys';
|
|
|
|
export const MAX_EXTENSION_DECISION_NOTE_LENGTH = 300;
|
|
|
|
export type ExtensionRequestDecisionAction = 'approve' | 'reject';
|
|
|
|
interface UseExtensionRequestDecisionOptions {
|
|
ticketId: string;
|
|
assignmentId: number;
|
|
onSuccess?: () => void;
|
|
}
|
|
|
|
export function useExtensionRequestDecision({
|
|
ticketId,
|
|
assignmentId,
|
|
onSuccess,
|
|
}: UseExtensionRequestDecisionOptions) {
|
|
const queryClient = useQueryClient();
|
|
const [note, setNoteValue] = useState('');
|
|
const [noteError, setNoteError] = useState<string | null>(null);
|
|
const [selectedAction, setSelectedAction] =
|
|
useState<ExtensionRequestDecisionAction | null>(null);
|
|
const [isRejectConfirmationOpen, setIsRejectConfirmationOpen] =
|
|
useState(false);
|
|
|
|
const reviewMutation = 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: extensionRequestKeys.lists(),
|
|
});
|
|
queryClient.invalidateQueries({
|
|
queryKey: ticketKeys.assignments(ticketId),
|
|
});
|
|
queryClient.invalidateQueries({
|
|
queryKey: ticketKeys.timeline(ticketId),
|
|
});
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
|
|
queryClient.invalidateQueries({ queryKey: ticketKeys.summaries() });
|
|
onSuccess?.();
|
|
},
|
|
onError: () => toast.error('Failed to review extension request'),
|
|
});
|
|
|
|
const setNote = useCallback((value: string) => {
|
|
const nextNote = value.slice(0, MAX_EXTENSION_DECISION_NOTE_LENGTH);
|
|
setNoteValue(nextNote);
|
|
if (nextNote.trim()) setNoteError(null);
|
|
}, []);
|
|
|
|
const submitDecision = useCallback(
|
|
async (action: ExtensionRequestDecisionAction) => {
|
|
const trimmedNote = note.trim();
|
|
|
|
if (action === 'reject' && !trimmedNote) {
|
|
setNoteError('Manager comments are required to reject this request.');
|
|
setIsRejectConfirmationOpen(false);
|
|
return;
|
|
}
|
|
|
|
setSelectedAction(action);
|
|
try {
|
|
await reviewMutation.mutateAsync({
|
|
action,
|
|
...(trimmedNote ? { note: trimmedNote } : {}),
|
|
});
|
|
} catch {
|
|
// The mutation displays the error toast and leaves the form open.
|
|
} finally {
|
|
setSelectedAction(null);
|
|
}
|
|
},
|
|
[note, reviewMutation],
|
|
);
|
|
|
|
const requestRejection = useCallback(() => {
|
|
if (!note.trim()) {
|
|
setNoteError('Manager comments are required to reject this request.');
|
|
return;
|
|
}
|
|
|
|
setNoteError(null);
|
|
setIsRejectConfirmationOpen(true);
|
|
}, [note]);
|
|
|
|
const resetDecision = useCallback(() => {
|
|
setNoteValue('');
|
|
setNoteError(null);
|
|
setSelectedAction(null);
|
|
setIsRejectConfirmationOpen(false);
|
|
}, []);
|
|
|
|
return {
|
|
note,
|
|
setNote,
|
|
noteError,
|
|
selectedAction,
|
|
isPending: reviewMutation.isPending,
|
|
isRejectConfirmationOpen,
|
|
setIsRejectConfirmationOpen,
|
|
submitDecision,
|
|
requestRejection,
|
|
resetDecision,
|
|
};
|
|
}
|