'use client'; import { History, MessageSquareText, RefreshCw } from 'lucide-react'; import { PersonInfo } from '@/components/person-avatar'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Timeline, TimelineItem } from '@/components/ui/timeline'; import { cn } from '@/lib/utils'; import type { TicketTimelineEventType, TicketTimelineItem, TicketTimelineNoteKind, TicketTimelineSource, TicketTimelineUser, } from '@/types'; import { formatTimelineDateTime } from '@/utils/date'; import { getPersonLabel } from '@/utils/person'; import { useTicketTimelineQuery } from '../../hooks/useTicketQueries'; import { TicketTimelineSkeleton } from './TicketTimelineSkeleton'; function TimelineDate({ value }: { value: string }) { const { date, time } = formatTimelineDateTime(value); return ( ); } function getNoteKindLabel(kind: TicketTimelineNoteKind) { switch (kind) { case 'system_note': return 'System note'; case 'repair_note': return 'Repair note'; case 'review_comment': return 'Review comment'; default: return 'Note'; } } function HistoryNote({ item }: { item: TicketTimelineItem }) { const note = item.note; if (!note) return null; const noteText = note.text.trim(); if (!noteText) return null; return (
{getNoteKindLabel(note.kind)} by {getPersonLabel(note.author)}

{noteText}

); } function getSourceLabel(source: TicketTimelineSource) { return source === 'system' ? 'System Generated' : 'User Action'; } function SystemHistoryEntry({ item }: { item: TicketTimelineItem }) { const noteText = item.note?.text?.trim(); return (
{getSourceLabel(item.source)}

{item.title}

{noteText ? (

{noteText}

) : null}
); } function isSameUser(actor: TicketTimelineUser, target: TicketTimelineUser) { if (actor.user_id !== null && target.user_id !== null) { return actor.user_id === target.user_id; } const actorEmail = actor.email?.trim().toLowerCase(); const targetEmail = target.email?.trim().toLowerCase(); if (actorEmail && targetEmail) return actorEmail === targetEmail; return ( Boolean(actor.name?.trim()) && actor.name?.trim().toLowerCase() === target.name?.trim().toLowerCase() ); } function getTargetLabel(eventType: TicketTimelineEventType | null) { switch (eventType) { case 'assignment_added': return 'Assigned to'; case 'assignment_replaced': return 'Replacement worker'; case 'assignment_updated': return 'Assigned worker'; case 'assignment_deleted': return 'Removed worker'; default: return 'Affected worker'; } } function UserHistoryEntry({ item }: { item: TicketTimelineItem }) { const noteText = item.note?.text?.trim(); const showTargetUser = item.target_user && !isSameUser(item.actor, item.target_user); return (

{item.title}

{showTargetUser && item.target_user ? (

{getTargetLabel(item.event_type)}

) : null} {noteText ? : null}
); } function TimelineHeader() { return (

Audit & Lifecycle Timeline

); } export function TicketHistoryCard({ ticketId }: { ticketId: string }) { const timelineQuery = useTicketTimelineQuery(ticketId); if (timelineQuery.isLoading) { return ; } const history = timelineQuery.data?.history ?? []; return (
{timelineQuery.isError ? (

Failed to load the ticket timeline.

) : history.length ? ( {history.map((item, index) => ( } marker={ } > {item.source === 'system' ? ( ) : ( )} ))} ) : (

No timeline entries yet

)}
); }