import { useRef, useState } from 'react'; import { Alert, Badge, Button, Card, FileButton, Group, Loader, Stack, Text } from '@mantine/core'; import { IconAlertTriangle, IconCheck, IconFileUpload } from '@tabler/icons-react'; import { SEAFARER_REGISTRATION_DOCUMENTS, isEthiopianNationality, uploadDocument, type Attachment, } from '@ema-platform/api'; const MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024; /** The document slots a registration asks for. */ export function documentSlots(passportDeclared: boolean, nationality?: string | null) { const ethiopian = isEthiopianNationality(nationality); return SEAFARER_REGISTRATION_DOCUMENTS.map((d) => ({ ...d, isRequired: d.required === 'passport' ? passportDeclared : d.required === 'ethiopian' ? ethiopian : d.required, })).filter((d) => (d.required !== 'passport' || passportDeclared) && (d.required !== 'ethiopian' || ethiopian)); } export function RegistrationDocuments({ registrationId, passportDeclared, nationality, attachments, readOnly, onUploaded, }: { registrationId: string; passportDeclared: boolean; nationality?: string | null; attachments: Attachment[]; readOnly?: boolean; onUploaded: () => void; }) { const [busy, setBusy] = useState(null); const [error, setError] = useState(null); const resetRefs = useRef void>>({}); async function handle(documentKey: string, file: File | null) { if (!file) return; if (file.size > MAX_FILE_SIZE_BYTES) { setError(`File exceeds 5MB limit (${(file.size / 1024 / 1024).toFixed(1)}MB).`); resetRefs.current[documentKey]?.(); return; } setBusy(documentKey); setError(null); const result = await uploadDocument({ ownerType: 'SEAFARER_REGISTRATION', ownerId: registrationId, documentKey, file, }); setBusy(null); resetRefs.current[documentKey]?.(); if (result.ok) onUploaded(); else setError(result.error); } return ( {error && ( }> {error} )} {documentSlots(passportDeclared, nationality).map((slot) => { const existing = attachments.find((a) => a.documentKey === slot.key); const uploaded = Boolean(existing?.files?.length); return (
{slot.name} {!slot.isRequired && ( optional )} {uploaded && ( }> uploaded )} {slot.description && ( {slot.description} )} {existing?.files?.[0] && ( {existing.files[0].originalName} ยท {(existing.files[0].sizeBytes / 1024).toFixed(0)} KB )}
{existing?.files?.[0]?.url && ( )} {!readOnly && ( { if (r) resetRefs.current[slot.key] = r; }} onChange={(file) => handle(slot.key, file)} accept={slot.accept} > {(props) => ( )} )}
); })}
); }