import { useState } from "react"; import { Button, FileInput, Group, Modal, Stack, Text, TextInput, Textarea, } from "@mantine/core"; import { useMutation } from "@tanstack/react-query"; import { Camera, PenLine } from "lucide-react"; import { ContractSignaturePad } from "@/components/bookings/ContractSignaturePad"; import { lastMileService } from "@/services/last-mile.service"; import { useToast } from "@/hooks/use-toast"; interface ProofOfDeliveryModalProps { opened: boolean; onClose: () => void; lastMileId: string | null; reference?: string | null; /** Called after a successful capture so the caller can refetch. */ onDone: () => void; } /** * Proof of delivery capture for an EDR last-mile leg: recipient name, a drawn * signature, and proof photos. On confirm it uploads everything and completes * the delivery (marks the leg DELIVERED). */ export function ProofOfDeliveryModal({ opened, onClose, lastMileId, reference, onDone, }: ProofOfDeliveryModalProps) { const { toast } = useToast(); const [recipient, setRecipient] = useState(""); const [notes, setNotes] = useState(""); const [signatureUrl, setSignatureUrl] = useState(null); const [photos, setPhotos] = useState([]); const reset = () => { setRecipient(""); setNotes(""); setSignatureUrl(null); setPhotos([]); }; const close = () => { reset(); onClose(); }; const submit = useMutation({ mutationFn: async () => { if (!lastMileId) throw new Error("No delivery selected"); const signature = signatureUrl ? await (await fetch(signatureUrl)).blob() : null; return lastMileService.recordProofOfDelivery(lastMileId, { recipientName: recipient.trim(), notes: notes.trim() || undefined, signature, photos, }); }, onSuccess: () => { toast({ title: "Proof of delivery recorded", description: "The delivery has been completed.", }); reset(); onDone(); onClose(); }, onError: (e) => toast({ variant: "destructive", title: "Could not record delivery", description: e instanceof Error ? e.message : undefined, }), }); // Require a recipient plus at least one form of proof (signature or a photo). const canSubmit = recipient.trim().length > 0 && (Boolean(signatureUrl) || photos.length > 0); return ( setRecipient(e.currentTarget.value)} />
Recipient signature
} accept="image/*" multiple clearable value={photos} onChange={setPhotos} />