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/src/app/(modules)/ticket/[ticketId]/components/DefectClassStatusLegend.tsx b/src/app/(modules)/ticket/[ticketId]/components/DefectClassStatusLegend.tsx deleted file mode 100644 index 7c69db2..0000000 --- a/src/app/(modules)/ticket/[ticketId]/components/DefectClassStatusLegend.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Component } from 'lucide-react'; - -import { cn } from '@/lib/utils'; -import type { TicketDefectClassTicketStatus } from '@/types'; - -export const DEFECT_CLASS_STATUS_CONFIG: Record< - TicketDefectClassTicketStatus, - { - label: string; - iconClassName: string; - } -> = { - unassigned: { - label: 'Unassigned', - iconClassName: 'text-zinc-500', - }, - assigned: { - label: 'Assigned', - iconClassName: 'text-red-500', - }, - under_review: { - label: 'Under Review', - iconClassName: 'text-violet-500', - }, - approved: { - label: 'Approved', - iconClassName: 'text-emerald-500', - }, - rejected: { - label: 'Rejected', - iconClassName: 'text-orange-500', - }, -}; - -export function DefectClassStatusLegend() { - return ( -
- {Object.entries(DEFECT_CLASS_STATUS_CONFIG).map(([status, item]) => ( - - - {item.label} - - ))} -
- ); -} diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketDefectClassTabs.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketDefectClassTabs.tsx index 9e67d68..ee788f9 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketDefectClassTabs.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketDefectClassTabs.tsx @@ -4,12 +4,13 @@ import { useEffect, useMemo, useState } from 'react'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; import { Component } from 'lucide-react'; -import { Card, CardContent } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { DEFECT_CLASS_STATUS_CONFIG } from '@/constants/defectClassStatus'; import { cn } from '@/lib/utils'; import { useTicketDefectClassesQuery } from '../../hooks/useTicketQueries'; -import { DEFECT_CLASS_STATUS_CONFIG } from './DefectClassStatusLegend'; interface TicketDefectClassTabsProps { ticketId: string; @@ -56,12 +57,15 @@ export function TicketDefectClassTabs({ if (defectClassesQuery.isLoading) { return ( - + + +
+ {Array.from({ length: 3 }).map((_, index) => (
))} @@ -80,27 +84,45 @@ export function TicketDefectClassTabs({ orientation="vertical" className="w-full" > - - + + + + Issues + + {defectClasses.length} + + + + - {defectClasses.map((item) => ( - - { + const statusConfig = + DEFECT_CLASS_STATUS_CONFIG[item.assignment_status]; + + return ( + - {item.display_name} - - ))} + > + + {item.display_name} + + + + {statusConfig.label} + + + ); + })} diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx index 131fda3..c3349e7 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx @@ -10,7 +10,6 @@ import { PermissionGuard } from '@/guards'; import type { TicketDetail } from '@/types'; import { TicketStatusBadge } from '../../components/TicketStatusBadge'; -import { DefectClassStatusLegend } from './DefectClassStatusLegend'; export function TicketDetailHeader({ ticket }: { ticket: TicketDetail }) { const router = useRouter(); @@ -26,7 +25,6 @@ export function TicketDetailHeader({ ticket }: { ticket: TicketDetail }) {

Ticket: {ticketLabel}

-
diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketDetailSkeleton.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketDetailSkeleton.tsx index 714c079..08eae16 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketDetailSkeleton.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketDetailSkeleton.tsx @@ -1,5 +1,6 @@ 'use client'; +import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; function OverviewSkeletonField() { @@ -53,50 +54,61 @@ export function TicketDetailSkeleton() {
-
-
+ + -
-
+ + {Array.from({ length: 8 }).map((_, index) => ( ))} -
-
+ +
-
- -
+ + + + + -
-
- + + + + + + + + + +
-
-
+ + -
-
+ + -
-
+ +
- + +
); diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketHistoryCard.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketHistoryCard.tsx index ea1c1a5..3bd4ef8 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketHistoryCard.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketHistoryCard.tsx @@ -2,6 +2,7 @@ import { History, MessageSquareText } from 'lucide-react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { cn } from '@/lib/utils'; import type { TicketDetail, TicketHistoryItem, TicketStatus } from '@/types'; import { formatDate } from '@/utils/date'; @@ -93,31 +94,33 @@ export function TicketHistoryCard({ ticket }: { ticket: TicketDetail }) { const history = ticket.history ?? []; return ( -
-
- -

+ + + + Audit & Lifecycle Timeline -

-
+ + - {history.length ? ( -
- {history.map((item, index) => ( - - ))} -
- ) : ( -
-

- No timeline entries yet -

-
- )} -
+ + {history.length ? ( +
+ {history.map((item, index) => ( + + ))} +
+ ) : ( +
+

+ No timeline entries yet +

+
+ )} +
+ ); } diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketOverviewCard.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketOverviewCard.tsx index 1643209..3735edd 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketOverviewCard.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketOverviewCard.tsx @@ -1,5 +1,12 @@ 'use client'; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; import type { TicketActor, TicketDetail } from '@/types'; import { formatDate } from '@/utils/date'; @@ -78,19 +85,19 @@ export function TicketOverviewCard({ ticket }: { ticket: TicketDetail }) { const detectionCount = ticket.ai_result?.detection_count; return ( -
-
-

Overview

-
+ + + Overview + -
+ {detectionCount != null ? `${detectionCount} Occurrences` : '-'} - + {ticket.chainage_name} @@ -103,13 +110,13 @@ export function TicketOverviewCard({ ticket }: { ticket: TicketDetail }) { {formatCreatedDate(ticket.timestamps.created_at)} -
+ {ticket.ai_result?.completed_at ? ( -
+ AI analysis completed {formatDate(ticket.ai_result.completed_at)} -
+ ) : null} -
+ ); } diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketRepairReportCard.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketRepairReportCard.tsx index 5db9f75..342324f 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketRepairReportCard.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketRepairReportCard.tsx @@ -2,6 +2,7 @@ import { ImageIcon } from 'lucide-react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import type { TicketDetail } from '@/types'; import { RepairProofImagesGrid } from './repair-report/RepairProofImagesGrid'; @@ -21,17 +22,21 @@ export function TicketRepairReportCard({ ticket }: { ticket: TicketDetail }) { -
-

- - Repair Images -

+ + + + + Repair Images + + - -
+ + + + ); } diff --git a/src/app/(modules)/ticket/[ticketId]/components/actions/ClosedTicketAction.tsx b/src/app/(modules)/ticket/[ticketId]/components/actions/ClosedTicketAction.tsx index d0777c2..e8aad22 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/actions/ClosedTicketAction.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/actions/ClosedTicketAction.tsx @@ -98,13 +98,13 @@ export function ClosedTicketAction({ ticket }: { ticket: TicketDetail }) { const reviewedAt = reviewer?.reviewed_at ?? getClosedAt(ticket); return ( - - + + Resolution Details Review record for {ticket.id} - +
- + {title} diff --git a/src/app/(modules)/ticket/[ticketId]/components/repair-report/RepairReportPanel.tsx b/src/app/(modules)/ticket/[ticketId]/components/repair-report/RepairReportPanel.tsx index b884944..2f8f70e 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/repair-report/RepairReportPanel.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/repair-report/RepairReportPanel.tsx @@ -1,7 +1,8 @@ 'use client'; -import { FileText, MessageSquareQuote, BadgeCheck } from 'lucide-react'; +import { FileText, MessageSquareQuote } from 'lucide-react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import type { TicketDetail } from '@/types'; import { @@ -38,67 +39,70 @@ export function RepairReportPanel({ ticket }: { ticket: TicketDetail }) { if (!repair) { return ( -
-
- -

Repair Report

-
- -
+ + + + + Repair Report + + + + + + ); } return ( -
-
-

+ + + Repair Report -

+ {repair.submitted_at ? ( - + Completed {formatRepairDate(repair.submitted_at)} ) : null} -
+ -
- - -
+ +
+ + +
-
- Repair Summary - {repair.notes?.trim() ? ( - <> +
+ Repair Summary + {repair.notes?.trim() ? (

{repair.notes}

- {/*
- - Self-certified by {getPersonLabel(ticket.worker)} -
*/} - - ) : ( - - )} -
-
+ ) : ( + + )} +
+
+
); } diff --git a/src/app/(modules)/ticket/[ticketId]/components/repair-report/RepairVideoComparison.tsx b/src/app/(modules)/ticket/[ticketId]/components/repair-report/RepairVideoComparison.tsx index 60c8b07..cf00314 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/repair-report/RepairVideoComparison.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/repair-report/RepairVideoComparison.tsx @@ -3,6 +3,7 @@ import { ShieldCheck } from 'lucide-react'; import { ProtectedVideoPlayer } from '@/components/media/ProtectedVideoPlayer'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import type { TicketDetail } from '@/types'; export function RepairVideoComparison({ ticket }: { ticket: TicketDetail }) { @@ -10,13 +11,15 @@ export function RepairVideoComparison({ ticket }: { ticket: TicketDetail }) { const repairVideoUrl = ticket.repair?.proof_video_url ?? null; return ( -
-

- - Repair Verification -

+ + + + + Repair Verification + + -
+ -
-
+ +
); } diff --git a/src/app/(modules)/ticket/[ticketId]/components/repair-report/repairReportUtils.tsx b/src/app/(modules)/ticket/[ticketId]/components/repair-report/repairReportUtils.tsx index 5235c58..0108762 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/repair-report/repairReportUtils.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/repair-report/repairReportUtils.tsx @@ -33,7 +33,7 @@ export function ReportSurface({ className?: string; }) { return ( -
+
{children}
); diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 3d8a3c1..b570017 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<'div'>) {
) {
) { return (
); @@ -65,7 +65,7 @@ function CardContent({ className, ...props }: React.ComponentProps<'div'>) { return (
); @@ -75,7 +75,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
); diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index b463afd..3b66ad1 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -65,9 +65,9 @@ function TabsTrigger({ data-slot="tabs-trigger" className={cn( "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", - "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent", - "data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground", - "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100", + "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent", + "data-[state=active]:border-primary data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-primary dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground", + "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:-bottom-1.25 group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100", className )} {...props} diff --git a/src/constants/defectClassStatus.ts b/src/constants/defectClassStatus.ts new file mode 100644 index 0000000..50d00a4 --- /dev/null +++ b/src/constants/defectClassStatus.ts @@ -0,0 +1,35 @@ +import type { TicketDefectClassTicketStatus } from '@/types'; + +export const DEFECT_CLASS_STATUS_CONFIG: Record< + TicketDefectClassTicketStatus, + { + label: string; + badgeClassName: string; + } +> = { + unassigned: { + label: 'Unassigned', + badgeClassName: + 'bg-zinc-500/15 text-zinc-600 dark:bg-zinc-500/20 dark:text-zinc-300', + }, + assigned: { + label: 'Assigned', + badgeClassName: + 'bg-red-500/15 text-red-600 dark:bg-red-500/20 dark:text-red-400', + }, + under_review: { + label: 'Under Review', + badgeClassName: + 'bg-violet-500/15 text-violet-600 dark:bg-violet-500/20 dark:text-violet-400', + }, + approved: { + label: 'Approved', + badgeClassName: + 'bg-emerald-500/15 text-emerald-600 dark:bg-emerald-500/20 dark:text-emerald-400', + }, + rejected: { + label: 'Rejected', + badgeClassName: + 'bg-orange-500/15 text-orange-600 dark:bg-orange-500/20 dark:text-orange-400', + }, +};