Add Rank and Department management to ConfigurationPage with new RankDepartmentTab

This commit is contained in:
Nati
2026-08-24 06:13:43 +00:00
parent e82714ba1f
commit f973bff641
4 changed files with 678 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ import type {
ApplicationPayment,
ApplicationStaff,
Attachment,
Department,
DocumentRequirement,
FormSchemaPalette,
FormSectionConfig,
@@ -27,6 +28,8 @@ import type {
Paginated,
QueueCounts,
QueueFilter,
Rank,
RankCertificateCategory,
RemarkTargetType,
SavedQueueView,
SchemaIssue,
@@ -72,6 +75,8 @@ const TAGS = [
'SavedView',
'LicenseTemplate',
'DocumentRequirement',
'Department',
'Rank',
] as const;
const listTag = (type: (typeof TAGS)[number]) => ({ type, id: 'LIST' }) as const;
@@ -254,6 +259,75 @@ export const licensingApi = baseApi
invalidatesTags: (_r, error) => (error ? [] : [listTag('DocumentRequirement')]),
}),
// --------------------------------------------------- departments & ranks
/** Every department, for the admin editor. */
getDepartments: builder.query<Paginated<Department>, void>({
query: () => ({ url: '/departments' }),
providesTags: () => [listTag('Department')],
}),
/** Active departments only — the applicant-facing picker. */
getActiveDepartments: builder.query<Department[], void>({
query: () => ({ url: '/departments/active/list' }),
providesTags: () => [listTag('Department')],
}),
createDepartment: builder.mutation<
Department,
Partial<Department> & { code: string; name: Department['name'] }
>({
query: (body) => ({ url: '/departments', method: 'POST', body }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('Department')]),
}),
updateDepartment: builder.mutation<Department, { id: string } & Partial<Department>>({
query: ({ id, ...body }) => ({ url: `/departments/${id}`, method: 'PUT', body }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('Department')]),
}),
deleteDepartment: builder.mutation<unknown, string>({
query: (id) => ({ url: `/departments/${id}`, method: 'DELETE' }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('Department')]),
}),
/** Every rank, for the admin editor to filter/group by department client-side. */
getRanks: builder.query<Paginated<Rank>, void>({
query: () => ({ url: '/ranks' }),
providesTags: () => [listTag('Rank')],
}),
/** One department's ladder for a category, ordered — the applicant wizard's rank picker. */
getRankLadder: builder.query<
Rank[],
{ departmentId: string; certificateCategory: RankCertificateCategory }
>({
query: (params) => ({ url: '/ranks/ladder', params }),
providesTags: () => [listTag('Rank')],
}),
createRank: builder.mutation<
Rank,
Partial<Rank> & {
departmentId: string;
certificateCategory: RankCertificateCategory;
key: string;
name: Rank['name'];
}
>({
query: (body) => ({ url: '/ranks', method: 'POST', body }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('Rank')]),
}),
updateRank: builder.mutation<Rank, { id: string } & Partial<Rank>>({
query: ({ id, ...body }) => ({ url: `/ranks/${id}`, method: 'PUT', body }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('Rank')]),
}),
deleteRank: builder.mutation<unknown, string>({
query: (id) => ({ url: `/ranks/${id}`, method: 'DELETE' }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('Rank')]),
}),
// -------------------------------------------------------- application
createApplication: builder.mutation<
LicenseApplication,
@@ -641,6 +715,8 @@ export const licensingApi = baseApi
LicenseTemplate,
{
licenseTypeId: string;
/** Scopes the draft to one rank's certificate. Omit for the type's default design. */
rankId?: string | null;
name: string;
hbsSource?: string;
pageOptions?: TemplatePageOptions;
@@ -654,6 +730,7 @@ export const licensingApi = baseApi
LicenseTemplate,
{
id: string;
rankId?: string | null;
name?: string;
hbsSource?: string;
pageOptions?: TemplatePageOptions;
@@ -919,6 +996,16 @@ export const {
useCreateDocumentRequirementMutation,
useUpdateDocumentRequirementMutation,
useDeleteDocumentRequirementMutation,
useGetDepartmentsQuery,
useGetActiveDepartmentsQuery,
useCreateDepartmentMutation,
useUpdateDepartmentMutation,
useDeleteDepartmentMutation,
useGetRanksQuery,
useGetRankLadderQuery,
useCreateRankMutation,
useUpdateRankMutation,
useDeleteRankMutation,
useUpdateLicenseValidityMutation,
useGetLicenseTypeRequirementsQuery,
useCreateApplicationMutation,

View File

@@ -575,9 +575,37 @@ export interface TemplateFieldPlacement {
}
/** A certificate design authored in the backoffice. */
/** An STCW seafarer department (Deck, Engine, Catering), backoffice-managed. */
export interface Department {
id: string;
/** Matches the ESeafarerDepartment value stored elsewhere, e.g. "DECK". */
code: string;
name: Bilingual;
sortOrder: number;
isActive: boolean;
}
export type RankCertificateCategory = "COC" | "COP";
/** One rung of a CoC/CoP ladder for a department. */
export interface Rank {
id: string;
departmentId: string;
certificateCategory: RankCertificateCategory;
/** Value stored on License.rank / Certification.rankKey, e.g. "CHIEF_MATE". */
key: string;
name: Bilingual;
/** Rung position within its department+category ladder. 0 is the entry rank. */
ladderOrder: number;
sortOrder: number;
isActive: boolean;
}
export interface LicenseTemplate {
id: string;
licenseTypeId: string;
/** Scopes this design to one rank's certificate. Null = the type's default. */
rankId?: string | null;
name: string;
version: number;
hbsSource: string;