From 9cf4f0e79560cd28f3d1ce0587f98c2db7ba4ed0 Mon Sep 17 00:00:00 2001 From: "santasri.pachhal" Date: Fri, 3 Jul 2026 10:30:59 +0530 Subject: [PATCH] feat(ticket): redesign overview with AI summary, location, and shared defect visuals --- .../components/TicketDetailHeader.tsx | 14 - .../components/TicketOverviewCard.tsx | 270 +++++++++++++++--- src/app/(modules)/ticket/[ticketId]/page.tsx | 2 +- src/components/video/currentDetectionBar.tsx | 2 +- src/components/video/resultStatsGrid.tsx | 49 ++-- .../video => constants}/defectVisualConfig.ts | 17 +- src/types/ticket/detail.ts | 30 ++ 7 files changed, 309 insertions(+), 75 deletions(-) rename src/{components/video => constants}/defectVisualConfig.ts (77%) diff --git a/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx b/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx index a41ddc3..082c1cc 100644 --- a/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx +++ b/src/app/(modules)/ticket/[ticketId]/components/TicketDetailHeader.tsx @@ -4,9 +4,6 @@ import { ArrowLeft } from 'lucide-react'; import { useRouter } from 'next/navigation'; import { Button } from '@/components/ui/button'; -import { SparkleButton } from '@/components/ui/sparkle-button'; -import { PERMISSIONS } from '@/constants/permissions'; -import { PermissionGuard } from '@/guards'; import type { TicketOverviewDetail } from '@/types'; export function TicketDetailHeader({ @@ -25,17 +22,6 @@ export function TicketDetailHeader({
- {ticket.video_id ? ( - - router.push(`/results/${ticket.video_id}`)} - > - View Analysis - - - ) : null} - + + + ) : null} + + +
+ + {ticket.ai_result.detections_by_class.map((item) => { + const visual = getDefectVisual(item.class_name); + return ( + + ); + })} +
+
+ + + +
+
+ + + + + + + Ticket Overview + + + + + + + + +
); } diff --git a/src/app/(modules)/ticket/[ticketId]/page.tsx b/src/app/(modules)/ticket/[ticketId]/page.tsx index 60b8bc5..420c87f 100644 --- a/src/app/(modules)/ticket/[ticketId]/page.tsx +++ b/src/app/(modules)/ticket/[ticketId]/page.tsx @@ -69,7 +69,7 @@ export default function TicketDetailPage() {
{overview ? ( - + ) : ( )} diff --git a/src/components/video/currentDetectionBar.tsx b/src/components/video/currentDetectionBar.tsx index e45556c..244ff85 100644 --- a/src/components/video/currentDetectionBar.tsx +++ b/src/components/video/currentDetectionBar.tsx @@ -4,7 +4,7 @@ import { Activity, MapPin } from 'lucide-react'; import type { CompletedVideoResult, DetectionResultLog } from '@/types'; -import { getDefectVisual } from './defectVisualConfig'; +import { getDefectVisual } from '@/constants/defectVisualConfig'; interface CurrentDetectionBarProps { activeLog?: DetectionResultLog; diff --git a/src/components/video/resultStatsGrid.tsx b/src/components/video/resultStatsGrid.tsx index ff2efd7..047ba58 100644 --- a/src/components/video/resultStatsGrid.tsx +++ b/src/components/video/resultStatsGrid.tsx @@ -1,9 +1,10 @@ 'use client'; import { Activity, Clock, Gauge } from 'lucide-react'; -import { CompletedVideoResult } from '@/types'; -import { getDefectVisual } from './defectVisualConfig'; +import { getDefectVisual } from '@/constants/defectVisualConfig'; +import { cn } from '@/lib/utils'; +import type { CompletedVideoResult } from '@/types'; const formatDuration = (seconds?: number) => { if (typeof seconds !== 'number' || !Number.isFinite(seconds)) return '-'; @@ -28,8 +29,8 @@ export default function ResultStatsGrid({ data }: ResultStatsGridProps) { label: item.display_name, value: item.count, icon: visual.icon, - color: visual.colorClassName, - bgColor: visual.iconClassName, + colorClassName: visual.colorClassName, + iconClassName: visual.iconClassName, }; }); @@ -38,23 +39,23 @@ export default function ResultStatsGrid({ data }: ResultStatsGridProps) { label: 'Total Detections', value: data.summary.total_detections, icon: Activity, - color: 'text-green-500', - bgColor: 'bg-green-500/10 text-green-500', + colorClassName: 'text-green-500', + iconClassName: 'bg-green-500/10 text-green-500', }, ...defectStats, { label: 'FPS', value: formatFps(data.summary.fps), icon: Gauge, - color: 'text-purple-500', - bgColor: 'bg-purple-500/10 text-purple-500', + colorClassName: 'text-purple-500', + iconClassName: 'bg-purple-500/10 text-purple-500', }, { label: 'Duration', value: formatDuration(data.summary.duration_seconds), icon: Clock, - color: 'text-cyan-500', - bgColor: 'bg-cyan-500/10 text-cyan-500', + colorClassName: 'text-cyan-500', + iconClassName: 'bg-cyan-500/10 text-cyan-500', }, ]; @@ -66,17 +67,29 @@ export default function ResultStatsGrid({ data }: ResultStatsGridProps) { return (
-
- +
+

+ {stat.value} +

+ + +
-
- {stat.value} -
-
+

{stat.label} -

+

); })} diff --git a/src/components/video/defectVisualConfig.ts b/src/constants/defectVisualConfig.ts similarity index 77% rename from src/components/video/defectVisualConfig.ts rename to src/constants/defectVisualConfig.ts index d3e4bf3..3149b5f 100644 --- a/src/components/video/defectVisualConfig.ts +++ b/src/constants/defectVisualConfig.ts @@ -2,11 +2,11 @@ import { Activity, - AlertTriangle, - CircleAlert, + Construction, + Spline, CircleDot, Droplets, - LucideIcon, + type LucideIcon, SignpostBig, } from 'lucide-react'; @@ -14,6 +14,7 @@ type DefectVisualConfig = { icon: LucideIcon; colorClassName: string; iconClassName: string; + cardClassName: string; }; const DEFECT_VISUALS: Record = { @@ -21,26 +22,31 @@ const DEFECT_VISUALS: Record = { icon: CircleDot, colorClassName: 'text-zinc-500', iconClassName: 'bg-zinc-500/10 text-zinc-500', + cardClassName: 'bg-zinc-500/5', }, pothole: { - icon: AlertTriangle, + icon: Construction, colorClassName: 'text-orange-500', iconClassName: 'bg-orange-500/10 text-orange-500', + cardClassName: 'bg-orange-500/5', }, road_crack: { - icon: CircleAlert, + icon: Spline, colorClassName: 'text-rose-500', iconClassName: 'bg-rose-500/10 text-rose-500', + cardClassName: 'bg-rose-500/5', }, sign_board: { icon: SignpostBig, colorClassName: 'text-blue-500', iconClassName: 'bg-blue-500/10 text-blue-500', + cardClassName: 'bg-blue-500/5', }, water_puddle: { icon: Droplets, colorClassName: 'text-cyan-500', iconClassName: 'bg-cyan-500/10 text-cyan-500', + cardClassName: 'bg-cyan-500/5', }, }; @@ -48,6 +54,7 @@ const DEFAULT_VISUAL: DefectVisualConfig = { icon: Activity, colorClassName: 'text-green-500', iconClassName: 'bg-green-500/10 text-green-500', + cardClassName: 'bg-green-500/5', }; export const getDefectVisual = (className: string) => diff --git a/src/types/ticket/detail.ts b/src/types/ticket/detail.ts index be2430f..30d4ff4 100644 --- a/src/types/ticket/detail.ts +++ b/src/types/ticket/detail.ts @@ -80,12 +80,41 @@ export interface TicketTimestamps { updated_at: string; } +export interface TicketLocationReference { + id: string; + name: string; +} + +export interface TicketLocation { + project: TicketLocationReference; + package: TicketLocationReference; + chainage: TicketLocationReference; +} + +export interface TicketVideoMetadata { + duration_seconds: number; + fps: number; + resolution: { + width: number; + height: number; + label: string; + }; +} + +export interface TicketDetectionClassCount { + class_name: string; + display_name: string; + count: number; +} + export interface TicketOverviewDetail { id: string; ticket_name: string; video_id: string; chainage_id: string; chainage_name: string; + location: TicketLocation; + video_metadata: TicketVideoMetadata; uploader: { user_id: number; name: string; @@ -95,6 +124,7 @@ export interface TicketOverviewDetail { detection_count: number; completed_at: string; available_defect_classes: string[]; + detections_by_class: TicketDetectionClassCount[]; }; timestamps: TicketTimestamps; }