mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
feat: add account type synchronization to operations form based on selected license types
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
useGetMyOperatorTypesQuery,
|
||||
useUpdateMyOperatorTypesMutation,
|
||||
} from '@ema-platform/api';
|
||||
import { useUpdateMyAccountTypeMutation } from '@ema-platform/auth';
|
||||
import { notify, ModalFooter } from '@ema-platform/ui';
|
||||
import { useDateDisplayer } from '@ema-platform/shared';
|
||||
|
||||
@@ -35,6 +36,29 @@ const PERSONAL_REGISTRATION_KEYS = [
|
||||
'VESSEL_REGISTRATION',
|
||||
];
|
||||
|
||||
/**
|
||||
* Declared modes -> portal account type.
|
||||
*
|
||||
* The account type is what the server computes portal permissions from, and
|
||||
* therefore what decides which services appear in the sidebar at all. Modes
|
||||
* of operation grant nothing on their own: an applicant who declared
|
||||
* "freight forwarder" here was still left on the seafarer default and never
|
||||
* saw the logistics or vessel entries.
|
||||
*
|
||||
* One type per account against a multi-select, so the widest capability wins:
|
||||
* a company representative who also sails still needs the company services,
|
||||
* and the seafarer side stays reachable through their own registration.
|
||||
*/
|
||||
function accountTypeFor(keys: string[]): string | null {
|
||||
if (keys.some((key) => !PERSONAL_REGISTRATION_KEYS.includes(key))) {
|
||||
// Historical enum value for the logistics company representative.
|
||||
return 'FRIGHTER';
|
||||
}
|
||||
if (keys.includes('VESSEL_REGISTRATION')) return 'VESSEL_OWNER';
|
||||
if (keys.includes('SEAFARER_REGISTRATION')) return 'SEAFARER';
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The applicant's modes of operation — what they do, and therefore which
|
||||
* licences the portal offers them.
|
||||
@@ -58,6 +82,7 @@ export function OperationsFormContent({
|
||||
const { data: catalogue, isLoading: loadingTypes } = useGetLicenseTypesQuery();
|
||||
const { data: mine, isLoading: loadingMine } = useGetMyOperatorTypesQuery();
|
||||
const [save, { isLoading: saving }] = useUpdateMyOperatorTypesMutation();
|
||||
const [setAccountType] = useUpdateMyAccountTypeMutation();
|
||||
const localized = useLocalized();
|
||||
|
||||
const declaredIds = useMemo(
|
||||
@@ -118,14 +143,25 @@ export function OperationsFormContent({
|
||||
async function persist() {
|
||||
try {
|
||||
await save({ licenseTypeIds: selected }).unwrap();
|
||||
const selectedKeys = options
|
||||
.filter((t) => selected.includes(t.id))
|
||||
.map((t) => t.key);
|
||||
|
||||
const type = accountTypeFor(selectedKeys);
|
||||
if (type) {
|
||||
// Refused for a registered seafarer, whose type the authority owns
|
||||
// (`registered_seafarer_type_is_fixed`), and for staff. The modes are
|
||||
// already stored either way, so a refusal here is not the applicant's
|
||||
// problem to see.
|
||||
await setAccountType({ type }).unwrap().catch(() => undefined);
|
||||
}
|
||||
|
||||
setConfirmingRemoval(false);
|
||||
notify.success(
|
||||
t('profileOperations.updateSuccessBody'),
|
||||
t('profileOperations.updateSuccessTitle'),
|
||||
);
|
||||
onSaved?.(
|
||||
options.filter((t) => selected.includes(t.id)).map((t) => t.key),
|
||||
);
|
||||
onSaved?.(selectedKeys);
|
||||
} catch (err) {
|
||||
notify.error(extractErrorMessage(err), t('profileOperations.updateErrorTitle'));
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ export {
|
||||
useGetMyProfileQuery,
|
||||
useUpdateMyProfileMutation,
|
||||
useUpdateMyAddressMutation,
|
||||
useUpdateMyAccountTypeMutation,
|
||||
PROFILE_FIELDS,
|
||||
PROFILE_FIELD_SECTION,
|
||||
} from "./lib/hooks/useCurrentProfile";
|
||||
|
||||
@@ -119,6 +119,22 @@ const profileApi = baseApi
|
||||
query: ({ id, body }) => ({ url: `/profiles/${id}`, method: 'PUT', body }),
|
||||
invalidatesTags: ['CurrentProfile'],
|
||||
}),
|
||||
/**
|
||||
* The portal role — seafarer, vessel owner or logistics operator.
|
||||
*
|
||||
* Sidebar and route permissions are computed from this server-side
|
||||
* (`profiles.type`), not from the declared modes of operation, so the
|
||||
* Operations tab writes it here too. Invalidates the profile so the
|
||||
* shell repermissions itself without a reload.
|
||||
*/
|
||||
updateMyAccountType: builder.mutation<unknown, { type: string }>({
|
||||
query: (body) => ({
|
||||
url: '/profiles/me/account-type',
|
||||
method: 'PUT',
|
||||
body,
|
||||
}),
|
||||
invalidatesTags: (_result, error) => (error ? [] : ['CurrentProfile']),
|
||||
}),
|
||||
updateMyAddress: builder.mutation<
|
||||
unknown,
|
||||
{ id: string; body: Record<string, unknown> }
|
||||
@@ -134,6 +150,7 @@ export const {
|
||||
useGetMyProfileQuery,
|
||||
useUpdateMyProfileMutation,
|
||||
useUpdateMyAddressMutation,
|
||||
useUpdateMyAccountTypeMutation,
|
||||
} = profileApi;
|
||||
export const currentProfileApi = profileApi;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user