mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-09-01 19:23:28 +00:00
feat: add configuration UI for license behavior and multi-stage examination fees
This commit is contained in:
@@ -21,6 +21,8 @@ import type {
|
||||
LicenseTypeRequirements,
|
||||
OperatorType,
|
||||
AssignableOfficer,
|
||||
CertificateCategory,
|
||||
CompletionEffect,
|
||||
DocumentDecision,
|
||||
DocumentReview,
|
||||
EligibleExam,
|
||||
@@ -37,12 +39,14 @@ import type {
|
||||
RemarkTargetType,
|
||||
SavedQueueView,
|
||||
SchemaIssue,
|
||||
ServiceKind,
|
||||
TemplateFieldPlacement,
|
||||
TemplateLogoPlacement,
|
||||
TemplatePageOptions,
|
||||
TemplateVariable,
|
||||
PersonalDocumentFilter,
|
||||
PersonalDocumentGroup,
|
||||
WorkflowProfile,
|
||||
} from './licensing.types';
|
||||
|
||||
/**
|
||||
@@ -182,6 +186,12 @@ export const licensingApi = baseApi
|
||||
feeNewApplication?: number | null;
|
||||
feeRenewal?: number | null;
|
||||
feeCurrency?: string;
|
||||
// The examined-certificate stages. Unlike the two above, these are
|
||||
// read live off the licence type rather than snapshotted, so the
|
||||
// server refuses to clear one a candidate is currently waiting on.
|
||||
feeEligibility?: number | null;
|
||||
feeExamination?: number | null;
|
||||
feeCertificate?: number | null;
|
||||
}
|
||||
>({
|
||||
query: ({ id, ...body }) => ({
|
||||
@@ -193,6 +203,47 @@ export const licensingApi = baseApi
|
||||
error ? [] : [listTag('LicenseType'), itemTag('LicenseType', id)],
|
||||
}),
|
||||
|
||||
/**
|
||||
* How a licence type behaves: its workflow, eligibility gates, renewal
|
||||
* policy and applicant rules — everything that used to be settable only
|
||||
* by editing a seed file.
|
||||
*
|
||||
* Three of these (`workflowProfile`, `completionEffect`,
|
||||
* `requiresExamination`) decide the course an application runs, and are
|
||||
* read live rather than snapshotted. The server answers 409
|
||||
* `license_type_in_use` when changing one would strand applications that
|
||||
* have not yet had their approval decision.
|
||||
*/
|
||||
updateLicenseBehavior: builder.mutation<
|
||||
LicenseType,
|
||||
{
|
||||
id: string;
|
||||
workflowProfile?: WorkflowProfile;
|
||||
serviceKind?: ServiceKind;
|
||||
completionEffect?: CompletionEffect | null;
|
||||
certificateCategory?: CertificateCategory | null;
|
||||
requiresExamination?: boolean;
|
||||
requiresSeafarerRegistration?: boolean;
|
||||
requiresValidMedical?: boolean;
|
||||
minSeaTimeDays?: number | null;
|
||||
renewalWindowDays?: number;
|
||||
expiryReminderDays?: number[];
|
||||
requiresOperatorMode?: boolean;
|
||||
allowMultipleOpenDrafts?: boolean;
|
||||
requiresIssuanceScheduling?: boolean;
|
||||
uniqueFormKeyPath?: string | null;
|
||||
slaHours?: number | null;
|
||||
}
|
||||
>({
|
||||
query: ({ id, ...body }) => ({
|
||||
url: `/license-types/${id}/behavior`,
|
||||
method: 'PATCH',
|
||||
body,
|
||||
}),
|
||||
invalidatesTags: (_r, error, { id }) =>
|
||||
error ? [] : [listTag('LicenseType'), itemTag('LicenseType', id)],
|
||||
}),
|
||||
|
||||
/** Validity is edited beside the certificate design, not with the fees. */
|
||||
updateLicenseValidity: builder.mutation<
|
||||
LicenseType,
|
||||
@@ -1240,6 +1291,7 @@ export const {
|
||||
useGetLicenseTypesQuery,
|
||||
useGetLicenseCategoriesQuery,
|
||||
useUpdateLicenseFeesMutation,
|
||||
useUpdateLicenseBehaviorMutation,
|
||||
useUpdateFormSchemaMutation,
|
||||
useValidateFormSchemaMutation,
|
||||
useGetFormSchemaPaletteQuery,
|
||||
|
||||
@@ -400,6 +400,15 @@ const ERROR_MESSAGES: Record<string, string> = {
|
||||
inspection_not_passed:
|
||||
'Approval requires a passed inspection. Schedule a re-inspection or request corrections.',
|
||||
license_type_inactive: 'This licence type is not currently accepting applications.',
|
||||
|
||||
// Licence-type configuration guards. Each of these refuses a change that
|
||||
// would leave applications already in progress unable to move.
|
||||
license_type_in_use:
|
||||
'Applications of this type are already in progress, and this setting decides the course they run. Wait until those have been decided, or change something else.',
|
||||
stage_fee_in_use:
|
||||
'Candidates are currently waiting to pay this fee. Removing it would leave them unable to pay and unable to continue — set a different amount instead.',
|
||||
form_schema_missing_protected_paths:
|
||||
'The form for this licence type does not contain the field this setting depends on. Add the field to the form first.',
|
||||
};
|
||||
|
||||
export function extractErrorMessage(error: unknown, fallback = 'Something went wrong'): string {
|
||||
|
||||
@@ -216,11 +216,39 @@ export interface LicenseType {
|
||||
// --------------------------------------------------- examined certificates
|
||||
/** Approval establishes eligibility; the certificate is earned by exam. */
|
||||
requiresExamination?: boolean;
|
||||
/** Assessment fee, due on submission before any officer review. */
|
||||
feeEligibility?: string | number | null;
|
||||
/** 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;
|
||||
|
||||
// ------------------------------------------------------- behaviour config
|
||||
// Editable from the backoffice Behavior tab. Optional because the API has
|
||||
// only recently begun returning them and older mocks/fixtures omit them.
|
||||
|
||||
/** Licence or registration. Catalogue metadata; no behaviour hangs off it. */
|
||||
serviceKind?: ServiceKind;
|
||||
/** Platform side effect fired when an application of this type completes. */
|
||||
completionEffect?: CompletionEffect | null;
|
||||
/** Only ACTIVE registered seafarers may apply. */
|
||||
requiresSeafarerRegistration?: boolean;
|
||||
/** Submission requires a current, unexpired medical certificate. */
|
||||
requiresValidMedical?: boolean;
|
||||
/** Minimum VERIFIED sea time in days at submission. Null means no floor. */
|
||||
minSeaTimeDays?: number | null;
|
||||
/** Whether several open drafts may exist at once, for per-asset registrations. */
|
||||
allowMultipleOpenDrafts?: boolean;
|
||||
/** Days before expiry that renewal opens. */
|
||||
renewalWindowDays?: number;
|
||||
/** Days before expiry to remind the holder, most distant first. */
|
||||
expiryReminderDays?: number[];
|
||||
/**
|
||||
* Dotted `formData` path whose answer may appear on only one live
|
||||
* application of this type. Null means no such rule.
|
||||
*/
|
||||
uniqueFormKeyPath?: string | null;
|
||||
|
||||
// ---------------------------------------------------------- STCW mapping
|
||||
certificateCategory?: CertificateCategory | null;
|
||||
stcwControlled?: boolean;
|
||||
@@ -579,6 +607,15 @@ export type QueueSortField =
|
||||
/** Review → evaluation → (inspection) → approval, or the short registration course. */
|
||||
export type WorkflowProfile = "STANDARD" | "REGISTRATION";
|
||||
|
||||
/** A permission to operate, or a registration granting a status and a number. */
|
||||
export type ServiceKind = "LICENSE" | "REGISTRATION";
|
||||
|
||||
/** Platform side effect fired when an application reaches COMPLETED. */
|
||||
export type CompletionEffect =
|
||||
| "REGISTER_SEAFARER"
|
||||
| "REGISTER_VESSEL"
|
||||
| "OPEN_SEAFARER_DOCUMENTS";
|
||||
|
||||
/** Row counts behind the queue's saved-view tabs. */
|
||||
export interface QueueCounts {
|
||||
unassigned: number;
|
||||
|
||||
Reference in New Issue
Block a user