UI improvements

This commit is contained in:
estifanos
2026-08-18 11:46:52 +00:00
parent de78052429
commit 98277924a3
4 changed files with 98 additions and 46 deletions

View File

@@ -0,0 +1,76 @@
import { Badge, Button, Group, Progress, Text } from '@mantine/core';
import { IconArrowRight } from '@tabler/icons-react';
import type { TFunction } from 'i18next';
import type { AdvancedColumn } from '@ema-platform/ui';
import {
STATUS_COLORS,
STATUS_LABELS,
STATUS_PROGRESS,
type LicenseApplication,
} from '@ema-platform/api';
/** Columns for the applicant's in-flight vessel registration applications. */
export function inFlightColumns(
t: TFunction,
deps: { onOpen: (app: LicenseApplication) => void },
): AdvancedColumn<LicenseApplication>[] {
return [
{
header: t('applications.table.applicationNumber'),
cell: ({ row }) => (
<Group gap="xs" wrap="nowrap">
<Text fw={600} fz="sm">{row.original.applicationNumber}</Text>
{row.original.kind === 'RENEWAL' && (
<Badge size="sm" variant="light">
{t('vesselRegistration.inFlight.renewalBadge')}
</Badge>
)}
</Group>
),
},
{
header: t('common.status'),
cell: ({ row }) => (
<Badge color={STATUS_COLORS[row.original.status]} variant="light">
{STATUS_LABELS[row.original.status]}
</Badge>
),
},
{
header: t('applications.table.progress'),
size: 140,
cell: ({ row }) => (
<Progress
value={STATUS_PROGRESS[row.original.status]}
color={STATUS_COLORS[row.original.status]}
size="sm"
radius="xl"
/>
),
},
{
header: '',
label: t('common.actions'),
align: 'right',
cell: ({ row }) => {
const app = row.original;
const needsAction = app.status === 'RESUBMIT_REQUIRED';
return (
<Button
size="compact-sm"
variant={needsAction ? 'filled' : 'light'}
color={needsAction ? 'orange' : undefined}
rightSection={<IconArrowRight size={14} />}
onClick={() => deps.onOpen(app)}
>
{app.status === 'DRAFT'
? t('common.continue')
: needsAction
? t('vesselRegistration.inFlight.fix')
: t('vesselRegistration.inFlight.view')}
</Button>
);
},
},
];
}

View File

@@ -9,7 +9,6 @@ import {
Divider,
Group,
Paper,
Progress,
SimpleGrid,
Stack,
Text,
@@ -27,12 +26,10 @@ import {
IconInfoCircle,
IconClockHour4,
IconTransferIn,
IconArrowRight,
} from '@tabler/icons-react';
import { AdvancedTable } from '@ema-platform/ui';
import { inFlightColumns } from '../inFlightColumns';
import {
STATUS_COLORS,
STATUS_LABELS,
STATUS_PROGRESS,
TERMINAL_STATUSES,
useApiMutation,
useGetMyApplicationsQuery,
@@ -45,6 +42,8 @@ import { authStorage } from '@ema-platform/auth';
/** Registration applications run through the config-driven licensing wizard. */
const REGISTRATION_TYPE_KEY = 'VESSEL_REGISTRATION';
const PAGE_SIZE = 5;
type VesselRegStatus = 'Pending' | 'Under Review' | 'Approved' | 'Rejected' | 'Correction Required';
type VesselCategory = 'Inland Waterway Vessel' | 'Sea-going Vessel (International)';
type RenewalStatus = 'Valid' | 'Due Soon' | 'Overdue' | 'Not Applicable';
@@ -133,6 +132,7 @@ export function VesselRegistrationPage() {
const navigate = useNavigate();
const { t } = useTranslation();
const { data: applications } = useGetMyApplicationsQuery();
const [page, setPage] = useState(0);
const [registration, setRegistration] = useState<VesselRegistration | null>(null);
const [fetchTrigger] = useApiMutation<VesselRegistration>();
const fetched = useRef(false);
@@ -155,6 +155,11 @@ export function VesselRegistrationPage() {
!TERMINAL_STATUSES.includes(app.status),
);
const columns = inFlightColumns(t, {
onOpen: (app) =>
navigate(`/licensing/${REGISTRATION_TYPE_KEY}/applications/${app.id}`),
});
const certs = registration?.category === 'Sea-going Vessel (International)'
? SEAGOING_CERTIFICATES
: INLAND_CERTIFICATES;
@@ -175,47 +180,16 @@ export function VesselRegistrationPage() {
{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>
);
})}
<AdvancedTable
tableName="vessel-registration-in-flight"
columns={columns}
// Client-side paging: getMyApplications returns the whole list.
data={inFlight.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE)}
itemCount={inFlight.length}
pageIndex={page}
pageSize={PAGE_SIZE}
onPageChange={setPage}
/>
</Stack>
)}

View File

@@ -208,6 +208,7 @@ export const am: Translations = {
licence: 'ፍቃድ',
applicant: 'አመልካች',
progress: 'ደረጃ',
applicationNumber: 'የማመልከቻ ቁጥር',
},
actions: {
continue: 'ቀጥል',

View File

@@ -207,6 +207,7 @@ export const en = {
licence: 'Licence',
applicant: 'Applicant',
progress: 'Progress',
applicationNumber: 'Application №',
},
actions: {
continue: 'Continue',