refactor: video section ui modification
This commit is contained in:
@@ -1,86 +1,138 @@
|
||||
'use client';
|
||||
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import type { TicketActor, TicketDetail } from '@/types';
|
||||
|
||||
import { TicketStatusBadge } from '../../components/TicketStatusBadge';
|
||||
|
||||
import { formatDate } from '@/utils/date';
|
||||
|
||||
function OverviewMeta({
|
||||
label,
|
||||
value,
|
||||
}: {
|
||||
label: string;
|
||||
value: string | number | null | undefined;
|
||||
}) {
|
||||
import {
|
||||
TicketStatusBadge,
|
||||
} from '../../components/TicketStatusBadge';
|
||||
|
||||
function formatCreatedDate(value: string | null | undefined) {
|
||||
if (!value) return '-';
|
||||
return new Intl.DateTimeFormat('en-IN', { dateStyle: 'medium' }).format(
|
||||
new Date(value),
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs text-muted-foreground">{label}</p>
|
||||
<p className="text-sm font-medium">{value ?? '-'}</p>
|
||||
<p className="text-xs font-medium tracking-wide text-muted-foreground">
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
function MetaValue({ children }: { children: React.ReactNode }) {
|
||||
return <p className="text-sm font-semibold text-foreground">{children}</p>;
|
||||
}
|
||||
|
||||
function PersonMetaValue({
|
||||
person,
|
||||
accentClassName = 'bg-primary/15 text-primary',
|
||||
}: {
|
||||
person: TicketActor | null | undefined;
|
||||
accentClassName?: string;
|
||||
}) {
|
||||
if (!person) {
|
||||
return <MetaValue>-</MetaValue>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span
|
||||
className={`flex size-5 shrink-0 items-center justify-center rounded-full text-[10px] font-bold ${accentClassName}`}
|
||||
>
|
||||
{getInitials(person)}
|
||||
</span>
|
||||
<p className="truncate text-sm font-medium">{getPersonLabel(person)}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PersonMeta({
|
||||
function OverviewField({
|
||||
label,
|
||||
person,
|
||||
children,
|
||||
}: {
|
||||
label: string;
|
||||
person: TicketActor | null | undefined;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs text-muted-foreground">{label}</p>
|
||||
{person ? (
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-sm font-medium">{person.name || '-'}</p>
|
||||
{person.email && (
|
||||
<p className="truncate text-xs text-muted-foreground">
|
||||
{person.email}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm font-medium">-</p>
|
||||
)}
|
||||
<MetaLabel>{label}</MetaLabel>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TicketOverviewCard({ ticket }: { ticket: TicketDetail }) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between gap-4">
|
||||
<CardTitle className="text-base">Overview</CardTitle>
|
||||
<TicketStatusBadge status={ticket.status} />
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid gap-5 md:grid-cols-[180px_minmax(0,1fr)]">
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs text-muted-foreground">AI Detections</p>
|
||||
<p className="text-3xl font-semibold leading-none">
|
||||
{ticket.ai_result?.detection_count ?? '-'}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Completed {formatDate(ticket.ai_result?.completed_at)}
|
||||
</p>
|
||||
</div>
|
||||
const detectionCount = ticket.ai_result?.detection_count;
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<PersonMeta label="Uploaded By" person={ticket.uploader} />
|
||||
<PersonMeta label="Assigned To" person={ticket.worker} />
|
||||
<PersonMeta
|
||||
label="Assigned By"
|
||||
person={ticket.worker?.assigned_by}
|
||||
/>
|
||||
<OverviewMeta
|
||||
label="Assigned At"
|
||||
value={formatDate(ticket.worker?.assigned_at)}
|
||||
/>
|
||||
</div>
|
||||
return (
|
||||
<section className="overflow-hidden rounded-xl border bg-card">
|
||||
<div className="border-b px-4 py-2.5">
|
||||
<h2 className="text-xs font-medium ">
|
||||
Overview
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-x-4 gap-y-6 p-4 md:grid-cols-4">
|
||||
<OverviewField label="Detection Count">
|
||||
<MetaValue>
|
||||
{detectionCount != null ? `${detectionCount} Occurrences` : '-'}
|
||||
</MetaValue>
|
||||
</OverviewField>
|
||||
|
||||
<OverviewField label="Chainage">
|
||||
<MetaValue>{ticket.chainage_id || '-'}</MetaValue>
|
||||
</OverviewField>
|
||||
|
||||
<OverviewField label="Uploaded By">
|
||||
<PersonMetaValue person={ticket.uploader} />
|
||||
</OverviewField>
|
||||
|
||||
<OverviewField label="Created Date">
|
||||
<MetaValue>{formatCreatedDate(ticket.timestamps.created_at)}</MetaValue>
|
||||
</OverviewField>
|
||||
|
||||
<OverviewField label="Assigned To">
|
||||
<PersonMetaValue
|
||||
person={ticket.worker}
|
||||
accentClassName="bg-secondary text-secondary-foreground"
|
||||
/>
|
||||
</OverviewField>
|
||||
|
||||
<OverviewField label="Reviewer">
|
||||
<PersonMetaValue
|
||||
person={ticket.reviewer}
|
||||
accentClassName="bg-primary/20 text-primary"
|
||||
/>
|
||||
</OverviewField>
|
||||
|
||||
<OverviewField label="Assigned At">
|
||||
<MetaValue>{formatDate(ticket.worker?.assigned_at)}</MetaValue>
|
||||
</OverviewField>
|
||||
|
||||
</div>
|
||||
|
||||
{ticket.ai_result?.completed_at ? (
|
||||
<div className="border-t px-4 py-3 text-xs text-muted-foreground">
|
||||
AI analysis completed {formatDate(ticket.ai_result.completed_at)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user