mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
feat: add in-flight registration application tracking and status dashboard to vessel registration page
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Badge,
|
Badge,
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
Group,
|
Group,
|
||||||
Paper,
|
Paper,
|
||||||
|
Progress,
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
@@ -25,13 +27,24 @@ import {
|
|||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
IconClockHour4,
|
IconClockHour4,
|
||||||
IconTransferIn,
|
IconTransferIn,
|
||||||
|
IconArrowRight,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useApiMutation } from '@ema-platform/api';
|
import {
|
||||||
|
STATUS_COLORS,
|
||||||
|
STATUS_LABELS,
|
||||||
|
STATUS_PROGRESS,
|
||||||
|
TERMINAL_STATUSES,
|
||||||
|
useApiMutation,
|
||||||
|
useGetMyApplicationsQuery,
|
||||||
|
} from '@ema-platform/api';
|
||||||
import { authStorage } from '@ema-platform/auth';
|
import { authStorage } from '@ema-platform/auth';
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
/** Registration applications run through the config-driven licensing wizard. */
|
||||||
|
const REGISTRATION_TYPE_KEY = 'VESSEL_REGISTRATION';
|
||||||
|
|
||||||
type VesselRegStatus = 'Pending' | 'Under Review' | 'Approved' | 'Rejected' | 'Correction Required';
|
type VesselRegStatus = 'Pending' | 'Under Review' | 'Approved' | 'Rejected' | 'Correction Required';
|
||||||
type VesselCategory = 'Inland Waterway Vessel' | 'Sea-going Vessel (International)';
|
type VesselCategory = 'Inland Waterway Vessel' | 'Sea-going Vessel (International)';
|
||||||
type RenewalStatus = 'Valid' | 'Due Soon' | 'Overdue' | 'Not Applicable';
|
type RenewalStatus = 'Valid' | 'Due Soon' | 'Overdue' | 'Not Applicable';
|
||||||
@@ -118,6 +131,8 @@ function CertificateCard({ label, description }: { label: string; description: s
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
export function VesselRegistrationPage() {
|
export function VesselRegistrationPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { data: applications } = useGetMyApplicationsQuery();
|
||||||
const [registration, setRegistration] = useState<VesselRegistration | null>(null);
|
const [registration, setRegistration] = useState<VesselRegistration | null>(null);
|
||||||
const [fetchTrigger] = useApiMutation<VesselRegistration>();
|
const [fetchTrigger] = useApiMutation<VesselRegistration>();
|
||||||
const fetched = useRef(false);
|
const fetched = useRef(false);
|
||||||
@@ -132,6 +147,14 @@ export function VesselRegistrationPage() {
|
|||||||
.catch(() => {/* no registration yet */});
|
.catch(() => {/* no registration yet */});
|
||||||
}, [fetchTrigger]);
|
}, [fetchTrigger]);
|
||||||
|
|
||||||
|
// Drafts and anything still moving through review — the applicant's own
|
||||||
|
// registration applications, straight from the licensing queue.
|
||||||
|
const inFlight = (applications?.items ?? []).filter(
|
||||||
|
(app) =>
|
||||||
|
app.licenseType?.key === REGISTRATION_TYPE_KEY &&
|
||||||
|
!TERMINAL_STATUSES.includes(app.status),
|
||||||
|
);
|
||||||
|
|
||||||
const certs = registration?.category === 'Sea-going Vessel (International)'
|
const certs = registration?.category === 'Sea-going Vessel (International)'
|
||||||
? SEAGOING_CERTIFICATES
|
? SEAGOING_CERTIFICATES
|
||||||
: INLAND_CERTIFICATES;
|
: INLAND_CERTIFICATES;
|
||||||
@@ -148,6 +171,54 @@ export function VesselRegistrationPage() {
|
|||||||
</div>
|
</div>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
{/* ── Drafts / submitted applications ───────────────────────────── */}
|
||||||
|
{inFlight.length > 0 && (
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Text fw={700} fz="md">{t('vesselRegistration.inFlight.title')}</Text>
|
||||||
|
{inFlight.map((app) => {
|
||||||
|
const isDraft = app.status === 'DRAFT';
|
||||||
|
const needsAction = app.status === 'RESUBMIT_REQUIRED';
|
||||||
|
return (
|
||||||
|
<Card key={app.id} withBorder radius="md" p="md">
|
||||||
|
<Group justify="space-between" wrap="nowrap">
|
||||||
|
<div>
|
||||||
|
<Group gap="xs">
|
||||||
|
<Text fw={600}>{app.applicationNumber}</Text>
|
||||||
|
{app.kind === 'RENEWAL' && (
|
||||||
|
<Badge size="sm" variant="light">
|
||||||
|
{t('vesselRegistration.inFlight.renewalBadge')}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
<Progress value={STATUS_PROGRESS[app.status]} mt="xs" w={260} />
|
||||||
|
</div>
|
||||||
|
<Group wrap="nowrap">
|
||||||
|
<Badge color={STATUS_COLORS[app.status]}>
|
||||||
|
{STATUS_LABELS[app.status]}
|
||||||
|
</Badge>
|
||||||
|
<Button
|
||||||
|
size="compact-sm"
|
||||||
|
variant={needsAction ? 'filled' : 'light'}
|
||||||
|
color={needsAction ? 'orange' : undefined}
|
||||||
|
rightSection={<IconArrowRight size={14} />}
|
||||||
|
onClick={() =>
|
||||||
|
navigate(`/licensing/${REGISTRATION_TYPE_KEY}/applications/${app.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isDraft
|
||||||
|
? t('common.continue')
|
||||||
|
: needsAction
|
||||||
|
? t('vesselRegistration.inFlight.fix')
|
||||||
|
: t('vesselRegistration.inFlight.view')}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* ── No registration yet ───────────────────────────────────────── */}
|
{/* ── No registration yet ───────────────────────────────────────── */}
|
||||||
{!registration && (
|
{!registration && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user