From 88d3bd58bc61a71a31a407e8ee7e653682d97bfc Mon Sep 17 00:00:00 2001 From: "santasri.pachhal" Date: Thu, 25 Jun 2026 14:15:10 +0530 Subject: [PATCH] feat: assign base --- next-env.d.ts | 2 +- opencode.json | 10 + .../components/TicketOverviewCard.tsx | 87 ++-- .../components/TicketRepairReportCard.tsx | 19 +- .../components/TicketStatusActions.tsx | 36 +- .../components/actions/AssignTicketAction.tsx | 65 +-- .../components/actions/ClosedTicketAction.tsx | 25 +- .../components/actions/RepairEvidenceForm.tsx | 17 +- .../components/actions/ReviewRepairAction.tsx | 60 +-- .../components/actions/SubmitRepairAction.tsx | 13 +- .../actions/TicketAssignmentSheet.tsx | 465 ++++++++++++++++++ .../repair-report/RepairReportPanel.tsx | 55 ++- .../repair-report/RepairVideoComparison.tsx | 9 +- .../ticket/components/TicketColumns.tsx | 26 +- .../hooks/useTenantTicketTableEvents.ts | 18 +- .../ticket/hooks/useTicketDetailEvents.ts | 4 +- .../ticket/hooks/useTicketQueries.ts | 78 +-- .../(modules)/ticket/queries/ticketKeys.ts | 4 + .../lookups/AssignableWorkerSelect.tsx | 2 + .../lookups/AssignableWorkerSelect.types.ts | 3 + src/constants/apiRoutes.ts | 17 +- src/services/api/ticket.service.ts | 96 +++- src/services/axios/axios.ts | 2 +- src/types/ticket/actions.ts | 69 ++- src/types/ticket/detail.ts | 25 +- src/types/ticket/events.ts | 10 +- src/types/ticket/list.ts | 4 +- 27 files changed, 893 insertions(+), 328 deletions(-) create mode 100644 opencode.json create mode 100644 src/app/(modules)/ticket/[ticketId]/components/actions/TicketAssignmentSheet.tsx diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..6b8b42d --- /dev/null +++ b/opencode.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "dual-graph": { + "type": "remote", + "url": "http://127.0.0.1:8080/mcp", + "enabled": true + } + } +} diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketOverviewCard.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketOverviewCard.tsx index af2693e..82389e3 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketOverviewCard.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketOverviewCard.tsx @@ -1,12 +1,8 @@ 'use client'; -import type { TicketActor, TicketDetail } from '@/types'; +import type { TicketDetail } from '@/types'; import { formatDate } from '@/utils/date'; -import { - TicketStatusBadge, -} from '../../components/TicketStatusBadge'; - function formatCreatedDate(value: string | null | undefined) { if (!value) return '-'; return new Intl.DateTimeFormat('en-IN', { dateStyle: 'medium' }).format( @@ -14,20 +10,6 @@ function formatCreatedDate(value: string | null | undefined) { ); } -function getInitials(person: TicketActor | null | undefined) { - const source = person?.name || person?.email || '?'; - return source - .split(/[\s@._-]+/) - .filter(Boolean) - .slice(0, 2) - .map((part) => part.charAt(0).toUpperCase()) - .join(''); -} - -function getPersonLabel(person: TicketActor | null | undefined) { - return person?.name || person?.email || '-'; -} - function MetaLabel({ children }: { children: React.ReactNode }) { return (

@@ -40,29 +22,6 @@ function MetaValue({ children }: { children: React.ReactNode }) { return

{children}

; } -function PersonMetaValue({ - person, - accentClassName = 'bg-primary/15 text-primary', -}: { - person: TicketActor | null | undefined; - accentClassName?: string; -}) { - if (!person) { - return -; - } - - return ( -
- - {getInitials(person)} - -

{getPersonLabel(person)}

-
- ); -} - function OverviewField({ label, children, @@ -80,6 +39,22 @@ function OverviewField({ export function TicketOverviewCard({ ticket }: { ticket: TicketDetail }) { const detectionCount = ticket.ai_result?.detection_count; + const assignments = ticket.assignments ?? []; + const primaryAssignment = assignments[0] ?? null; + const assignedNames = Array.from( + new Set( + assignments + .map((assignment) => assignment.assigned_to_name) + .filter(Boolean), + ), + ); + const reviewerNames = Array.from( + new Set( + assignments + .map((assignment) => assignment.reviewed_by_name) + .filter(Boolean), + ), + ); return (
@@ -101,7 +76,9 @@ export function TicketOverviewCard({ ticket }: { ticket: TicketDetail }) { - + + {ticket.uploader?.name || ticket.uploader?.email || '-'} + @@ -109,21 +86,27 @@ export function TicketOverviewCard({ ticket }: { ticket: TicketDetail }) { - + + {assignedNames.length ? assignedNames.join(', ') : '-'} + - + + {reviewerNames.length ? reviewerNames.join(', ') : '-'} + - {formatDate(ticket.worker?.assigned_at)} + {formatDate(primaryAssignment?.assigned_at)} + + + + + {ticket.coverage + ? `${ticket.coverage.assigned_count}/${ticket.coverage.total_count} Classes` + : '-'} + diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketRepairReportCard.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketRepairReportCard.tsx index 5db9f75..7eaaa7b 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketRepairReportCard.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketRepairReportCard.tsx @@ -9,12 +9,23 @@ import { RepairReportPanel } from './repair-report/RepairReportPanel'; import { RepairVideoComparison } from './repair-report/RepairVideoComparison'; export function TicketRepairReportCard({ ticket }: { ticket: TicketDetail }) { - const repair = ticket.repair; + const repairedAssignments = (ticket.assignments ?? []).filter( + (assignment) => + assignment.repair_submitted_at || + assignment.repair_notes || + assignment.repair_proof_video_url || + assignment.repair_proof_image_urls.length, + ); - if (!repair) { + if (!repairedAssignments.length) { return null; } + const imageUrls = repairedAssignments.flatMap( + (assignment) => assignment.repair_proof_image_urls, + ); + const submittedAt = repairedAssignments[0]?.repair_submitted_at ?? null; + return (
@@ -28,8 +39,8 @@ export function TicketRepairReportCard({ ticket }: { ticket: TicketDetail }) {
diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx index e0a08a3..0434728 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketStatusActions.tsx @@ -9,8 +9,6 @@ import { AssignTicketAction } from './actions/AssignTicketAction'; import { ClosedTicketAction } from './actions/ClosedTicketAction'; import { NoTicketAction } from './actions/NoTicketAction'; import { ProcessingTicketAction } from './actions/ProcessingTicketAction'; -import { ReviewRepairAction } from './actions/ReviewRepairAction'; -import { SubmitRepairAction } from './actions/SubmitRepairAction'; interface TicketStatusActionsProps { ticket: TicketDetail; @@ -30,10 +28,18 @@ export function TicketStatusActions({ ); } - if (ticket.status === 'unassigned') { + if ( + ticket.status === 'unassigned' || + ticket.status === 'assigned' || + ticket.status === 'under_review' + ) { return ( } > @@ -41,28 +47,6 @@ export function TicketStatusActions({ ); } - if (ticket.status === 'assigned') { - return ( - } - > - - - ); - } - - if (ticket.status === 'under_review') { - return ( - } - > - - - ); - } - if (ticket.status === 'closed') { return ; } diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/AssignTicketAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/AssignTicketAction.tsx index d721abd..df092b0 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/AssignTicketAction.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/AssignTicketAction.tsx @@ -1,57 +1,44 @@ 'use client'; import { useState } from 'react'; -import { Send, UserPlus } from 'lucide-react'; +import { UserPlus } from 'lucide-react'; import { Button } from '@/components/ui/button'; -import { Label } from '@/components/ui/label'; -import { Textarea } from '@/components/ui/textarea'; -import { AssignableWorkerSelect } from '@/components/lookups/AssignableWorkerSelect'; import type { TicketDetail } from '@/types'; -import { useAssignTicketMutation } from '../../../hooks/useTicketQueries'; +import { TicketAssignmentSheet } from './TicketAssignmentSheet'; import { TicketActionCard } from './TicketActionCard'; export function AssignTicketAction({ ticket }: { ticket: TicketDetail }) { - const [selectedUserId, setSelectedUserId] = useState(''); - const [assignNote, setAssignNote] = useState(''); - - const assignMutation = useAssignTicketMutation(ticket.id); + const [open, setOpen] = useState(false); + const coverage = ticket.coverage; return ( - +
-
- - -
-
- -