diff --git a/apps/backoffice/src/app/features/seafarer-registration-review/pages/SeafarerRegistrationQueuePage.tsx b/apps/backoffice/src/app/features/seafarer-registration-review/pages/SeafarerRegistrationQueuePage.tsx new file mode 100644 index 000000000..26858983f --- /dev/null +++ b/apps/backoffice/src/app/features/seafarer-registration-review/pages/SeafarerRegistrationQueuePage.tsx @@ -0,0 +1,147 @@ +import { useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Badge, Container, Group, Select, Text, TextInput, Title } from '@mantine/core'; +import { IconSearch } from '@tabler/icons-react'; +import { useDebouncedValue } from '@mantine/hooks'; +import { + SEAFARER_REGISTRATION_STATUS_COLORS, + SEAFARER_REGISTRATION_STATUS_LABELS, + displaySeafarerAnswer, + useListSeafarerRegistrationsQuery, + type SeafarerRegistration, + type SeafarerRegistrationStatus, +} from '@ema-platform/api'; +import { AdvancedTable, type AdvancedColumn } from '@ema-platform/ui'; +import { useDateDisplayer } from '@ema-platform/shared'; + +const PAGE_SIZE = 10; + +const STATUS_FILTERS = (Object.keys(SEAFARER_REGISTRATION_STATUS_LABELS) as SeafarerRegistrationStatus[]) + .filter((s) => s !== 'DRAFT') + .map((value) => ({ value, label: SEAFARER_REGISTRATION_STATUS_LABELS[value] })); + +export function applicantName(r: Pick): string { + return [r.firstName, r.middleName, r.lastName].filter(Boolean).join(' ') || '—'; +} + +/** Submitted seafarer registrations, oldest first — click a row to review it. */ +export function SeafarerRegistrationQueuePage() { + const navigate = useNavigate(); + const showDate = useDateDisplayer(); + const [status, setStatus] = useState(null); + const [search, setSearch] = useState(''); + const [debouncedSearch] = useDebouncedValue(search, 300); + const [page, setPage] = useState(0); + const [pageSize, setPageSize] = useState(PAGE_SIZE); + + const { data, isLoading, isFetching, refetch } = useListSeafarerRegistrationsQuery({ + status: status ?? undefined, + search: debouncedSearch || undefined, + take: pageSize, + skip: page * pageSize, + }); + + const columns: AdvancedColumn[] = useMemo( + () => [ + { + header: 'Registration №', + accessorKey: 'registrationNumber', + cell: ({ row }) => ( + + {row.original.registrationNumber} + + ), + }, + { + header: 'Applicant', + accessorKey: 'lastName', + cell: ({ row }) => ( +
+ + {applicantName(row.original)} + + + {row.original.nationalIdNumber ?? '—'} + +
+ ), + }, + { + header: 'Department', + accessorKey: 'department', + cell: ({ row }) => {displaySeafarerAnswer('department', row.original.department)}, + }, + { + header: 'Submitted', + accessorKey: 'submittedAt', + cell: ({ row }) => ( + {row.original.submittedAt ? showDate(row.original.submittedAt) : '—'} + ), + }, + { + header: 'Status', + accessorKey: 'status', + cell: ({ row }) => ( + + {SEAFARER_REGISTRATION_STATUS_LABELS[row.original.status]} + + ), + }, + ], + [showDate], + ); + + return ( + + + Seafarer Registration Queue + + + Registrations awaiting review. Approval numbers the seafarer and opens their Seaman Book and + BTC applications. + + + } + value={search} + onChange={(e) => { + setSearch(e.currentTarget.value); + setPage(0); + }} + w={280} + /> +