From e8f2955acf934d75de9a232104a647adef849d6b Mon Sep 17 00:00:00 2001 From: "santasri.pachhal" Date: Wed, 29 Jul 2026 18:43:22 +0530 Subject: [PATCH] feat(ticket): scope issue preview to selected assignment lot --- .../components/TicketDetailHeader.tsx | 10 +++- .../components/TicketStatusActions.tsx | 51 +++++++++++++------ .../actions/OpenRepairReviewAction.tsx | 33 ++++-------- .../actions/RequestExtensionAction.tsx | 16 +++++- .../actions/RequestExtensionForm.tsx | 15 +++--- .../actions/ReviewExtensionRequestAction.tsx | 16 ++++-- .../actions/TicketExtensionRequestPanel.tsx | 21 ++++---- src/app/(modules)/ticket/[ticketId]/page.tsx | 21 ++++++-- .../ticket/hooks/useTicketQueries.ts | 6 +++ 9 files changed, 121 insertions(+), 68 deletions(-) diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx index 5825408..e0a260a 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx @@ -6,14 +6,17 @@ import { useRouter } from 'next/navigation'; import { Button } from '@/components/ui/button'; import { PERMISSIONS } from '@/constants/permissions'; import { PermissionGuard } from '@/guards'; -import type { TicketOverviewDetail } from '@/types'; +import type { TicketDetail, TicketOverviewDetail } from '@/types'; import { AssignTicketAction } from './actions/AssignTicketAction'; +import { OpenRepairReviewAction } from './actions/OpenRepairReviewAction'; export function TicketDetailHeader({ ticket, + classDetail, }: { ticket: TicketOverviewDetail; + classDetail?: TicketDetail; }) { const router = useRouter(); const ticketLabel = ticket.ticket_name || ticket.id; @@ -26,6 +29,11 @@ export function TicketDetailHeader({
+ {classDetail?.assignment_status === 'under_review' ? ( + + + + ) : null} diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx index 8b3d8e9..a8d691a 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx @@ -2,25 +2,24 @@ import { PERMISSIONS } from '@/constants/permissions'; import { PermissionGuard } from '@/guards'; -import type { TicketDetail } from '@/types'; +import type { TicketAssignmentSummary, TicketDetail } from '@/types'; import { AssignedContractorAction } from './actions/AssignedContractorAction'; import { ClosedTicketAction } from './actions/ClosedTicketAction'; import { NoTicketAction } from './actions/NoTicketAction'; -import { OpenRepairReviewAction } from './actions/OpenRepairReviewAction'; import { RequestExtensionAction } from './actions/RequestExtensionAction'; import { ReviewExtensionRequestAction } from './actions/ReviewExtensionRequestAction'; interface TicketStatusActionsProps { ticket: TicketDetail; + assignment?: TicketAssignmentSummary; } -function getTicketAction(ticket: TicketDetail) { - if (ticket.assignment_status === 'unassigned') { - return null; - } - - if (ticket.assignment_status === 'assigned') { +function getTicketAction( + ticket: TicketDetail, + assignment?: TicketAssignmentSummary, +) { + if (assignment && !assignment.is_complete) { return ( } > - + } > <> - - + {ticket.assignment_status === 'assigned' ? ( + + ) : null} + ); } - if (ticket.assignment_status === 'under_review') { + if (ticket.assignment_status === 'unassigned') { + return null; + } + + if (ticket.assignment_status === 'assigned') { return ( } > - + ); } + if (ticket.assignment_status === 'under_review') { + return null; + } + if ( ticket.assignment_status === 'approved' || ticket.assignment_status === 'rejected' @@ -62,8 +78,11 @@ function getTicketAction(ticket: TicketDetail) { return ; } -export function TicketStatusActions({ ticket }: TicketStatusActionsProps) { - const action = getTicketAction(ticket); +export function TicketStatusActions({ + ticket, + assignment, +}: TicketStatusActionsProps) { + const action = getTicketAction(ticket, assignment); return action ?
{action}
: null; } diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/OpenRepairReviewAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/OpenRepairReviewAction.tsx index 24963d5..72ae0a1 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/OpenRepairReviewAction.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/OpenRepairReviewAction.tsx @@ -1,37 +1,24 @@ 'use client'; import { useRouter } from 'next/navigation'; -import { ArrowRight, ClipboardCheck } from 'lucide-react'; +import { ClipboardCheck } from 'lucide-react'; import { Button } from '@/components/ui/button'; import type { TicketDetail } from '@/types'; import { ROUTES } from '@/utils/routes'; -import { TicketActionCard } from './TicketActionCard'; - export function OpenRepairReviewAction({ ticket }: { ticket: TicketDetail }) { const router = useRouter(); return ( - -
-

- Review every submitted repair separately using its detection image, - location, and repair proof. -

- -
-
+ ); } diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionAction.tsx index 9ffe907..48ba9a8 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionAction.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionAction.tsx @@ -5,10 +5,22 @@ import type { TicketDetail } from '@/types'; import { TicketActionCard } from './TicketActionCard'; import { TicketExtensionRequestPanel } from './TicketExtensionRequestPanel'; -export function RequestExtensionAction({ ticket }: { ticket: TicketDetail }) { +export function RequestExtensionAction({ + ticket, + assignmentId, + dueAt, +}: { + ticket: TicketDetail; + assignmentId: number; + dueAt: string | null; +}) { return ( - + ); } diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionForm.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionForm.tsx index 0bc0c88..3324992 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionForm.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/RequestExtensionForm.tsx @@ -16,10 +16,15 @@ const MAX_REASON_LENGTH = 300; interface RequestExtensionFormProps { ticket: TicketDetail; + assignmentId: number; + currentDueAt: string | null; } -export function RequestExtensionForm({ ticket }: RequestExtensionFormProps) { - const currentDueAt = ticket.worker?.due_at; +export function RequestExtensionForm({ + ticket, + assignmentId, + currentDueAt, +}: RequestExtensionFormProps) { const currentDueDate = useMemo( () => (currentDueAt ? new Date(currentDueAt) : undefined), [currentDueAt], @@ -36,7 +41,6 @@ export function RequestExtensionForm({ ticket }: RequestExtensionFormProps) { ); const [requestedDueDate, setRequestedDueDate] = useState(); const [reason, setReason] = useState(''); - const assignmentId = ticket.worker?.assignment_id; const requestExtensionMutation = useRequestTicketExtensionMutation( ticket.id, assignmentId, @@ -48,10 +52,7 @@ export function RequestExtensionForm({ ticket }: RequestExtensionFormProps) { (!currentDueDate || requestedDueDate.getTime() > currentDueDate.getTime()), ); const canSubmit = - Boolean(assignmentId) && - isRequestedDateValid && - Boolean(reason.trim()) && - !isPending; + isRequestedDateValid && Boolean(reason.trim()) && !isPending; const handleSubmit = async () => { if (!requestedDueDate || !canSubmit) return; diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/ReviewExtensionRequestAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/ReviewExtensionRequestAction.tsx index fa18c36..9da0b5b 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/ReviewExtensionRequestAction.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/ReviewExtensionRequestAction.tsx @@ -77,16 +77,17 @@ function Requester({ request }: { request: TicketExtensionRequest }) { function ExtensionReviewForm({ ticket, + assignmentId, request, }: { ticket: TicketDetail; + assignmentId: number; request: TicketExtensionRequest; }) { const [note, setNote] = useState(''); const [selectedAction, setSelectedAction] = useState< 'approve' | 'reject' | null >(null); - const assignmentId = ticket.worker?.assignment_id as number; const reviewMutation = useReviewTicketExtensionMutation( ticket.id, assignmentId, @@ -197,16 +198,17 @@ function ExtensionReviewForm({ export function ReviewExtensionRequestAction({ ticket, + assignmentId, }: { ticket: TicketDetail; + assignmentId: number; }) { - const assignmentId = ticket.worker?.assignment_id; const extensionRequestQuery = useTicketExtensionRequestQuery( ticket.id, assignmentId, ); - if (!assignmentId || extensionRequestQuery.isError) { + if (extensionRequestQuery.isError) { return null; } @@ -227,5 +229,11 @@ export function ReviewExtensionRequestAction({ if (!pendingRequest) return null; - return ; + return ( + + ); } diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/TicketExtensionRequestPanel.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/TicketExtensionRequestPanel.tsx index 6c3d392..9e48ef2 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/TicketExtensionRequestPanel.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/TicketExtensionRequestPanel.tsx @@ -187,23 +187,18 @@ function ExtensionRequestDetails({ export function TicketExtensionRequestPanel({ ticket, + assignmentId, + dueAt, }: { ticket: TicketDetail; + assignmentId: number; + dueAt: string | null; }) { - const assignmentId = ticket.worker?.assignment_id; const extensionRequestQuery = useTicketExtensionRequestQuery( ticket.id, assignmentId, ); - if (!assignmentId) { - return ( -
- No active assignment is available for this ticket. -
- ); - } - if (extensionRequestQuery.isLoading) { return (
@@ -237,7 +232,13 @@ export function TicketExtensionRequestPanel({ : null; if (!latestRequest) { - return ; + return ( + + ); } return ; diff --git a/src/app/(modules)/ticket/[ticketId]/page.tsx b/src/app/(modules)/ticket/[ticketId]/page.tsx index 6f39459..bec3a99 100644 --- a/src/app/(modules)/ticket/[ticketId]/page.tsx +++ b/src/app/(modules)/ticket/[ticketId]/page.tsx @@ -23,6 +23,7 @@ import { TicketStatusActions } from './components/TicketStatusActions'; import { TicketDefectClassTabs } from './components/TicketDefectClassTabs'; import { useTicketDetailEvents } from '../hooks/useTicketDetailEvents'; import { + useTicketAssignmentsQuery, useTicketClassDetailQuery, useTicketOverviewQuery, } from '../hooks/useTicketQueries'; @@ -35,11 +36,18 @@ export default function TicketDetailPage() { const [selectedAssignmentId, setSelectedAssignmentId] = useState(); const overviewQuery = useTicketOverviewQuery(ticketId); - const classDetailQuery = useTicketClassDetailQuery(ticketId, defectClass); const overview = overviewQuery.data; + const assignmentsQuery = useTicketAssignmentsQuery( + ticketId, + overview?.assignments, + ); + const classDetailQuery = useTicketClassDetailQuery(ticketId, defectClass); const classDetail = classDetailQuery.data; - const activeAssignmentId = - selectedAssignmentId ?? overview?.assignments?.[0]?.id; + const assignments = assignmentsQuery.data?.items ?? []; + const activeAssignmentId = selectedAssignmentId ?? assignments[0]?.id; + const activeAssignment = assignments.find( + (assignment) => assignment.id === activeAssignmentId, + ); const isClassDetailLoading = Boolean( defectClass && classDetailQuery.isLoading, ); @@ -66,7 +74,7 @@ export default function TicketDetailPage() {
{overview ? ( - + ) : ( )} @@ -101,7 +109,10 @@ export default function TicketDetailPage() {
{isClassDetailLoading ? : null} {!isClassDetailLoading && classDetail ? ( - + ) : null}
diff --git a/src/app/(modules)/ticket/hooks/useTicketQueries.ts b/src/app/(modules)/ticket/hooks/useTicketQueries.ts index f9c8e16..0cb5ee4 100644 --- a/src/app/(modules)/ticket/hooks/useTicketQueries.ts +++ b/src/app/(modules)/ticket/hooks/useTicketQueries.ts @@ -405,6 +405,9 @@ export function useRequestTicketExtensionMutation( queryClient.invalidateQueries({ queryKey: ticketKeys.classDetail(ticketId, defectClass), }); + queryClient.invalidateQueries({ + queryKey: ticketKeys.assignments(ticketId), + }); queryClient.invalidateQueries({ queryKey: ticketKeys.lists() }); }, onError: () => toast.error('Failed to request an extension'), @@ -432,6 +435,9 @@ export function useReviewTicketExtensionMutation( queryClient.invalidateQueries({ queryKey: ticketKeys.classDetail(ticketId, defectClass), }); + queryClient.invalidateQueries({ + queryKey: ticketKeys.assignments(ticketId), + }); queryClient.invalidateQueries({ queryKey: ticketKeys.lists() }); }, onError: () => toast.error('Failed to review extension request'),