'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(null); const [selectedAction, setSelectedAction] = useState(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, }; }