104 lines
2.9 KiB
TypeScript
104 lines
2.9 KiB
TypeScript
import { CheckCircle2, HardHat, Wrench, ToolCase } from 'lucide-react';
|
|
|
|
import { ProtectedVideoPlayer } from '@/components/media/ProtectedVideoPlayer';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import type { TicketDetail } from '@/types';
|
|
|
|
import { formatDate } from '@/utils/date';
|
|
import { RepairProofGallery } from './RepairProofGallery';
|
|
|
|
function SubmittedBanner({
|
|
submittedAt,
|
|
submitterName,
|
|
}: {
|
|
submittedAt: string;
|
|
submitterName: string;
|
|
}) {
|
|
return (
|
|
<div className="flex items-start gap-3 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-3 dark:border-emerald-900/60 dark:bg-emerald-950/30">
|
|
<CheckCircle2 className="mt-0.5 size-5 shrink-0 text-emerald-600 dark:text-emerald-400" />
|
|
<div className="min-w-0 space-y-0.5">
|
|
<p className="text-sm font-semibold text-emerald-800 dark:text-emerald-200">
|
|
Repair Submitted
|
|
</p>
|
|
<p className="text-xs text-emerald-700/80 dark:text-emerald-300/80">
|
|
{formatDate(submittedAt)} by {submitterName}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ImageGallery({
|
|
imageUrls,
|
|
hasVideo,
|
|
}: {
|
|
imageUrls: string[];
|
|
hasVideo: boolean;
|
|
}) {
|
|
if (imageUrls.length === 0) {
|
|
return hasVideo ? null : (
|
|
<div className="flex items-center gap-2 rounded-lg border border-dashed px-3 py-4 text-sm text-muted-foreground">
|
|
<HardHat className="size-4 shrink-0" />
|
|
No media attached
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return <RepairProofGallery imageUrls={imageUrls} />;
|
|
}
|
|
|
|
function Notes({ notes }: { notes: string | null | undefined }) {
|
|
return (
|
|
<div className="space-y-1.5">
|
|
<p className="text-xs font-semibold text-foreground">Repair Notes</p>
|
|
<p className="text-sm leading-relaxed text-muted-foreground">
|
|
{notes?.trim() || 'No repair notes provided.'}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function RepairProofSummary({ ticket }: { ticket: TicketDetail }) {
|
|
const repair = ticket.repair;
|
|
|
|
if (!repair?.submitted_at) {
|
|
return null;
|
|
}
|
|
|
|
const submitterName = ticket.worker?.name?.trim() || 'Assigned worker';
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2 text-base">
|
|
<ToolCase className="text-primary" />
|
|
Repair Proof
|
|
</CardTitle>
|
|
</CardHeader>
|
|
|
|
<CardContent className="space-y-4">
|
|
<SubmittedBanner
|
|
submittedAt={repair.submitted_at}
|
|
submitterName={submitterName}
|
|
/>
|
|
|
|
<ProtectedVideoPlayer
|
|
url={repair.proof_video_url}
|
|
meta="Contractor evidence video"
|
|
emptyTitle="No repair video uploaded"
|
|
emptyDescription="The assigned worker has not uploaded a repair video."
|
|
className="rounded-lg"
|
|
/>
|
|
|
|
<ImageGallery
|
|
imageUrls={repair.proof_image_urls}
|
|
hasVideo={Boolean(repair.proof_video_url)}
|
|
/>
|
|
|
|
<Notes notes={repair.notes} />
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|