mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-28 06:40:58 +00:00
Merge branch 'feature/seaman' of https://github.com/Tria-plc/emaui into certficate
This commit is contained in:
@@ -485,6 +485,33 @@ export const licensingApi = baseApi
|
||||
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
|
||||
}),
|
||||
|
||||
/** Places a candidate who has paid the examination fee into a sitting. */
|
||||
scheduleExam: builder.mutation<
|
||||
LicenseApplication,
|
||||
{ id: string; examId: string; admissionNumber?: string; examDate?: string }
|
||||
>({
|
||||
query: ({ id, ...body }) => ({
|
||||
url: `/license-application-review/${id}/schedule-exam`,
|
||||
method: 'POST',
|
||||
body,
|
||||
}),
|
||||
invalidatesTags: (_r, error, { id }) =>
|
||||
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
|
||||
}),
|
||||
|
||||
/**
|
||||
* Raises the examination fee — after eligibility approval, or again when
|
||||
* a failed candidate elects to resit.
|
||||
*/
|
||||
requestExamPayment: builder.mutation<LicenseApplication, string>({
|
||||
query: (id) => ({
|
||||
url: `/license-applications/${id}/request-exam-payment`,
|
||||
method: 'POST',
|
||||
}),
|
||||
invalidatesTags: (_r, error, id) =>
|
||||
error ? [] : [itemTag('LicenseApplication', id), listTag('LicenseApplication')],
|
||||
}),
|
||||
|
||||
// ------------------------------------------------- certificate designs
|
||||
getLicenseTemplates: builder.query<LicenseTemplate[], string | void>({
|
||||
query: (licenseTypeId) => ({
|
||||
@@ -522,6 +549,7 @@ export const licensingApi = baseApi
|
||||
name?: string;
|
||||
hbsSource?: string;
|
||||
pageOptions?: TemplatePageOptions;
|
||||
backgroundUrl?: string;
|
||||
}
|
||||
>({
|
||||
query: ({ id, ...body }) => ({
|
||||
@@ -805,6 +833,8 @@ export const {
|
||||
useApproveDocumentsMutation,
|
||||
useFinalApproveMutation,
|
||||
useRejectApplicationMutation,
|
||||
useScheduleExamMutation,
|
||||
useRequestExamPaymentMutation,
|
||||
useConfirmPaymentMutation,
|
||||
useScheduleInspectionMutation,
|
||||
useGetInspectionsQuery,
|
||||
|
||||
@@ -70,6 +70,12 @@ export const STATUS_LABELS: Record<LicenseStatus, string> = {
|
||||
PAYMENT_CONFIRMED: 'Preparing Certificate',
|
||||
CERTIFICATE_ISSUED: 'Certificate Issued',
|
||||
COMPLETED: 'Completed',
|
||||
ELIGIBILITY_APPROVED: 'Eligible to Sit',
|
||||
EXAM_PAYMENT_PENDING: 'Exam Fee Due',
|
||||
EXAM_PAID: 'Awaiting Exam Date',
|
||||
EXAM_SCHEDULED: 'Exam Scheduled',
|
||||
EXAM_PASSED: 'Exam Passed',
|
||||
EXAM_FAILED: 'Exam Not Passed',
|
||||
};
|
||||
|
||||
/** Mantine colour per status — green progresses, orange needs the applicant. */
|
||||
@@ -89,6 +95,13 @@ export const STATUS_COLORS: Record<LicenseStatus, string> = {
|
||||
PAYMENT_CONFIRMED: 'teal',
|
||||
CERTIFICATE_ISSUED: 'green',
|
||||
COMPLETED: 'green',
|
||||
ELIGIBILITY_APPROVED: 'teal',
|
||||
EXAM_PAYMENT_PENDING: 'yellow',
|
||||
EXAM_PAID: 'lime',
|
||||
EXAM_SCHEDULED: 'cyan',
|
||||
EXAM_PASSED: 'teal',
|
||||
// Orange, not red: a failure is recoverable here — the candidate resits.
|
||||
EXAM_FAILED: 'orange',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -114,6 +127,15 @@ export const STATUS_PROGRESS: Record<LicenseStatus, number> = {
|
||||
CERTIFICATE_ISSUED: 100,
|
||||
COMPLETED: 100,
|
||||
REJECTED: 100,
|
||||
// The exam leg sits between approval and the certificate fee, so these
|
||||
// interleave with PAYMENT_PENDING (80) rather than running past it.
|
||||
ELIGIBILITY_APPROVED: 60,
|
||||
EXAM_PAYMENT_PENDING: 64,
|
||||
EXAM_PAID: 68,
|
||||
EXAM_SCHEDULED: 72,
|
||||
EXAM_PASSED: 78,
|
||||
// A resit returns to the fee step, so this is not further along than a pass.
|
||||
EXAM_FAILED: 64,
|
||||
};
|
||||
|
||||
/** Statuses where nothing moves until the applicant does something. */
|
||||
@@ -121,6 +143,10 @@ export const APPLICANT_ACTION_STATUSES: LicenseStatus[] = [
|
||||
'DRAFT',
|
||||
'RESUBMIT_REQUIRED',
|
||||
'PAYMENT_PENDING',
|
||||
// Both wait on the candidate: one to pay for a sitting, one to decide to
|
||||
// sit again after a failure.
|
||||
'EXAM_PAYMENT_PENDING',
|
||||
'EXAM_FAILED',
|
||||
];
|
||||
|
||||
/** Statuses that are finished, whichever way they went. */
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
/** Shared licensing contract — mirrors the emaapi domain model. */
|
||||
|
||||
// Reused rather than redeclared: the department vocabulary belongs to the
|
||||
// seafarer domain, and two copies would drift.
|
||||
import type { SeafarerDepartment } from '../seafarer/seafarer.types';
|
||||
|
||||
export type Bilingual = { en?: string; am?: string };
|
||||
|
||||
/**
|
||||
@@ -21,7 +25,15 @@ export type LicenseStatus =
|
||||
| "PAID"
|
||||
| "PAYMENT_CONFIRMED"
|
||||
| "CERTIFICATE_ISSUED"
|
||||
| "COMPLETED";
|
||||
| "COMPLETED"
|
||||
// Examined certificates (CoC, some CoP): approval establishes eligibility,
|
||||
// the candidate pays to sit, and the certificate fee falls due on a pass.
|
||||
| "ELIGIBILITY_APPROVED"
|
||||
| "EXAM_PAYMENT_PENDING"
|
||||
| "EXAM_PAID"
|
||||
| "EXAM_SCHEDULED"
|
||||
| "EXAM_PASSED"
|
||||
| "EXAM_FAILED";
|
||||
|
||||
export type ApplicationKind = "NEW" | "RENEWAL";
|
||||
|
||||
@@ -130,6 +142,58 @@ export interface LicenseType {
|
||||
isActive: boolean;
|
||||
/** Display order set by EMA; lower comes first. */
|
||||
sortOrder: number;
|
||||
|
||||
// --------------------------------------------------- examined certificates
|
||||
/** Approval establishes eligibility; the certificate is earned by exam. */
|
||||
requiresExamination?: boolean;
|
||||
/** Fee per sitting. Falls back to `feeNewApplication` when null. */
|
||||
feeExamination?: string | number | null;
|
||||
/** Fee to issue after a pass. Falls back to `feeNewApplication` when null. */
|
||||
feeCertificate?: string | number | null;
|
||||
|
||||
// ---------------------------------------------------------- STCW mapping
|
||||
certificateCategory?: CertificateCategory | null;
|
||||
stcwControlled?: boolean;
|
||||
/** Convention regulation, e.g. `III/2`. */
|
||||
stcwRegulation?: string | null;
|
||||
/** Code section, e.g. `A-III/2`. */
|
||||
stcwCodeSection?: string | null;
|
||||
stcwDepartment?: SeafarerDepartment | null;
|
||||
competencyLevel?: CompetencyLevel | null;
|
||||
stcwFunctions?: StcwFunctionRow[] | null;
|
||||
stcwCapacities?: StcwCapacityRow[] | null;
|
||||
/** Licence keys that must be held before this may be applied for. */
|
||||
prerequisiteLicenseKeys?: string[] | null;
|
||||
}
|
||||
|
||||
/** What kind of document a certificate type produces. */
|
||||
export type CertificateCategory =
|
||||
| "COC"
|
||||
| "COP"
|
||||
| "ENDORSEMENT"
|
||||
| "GOC"
|
||||
| "NATIONAL";
|
||||
|
||||
/**
|
||||
* STCW responsibility level. Cadet is absent by design — under STCW a cadet is
|
||||
* a seafarer in training before holding any certificate, not a level of
|
||||
* competence.
|
||||
*/
|
||||
export type CompetencyLevel = "SUPPORT" | "OPERATIONAL" | "MANAGEMENT";
|
||||
|
||||
/** One row of a CoC's function table (STCW Code A-I/2). */
|
||||
export interface StcwFunctionRow {
|
||||
function: Bilingual;
|
||||
level: CompetencyLevel;
|
||||
limitation?: Bilingual;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
/** One row of a CoC's capacity table — what the holder may serve as. */
|
||||
export interface StcwCapacityRow {
|
||||
capacity: Bilingual;
|
||||
limitation?: Bilingual;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export interface DocumentRequirement {
|
||||
@@ -380,6 +444,11 @@ export interface LicenseTemplate {
|
||||
version: number;
|
||||
hbsSource: string;
|
||||
pageOptions: TemplatePageOptions | null;
|
||||
/**
|
||||
* Artwork printed under the rendered text. A background, not the whole
|
||||
* certificate — per-certificate data stays in the template layer above it.
|
||||
*/
|
||||
backgroundUrl?: string | null;
|
||||
status: TemplateStatus;
|
||||
publishedAt: string | null;
|
||||
createdAt: string;
|
||||
|
||||
Reference in New Issue
Block a user