mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
Add AttachmentsModal and refactor MedicalVerificationPage with AdvancedTable integration
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
Center,
|
||||
Container,
|
||||
Group,
|
||||
Loader,
|
||||
Modal,
|
||||
Paper,
|
||||
Stack,
|
||||
Table,
|
||||
Tabs,
|
||||
Text,
|
||||
Textarea,
|
||||
@@ -18,12 +17,16 @@ import {
|
||||
import {
|
||||
IconAnchor,
|
||||
IconCheck,
|
||||
IconEye,
|
||||
IconInbox,
|
||||
IconPaperclip,
|
||||
IconStethoscope,
|
||||
IconX,
|
||||
} from '@tabler/icons-react';
|
||||
import { notify } from '@ema-platform/ui';
|
||||
import { AdvancedTable, notify, type AdvancedColumn } from '@ema-platform/ui';
|
||||
import {
|
||||
extractErrorMessage,
|
||||
useGetAttachmentsQuery,
|
||||
useGetPendingMedicalQuery,
|
||||
useGetPendingSeaServiceQuery,
|
||||
useVerifyMedicalCertificateMutation,
|
||||
@@ -35,6 +38,8 @@ import type {
|
||||
SeafarerProfileSummary,
|
||||
} from '@ema-platform/api';
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
function ownerName(profile?: SeafarerProfileSummary): string {
|
||||
if (!profile) return '—';
|
||||
return (
|
||||
@@ -90,6 +95,89 @@ function RejectModal({
|
||||
);
|
||||
}
|
||||
|
||||
/** Attachments / Evidence Modal for viewing uploaded files for a record */
|
||||
function AttachmentsModal({
|
||||
opened,
|
||||
ownerType,
|
||||
ownerId,
|
||||
title,
|
||||
onClose,
|
||||
}: {
|
||||
opened: boolean;
|
||||
ownerType: 'MEDICAL_CERTIFICATE' | 'SEA_SERVICE_RECORD';
|
||||
ownerId: string | null;
|
||||
title: string;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const { data: attachments, isLoading } = useGetAttachmentsQuery(
|
||||
{ ownerType, ownerId: ownerId ?? '' },
|
||||
{ skip: !ownerId },
|
||||
);
|
||||
|
||||
const files = useMemo(
|
||||
() => (attachments ?? []).flatMap((a) => a.files ?? []),
|
||||
[attachments],
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title={title} size="md" centered>
|
||||
<Stack gap="sm">
|
||||
{isLoading ? (
|
||||
<Center py="xl">
|
||||
<Loader size="sm" />
|
||||
</Center>
|
||||
) : files.length === 0 ? (
|
||||
<Center py="xl">
|
||||
<Group gap="xs" c="dimmed">
|
||||
<IconInbox size={18} />
|
||||
<Text size="sm" c="dimmed">
|
||||
No evidence attachments uploaded for this record.
|
||||
</Text>
|
||||
</Group>
|
||||
</Center>
|
||||
) : (
|
||||
files.map((file) => (
|
||||
<Paper key={file.id} withBorder p="xs" radius="sm">
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Group gap="xs" wrap="nowrap" style={{ minWidth: 0 }}>
|
||||
<IconPaperclip size={18} />
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<Text size="sm" fw={500} truncate>
|
||||
{file.originalName}
|
||||
</Text>
|
||||
{file.sizeBytes && (
|
||||
<Text size="xs" c="dimmed">
|
||||
{(file.sizeBytes / 1024).toFixed(1)} KB
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</Group>
|
||||
{file.url ? (
|
||||
<Button
|
||||
size="compact-xs"
|
||||
variant="light"
|
||||
leftSection={<IconEye size={14} />}
|
||||
component="a"
|
||||
href={file.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
View
|
||||
</Button>
|
||||
) : (
|
||||
<Badge size="sm" variant="light" color="gray">
|
||||
No URL
|
||||
</Badge>
|
||||
)}
|
||||
</Group>
|
||||
</Paper>
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The record-verification workspace (US-SSM-003/007): everything seafarers
|
||||
* have submitted and no officer has ruled on yet, oldest first. VERIFIED
|
||||
@@ -97,31 +185,282 @@ function RejectModal({
|
||||
* certificate starts satisfying the submission gate.
|
||||
*/
|
||||
export function MedicalVerificationPage() {
|
||||
const { data: pendingMedical, isLoading: loadingMedical } =
|
||||
useGetPendingMedicalQuery();
|
||||
const { data: pendingSeaService, isLoading: loadingSeaService } =
|
||||
useGetPendingSeaServiceQuery();
|
||||
const {
|
||||
data: pendingMedical,
|
||||
isLoading: loadingMedical,
|
||||
isFetching: fetchingMedical,
|
||||
refetch: refetchMedical,
|
||||
} = useGetPendingMedicalQuery();
|
||||
|
||||
const {
|
||||
data: pendingSeaService,
|
||||
isLoading: loadingSeaService,
|
||||
isFetching: fetchingSeaService,
|
||||
refetch: refetchSeaService,
|
||||
} = useGetPendingSeaServiceQuery();
|
||||
|
||||
const [verifyMedical, { isLoading: rulingMedical }] =
|
||||
useVerifyMedicalCertificateMutation();
|
||||
const [verifySeaService, { isLoading: rulingSeaService }] =
|
||||
useVerifySeaServiceRecordMutation();
|
||||
|
||||
const [rejectMedical, setRejectMedical] = useState<MedicalCertificate | null>(
|
||||
null,
|
||||
);
|
||||
const [rejectSeaService, setRejectSeaService] =
|
||||
useState<SeaServiceRecord | null>(null);
|
||||
|
||||
const rule = async (
|
||||
run: () => Promise<unknown>,
|
||||
done: string,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
await run();
|
||||
notify.success(done);
|
||||
} catch (error) {
|
||||
notify.error(extractErrorMessage(error, 'Could not record the ruling'));
|
||||
}
|
||||
};
|
||||
const [attachmentModal, setAttachmentModal] = useState<{
|
||||
ownerType: 'MEDICAL_CERTIFICATE' | 'SEA_SERVICE_RECORD';
|
||||
ownerId: string;
|
||||
title: string;
|
||||
} | null>(null);
|
||||
|
||||
const [medicalPage, setMedicalPage] = useState(0);
|
||||
const [seaServicePage, setSeaServicePage] = useState(0);
|
||||
|
||||
const rule = useCallback(
|
||||
async (run: () => Promise<unknown>, done: string): Promise<void> => {
|
||||
try {
|
||||
await run();
|
||||
notify.success(done);
|
||||
} catch (error) {
|
||||
notify.error(extractErrorMessage(error, 'Could not record the ruling'));
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const pendingMedicalList = useMemo(
|
||||
() => pendingMedical ?? [],
|
||||
[pendingMedical],
|
||||
);
|
||||
const pendingSeaServiceList = useMemo(
|
||||
() => pendingSeaService ?? [],
|
||||
[pendingSeaService],
|
||||
);
|
||||
|
||||
const pagedMedical = useMemo(() => {
|
||||
const start = medicalPage * PAGE_SIZE;
|
||||
return pendingMedicalList.slice(start, start + PAGE_SIZE);
|
||||
}, [pendingMedicalList, medicalPage]);
|
||||
|
||||
const pagedSeaService = useMemo(() => {
|
||||
const start = seaServicePage * PAGE_SIZE;
|
||||
return pendingSeaServiceList.slice(start, start + PAGE_SIZE);
|
||||
}, [pendingSeaServiceList, seaServicePage]);
|
||||
|
||||
const medicalColumns: AdvancedColumn<MedicalCertificate>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
header: 'Seafarer',
|
||||
label: 'Seafarer',
|
||||
accessorKey: 'profile.firstName',
|
||||
cell: ({ row }) => (
|
||||
<div>
|
||||
<Text size="sm" fw={500}>
|
||||
{ownerName(row.original.profile)}
|
||||
</Text>
|
||||
{row.original.profile?.seafarerNumber && (
|
||||
<Text size="xs" c="dimmed" ff="monospace">
|
||||
{row.original.profile.seafarerNumber}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Issuer',
|
||||
label: 'Issuer',
|
||||
accessorKey: 'issuerName',
|
||||
cell: ({ row }) => (
|
||||
<div>
|
||||
<Text size="sm">{row.original.issuerName}</Text>
|
||||
{row.original.certificateNumber && (
|
||||
<Text size="xs" c="dimmed">
|
||||
№ {row.original.certificateNumber}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Validity',
|
||||
label: 'Validity',
|
||||
cell: ({ row }) => (
|
||||
<Text size="sm">
|
||||
{row.original.issueDate} → {row.original.expiryDate}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Fitness',
|
||||
label: 'Fitness',
|
||||
accessorKey: 'fitnessStatus',
|
||||
cell: ({ row }) => (
|
||||
<Badge size="sm" variant="light">
|
||||
{row.original.fitnessStatus}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
label: 'Actions',
|
||||
align: 'right',
|
||||
cell: ({ row }) => (
|
||||
<Group gap="xs" justify="flex-end" wrap="nowrap">
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="blue"
|
||||
variant="light"
|
||||
leftSection={<IconPaperclip size={14} />}
|
||||
onClick={() =>
|
||||
setAttachmentModal({
|
||||
ownerType: 'MEDICAL_CERTIFICATE',
|
||||
ownerId: row.original.id,
|
||||
title: `Evidence for ${ownerName(row.original.profile)} Medical Certificate`,
|
||||
})
|
||||
}
|
||||
>
|
||||
Evidence
|
||||
</Button>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="teal"
|
||||
leftSection={<IconCheck size={14} />}
|
||||
loading={rulingMedical}
|
||||
onClick={() =>
|
||||
rule(
|
||||
() =>
|
||||
verifyMedical({
|
||||
id: row.original.id,
|
||||
outcome: 'VERIFIED',
|
||||
}).unwrap(),
|
||||
'Certificate verified',
|
||||
)
|
||||
}
|
||||
>
|
||||
Verify
|
||||
</Button>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="red"
|
||||
variant="light"
|
||||
leftSection={<IconX size={14} />}
|
||||
onClick={() => setRejectMedical(row.original)}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Group>
|
||||
),
|
||||
},
|
||||
],
|
||||
[rulingMedical, rule, verifyMedical],
|
||||
);
|
||||
|
||||
const seaServiceColumns: AdvancedColumn<SeaServiceRecord>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
header: 'Seafarer',
|
||||
label: 'Seafarer',
|
||||
accessorKey: 'profile.firstName',
|
||||
cell: ({ row }) => (
|
||||
<div>
|
||||
<Text size="sm" fw={500}>
|
||||
{ownerName(row.original.profile)}
|
||||
</Text>
|
||||
{row.original.profile?.seafarerNumber && (
|
||||
<Text size="xs" c="dimmed" ff="monospace">
|
||||
{row.original.profile.seafarerNumber}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Vessel',
|
||||
label: 'Vessel',
|
||||
accessorKey: 'vesselName',
|
||||
cell: ({ row }) => (
|
||||
<div>
|
||||
<Text size="sm">{row.original.vesselName}</Text>
|
||||
{row.original.imoNumber && (
|
||||
<Text size="xs" c="dimmed">
|
||||
IMO {row.original.imoNumber}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Rank',
|
||||
label: 'Rank',
|
||||
accessorKey: 'rank',
|
||||
cell: ({ row }) => <Text size="sm">{row.original.rank}</Text>,
|
||||
},
|
||||
{
|
||||
header: 'Period',
|
||||
label: 'Period',
|
||||
cell: ({ row }) => (
|
||||
<Text size="sm">
|
||||
{row.original.engagementDate} → {row.original.dischargeDate}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
label: 'Actions',
|
||||
align: 'right',
|
||||
cell: ({ row }) => (
|
||||
<Group gap="xs" justify="flex-end" wrap="nowrap">
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="blue"
|
||||
variant="light"
|
||||
leftSection={<IconPaperclip size={14} />}
|
||||
onClick={() =>
|
||||
setAttachmentModal({
|
||||
ownerType: 'SEA_SERVICE_RECORD',
|
||||
ownerId: row.original.id,
|
||||
title: `Evidence for ${ownerName(row.original.profile)} Sea Service Record`,
|
||||
})
|
||||
}
|
||||
>
|
||||
Evidence
|
||||
</Button>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="teal"
|
||||
leftSection={<IconCheck size={14} />}
|
||||
loading={rulingSeaService}
|
||||
onClick={() =>
|
||||
rule(
|
||||
() =>
|
||||
verifySeaService({
|
||||
id: row.original.id,
|
||||
outcome: 'VERIFIED',
|
||||
}).unwrap(),
|
||||
'Sea-service record verified',
|
||||
)
|
||||
}
|
||||
>
|
||||
Verify
|
||||
</Button>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="red"
|
||||
variant="light"
|
||||
leftSection={<IconX size={14} />}
|
||||
onClick={() => setRejectSeaService(row.original)}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Group>
|
||||
),
|
||||
},
|
||||
],
|
||||
[rulingSeaService, rule, verifySeaService],
|
||||
);
|
||||
|
||||
return (
|
||||
<Container size="xl" py="md">
|
||||
@@ -137,192 +476,52 @@ export function MedicalVerificationPage() {
|
||||
<Tabs defaultValue="medical" keepMounted={false}>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="medical" leftSection={<IconStethoscope size={16} />}>
|
||||
Medical ({pendingMedical?.length ?? 0})
|
||||
Medical ({pendingMedicalList.length})
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value="sea-service" leftSection={<IconAnchor size={16} />}>
|
||||
Sea Service ({pendingSeaService?.length ?? 0})
|
||||
Sea Service ({pendingSeaServiceList.length})
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value="medical" pt="md">
|
||||
<Card withBorder padding={0}>
|
||||
{loadingMedical ? (
|
||||
<Center h={160}>
|
||||
<Loader />
|
||||
</Center>
|
||||
) : (pendingMedical ?? []).length === 0 ? (
|
||||
<Center h={120}>
|
||||
<Text size="sm" c="dimmed">
|
||||
Nothing awaiting verification.
|
||||
</Text>
|
||||
</Center>
|
||||
) : (
|
||||
<Table highlightOnHover>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Seafarer</Table.Th>
|
||||
<Table.Th>Issuer</Table.Th>
|
||||
<Table.Th>Validity</Table.Th>
|
||||
<Table.Th>Fitness</Table.Th>
|
||||
<Table.Th />
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{(pendingMedical ?? []).map((certificate) => (
|
||||
<Table.Tr key={certificate.id}>
|
||||
<Table.Td>
|
||||
<Text size="sm" fw={500}>
|
||||
{ownerName(certificate.profile)}
|
||||
</Text>
|
||||
{certificate.profile?.seafarerNumber && (
|
||||
<Text size="xs" c="dimmed" ff="monospace">
|
||||
{certificate.profile.seafarerNumber}
|
||||
</Text>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{certificate.issuerName}
|
||||
{certificate.certificateNumber && (
|
||||
<Text size="xs" c="dimmed">
|
||||
№ {certificate.certificateNumber}
|
||||
</Text>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{certificate.issueDate} → {certificate.expiryDate}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge size="sm" variant="light">
|
||||
{certificate.fitnessStatus}
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Group gap="xs" justify="flex-end" wrap="nowrap">
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="teal"
|
||||
leftSection={<IconCheck size={14} />}
|
||||
loading={rulingMedical}
|
||||
onClick={() =>
|
||||
rule(
|
||||
() =>
|
||||
verifyMedical({
|
||||
id: certificate.id,
|
||||
outcome: 'VERIFIED',
|
||||
}).unwrap(),
|
||||
'Certificate verified',
|
||||
)
|
||||
}
|
||||
>
|
||||
Verify
|
||||
</Button>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="red"
|
||||
variant="light"
|
||||
leftSection={<IconX size={14} />}
|
||||
onClick={() => setRejectMedical(certificate)}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
)}
|
||||
</Card>
|
||||
<AdvancedTable
|
||||
columns={medicalColumns}
|
||||
data={pagedMedical}
|
||||
tableName="Medical Verification"
|
||||
itemCount={pendingMedicalList.length}
|
||||
pageIndex={medicalPage}
|
||||
onPageChange={setMedicalPage}
|
||||
pageSize={PAGE_SIZE}
|
||||
refresh={refetchMedical}
|
||||
isLoading={loadingMedical || fetchingMedical}
|
||||
emptyText="Nothing awaiting verification."
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="sea-service" pt="md">
|
||||
<Card withBorder padding={0}>
|
||||
{loadingSeaService ? (
|
||||
<Center h={160}>
|
||||
<Loader />
|
||||
</Center>
|
||||
) : (pendingSeaService ?? []).length === 0 ? (
|
||||
<Center h={120}>
|
||||
<Text size="sm" c="dimmed">
|
||||
Nothing awaiting verification.
|
||||
</Text>
|
||||
</Center>
|
||||
) : (
|
||||
<Table highlightOnHover>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Seafarer</Table.Th>
|
||||
<Table.Th>Vessel</Table.Th>
|
||||
<Table.Th>Rank</Table.Th>
|
||||
<Table.Th>Period</Table.Th>
|
||||
<Table.Th />
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{(pendingSeaService ?? []).map((record) => (
|
||||
<Table.Tr key={record.id}>
|
||||
<Table.Td>
|
||||
<Text size="sm" fw={500}>
|
||||
{ownerName(record.profile)}
|
||||
</Text>
|
||||
{record.profile?.seafarerNumber && (
|
||||
<Text size="xs" c="dimmed" ff="monospace">
|
||||
{record.profile.seafarerNumber}
|
||||
</Text>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{record.vesselName}
|
||||
{record.imoNumber && (
|
||||
<Text size="xs" c="dimmed">
|
||||
IMO {record.imoNumber}
|
||||
</Text>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>{record.rank}</Table.Td>
|
||||
<Table.Td>
|
||||
{record.engagementDate} → {record.dischargeDate}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Group gap="xs" justify="flex-end" wrap="nowrap">
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="teal"
|
||||
leftSection={<IconCheck size={14} />}
|
||||
loading={rulingSeaService}
|
||||
onClick={() =>
|
||||
rule(
|
||||
() =>
|
||||
verifySeaService({
|
||||
id: record.id,
|
||||
outcome: 'VERIFIED',
|
||||
}).unwrap(),
|
||||
'Sea-service record verified',
|
||||
)
|
||||
}
|
||||
>
|
||||
Verify
|
||||
</Button>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
color="red"
|
||||
variant="light"
|
||||
leftSection={<IconX size={14} />}
|
||||
onClick={() => setRejectSeaService(record)}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
)}
|
||||
</Card>
|
||||
<AdvancedTable
|
||||
columns={seaServiceColumns}
|
||||
data={pagedSeaService}
|
||||
tableName="Sea Service Verification"
|
||||
itemCount={pendingSeaServiceList.length}
|
||||
pageIndex={seaServicePage}
|
||||
onPageChange={setSeaServicePage}
|
||||
pageSize={PAGE_SIZE}
|
||||
refresh={refetchSeaService}
|
||||
isLoading={loadingSeaService || fetchingSeaService}
|
||||
emptyText="Nothing awaiting verification."
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
|
||||
<AttachmentsModal
|
||||
opened={Boolean(attachmentModal)}
|
||||
ownerType={attachmentModal?.ownerType ?? 'MEDICAL_CERTIFICATE'}
|
||||
ownerId={attachmentModal?.ownerId ?? null}
|
||||
title={attachmentModal?.title ?? 'Evidence Attachments'}
|
||||
onClose={() => setAttachmentModal(null)}
|
||||
/>
|
||||
|
||||
<RejectModal
|
||||
title="Reject medical certificate"
|
||||
opened={Boolean(rejectMedical)}
|
||||
|
||||
@@ -156,7 +156,7 @@ const EMPTY_SEA_SERVICE = {
|
||||
};
|
||||
|
||||
function SeaServiceTab() {
|
||||
const { data: records, isLoading } = useGetMySeaServiceRecordsQuery();
|
||||
const { data: records, isLoading, refetch } = useGetMySeaServiceRecordsQuery();
|
||||
const { data: seaTime } = useGetMySeaTimeQuery();
|
||||
const [createRecord, { isLoading: creating }] =
|
||||
useCreateSeaServiceRecordMutation();
|
||||
@@ -349,6 +349,7 @@ function SeaServiceTab() {
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={pageSize}
|
||||
isLoading={isLoading}
|
||||
refresh={refetch}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
@@ -461,7 +462,7 @@ const EMPTY_MEDICAL = {
|
||||
};
|
||||
|
||||
function MedicalTab() {
|
||||
const { data: certificates, isLoading } = useGetMyMedicalCertificatesQuery();
|
||||
const { data: certificates, isLoading, refetch } = useGetMyMedicalCertificatesQuery();
|
||||
const [createCertificate, { isLoading: creating }] =
|
||||
useCreateMedicalCertificateMutation();
|
||||
const [updateCertificate, { isLoading: updating }] =
|
||||
@@ -654,6 +655,7 @@ function MedicalTab() {
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={pageSize}
|
||||
isLoading={isLoading}
|
||||
refresh={refetch}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user