import { Alert, Badge, Button, Divider, Group, Loader, Modal, Paper, Stack, Table, Text, } from '@mantine/core'; import { DateTimePicker } from '@mantine/dates'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Receipt } from 'lucide-react'; import { useEffect, useState } from 'react'; import { QUERY_KEYS } from '@/constants/QUERY_KEYS'; import { useToast } from '@/hooks/use-toast'; import { isBackdated } from '@/lib/no-backdate'; import { lastMileService, type LastMileRecord } from '@/services/last-mile.service'; interface TruckDetentionModalProps { opened: boolean; onClose: () => void; record: LastMileRecord | null; } const money = (amount: number, currency: string) => `${Number(amount).toLocaleString()} ${currency === 'ETB' ? 'Birr (ETB)' : currency}`; function Stat({ label, value, strong }: { label: string; value: React.ReactNode; strong?: boolean }) { return ( {label} {value} ); } /** * View/override the detention clock (arrival + delivery/return) for a last-mile * leg, preview the per-truck-per-day charge, and generate the detention invoice. */ export function TruckDetentionModal({ opened, onClose, record }: TruckDetentionModalProps) { const { toast } = useToast(); const qc = useQueryClient(); const id = record?.id ?? null; const [arrived, setArrived] = useState(null); const [delivered, setDelivered] = useState(null); useEffect(() => { setArrived(record?.arrivedAt ? new Date(record.arrivedAt) : null); setDelivered(record?.deliveredAt ? new Date(record.deliveredAt) : null); }, [record?.id, record?.arrivedAt, record?.deliveredAt, opened]); const previewQuery = useQuery({ queryKey: ['truck-detention-preview', id], queryFn: async () => (await lastMileService.truckDetentionPreview(id as string)).data, enabled: opened && Boolean(id), }); const preview = previewQuery.data; const saveTimes = useMutation({ mutationFn: () => lastMileService.update(id as string, { arrivedAt: arrived ? arrived.toISOString() : null, deliveredAt: delivered ? delivered.toISOString() : null, }), onSuccess: () => { void qc.invalidateQueries({ queryKey: QUERY_KEYS.LAST_MILE.ROOT }); void previewQuery.refetch(); toast({ title: 'Detention times saved' }); }, onError: () => toast({ title: 'Save failed', variant: 'destructive' }), }); const generate = useMutation({ mutationFn: () => lastMileService.generateTruckDetentionInvoice(id as string), onSuccess: () => { void qc.invalidateQueries({ queryKey: QUERY_KEYS.LAST_MILE.ROOT }); toast({ title: 'Truck detention invoice generated' }); onClose(); }, onError: (e: unknown) => { const description = (e as { response?: { data?: { message?: string } } })?.response?.data?.message; toast({ title: 'Detention invoice failed', description, variant: 'destructive' }); }, }); return ( Truck detention{record?.booking?.reference ? ` · ${record.booking.reference}` : ''} } > setArrived(v ? new Date(v) : null)} minDate={new Date()} clearable /> setDelivered(v ? new Date(v) : null)} minDate={new Date()} clearable /> {previewQuery.isLoading ? ( ) : !preview ? ( No preview available. ) : !preview.ruleId ? ( No active Truck Detention rule matches this booking. Create one under Warehouse → Fee rules (rule type "Truck Detention Cost"). ) : ( {preview.endIsOpen && ( Still accruing — no delivery/return time yet. The amount grows until the vehicle is returned. )} {preview.groups && preview.groups.length > 1 ? ( Truck type Trucks Days Rate / truck / day Amount {preview.groups.map((g, i) => ( {g.vehicleType ?? 'Unknown'} {!g.ruleId && ( {' '}· no rule )} {g.truckCount} {g.chargeableDays} {money(g.ratePerDay, preview.currency)} {money(g.amount, preview.currency)} ))}
) : preview.tiers && preview.tiers.length > 0 ? ( From day To day Days Rate / truck / day Amount {preview.tiers.map((t, i) => ( {t.appliedFromDay} {t.appliedToDay} {t.days} {money(t.ratePerDay, preview.currency)} {money(t.amount, preview.currency)} ))}
) : ( Flat {money(preview.ratePerDay, preview.currency)} per truck per day after the grace window. )} {preview.ruleName ?? 'Detention rule'} {preview.billableUnits > 0 && ( {preview.billableUnits} billable truck-day(s) )}
)}
); }