chore: class location change

This commit is contained in:
2026-07-07 12:02:29 +05:30
parent ba9731de86
commit d88978a830
5 changed files with 229 additions and 165 deletions

View File

@@ -2,22 +2,27 @@
import { useEffect, useMemo, useState } from 'react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { AlertTriangle, Component } from 'lucide-react';
import { CalendarCheck2, Component, FileVideo, ListChecks } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Card, CardContent } from '@/components/ui/card';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { DEFECT_CLASS_STATUS_CONFIG } from '@/constants/defectClassStatus';
import { getDefectVisual } from '@/constants/defectVisualConfig';
import { cn } from '@/lib/utils';
import type { TicketOverviewDetail } from '@/types';
import { formatDate } from '@/utils/date';
import { useTicketDefectClassesQuery } from '../../hooks/useTicketQueries';
interface TicketDefectClassTabsProps {
ticketId: string;
ticket?: TicketOverviewDetail;
}
export function TicketDefectClassTabs({
ticketId,
ticket,
}: TicketDefectClassTabsProps) {
const router = useRouter();
const pathname = usePathname();
@@ -56,20 +61,27 @@ export function TicketDefectClassTabs({
setSelectedClass(className);
router.replace(`${pathname}?${nextParams.toString()}`, { scroll: false });
};
const selectedIssue = ticket?.ai_result.detections_by_class.find(
(item) => item.class_name === selectedClass,
);
const selectedIssueVisual = getDefectVisual(selectedIssue?.class_name ?? '');
const SelectedIssueIcon = selectedIssueVisual.icon;
const completedAt = ticket?.ai_result.completed_at;
const videoName = ticket?.video?.name;
if (defectClassesQuery.isLoading) {
return (
<Card className="w-full">
<CardHeader>
<CardContent className="space-y-4 p-5">
<div className="h-5 w-24 animate-pulse rounded bg-muted" />
</CardHeader>
<CardContent className="flex flex-col gap-2">
<div className="flex flex-col gap-2">
{Array.from({ length: 3 }).map((_, index) => (
<div
key={index}
className="h-11 w-full animate-pulse rounded-lg bg-muted"
/>
))}
</div>
</CardContent>
</Card>
);
@@ -87,49 +99,136 @@ export function TicketDefectClassTabs({
className="w-full"
>
<Card className="w-full">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<AlertTriangle className="size-5 shrink-0 text-primary" />
<span>Issues</span>
<Badge
variant="secondary"
className="min-w-5 min-h-5 justify-center rounded-full"
>
{defectClasses.length}
</Badge>
</CardTitle>
</CardHeader>
<CardContent>
<TabsList className="flex h-auto w-full flex-col items-stretch gap-2 bg-transparent p-0">
{defectClasses.map((item) => {
const statusConfig =
DEFECT_CLASS_STATUS_CONFIG[item.assignment_status];
<CardContent className="grid gap-0 p-0 lg:grid-cols-[minmax(220px,280px)_1fr] lg:divide-x lg:divide-border">
<div className="space-y-4 p-5">
<div className="flex items-center gap-2">
<ListChecks className="size-5 shrink-0 text-primary" />
<h2 className="text-base font-semibold">Detected Issues</h2>
<Badge
variant="secondary"
className="min-w-5 min-h-5 justify-center rounded-full"
>
{defectClasses.length}
</Badge>
</div>
return (
<TabsTrigger
key={item.class_name}
value={item.class_name}
className={cn(
'group h-auto w-full flex-none items-center justify-between! gap-2 rounded-lg border border-border bg-muted/30 px-3 py-2.5 text-sm text-muted-foreground shadow-none after:hidden',
'data-[state=active]:bg-muted data-[state=active]:text-foreground',
)}
>
<span className="min-w-0 truncate text-left font-medium">
{item.display_name}
</span>
<Badge
<TabsList className="flex h-auto w-full flex-col items-stretch gap-2 bg-transparent p-0">
{defectClasses.map((item) => {
const statusConfig =
DEFECT_CLASS_STATUS_CONFIG[item.assignment_status];
return (
<TabsTrigger
key={item.class_name}
value={item.class_name}
className={cn(
'shrink-0 gap-1.5 rounded-full border-0 px-2.5 py-1 text-xs font-medium',
statusConfig.badgeClassName,
'group h-auto w-full flex-none items-center justify-between! gap-2 rounded-lg border border-border bg-muted/30 px-3 py-2.5 text-sm text-muted-foreground shadow-none after:hidden',
'data-[state=active]:bg-muted data-[state=active]:text-foreground',
)}
>
<Component className="size-3" />
{statusConfig.label}
</Badge>
</TabsTrigger>
);
})}
</TabsList>
<span className="min-w-0 truncate text-left font-medium">
{item.display_name}
</span>
<Badge
className={cn(
'shrink-0 gap-1.5 rounded-full border-0 px-2.5 py-1 text-xs font-medium',
statusConfig.badgeClassName,
)}
>
<Component className="size-3" />
{statusConfig.label}
</Badge>
</TabsTrigger>
);
})}
</TabsList>
</div>
<div className="min-w-0 p-5">
{selectedIssue ? (
<div className="space-y-4">
<div className="flex min-w-0 items-center gap-3">
<span
className={cn(
'flex size-10 shrink-0 items-center justify-center rounded-full',
selectedIssueVisual.cardClassName,
)}
>
<SelectedIssueIcon
className={cn(
'size-5',
selectedIssueVisual.colorClassName,
)}
/>
</span>
<div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1">
<h3 className="truncate text-lg font-semibold text-foreground">
{selectedIssue.display_name}
</h3>
<Badge
variant="secondary"
className={cn(
'shrink-0 rounded-full border-0 px-2.5 py-1 text-xs font-semibold',
selectedIssueVisual.cardClassName,
selectedIssueVisual.colorClassName,
)}
>
{selectedIssue.count} Detections
</Badge>
</div>
</div>
<div className="border-t pt-4">
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-1">
<p className="text-xs font-medium text-muted-foreground">
Current Issue
</p>
<p className="text-sm font-semibold text-foreground">
{selectedIssue.display_name}
</p>
</div>
<div className="space-y-1">
<p className="text-xs font-medium text-muted-foreground">
Current Issue Count
</p>
<p className="text-sm font-semibold text-violet-600">
{selectedIssue.count}
</p>
</div>
{completedAt ? (
<div className="space-y-1">
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
<CalendarCheck2 className="size-4" />
<span>AI Completed At</span>
</div>
<p className="text-sm font-semibold text-foreground">
{formatDate(completedAt)}
</p>
</div>
) : null}
{videoName ? (
<div className="space-y-1">
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
<FileVideo className="size-4" />
<span>Video</span>
</div>
<p className="truncate text-sm font-semibold text-foreground">
{videoName}
</p>
</div>
) : null}
</div>
</div>
</div>
) : (
<div className="flex min-h-48 items-center justify-center text-center">
<p className="text-sm font-medium text-muted-foreground">
Select an issue to view details.
</p>
</div>
)}
</div>
</CardContent>
</Card>
</Tabs>