feat(ticket): redesign audit timeline with structured history events

This commit is contained in:
2026-07-01 22:23:49 +05:30
parent 1a94ff80e6
commit c73ba0a01f
13 changed files with 390 additions and 243 deletions

View File

@@ -3,7 +3,7 @@
import type { ReactNode } from 'react';
import { Lock, MessageSquare } from 'lucide-react';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { PersonInfo } from '@/components/person-avatar';
import { Button } from '@/components/ui/button';
import {
Card,
@@ -14,34 +14,18 @@ import {
} from '@/components/ui/card';
import { cn } from '@/lib/utils';
import type { TicketDetail } from '@/types';
function getInitials(name?: string | null, email?: string | null) {
const source = name || email || '?';
return source
.split(/[\s@._-]+/)
.filter(Boolean)
.slice(0, 2)
.map((part) => part.charAt(0).toUpperCase())
.join('');
}
function getPersonLabel(person: TicketDetail['reviewer']) {
return person?.name || person?.email || 'Unknown';
}
function formatReviewDate(value: string | null | undefined) {
if (!value) return '-';
return new Intl.DateTimeFormat('en-IN', { dateStyle: 'medium' }).format(
new Date(value),
);
}
import { formatDateOnly } from '@/utils/date';
function getClosedAt(ticket: TicketDetail) {
const closedEntry = ticket.history?.find(
(item) => item.to_status === 'closed',
);
const approvedEntry = [...(ticket.history ?? [])]
.reverse()
.find(
(item) =>
item.event_type === 'repair_approved' ||
item.to_class_status === 'approved',
);
return (
closedEntry?.created_at ??
approvedEntry?.created_at ??
ticket.reviewer?.reviewed_at ??
ticket.timestamps.updated_at
);
@@ -101,35 +85,19 @@ export function ClosedTicketAction({ ticket }: { ticket: TicketDetail }) {
<Card>
<CardHeader>
<CardTitle className="text-lg">Resolution Details</CardTitle>
<CardDescription>Review record for {ticket.id}</CardDescription>
<CardDescription>Review record for {ticket.ticket_name}</CardDescription>
</CardHeader>
<CardContent className="space-y-5">
<div className="space-y-4 rounded-xl border bg-muted/20 p-4">
<MetaField
label="Reviewed By"
value={
<div className="flex min-w-0 items-center gap-3">
<Avatar className="size-10 border border-primary/30">
<AvatarFallback className="bg-primary/15 text-sm text-primary">
{getInitials(reviewer?.name, reviewer?.email)}
</AvatarFallback>
</Avatar>
<div className="min-w-0">
<p className="truncate">{getPersonLabel(reviewer)}</p>
{reviewer?.email ? (
<p className="mt-0.5 truncate text-xs font-medium text-muted-foreground">
{reviewer.email}
</p>
) : null}
</div>
</div>
}
value={<PersonInfo person={reviewer} size="lg" />}
/>
<MetaField
label="Reviewed Date"
value={formatReviewDate(reviewedAt)}
value={formatDateOnly(reviewedAt)}
/>
</div>