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 ( - +
-
- - -
-
- -