import { type StatusTone, STATUS_TONE_COLOR } from '@ema-platform/shared'; import { useRef, useState } from 'react'; import { useApiQuery } from '@ema-platform/api'; import { Alert, Badge, Box, Button, Card, FileButton, Group, List, Modal, Paper, Stack, Text, TextInput, ThemeIcon, Title, } from '@mantine/core'; import { IconAlertTriangle, IconBook2, IconCalendar, IconCheck, IconCircleCheck, IconDownload, IconInfoCircle, IconRefresh, IconShieldCheck, IconTrash, IconUpload, } from '@tabler/icons-react'; import { StatusBadge, notify } from '@ema-platform/ui'; import { useTranslation } from 'react-i18next'; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- interface BSTRecord { issuer: string; issueDate: string; expiryDate: string; certNumber: string; fileName: string; status: 'Valid' | 'Expiring' | 'Expired' | 'Pending Verification'; } const STATUS_TONE: Record = { Valid: 'success', Expiring: 'pending', Expired: 'danger', 'Pending Verification': 'warning', }; /** Progress across the five STCW A-VI/1 modules, as `/bst/my` reports it. */ interface BstProgress { modules: { key: string; label: string; licenseTypeKey: string; done: boolean }[]; completed: number; total: number; complete: boolean; } // `short` doubles as the key the API reports each module under, so the two // stay matched without a second lookup table between them. const BST_COMPONENTS = [ { label: 'Personal Survival Techniques', short: 'PST', course: 'IMO 1.19' }, { label: 'Fire Prevention & Fire Fighting', short: 'FPFF', course: 'IMO 1.20' }, { label: 'Elementary First Aid', short: 'EFA', course: 'IMO 1.13' }, { label: 'Personal Safety & Social Responsibility', short: 'PSSR', course: 'IMO 1.21' }, { label: 'Security Awareness', short: 'SSA', course: 'STCW A-VI/6' }, ]; function formatDate(dateStr: string) { return new Date(dateStr).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric', }); } function daysUntil(dateStr: string) { return Math.ceil((new Date(dateStr).getTime() - Date.now()) / (1000 * 60 * 60 * 24)); } // --------------------------------------------------------------------------- // Upload modal // --------------------------------------------------------------------------- function UploadModal({ opened, onClose, onUploaded, }: { opened: boolean; onClose: () => void; onUploaded: (record: BSTRecord) => void; }) { const [file, setFile] = useState(null); const [issuer, setIssuer] = useState(''); const [certNumber, setCertNumber] = useState(''); const [issueDate, setIssueDate] = useState(''); const [expiryDate, setExpiryDate] = useState(''); const [submitting, setSubmitting] = useState(false); const resetRef = useRef<() => void>(null); const reset = () => { setFile(null); setIssuer(''); setCertNumber(''); setIssueDate(''); setExpiryDate(''); resetRef.current?.(); }; const handleSubmit = async () => { if (!file || !issuer || !certNumber || !issueDate || !expiryDate) { notify.error('Please fill all required fields and upload the certificate file.'); return; } setSubmitting(true); await new Promise((r) => setTimeout(r, 1000)); setSubmitting(false); onUploaded({ issuer, issueDate, expiryDate, certNumber, fileName: file.name, status: 'Pending Verification', }); notify.success('Basic Safety Training certificate submitted for verification.'); reset(); onClose(); }; return ( { reset(); onClose(); }} title="Upload Basic Safety Training Certificate" size="md" centered > } p="xs"> Upload your combined BST certificate issued by an EMA-approved training institution. The certificate must cover all 5 components (PST, FPFF, EFA, PSSR, SHPT). setIssuer(e.currentTarget.value)} size="sm" /> setCertNumber(e.currentTarget.value)} size="sm" /> setIssueDate(e.currentTarget.value)} size="sm" /> setExpiryDate(e.currentTarget.value)} size="sm" />
Certificate File * {file ? ( {file.name} ) : ( {(props) => ( )} )}
); } // --------------------------------------------------------------------------- // Main page // --------------------------------------------------------------------------- export function BasicSafetyTrainingPage() { const [record, setRecord] = useState(null); const [modalOpen, setModalOpen] = useState(false); // Which of the five modules the seafarer actually holds. The certificate // itself is the evidence, so this is read from the issued licences rather // than tracked separately — two places to record it would disagree. const { data: bst, isLoading: bstLoading } = useApiQuery({ url: '/bst/my', method: 'GET', }); const doneByKey = new Map( (bst?.modules ?? []).map((m) => [m.key, m.done]), ); const days = record?.expiryDate ? daysUntil(record.expiryDate) : null; const isExpiringSoon = days !== null && days <= 180 && days > 0; const isExpired = days !== null && days <= 0; const { t } = useTranslation(); return ( {/* Header */}
Basic Safety Training Certificate STCW Chapter VI/1 — mandatory for all seafarers before joining a vessel.
{record && ( } /> )}
{/* Expiry alert */} {isExpired && ( }> Your BST certificate has expired. Upload a renewed certificate to remain eligible. )} {isExpiringSoon && ( }> Your BST certificate expires in {days} days. Renew before it lapses. )} {/* Certificate card */} {record ? (
Basic Safety Training (BST) Combined certificate — all 5 STCW components
Certificate Number {record.certNumber} Issuing Institution {record.issuer} Issue Date {formatDate(record.issueDate)} Expiry Date {formatDate(record.expiryDate)} {days !== null && days > 0 && ( ({days} days remaining) )} File {record.fileName}
) : (
No BST Certificate Uploaded You must upload a valid Basic Safety Training certificate issued by an EMA-approved institution before applying for a Seaman Book.
)} {/* Components covered */} Certificate Components (STCW VI/1) A combined BST certificate from an EMA-approved institution covers all five components: } > {BST_COMPONENTS.map((c) => { const done = doneByKey.get(c.short); return ( } > {c.short} — {c.label} {c.course} {/* Only stated once known: an absent badge reads as "not loaded", where a "Not held" badge would read as fact. */} {!bstLoading && ( {done ? 'Held' : 'Not held'} )} ); })} {/* Info */} Validity & Renewal BST certificates are typically valid for 5 years. PST and FPFF components require evidence of maintained competence at the 5-year point (STCW Reg. VI/1). EFA and PSSR do not have a mandatory 5-year revalidation under STCW but your institution's combined certificate carries a unified expiry date. Certificates must be from EMA-approved training institutions. setModalOpen(false)} onUploaded={(rec) => setRecord(rec)} />
); }