mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-29 23:28:12 +00:00
fixes
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
import { useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import {
|
||||
Alert,
|
||||
Badge,
|
||||
Button,
|
||||
Divider,
|
||||
Group,
|
||||
Paper,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
Stepper,
|
||||
Text,
|
||||
ThemeIcon,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import {
|
||||
IconAlertTriangle,
|
||||
IconArrowLeft,
|
||||
IconCircleCheck,
|
||||
IconClock,
|
||||
IconDownload,
|
||||
IconInfoCircle,
|
||||
IconShip,
|
||||
} from '@tabler/icons-react';
|
||||
import { notify } from '@ema-platform/ui';
|
||||
import { MOCK_REGISTRATIONS, recordDownload, STATUS_COLOR } from '../mock';
|
||||
|
||||
// ponytail: placeholder PDF blob; wire real cert endpoint when backend lands.
|
||||
const BLANK_PDF = 'data:application/pdf;base64,JVBERi0xLjQKJcfsj6IKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFI+PgplbmRvYmoKMiAwIG9iago8PC9UeXBlL1BhZ2VzL0tpZHNbMyAwIFJdL0NvdW50IDE+PgplbmRvYmoKMyAwIG9iago8PC9UeXBlL1BhZ2UvUGFyZW50IDIgMCBSL01lZGlhQm94WzAgMCA2MTIgNzkyXT4+CmVuZG9iagp4cmVmCjAgNAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMDkgMDAwMDAgbiAKMDAwMDAwMDA1OCAwMDAwMCBuIAowMDAwMDAwMTE1IDAwMDAwIG4gCnRyYWlsZXIKPDwvU2l6ZSA0L1Jvb3QgMSAwIFI+PgpzdGFydHhyZWYKMjE3CiUlRU9G';
|
||||
|
||||
function downloadCertificate(filename: string) {
|
||||
const a = document.createElement('a');
|
||||
a.href = BLANK_PDF;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
}
|
||||
|
||||
export function VesselRegistrationStatusPage() {
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
const [reg] = useState(() => MOCK_REGISTRATIONS.find((r) => r.id === id) ?? null);
|
||||
const [, forceUpdate] = useState(0);
|
||||
|
||||
if (!reg) {
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<Button variant="subtle" leftSection={<IconArrowLeft size={14} />} onClick={() => navigate('/vessel-registrations')}>Back</Button>
|
||||
<Alert variant="light" color="red" icon={<IconInfoCircle size={15} />}>Registration not found.</Alert>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const activeStep = reg.timeline.filter((t) => t.done).length - 1;
|
||||
const needsCorrection = reg.status === 'Correction Required' || reg.status === 'Rejected';
|
||||
|
||||
const handleDownload = (certName: string, certNumber: string) => {
|
||||
downloadCertificate(`${certName.replace(/\s+/g, '-')}-${certNumber}.pdf`);
|
||||
recordDownload(reg.id, certName);
|
||||
forceUpdate((n) => n + 1);
|
||||
notify.success(`${certName} downloaded.`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<Group gap="sm">
|
||||
<Button variant="subtle" leftSection={<IconArrowLeft size={14} />} onClick={() => navigate('/vessel-registrations')}>Back</Button>
|
||||
<div>
|
||||
<Title order={3}>{reg.vesselName}</Title>
|
||||
<Text fz="sm" c="dimmed">{reg.id} — {reg.category}</Text>
|
||||
</div>
|
||||
</Group>
|
||||
|
||||
<Paper withBorder radius="lg" p="xl">
|
||||
<Group justify="space-between" mb="md" wrap="wrap" gap="sm">
|
||||
<Group gap="sm">
|
||||
<ThemeIcon size={36} radius="md" color="blue" variant="light"><IconShip size={18} /></ThemeIcon>
|
||||
<div>
|
||||
<Text fw={700} fz="sm">Registration Status</Text>
|
||||
<Text fz="xs" c="dimmed">Submitted {reg.submitted}</Text>
|
||||
</div>
|
||||
</Group>
|
||||
<Badge color={STATUS_COLOR[reg.status]} variant="light" size="lg">{reg.status}</Badge>
|
||||
</Group>
|
||||
|
||||
{reg.renewal && reg.renewal !== 'OK' && (
|
||||
<Alert
|
||||
variant="light"
|
||||
color={reg.renewal === 'Overdue' ? 'red' : 'orange'}
|
||||
icon={<IconAlertTriangle size={15} />}
|
||||
mb="md"
|
||||
p="sm"
|
||||
>
|
||||
<Text fz="sm">
|
||||
Registration {reg.renewal === 'Overdue' ? 'is overdue for renewal' : 'is due for renewal soon'}
|
||||
{reg.expiryDate ? ` — expires ${reg.expiryDate}` : ''}.
|
||||
</Text>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{reg.remarks && (
|
||||
<Alert variant="light" color={needsCorrection ? 'orange' : 'blue'} icon={<IconInfoCircle size={15} />} mb="md" p="sm">
|
||||
<Text fz="sm" fw={600} mb={2}>Officer Remarks</Text>
|
||||
<Text fz="sm">{reg.remarks}</Text>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Stepper active={activeStep} size="sm" color="teal">
|
||||
{reg.timeline.map((step, i) => (
|
||||
<Stepper.Step
|
||||
key={i}
|
||||
label={step.event}
|
||||
description={step.date ?? 'Pending'}
|
||||
icon={step.done ? <IconCircleCheck size={16} /> : <IconClock size={16} />}
|
||||
/>
|
||||
))}
|
||||
</Stepper>
|
||||
|
||||
{needsCorrection && (
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Button color="orange" onClick={() => navigate('/vessel-registrations/apply')}>Resubmit Application</Button>
|
||||
</Group>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
{reg.status === 'Approved' && reg.certificates && (
|
||||
<Paper withBorder radius="lg" p="xl">
|
||||
<Text fw={700} mb="md">Certificates</Text>
|
||||
<Stack gap="sm">
|
||||
{reg.certificates.map((cert) => (
|
||||
<div key={cert.name}>
|
||||
<Group justify="space-between" wrap="wrap" gap="sm">
|
||||
<div>
|
||||
<Text fw={600} fz="sm">{cert.name}</Text>
|
||||
<Text fz="xs" c="dimmed">Certificate No. {cert.number} — Issued {cert.issueDate}</Text>
|
||||
{cert.downloads > 0 && (
|
||||
<Text fz="xs" c="dimmed">Downloaded {cert.downloads} time{cert.downloads === 1 ? '' : 's'}</Text>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
leftSection={<IconDownload size={14} />}
|
||||
onClick={() => handleDownload(cert.name, cert.number)}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</Group>
|
||||
<Divider mt="sm" />
|
||||
</div>
|
||||
))}
|
||||
</Stack>
|
||||
</Paper>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user