'use client' import { useState } from 'react' import { apiFetch } from '@/lib/api' import { useDashboardI18n } from '@/components/I18nProvider' import VehicleConditionSheet from './VehicleConditionSheet' type InspectionType = 'CHECKIN' | 'CHECKOUT' type FuelLevel = 'FULL' | 'SEVEN_EIGHTHS' | 'THREE_QUARTERS' | 'FIVE_EIGHTHS' | 'HALF' | 'THREE_EIGHTHS' | 'QUARTER' | 'ONE_EIGHTH' | 'EMPTY' type DamageType = 'SCRATCH' | 'DENT' | 'CRACK' | 'CHIP' | 'MISSING' | 'STAIN' | 'OTHER' type DamageSeverity = 'MINOR' | 'MODERATE' | 'MAJOR' export interface DamagePoint { id?: string viewType: 'TOP' | 'FRONT' | 'REAR' | 'LEFT' | 'RIGHT' x: number y: number damageType: DamageType severity: DamageSeverity description?: string | null isPreExisting: boolean } export interface DamageInspection { id: string type: InspectionType mileage: number | null fuelLevel: FuelLevel fuelCharge: number | null generalCondition: string | null employeeNotes: string | null customerAgreed: boolean damagePoints: DamagePoint[] } const fuelLevels: FuelLevel[] = ['FULL', 'SEVEN_EIGHTHS', 'THREE_QUARTERS', 'FIVE_EIGHTHS', 'HALF', 'THREE_EIGHTHS', 'QUARTER', 'ONE_EIGHTH', 'EMPTY'] const damageTypes: DamageType[] = ['SCRATCH', 'DENT', 'CRACK', 'CHIP', 'MISSING', 'STAIN', 'OTHER'] const severityColors: Record = { MINOR: '#f59e0b', MODERATE: '#f97316', MAJOR: '#dc2626', } export function pointKey(point: Pick) { return `${point.viewType}:${point.x.toFixed(2)}:${point.y.toFixed(2)}:${point.damageType}:${point.severity}` } export function comparisonPointsForCheckout(initialInspection: DamageInspection | null | undefined, comparisonPoints: DamagePoint[]): DamagePoint[] { const storedPreExistingPoints = initialInspection?.damagePoints?.filter((point) => point.isPreExisting) ?? [] return [...comparisonPoints, ...storedPreExistingPoints].filter((point, index, all) => all.findIndex((item) => pointKey(item) === pointKey(point)) === index) } export function editableDamagePointsForInspection(type: InspectionType, initialInspection: DamageInspection | null | undefined): DamagePoint[] { const points = initialInspection?.damagePoints ?? [] return type === 'CHECKOUT' ? points.filter((point) => !point.isPreExisting) : points } export function buildInspectionSavePayload(inspection: DamageInspection, overlayComparisonPoints: DamagePoint[]) { const payloadPoints = [ ...overlayComparisonPoints.map(({ viewType, x, y, damageType, severity, description }) => ({ viewType, x, y, damageType, severity, ...(description ? { description } : {}), isPreExisting: true, })), ...inspection.damagePoints.map(({ viewType, x, y, damageType, severity, description, isPreExisting }) => ({ viewType, x, y, damageType, severity, ...(description ? { description } : {}), isPreExisting, })), ] return { ...(inspection.mileage !== null ? { mileage: inspection.mileage } : {}), fuelLevel: inspection.fuelLevel, ...(inspection.fuelCharge !== null ? { fuelCharge: inspection.fuelCharge } : {}), ...(inspection.generalCondition ? { generalCondition: inspection.generalCondition } : {}), ...(inspection.employeeNotes ? { employeeNotes: inspection.employeeNotes } : {}), customerAgreed: inspection.customerAgreed, damagePoints: payloadPoints, } } export default function DamageInspectionCard({ reservationId, type, initialInspection, comparisonPoints = [], editable = true, readOnlyMessage, onSaved, }: { reservationId: string type: InspectionType initialInspection?: DamageInspection | null comparisonPoints?: DamagePoint[] editable?: boolean readOnlyMessage?: string | null onSaved: (inspection: DamageInspection) => void }) { const { dict } = useDashboardI18n() const r = dict.reservations const overlayComparisonPoints = type === 'CHECKOUT' ? comparisonPointsForCheckout(initialInspection, comparisonPoints) : [] const [inspection, setInspection] = useState({ id: initialInspection?.id ?? `${type.toLowerCase()}-draft`, type, mileage: initialInspection?.mileage ?? null, fuelLevel: initialInspection?.fuelLevel ?? 'FULL', fuelCharge: initialInspection?.fuelCharge ?? null, generalCondition: initialInspection?.generalCondition ?? '', employeeNotes: initialInspection?.employeeNotes ?? '', customerAgreed: initialInspection?.customerAgreed ?? false, damagePoints: editableDamagePointsForInspection(type, initialInspection), }) const [damageType, setDamageType] = useState('SCRATCH') const [severity, setSeverity] = useState('MINOR') const [saving, setSaving] = useState(false) const [error, setError] = useState(null) async function saveInspection() { setSaving(true) setError(null) try { const payload = buildInspectionSavePayload(inspection, overlayComparisonPoints) const result = await apiFetch(`/reservations/${reservationId}/inspections/${type.toLowerCase()}`, { method: 'PUT', body: JSON.stringify(payload), }) setInspection({ ...result, damagePoints: type === 'CHECKOUT' ? result.damagePoints.filter((point) => !point.isPreExisting) : result.damagePoints, }) onSaved(result) } catch (err: any) { setError(err.message ?? r.failedSaveInspection) } finally { setSaving(false) } } function addDamagePoint(viewType: DamagePoint['viewType'], x: number, y: number) { setInspection((current) => ({ ...current, damagePoints: [ ...current.damagePoints, { viewType, x, y, damageType, severity, description: '', isPreExisting: type === 'CHECKIN' }, ], })) } function removePoint(index: number) { setInspection((current) => ({ ...current, damagePoints: current.damagePoints.filter((_, i) => i !== index), })) } return (

{r.inspectionCardTitle(type)}

{r.inspectionSubtitle}

{error &&
{error}
} {!editable && readOnlyMessage && (
{readOnlyMessage}
)}
{damageTypes.map((option) => ( ))}
{(Object.keys(severityColors) as DamageSeverity[]).map((option) => ( ))}
{type === 'CHECKOUT' && overlayComparisonPoints.length > 0 && (
{r.inspectionCardTitle('CHECKIN')} {r.inspectionCardTitle('CHECKOUT')}
)}

{r.diagramHelp}

setInspection((cur) => ({ ...cur, mileage: e.target.value ? Number(e.target.value) : null }))} />