import { useRef, useState } from 'react'; import { Alert, Badge, Box, Button, Card, FileButton, Group, Modal, Paper, Progress, SimpleGrid, Stack, Text, TextInput, ThemeIcon, Title, rem, } from '@mantine/core'; import { IconAlertTriangle, IconBook2, IconCalendar, IconCheck, IconCircleCheck, IconDownload, IconFileDescription, IconInfoCircle, IconRefresh, IconShield, IconShieldCheck, IconTrash, IconUpload, IconX, } from '@tabler/icons-react'; import { notify } from '@ema-platform/ui'; // --------------------------------------------------------------------------- // Types & constants // --------------------------------------------------------------------------- interface BSTItem { key: string; label: string; shortLabel: string; description: string; modelCourse: string; refreshYears: number; required: boolean; } interface BSTRecord { key: string; issuer: string; issueDate: string; expiryDate: string; status: 'Valid' | 'Expiring' | 'Expired' | 'Pending Verification'; certNumber: string; fileName: string; } const BST_ITEMS: BSTItem[] = [ { key: 'pst', label: 'Personal Survival Techniques', shortLabel: 'PST', description: 'Covers lifeboat/life-raft operation, survival at sea, and distress signals.', modelCourse: 'IMO 1.19', refreshYears: 5, required: true, }, { key: 'fpff', label: 'Fire Prevention & Fire Fighting', shortLabel: 'FPFF', description: 'Covers fire prevention, detection, and fire-fighting on board vessels.', modelCourse: 'IMO 1.20', refreshYears: 5, required: true, }, { key: 'efa', label: 'Elementary First Aid', shortLabel: 'EFA', description: 'Basic first-aid procedures, CPR, and medical emergency response.', modelCourse: 'IMO 1.13', refreshYears: 0, required: true, }, { key: 'pssr', label: 'Personal Safety & Social Responsibility', shortLabel: 'PSSR', description: 'Shipboard safety culture, regulations, and working relationships.', modelCourse: 'IMO 1.21', refreshYears: 0, required: true, }, { key: 'shp', label: 'Sexual Harassment Prevention Training', shortLabel: 'SHPT', description: 'Awareness and prevention of harassment in the maritime workplace.', modelCourse: 'EMA National', refreshYears: 0, required: true, }, ]; const MOCK_RECORDS: Record = { pst: { key: 'pst', issuer: 'Bahirdar Maritime School', issueDate: '2023-04-10', expiryDate: '2028-04-09', status: 'Valid', certNumber: 'PST-2023-BMS-0421', fileName: 'pst_certificate.pdf', }, fpff: { key: 'fpff', issuer: 'Bahirdar Maritime School', issueDate: '2023-04-10', expiryDate: '2028-04-09', status: 'Valid', certNumber: 'FPFF-2023-BMS-0421', fileName: 'fpff_certificate.pdf', }, }; const STATUS_COLOR: Record = { Valid: 'teal', Expiring: 'orange', Expired: 'red', 'Pending Verification': 'yellow', }; function daysUntil(dateStr: string) { return Math.ceil((new Date(dateStr).getTime() - Date.now()) / (1000 * 60 * 60 * 24)); } function formatDate(dateStr: string) { return new Date(dateStr).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }); } // --------------------------------------------------------------------------- // Upload modal // --------------------------------------------------------------------------- function UploadModal({ item, opened, onClose, onUploaded, }: { item: BSTItem | null; opened: boolean; onClose: () => void; onUploaded: (key: string, 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) { notify.error('Please fill all required fields and upload the certificate.'); return; } setSubmitting(true); await new Promise((r) => setTimeout(r, 1000)); setSubmitting(false); onUploaded(item!.key, { key: item!.key, issuer, issueDate, expiryDate: expiryDate || '', status: 'Pending Verification', certNumber, fileName: file.name, }); notify.success(`${item!.shortLabel} certificate submitted for verification.`); reset(); onClose(); }; return ( { reset(); onClose(); }} title={`Upload ${item?.label}`} size="md" centered> {item && ( } p="xs"> {item.description} — Model Course: {item.modelCourse} setIssuer(e.currentTarget.value)} size="sm" /> setCertNumber(e.currentTarget.value)} size="sm" /> setIssueDate(e.currentTarget.value)} size="sm" /> {item.refreshYears > 0 && ( setExpiryDate(e.currentTarget.value)} size="sm" /> )}
Certificate File * {file ? ( {file.name} ) : ( {(props) => ( )} )}
)}
); } // --------------------------------------------------------------------------- // Main page // --------------------------------------------------------------------------- export function BasicSafetyTrainingPage() { const [records, setRecords] = useState>(MOCK_RECORDS); const [modalItem, setModalItem] = useState(null); const doneCount = BST_ITEMS.filter((i) => records[i.key]).length; const allDone = doneCount === BST_ITEMS.length; const handleUploaded = (key: string, record: BSTRecord) => { setRecords((prev) => ({ ...prev, [key]: record })); }; return ( {/* Header */}
Basic Safety Training All seafarers must complete 5 mandatory BST certificates before joining a vessel (STCW Chapter VI).
: } > {doneCount} / {BST_ITEMS.length} Complete
{/* Overall progress */} Overall Completion {Math.round((doneCount / BST_ITEMS.length) * 100)}% {!allDone && ( } p="sm"> You need all 5 certificates to apply for a Seaman Book. Missing: {BST_ITEMS.filter((i) => !records[i.key]).map((i) => i.shortLabel).join(', ')} )} {allDone && ( } p="sm"> All 5 BST training certificates are complete. You can now apply for your Seaman Book & BTC — the Basic Training Certificate (BTC) is issued by EMA alongside your Seaman Book after your application is approved. )} {/* Certificate cards */} {BST_ITEMS.map((item) => { const rec = records[item.key]; const days = rec?.expiryDate ? daysUntil(rec.expiryDate) : null; const needsRefresh = item.refreshYears > 0; return ( {rec ? : }
{item.shortLabel} {item.modelCourse}
{rec ? ( {rec.status} ) : ( Missing )}
{item.label} {rec ? ( Certificate No. {rec.certNumber} Issuer {rec.issuer} Issued {formatDate(rec.issueDate)} {needsRefresh && rec.expiryDate && ( <> Expires {formatDate(rec.expiryDate)} {days !== null && ( )} )} {needsRefresh && ( {item.refreshYears}-year refresh required )} ) : ( {item.description} {needsRefresh && ( Requires {item.refreshYears}-year refresh )} )}
); })}
{/* Info */} About Basic Safety Training (STCW VI/1) Basic Safety Training is mandatory for all seafarers regardless of department (Deck, Engine, or Catering). PST and FPFF certificates require evidence of maintained competence every 5 years. EFA and PSSR do not have a mandatory 5-year repeat under STCW. Certificates must be from EMA-approved training institutions (e.g. Bahirdar Maritime School, Babugaya Maritime School). EMA officers will verify authenticity before approving your Seaman Book application. {/* Upload modal */} setModalItem(null)} onUploaded={handleUploaded} />
); }