feat: add account type synchronization to operations form based on selected license types

This commit is contained in:
estifanos
2026-08-18 11:23:30 +00:00
parent 581eac7466
commit 2257989821
3 changed files with 57 additions and 3 deletions

View File

@@ -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'));
}

View File

@@ -37,6 +37,7 @@ export {
useGetMyProfileQuery,
useUpdateMyProfileMutation,
useUpdateMyAddressMutation,
useUpdateMyAccountTypeMutation,
PROFILE_FIELDS,
PROFILE_FIELD_SECTION,
} from "./lib/hooks/useCurrentProfile";

View File

@@ -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;