mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
Refactor SeaServiceTab and MedicalTab to use AdvancedTable for improved data handling and pagination
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
Anchor,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
FileButton,
|
||||
Group,
|
||||
Loader,
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
Paper,
|
||||
Select,
|
||||
Stack,
|
||||
Table,
|
||||
Tabs,
|
||||
Text,
|
||||
TextInput,
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
IconTrash,
|
||||
} from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
import { notify } from '@ema-platform/ui';
|
||||
import { AdvancedTable, notify, useServerTable, type AdvancedColumn } from '@ema-platform/ui';
|
||||
import {
|
||||
extractErrorMessage,
|
||||
uploadDocument,
|
||||
@@ -237,7 +237,82 @@ function SeaServiceTab() {
|
||||
form.dischargeDate &&
|
||||
form.engagementDate < form.dischargeDate;
|
||||
|
||||
if (isLoading) return <Loader />;
|
||||
const { setPageIndex, pageSize, paginate } = useServerTable({ pageSize: 10 });
|
||||
const page = paginate(records ?? []);
|
||||
|
||||
const columns: AdvancedColumn<SeaServiceRecord>[] = [
|
||||
{
|
||||
header: 'Vessel',
|
||||
cell: ({ row }) => (
|
||||
<>
|
||||
<Text fw={600} size="sm">
|
||||
{row.original.vesselName}
|
||||
</Text>
|
||||
{row.original.imoNumber && (
|
||||
<Text size="xs" c="dimmed">
|
||||
IMO {row.original.imoNumber}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{ header: 'Rank', accessorKey: 'rank' },
|
||||
{ header: 'From', accessorKey: 'engagementDate' },
|
||||
{ header: 'To', accessorKey: 'dischargeDate' },
|
||||
{
|
||||
header: 'Status',
|
||||
cell: ({ row }) => (
|
||||
<Tooltip
|
||||
label={row.original.verificationRemark ?? ''}
|
||||
disabled={!row.original.verificationRemark}
|
||||
>
|
||||
<Badge color={RECORD_STATUS_COLORS[row.original.status]}>
|
||||
{row.original.status}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
label: 'Actions',
|
||||
align: 'right',
|
||||
cell: ({ row }) => {
|
||||
const record = row.original;
|
||||
const locked = record.status !== 'SUBMITTED';
|
||||
return (
|
||||
<Group gap="xs" justify="flex-end" wrap="nowrap">
|
||||
<Tooltip label="Evidence">
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
onClick={() => setEvidenceFor(record.id)}
|
||||
>
|
||||
<IconPaperclip size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={locked ? 'Verified records are frozen' : 'Edit'}>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
disabled={locked}
|
||||
onClick={() => openEdit(record)}
|
||||
>
|
||||
<IconEdit size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={locked ? 'Verified records are frozen' : 'Delete'}>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="red"
|
||||
disabled={locked}
|
||||
onClick={() => remove(record)}
|
||||
>
|
||||
<IconTrash size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
@@ -264,87 +339,18 @@ function SeaServiceTab() {
|
||||
</Text>
|
||||
</Paper>
|
||||
) : (
|
||||
<Table.ScrollContainer minWidth={720}>
|
||||
<Table striped highlightOnHover>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Vessel</Table.Th>
|
||||
<Table.Th>Rank</Table.Th>
|
||||
<Table.Th>From</Table.Th>
|
||||
<Table.Th>To</Table.Th>
|
||||
<Table.Th>Status</Table.Th>
|
||||
<Table.Th />
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{(records ?? []).map((record) => {
|
||||
const locked = record.status !== 'SUBMITTED';
|
||||
return (
|
||||
<Table.Tr key={record.id}>
|
||||
<Table.Td>
|
||||
<Text fw={600} size="sm">
|
||||
{record.vesselName}
|
||||
</Text>
|
||||
{record.imoNumber && (
|
||||
<Text size="xs" c="dimmed">
|
||||
IMO {record.imoNumber}
|
||||
</Text>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>{record.rank}</Table.Td>
|
||||
<Table.Td>{record.engagementDate}</Table.Td>
|
||||
<Table.Td>{record.dischargeDate}</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip
|
||||
label={record.verificationRemark ?? ''}
|
||||
disabled={!record.verificationRemark}
|
||||
>
|
||||
<Badge color={RECORD_STATUS_COLORS[record.status]}>
|
||||
{record.status}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Group gap="xs" justify="flex-end" wrap="nowrap">
|
||||
<Tooltip label="Evidence">
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
onClick={() => setEvidenceFor(record.id)}
|
||||
>
|
||||
<IconPaperclip size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
label={locked ? 'Verified records are frozen' : 'Edit'}
|
||||
>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
disabled={locked}
|
||||
onClick={() => openEdit(record)}
|
||||
>
|
||||
<IconEdit size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
label={locked ? 'Verified records are frozen' : 'Delete'}
|
||||
>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="red"
|
||||
disabled={locked}
|
||||
onClick={() => remove(record)}
|
||||
>
|
||||
<IconTrash size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
})}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
<Card withBorder padding={0}>
|
||||
<AdvancedTable
|
||||
columns={columns}
|
||||
data={page.rows}
|
||||
tableName="Sea service"
|
||||
itemCount={page.itemCount}
|
||||
pageIndex={page.pageIndex}
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={pageSize}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
@@ -530,7 +536,95 @@ function MedicalTab() {
|
||||
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
|
||||
if (isLoading) return <Loader />;
|
||||
const { setPageIndex, pageSize, paginate } = useServerTable({ pageSize: 10 });
|
||||
const page = paginate(certificates ?? []);
|
||||
|
||||
const columns: AdvancedColumn<MedicalCertificate>[] = [
|
||||
{
|
||||
header: 'Issuer',
|
||||
cell: ({ row }) => (
|
||||
<>
|
||||
<Text fw={600} size="sm">
|
||||
{row.original.issuerName}
|
||||
</Text>
|
||||
{row.original.certificateNumber && (
|
||||
<Text size="xs" c="dimmed">
|
||||
№ {row.original.certificateNumber}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{ header: 'Issued', accessorKey: 'issueDate' },
|
||||
{
|
||||
header: 'Expires',
|
||||
cell: ({ row }) => (
|
||||
<Group gap={6} wrap="nowrap">
|
||||
{row.original.expiryDate}
|
||||
{row.original.expiryDate < today && <Badge color="red">Expired</Badge>}
|
||||
</Group>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Fitness',
|
||||
cell: ({ row }) =>
|
||||
FITNESS_OPTIONS.find((o) => o.value === row.original.fitnessStatus)
|
||||
?.label ?? row.original.fitnessStatus,
|
||||
},
|
||||
{
|
||||
header: 'Status',
|
||||
cell: ({ row }) => (
|
||||
<Tooltip
|
||||
label={row.original.verificationRemark ?? ''}
|
||||
disabled={!row.original.verificationRemark}
|
||||
>
|
||||
<Badge color={RECORD_STATUS_COLORS[row.original.status]}>
|
||||
{row.original.status}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
label: 'Actions',
|
||||
align: 'right',
|
||||
cell: ({ row }) => {
|
||||
const certificate = row.original;
|
||||
const locked = certificate.status !== 'SUBMITTED';
|
||||
return (
|
||||
<Group gap="xs" justify="flex-end" wrap="nowrap">
|
||||
<Tooltip label="Scan / evidence">
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
onClick={() => setEvidenceFor(certificate.id)}
|
||||
>
|
||||
<IconPaperclip size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={locked ? 'Verified certificates are frozen' : 'Edit'}>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
disabled={locked}
|
||||
onClick={() => openEdit(certificate)}
|
||||
>
|
||||
<IconEdit size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={locked ? 'Verified certificates are frozen' : 'Delete'}>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="red"
|
||||
disabled={locked}
|
||||
onClick={() => remove(certificate)}
|
||||
>
|
||||
<IconTrash size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
@@ -550,103 +644,18 @@ function MedicalTab() {
|
||||
</Text>
|
||||
</Paper>
|
||||
) : (
|
||||
<Table.ScrollContainer minWidth={720}>
|
||||
<Table striped highlightOnHover>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Issuer</Table.Th>
|
||||
<Table.Th>Issued</Table.Th>
|
||||
<Table.Th>Expires</Table.Th>
|
||||
<Table.Th>Fitness</Table.Th>
|
||||
<Table.Th>Status</Table.Th>
|
||||
<Table.Th />
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{(certificates ?? []).map((certificate) => {
|
||||
const locked = certificate.status !== 'SUBMITTED';
|
||||
const expired = certificate.expiryDate < today;
|
||||
return (
|
||||
<Table.Tr key={certificate.id}>
|
||||
<Table.Td>
|
||||
<Text fw={600} size="sm">
|
||||
{certificate.issuerName}
|
||||
</Text>
|
||||
{certificate.certificateNumber && (
|
||||
<Text size="xs" c="dimmed">
|
||||
№ {certificate.certificateNumber}
|
||||
</Text>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>{certificate.issueDate}</Table.Td>
|
||||
<Table.Td>
|
||||
<Group gap={6} wrap="nowrap">
|
||||
{certificate.expiryDate}
|
||||
{expired && <Badge color="red">Expired</Badge>}
|
||||
</Group>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{FITNESS_OPTIONS.find(
|
||||
(o) => o.value === certificate.fitnessStatus,
|
||||
)?.label ?? certificate.fitnessStatus}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Tooltip
|
||||
label={certificate.verificationRemark ?? ''}
|
||||
disabled={!certificate.verificationRemark}
|
||||
>
|
||||
<Badge color={RECORD_STATUS_COLORS[certificate.status]}>
|
||||
{certificate.status}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Group gap="xs" justify="flex-end" wrap="nowrap">
|
||||
<Tooltip label="Scan / evidence">
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
onClick={() => setEvidenceFor(certificate.id)}
|
||||
>
|
||||
<IconPaperclip size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
label={
|
||||
locked ? 'Verified certificates are frozen' : 'Edit'
|
||||
}
|
||||
>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
disabled={locked}
|
||||
onClick={() => openEdit(certificate)}
|
||||
>
|
||||
<IconEdit size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
label={
|
||||
locked
|
||||
? 'Verified certificates are frozen'
|
||||
: 'Delete'
|
||||
}
|
||||
>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="red"
|
||||
disabled={locked}
|
||||
onClick={() => remove(certificate)}
|
||||
>
|
||||
<IconTrash size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
})}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
<Card withBorder padding={0}>
|
||||
<AdvancedTable
|
||||
columns={columns}
|
||||
data={page.rows}
|
||||
tableName="Medical certificates"
|
||||
itemCount={page.itemCount}
|
||||
pageIndex={page.pageIndex}
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={pageSize}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
|
||||
Reference in New Issue
Block a user