Enhance certificate designer and seafarer registration features with image support and dynamic department/rank options

This commit is contained in:
Nati
2026-08-24 14:15:55 +00:00
parent 4a3ad7c2e6
commit bd65fc0eab
16 changed files with 320 additions and 115 deletions

View File

@@ -4,23 +4,26 @@ import { useDisclosure } from '@mantine/hooks';
import { useTranslation } from 'react-i18next';
import {IconPlus} from '@tabler/icons-react';
import { AdvancedTable, ErrorState, ModalFooter, notify, PageHeader, useErrorHandler, useServerTable } from '@ema-platform/ui';
import { useGetRanksQuery, useLocalized } from '@ema-platform/api';
import {
useGetCertificationsQuery,
useCreateCertificationMutation,
useUpdateCertificationMutation,
useDeleteCertificationMutation,
} from '../../api/certification-api';
import { RANK_KEY_OPTIONS, type Certification } from '../../types/certification';
import { type Certification } from '../../types/certification';
import { certificationColumns } from './columns';
import { certificationActionsColumn } from './actions';
function CertificationForm({
editing,
rankOptions,
isSubmitting,
onSubmit,
onCancel,
}: {
editing: Certification | null;
rankOptions: { value: string; label: string }[];
isSubmitting: boolean;
onSubmit: (values: { nameEn: string; nameAm: string; descEn: string; descAm: string; rankKey: string | null }, isEdit: boolean) => void;
onCancel: () => void;
@@ -53,7 +56,7 @@ function CertificationForm({
label={t('certification.form.rankKey', 'STCW rank (for exam scheduling)')}
description={t('certification.form.rankKeyHint', 'Leave blank if this certification is not part of the examined CoC/CoP ladder.')}
placeholder={t('certification.form.rankKeyPlaceholder', 'Not rank-specific')}
data={RANK_KEY_OPTIONS as unknown as { value: string; label: string }[]}
data={rankOptions}
value={rankKey}
onChange={setRankKey}
size="sm"
@@ -74,7 +77,10 @@ export function CertificationPage() {
const { t, i18n } = useTranslation();
const locale = i18n.language as 'en' | 'am';
const { handleError } = useErrorHandler();
const localized = useLocalized();
const { data, isFetching, isError, refetch } = useGetCertificationsQuery();
const { data: rankRes } = useGetRanksQuery();
const rankOptions = (rankRes?.items ?? []).map((r) => ({ value: r.key, label: localized(r.name) }));
const { setPageIndex, pageSize, setPageSize, paginate } = useServerTable();
const [createCert, { isLoading: isCreating }] = useCreateCertificationMutation();
const [updateCert, { isLoading: isUpdating }] = useUpdateCertificationMutation();
@@ -154,6 +160,7 @@ export function CertificationPage() {
{showForm && (
<CertificationForm
editing={editing}
rankOptions={rankOptions}
isSubmitting={isCreating || isUpdating}
onSubmit={handleSubmit}
onCancel={resetForm}

View File

@@ -3,24 +3,6 @@ export interface LocalePair {
am: string;
}
/**
* STCW rank an exam certification is for — the join that lets the
* schedule-exam picker offer only sittings valid for an application's rank.
* Matches `LicenseApplication.formData.certificate.rank` / `rankEngine` /
* `proficiency` on the backend. Not every certification is on the examined
* ladder, so this stays a plain optional string rather than a required enum.
*/
export const RANK_KEY_OPTIONS = [
{ value: 'OOW_DECK', label: 'Officer of the Watch (Deck)' },
{ value: 'CHIEF_MATE', label: 'Chief Mate' },
{ value: 'MASTER', label: 'Master' },
{ value: 'OOW_ENGINE', label: 'Officer of the Watch (Engine)' },
{ value: 'SECOND_ENGINEER', label: 'Second Engineer' },
{ value: 'CHIEF_ENGINEER', label: 'Chief Engineer' },
{ value: 'ABLE_SEAFARER_DECK', label: 'Able Seafarer Deck' },
{ value: 'ABLE_SEAFARER_ENGINE', label: 'Able Seafarer Engine' },
] as const;
export interface Certification {
id: string;
name: LocalePair;