mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
ui chnages
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
import { type StatusTone } from '@ema-platform/shared';
|
||||
import { Badge, Button, Text } from '@mantine/core';
|
||||
import { IconAlertTriangle } from '@tabler/icons-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import { StatusBadge } from '@ema-platform/ui';
|
||||
import { LICENSE_PERMISSIONS, RequirePermission } from '@ema-platform/auth';
|
||||
import type { ExamIncident, ExamIncidentStatus } from '../../types/exam';
|
||||
|
||||
const STATUS_COLOR: Record<ExamIncidentStatus, string> = {
|
||||
OPEN: 'red',
|
||||
UNDER_REVIEW: 'yellow',
|
||||
RESOLVED: 'teal',
|
||||
DISMISSED: 'gray',
|
||||
const STATUS_TONE: Record<ExamIncidentStatus, StatusTone> = {
|
||||
OPEN: 'danger',
|
||||
UNDER_REVIEW: 'warning',
|
||||
RESOLVED: 'success',
|
||||
DISMISSED: 'neutral',
|
||||
};
|
||||
|
||||
export function examIncidentColumns(
|
||||
@@ -56,13 +58,12 @@ export function examIncidentColumns(
|
||||
{
|
||||
header: t('exam.incidents.status'),
|
||||
cell: ({ row }) => (
|
||||
<Badge
|
||||
<StatusBadge
|
||||
tone={STATUS_TONE[row.original.status] ?? 'neutral'}
|
||||
label={t(`exam.incidentStatus.${row.original.status}`)}
|
||||
size="sm"
|
||||
variant="light"
|
||||
color={STATUS_COLOR[row.original.status] ?? 'gray'}
|
||||
>
|
||||
{t(`exam.incidentStatus.${row.original.status}`)}
|
||||
</Badge>
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type StatusTone } from '@ema-platform/shared';
|
||||
import { useState, useEffect, useRef, useMemo } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import {
|
||||
@@ -39,7 +40,7 @@ import {
|
||||
IconCheck,
|
||||
IconX,
|
||||
} from '@tabler/icons-react';
|
||||
import { ModalFooter, notify, useErrorHandler } from '@ema-platform/ui';
|
||||
import { StatusBadge, ModalFooter, notify, useErrorHandler } from '@ema-platform/ui';
|
||||
import { extractErrorMessage } from '@ema-platform/api';
|
||||
import { LICENSE_PERMISSIONS, RequirePermission } from '@ema-platform/auth';
|
||||
import {
|
||||
@@ -57,13 +58,13 @@ import { ExamIncidentsPanel } from '../components/ExamIncidentsPanel';
|
||||
import { PageLoader } from '@ema-platform/ui';
|
||||
import type { ExamStatus, QuestionBrief } from '../types/exam';
|
||||
|
||||
const STATUS_COLOR: Record<string, string> = {
|
||||
PENDING: "gray",
|
||||
ACTIVE: "blue",
|
||||
COMPLETED: "teal",
|
||||
CANCELLED: "red",
|
||||
POSTPONED: "orange",
|
||||
PUBLISHED: "green",
|
||||
const STATUS_TONE: Record<string, StatusTone> = {
|
||||
PENDING: 'neutral',
|
||||
ACTIVE: 'info',
|
||||
COMPLETED: 'success',
|
||||
CANCELLED: 'danger',
|
||||
POSTPONED: 'pending',
|
||||
PUBLISHED: 'success',
|
||||
};
|
||||
|
||||
const FORM_LABEL: Record<string, string> = { ESSAY: "Essay", CHOICE: "Choice" };
|
||||
@@ -313,14 +314,13 @@ export function ExamDetailPage() {
|
||||
</Group>
|
||||
|
||||
{/* Status badge */}
|
||||
<Badge
|
||||
<StatusBadge
|
||||
tone={STATUS_TONE[exam.status]}
|
||||
label={t(`exam.status.${exam.status}`)}
|
||||
size="lg"
|
||||
variant="light"
|
||||
color={STATUS_COLOR[exam.status]}
|
||||
style={{ width: "fit-content" }}
|
||||
>
|
||||
{t(`exam.status.${exam.status}`)}
|
||||
</Badge>
|
||||
/>
|
||||
|
||||
{/* Exam Info */}
|
||||
<Paper withBorder radius="lg" p="lg">
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { type StatusTone } from '@ema-platform/shared';
|
||||
import { Badge, Text } from "@mantine/core";
|
||||
import type { TFunction } from "i18next";
|
||||
import type { AdvancedColumn } from "@ema-platform/ui";
|
||||
import { StatusBadge } from '@ema-platform/ui';
|
||||
import type { Exam } from "../../types/exam";
|
||||
|
||||
const STATUS_COLOR: Record<string, string> = {
|
||||
PENDING: "gray",
|
||||
ACTIVE: "blue",
|
||||
COMPLETED: "teal",
|
||||
CANCELLED: "red",
|
||||
POSTPONED: "orange",
|
||||
PUBLISHED: "green",
|
||||
const STATUS_TONE: Record<string, StatusTone> = {
|
||||
PENDING: 'neutral',
|
||||
ACTIVE: 'info',
|
||||
COMPLETED: 'success',
|
||||
CANCELLED: 'danger',
|
||||
POSTPONED: 'pending',
|
||||
PUBLISHED: 'success',
|
||||
};
|
||||
|
||||
export function examColumns(
|
||||
@@ -72,9 +74,12 @@ export function examColumns(
|
||||
{
|
||||
header: t("exam.columns.status"),
|
||||
cell: ({ row }) => (
|
||||
<Badge size="sm" variant="light" color={STATUS_COLOR[row.original.status]}>
|
||||
{t(`exam.status.${row.original.status}`)}
|
||||
</Badge>
|
||||
<StatusBadge
|
||||
tone={STATUS_TONE[row.original.status]}
|
||||
label={t(`exam.status.${row.original.status}`)}
|
||||
size="sm"
|
||||
variant="light"
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Badge } from '@mantine/core';
|
||||
import { type StatusTone } from '@ema-platform/shared';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import { StatusBadge } from '@ema-platform/ui';
|
||||
import type { Item } from '../../api/item-api';
|
||||
|
||||
const STATUS_COLORS: Record<Item['status'], string> = {
|
||||
DRAFT: 'gray',
|
||||
ACTIVE: 'green',
|
||||
ARCHIVED: 'orange',
|
||||
const STATUS_TONES: Record<Item['status'], StatusTone> = {
|
||||
DRAFT: 'neutral',
|
||||
ACTIVE: 'success',
|
||||
ARCHIVED: 'pending',
|
||||
};
|
||||
|
||||
export function itemColumns(showDate: (date: string) => string): AdvancedColumn<Item>[] {
|
||||
@@ -14,7 +15,7 @@ export function itemColumns(showDate: (date: string) => string): AdvancedColumn<
|
||||
{
|
||||
header: 'Status',
|
||||
cell: ({ row }) => (
|
||||
<Badge color={STATUS_COLORS[row.original.status]}>{row.original.status}</Badge>
|
||||
<StatusBadge tone={STATUS_TONES[row.original.status]} label={row.original.status} />
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { Badge, Button, Text, Tooltip } from '@mantine/core';
|
||||
import { type StatusTone } from '@ema-platform/shared';
|
||||
import { Button, Text, Tooltip } from '@mantine/core';
|
||||
import { IconShieldCog } from '@tabler/icons-react';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import { StatusBadge } from '@ema-platform/ui';
|
||||
import type { Bilingual, IssuedLicense } from '@ema-platform/api';
|
||||
import { LICENSE_PERMISSIONS, RequirePermission } from '@ema-platform/auth';
|
||||
|
||||
const LICENSE_STATUS_COLORS: Record<string, string> = {
|
||||
ACTIVE: 'green',
|
||||
EXPIRED: 'yellow',
|
||||
SUSPENDED: 'orange',
|
||||
CANCELLED: 'red',
|
||||
SUPERSEDED: 'gray',
|
||||
const LICENSE_STATUS_TONES: Record<string, StatusTone> = {
|
||||
ACTIVE: 'success',
|
||||
EXPIRED: 'warning',
|
||||
SUSPENDED: 'pending',
|
||||
CANCELLED: 'danger',
|
||||
SUPERSEDED: 'neutral',
|
||||
};
|
||||
|
||||
export type LifecycleAction = 'suspend' | 'revoke' | 'reinstate';
|
||||
@@ -70,13 +72,12 @@ export function licenseRegisterColumns(
|
||||
{
|
||||
header: 'Status',
|
||||
cell: ({ row }) => (
|
||||
<Badge
|
||||
<StatusBadge
|
||||
tone={LICENSE_STATUS_TONES[row.original.status] ?? 'neutral'}
|
||||
label={row.original.status}
|
||||
size="sm"
|
||||
variant="light"
|
||||
color={LICENSE_STATUS_COLORS[row.original.status] ?? 'gray'}
|
||||
>
|
||||
{row.original.status}
|
||||
</Badge>
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { type StatusTone } from '@ema-platform/shared';
|
||||
import { Badge, Text } from '@mantine/core';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import { StatusBadge } from '@ema-platform/ui';
|
||||
import {
|
||||
seaServiceDays,
|
||||
type MedicalCertificate,
|
||||
@@ -18,10 +20,10 @@ export function ownerName(profile?: SeafarerProfileSummary): string {
|
||||
);
|
||||
}
|
||||
|
||||
const STATUS_COLOR: Record<SeafarerRecordStatus, string> = {
|
||||
SUBMITTED: 'yellow',
|
||||
VERIFIED: 'teal',
|
||||
REJECTED: 'red',
|
||||
const STATUS_TONE: Record<SeafarerRecordStatus, StatusTone> = {
|
||||
SUBMITTED: 'warning',
|
||||
VERIFIED: 'success',
|
||||
REJECTED: 'danger',
|
||||
};
|
||||
|
||||
/** Only meaningful now the queue can show ruled records too. */
|
||||
@@ -33,9 +35,12 @@ function statusColumn<T extends { status: SeafarerRecordStatus }>(
|
||||
label: t('recordVerification.columns.status', 'Status'),
|
||||
accessorKey: 'status',
|
||||
cell: ({ row }) => (
|
||||
<Badge size="sm" variant="light" color={STATUS_COLOR[row.original.status]}>
|
||||
{t(`recordVerification.status.${row.original.status}`, row.original.status)}
|
||||
</Badge>
|
||||
<StatusBadge
|
||||
tone={STATUS_TONE[row.original.status]}
|
||||
label={t(`recordVerification.status.${row.original.status}`, row.original.status)}
|
||||
size="sm"
|
||||
variant="light"
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Modal,
|
||||
Text,
|
||||
TextInput,
|
||||
Card,
|
||||
Alert,
|
||||
Select,
|
||||
NumberInput,
|
||||
@@ -238,9 +237,12 @@ export function QuestionPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
<Card withBorder padding={0}>
|
||||
<Group p="md" justify="space-between" wrap="wrap" gap="sm">
|
||||
<Text fw={600}>{t('question.pool')}</Text>
|
||||
<AdvancedTable
|
||||
columns={columns}
|
||||
data={page.rows}
|
||||
title={t('question.pool')}
|
||||
tableName={t('question.title')}
|
||||
toolbar={
|
||||
<Select
|
||||
placeholder={t('question.filterByCertification')}
|
||||
data={[{ value: '', label: 'All' }, ...certOptions]}
|
||||
@@ -250,12 +252,8 @@ export function QuestionPage() {
|
||||
style={{ width: 280 }}
|
||||
clearable
|
||||
/>
|
||||
</Group>
|
||||
<AdvancedTable
|
||||
columns={columns}
|
||||
data={page.rows}
|
||||
tableName={t('question.title')}
|
||||
itemCount={page.itemCount}
|
||||
}
|
||||
itemCount={page.itemCount}
|
||||
pageIndex={page.pageIndex}
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={pageSize}
|
||||
@@ -264,7 +262,6 @@ export function QuestionPage() {
|
||||
isLoading={isFetching}
|
||||
emptyText={t('question.noQuestions')}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Modal
|
||||
opened={Boolean(reviewTarget)}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { type StatusTone, STATUS_TONE_COLOR } from '@ema-platform/shared';
|
||||
import { Badge, Box, Text } from '@mantine/core';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import { StatusBadge } from '@ema-platform/ui';
|
||||
import type { Result, ResultReviewStatus } from '../../types/result';
|
||||
|
||||
export const STATUS_COLOR: Record<string, string> = {
|
||||
PASSED: 'teal',
|
||||
FAILED: 'red',
|
||||
export const STATUS_TONE: Record<string, StatusTone> = {
|
||||
PASSED: 'success',
|
||||
FAILED: 'danger',
|
||||
};
|
||||
|
||||
/** Where a mark sits in quality control (US-EXAM-011 → 014). */
|
||||
@@ -46,20 +48,22 @@ export function resultColumns(
|
||||
{
|
||||
header: t('result.columns.status'),
|
||||
cell: ({ row }) => (
|
||||
<Badge
|
||||
<StatusBadge
|
||||
tone={STATUS_TONE[row.original.status]}
|
||||
label={t(`result.status.${row.original.status}`)}
|
||||
size="sm"
|
||||
variant="light"
|
||||
color={STATUS_COLOR[row.original.status]}
|
||||
leftSection={
|
||||
<Box
|
||||
w={6}
|
||||
h={6}
|
||||
style={{ borderRadius: 999, background: `var(--mantine-color-${STATUS_COLOR[row.original.status]}-6)` }}
|
||||
style={{
|
||||
borderRadius: 999,
|
||||
background: `var(--mantine-color-${STATUS_TONE_COLOR[STATUS_TONE[row.original.status]]}-6)`,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{t(`result.status.${row.original.status}`)}
|
||||
</Badge>
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Modal,
|
||||
Text,
|
||||
Paper,
|
||||
Card,
|
||||
Loader,
|
||||
Center,
|
||||
Alert,
|
||||
@@ -34,7 +33,7 @@ import {
|
||||
IconSearch,
|
||||
IconSend,
|
||||
} from '@tabler/icons-react';
|
||||
import { notify, BilingualInput, useErrorHandler, AdvancedTable, useServerTable, ModalFooter } from '@ema-platform/ui';
|
||||
import { notify, BilingualInput, useErrorHandler, AdvancedTable, StatusBadge, useServerTable, ModalFooter } from '@ema-platform/ui';
|
||||
import { useDateDisplayer } from '@ema-platform/shared';
|
||||
import type { BilingualValue } from '@ema-platform/ui';
|
||||
import { extractErrorMessage, useLocalized } from '@ema-platform/api';
|
||||
@@ -53,7 +52,7 @@ import { useGetExamsQuery } from '../../../exam/api/exam-api';
|
||||
import { RecordResultModal } from '../../components/RecordResultModal';
|
||||
import type { Result, ResultBreakdown } from '../../types/result';
|
||||
import type { Exam } from '../../../exam/types/exam';
|
||||
import { resultColumns, STATUS_COLOR, REVIEW_COLOR } from './columns';
|
||||
import { resultColumns, STATUS_TONE, REVIEW_COLOR } from './columns';
|
||||
import { resultActionsColumn, type QcAction } from './actions';
|
||||
|
||||
function ResultStat({
|
||||
@@ -327,10 +326,13 @@ export function ResultPage() {
|
||||
<ResultStat label={t('result.stats.avgScore')} value={avgScore} icon={IconChartBar} color="indigo" />
|
||||
</SimpleGrid>
|
||||
|
||||
<Card withBorder padding={0}>
|
||||
<Group p="md" justify="space-between" wrap="wrap" gap="sm">
|
||||
<Text fw={600}>{t('result.section')}</Text>
|
||||
<Group gap="sm" wrap="wrap">
|
||||
<AdvancedTable
|
||||
columns={columns}
|
||||
data={page.rows}
|
||||
title={t('result.section')}
|
||||
tableName={t('result.title')}
|
||||
toolbar={
|
||||
<>
|
||||
<TextInput
|
||||
placeholder={t('result.search.seafarer')}
|
||||
leftSection={<IconSearch size={15} />}
|
||||
@@ -348,23 +350,17 @@ export function ResultPage() {
|
||||
style={{ width: 280 }}
|
||||
clearable
|
||||
/>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<AdvancedTable
|
||||
columns={columns}
|
||||
data={page.rows}
|
||||
tableName={t('result.title')}
|
||||
itemCount={page.itemCount}
|
||||
pageIndex={page.pageIndex}
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
refresh={refetch}
|
||||
isLoading={isFetching}
|
||||
emptyText={t('result.noItems')}
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
}
|
||||
itemCount={page.itemCount}
|
||||
pageIndex={page.pageIndex}
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
refresh={refetch}
|
||||
isLoading={isFetching}
|
||||
emptyText={t('result.noItems')}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
opened={detailOpened}
|
||||
@@ -421,9 +417,10 @@ export function ResultPage() {
|
||||
{t('result.review.derivedStatus')}
|
||||
</Text>
|
||||
<Group gap="xs" mt={4}>
|
||||
<Badge variant="light" color={STATUS_COLOR[detailResult.status]}>
|
||||
{t(`result.status.${detailResult.status}`)}
|
||||
</Badge>
|
||||
<StatusBadge
|
||||
tone={STATUS_TONE[detailResult.status]}
|
||||
label={t(`result.status.${detailResult.status}`)}
|
||||
/>
|
||||
<Badge variant="light" color={REVIEW_COLOR[detailResult.reviewStatus] ?? 'gray'}>
|
||||
{t(`result.review.${detailResult.reviewStatus}`)}
|
||||
</Badge>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Badge, Container, Group, Select, Text, TextInput, Title } from '@mantine/core';
|
||||
import {Badge, Container, Select, Text, TextInput, Title} from '@mantine/core';
|
||||
import { IconSearch } from '@tabler/icons-react';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import {
|
||||
@@ -113,33 +113,35 @@ export function SeafarerDocumentQueuePage({ kind }: { kind: SeafarerDocumentKind
|
||||
Requests released by an approved seafarer registration: confirm payment, schedule the
|
||||
collection date, then issue.
|
||||
</Text>
|
||||
<Group mb="md" gap="sm">
|
||||
<TextInput
|
||||
placeholder="Search number, name or seafarer №…"
|
||||
leftSection={<IconSearch size={14} />}
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.currentTarget.value);
|
||||
setPage(0);
|
||||
}}
|
||||
w={280}
|
||||
/>
|
||||
<Select
|
||||
placeholder="All statuses"
|
||||
data={STATUS_FILTERS}
|
||||
value={status}
|
||||
onChange={(v) => {
|
||||
setStatus(v as SeafarerDocumentStatus | null);
|
||||
setPage(0);
|
||||
}}
|
||||
clearable
|
||||
w={220}
|
||||
/>
|
||||
</Group>
|
||||
<AdvancedTable
|
||||
columns={columns}
|
||||
data={data?.items ?? []}
|
||||
tableName={`${SEAFARER_DOCUMENT_KIND_LABELS[kind]} requests`}
|
||||
toolbar={
|
||||
<>
|
||||
<TextInput
|
||||
placeholder="Search number, name or seafarer №…"
|
||||
leftSection={<IconSearch size={14} />}
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.currentTarget.value);
|
||||
setPage(0);
|
||||
}}
|
||||
w={260}
|
||||
/>
|
||||
<Select
|
||||
placeholder="All statuses"
|
||||
data={STATUS_FILTERS}
|
||||
value={status}
|
||||
onChange={(v) => {
|
||||
setStatus(v as SeafarerDocumentStatus | null);
|
||||
setPage(0);
|
||||
}}
|
||||
clearable
|
||||
w={200}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
itemCount={data?.total ?? 0}
|
||||
pageIndex={page}
|
||||
onPageChange={setPage}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Badge, Container, Group, Select, Text, TextInput, Title } from '@mantine/core';
|
||||
import {Container, 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_TONES,
|
||||
SEAFARER_REGISTRATION_STATUS_LABELS,
|
||||
displaySeafarerAnswer,
|
||||
useListSeafarerRegistrationsQuery,
|
||||
type SeafarerRegistration,
|
||||
type SeafarerRegistrationStatus,
|
||||
} from '@ema-platform/api';
|
||||
import { AdvancedTable, type AdvancedColumn } from '@ema-platform/ui';
|
||||
import { AdvancedTable, StatusBadge, type AdvancedColumn } from '@ema-platform/ui';
|
||||
import { useDateDisplayer } from '@ema-platform/shared';
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
@@ -82,9 +82,10 @@ export function SeafarerRegistrationQueuePage() {
|
||||
header: 'Status',
|
||||
accessorKey: 'status',
|
||||
cell: ({ row }) => (
|
||||
<Badge size="sm" variant="light" color={SEAFARER_REGISTRATION_STATUS_COLORS[row.original.status]}>
|
||||
{SEAFARER_REGISTRATION_STATUS_LABELS[row.original.status]}
|
||||
</Badge>
|
||||
<StatusBadge
|
||||
tone={SEAFARER_REGISTRATION_STATUS_TONES[row.original.status]}
|
||||
label={SEAFARER_REGISTRATION_STATUS_LABELS[row.original.status]}
|
||||
/>
|
||||
),
|
||||
},
|
||||
],
|
||||
@@ -100,33 +101,35 @@ export function SeafarerRegistrationQueuePage() {
|
||||
Registrations awaiting review. Approval numbers the seafarer and opens their Seaman Book and
|
||||
BTC applications.
|
||||
</Text>
|
||||
<Group mb="md" gap="sm">
|
||||
<TextInput
|
||||
placeholder="Search number, name or ID…"
|
||||
leftSection={<IconSearch size={14} />}
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.currentTarget.value);
|
||||
setPage(0);
|
||||
}}
|
||||
w={280}
|
||||
/>
|
||||
<Select
|
||||
placeholder="All statuses"
|
||||
data={STATUS_FILTERS}
|
||||
value={status}
|
||||
onChange={(v) => {
|
||||
setStatus(v as SeafarerRegistrationStatus | null);
|
||||
setPage(0);
|
||||
}}
|
||||
clearable
|
||||
w={220}
|
||||
/>
|
||||
</Group>
|
||||
<AdvancedTable
|
||||
columns={columns}
|
||||
data={data?.items ?? []}
|
||||
tableName="Seafarer registrations"
|
||||
toolbar={
|
||||
<>
|
||||
<TextInput
|
||||
placeholder="Search number, name or ID…"
|
||||
leftSection={<IconSearch size={14} />}
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.currentTarget.value);
|
||||
setPage(0);
|
||||
}}
|
||||
w={260}
|
||||
/>
|
||||
<Select
|
||||
placeholder="All statuses"
|
||||
data={STATUS_FILTERS}
|
||||
value={status}
|
||||
onChange={(v) => {
|
||||
setStatus(v as SeafarerRegistrationStatus | null);
|
||||
setPage(0);
|
||||
}}
|
||||
clearable
|
||||
w={200}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
itemCount={data?.total ?? 0}
|
||||
pageIndex={page}
|
||||
onPageChange={setPage}
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
SEAFARER_REGISTRATION_DOCUMENTS,
|
||||
SEAFARER_REGISTRATION_FIELD_LABELS,
|
||||
SEAFARER_REGISTRATION_SECTIONS,
|
||||
SEAFARER_REGISTRATION_STATUS_COLORS,
|
||||
SEAFARER_REGISTRATION_STATUS_TONES,
|
||||
SEAFARER_REGISTRATION_STATUS_LABELS,
|
||||
displaySeafarerAnswer,
|
||||
extractErrorMessage,
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
useRejectSeafarerRegistrationMutation,
|
||||
useRequestSeafarerRegistrationChangesMutation,
|
||||
} from '@ema-platform/api';
|
||||
import { notify } from '@ema-platform/ui';
|
||||
import { notify, StatusBadge } from '@ema-platform/ui';
|
||||
import { LICENSE_PERMISSIONS, RequirePermission } from '@ema-platform/auth';
|
||||
import { applicantName } from './SeafarerRegistrationQueuePage';
|
||||
|
||||
@@ -116,9 +116,10 @@ export function SeafarerRegistrationReviewPage() {
|
||||
<Text size="sm" c="dimmed" ff="monospace">
|
||||
{registration.registrationNumber}
|
||||
</Text>
|
||||
<Badge size="sm" variant="light" color={SEAFARER_REGISTRATION_STATUS_COLORS[registration.status]}>
|
||||
{SEAFARER_REGISTRATION_STATUS_LABELS[registration.status]}
|
||||
</Badge>
|
||||
<StatusBadge
|
||||
tone={SEAFARER_REGISTRATION_STATUS_TONES[registration.status]}
|
||||
label={SEAFARER_REGISTRATION_STATUS_LABELS[registration.status]}
|
||||
/>
|
||||
{registration.seafarerNumber && (
|
||||
<Badge size="sm" color="teal" leftSection={<IconCheck size={10} />}>
|
||||
{registration.seafarerNumber}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { StatusBadge } from '@ema-platform/ui';
|
||||
import { type StatusTone } from '@ema-platform/shared';
|
||||
import { useState } from 'react';
|
||||
import { useApiQuery } from '@ema-platform/api';
|
||||
import {
|
||||
@@ -59,7 +61,7 @@ const STATUS_OPTIONS = ['All', 'Active', 'Inactive', 'Suspended'];
|
||||
const MEDICAL_OPTIONS = ['All', 'Valid', 'Expiring', 'Expired'];
|
||||
|
||||
const MEDICAL_COLOR: Record<string, string> = { Valid: 'teal', Expiring: 'orange', Expired: 'red' };
|
||||
const STATUS_COLOR: Record<string, string> = { Active: 'teal', Inactive: 'gray', Suspended: 'red' };
|
||||
const STATUS_TONE: Record<string, StatusTone> = { Active: 'success', Inactive: 'neutral', Suspended: 'danger' };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Detail modal
|
||||
@@ -83,7 +85,7 @@ function DetailModal({ sf, opened, onClose }: { sf: Seafarer | null; opened: boo
|
||||
<Group justify="space-between"><Text fz="xs" c="dimmed">Nationality</Text><Text fz="xs">{sf.nationality}</Text></Group>
|
||||
<Group justify="space-between"><Text fz="xs" c="dimmed">Date of Birth</Text><Text fz="xs">{sf.dob}</Text></Group>
|
||||
<Group justify="space-between"><Text fz="xs" c="dimmed">Rank</Text><Text fz="xs" fw={600}>{sf.rank}</Text></Group>
|
||||
<Group justify="space-between"><Text fz="xs" c="dimmed">Status</Text><Badge color={STATUS_COLOR[sf.status]} variant="light" size="xs">{sf.status}</Badge></Group>
|
||||
<Group justify="space-between"><Text fz="xs" c="dimmed">Status</Text><StatusBadge tone={STATUS_TONE[sf.status]} label={sf.status} variant="light" size="xs" /></Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Paper withBorder radius="md" p="md">
|
||||
@@ -264,7 +266,12 @@ export function SeafarerRegistryPage() {
|
||||
: <Text fz="xs" c="dimmed">—</Text>}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge color={STATUS_COLOR[sf.status]} variant="light" size="xs">{sf.status}</Badge>
|
||||
<StatusBadge
|
||||
tone={STATUS_TONE[sf.status]}
|
||||
label={sf.status}
|
||||
variant="light"
|
||||
size="xs"
|
||||
/>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<ActionIcon variant="light" color="blue" size="sm" onClick={() => setSelected(sf)}>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { Badge, Button, Text, Tooltip } from '@mantine/core';
|
||||
import { type StatusTone } from '@ema-platform/shared';
|
||||
import { Button, Text, Tooltip } from '@mantine/core';
|
||||
import { IconShieldCog } from '@tabler/icons-react';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import { StatusBadge } from '@ema-platform/ui';
|
||||
import type { Vessel } from '@ema-platform/api';
|
||||
import { LICENSE_PERMISSIONS, RequirePermission } from '@ema-platform/auth';
|
||||
|
||||
const VESSEL_STATUS_COLORS: Record<string, string> = {
|
||||
REGISTERED: 'green',
|
||||
SUSPENDED: 'orange',
|
||||
DEREGISTERED: 'gray',
|
||||
const VESSEL_STATUS_TONES: Record<string, StatusTone> = {
|
||||
REGISTERED: 'success',
|
||||
SUSPENDED: 'pending',
|
||||
DEREGISTERED: 'neutral',
|
||||
};
|
||||
|
||||
export const CATEGORY_LABELS: Record<string, string> = {
|
||||
@@ -63,13 +65,12 @@ export function vesselRegistrationQueueColumns(
|
||||
{
|
||||
header: 'Status',
|
||||
cell: ({ row }) => (
|
||||
<Badge
|
||||
<StatusBadge
|
||||
tone={VESSEL_STATUS_TONES[row.original.status]}
|
||||
label={row.original.status}
|
||||
size="sm"
|
||||
variant="light"
|
||||
color={VESSEL_STATUS_COLORS[row.original.status]}
|
||||
>
|
||||
{row.original.status}
|
||||
</Badge>
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user