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,
|
useGetMyOperatorTypesQuery,
|
||||||
useUpdateMyOperatorTypesMutation,
|
useUpdateMyOperatorTypesMutation,
|
||||||
} from '@ema-platform/api';
|
} from '@ema-platform/api';
|
||||||
|
import { useUpdateMyAccountTypeMutation } from '@ema-platform/auth';
|
||||||
import { notify, ModalFooter } from '@ema-platform/ui';
|
import { notify, ModalFooter } from '@ema-platform/ui';
|
||||||
import { useDateDisplayer } from '@ema-platform/shared';
|
import { useDateDisplayer } from '@ema-platform/shared';
|
||||||
|
|
||||||
@@ -35,6 +36,29 @@ const PERSONAL_REGISTRATION_KEYS = [
|
|||||||
'VESSEL_REGISTRATION',
|
'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
|
* The applicant's modes of operation — what they do, and therefore which
|
||||||
* licences the portal offers them.
|
* licences the portal offers them.
|
||||||
@@ -58,6 +82,7 @@ export function OperationsFormContent({
|
|||||||
const { data: catalogue, isLoading: loadingTypes } = useGetLicenseTypesQuery();
|
const { data: catalogue, isLoading: loadingTypes } = useGetLicenseTypesQuery();
|
||||||
const { data: mine, isLoading: loadingMine } = useGetMyOperatorTypesQuery();
|
const { data: mine, isLoading: loadingMine } = useGetMyOperatorTypesQuery();
|
||||||
const [save, { isLoading: saving }] = useUpdateMyOperatorTypesMutation();
|
const [save, { isLoading: saving }] = useUpdateMyOperatorTypesMutation();
|
||||||
|
const [setAccountType] = useUpdateMyAccountTypeMutation();
|
||||||
const localized = useLocalized();
|
const localized = useLocalized();
|
||||||
|
|
||||||
const declaredIds = useMemo(
|
const declaredIds = useMemo(
|
||||||
@@ -118,14 +143,25 @@ export function OperationsFormContent({
|
|||||||
async function persist() {
|
async function persist() {
|
||||||
try {
|
try {
|
||||||
await save({ licenseTypeIds: selected }).unwrap();
|
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);
|
setConfirmingRemoval(false);
|
||||||
notify.success(
|
notify.success(
|
||||||
t('profileOperations.updateSuccessBody'),
|
t('profileOperations.updateSuccessBody'),
|
||||||
t('profileOperations.updateSuccessTitle'),
|
t('profileOperations.updateSuccessTitle'),
|
||||||
);
|
);
|
||||||
onSaved?.(
|
onSaved?.(selectedKeys);
|
||||||
options.filter((t) => selected.includes(t.id)).map((t) => t.key),
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notify.error(extractErrorMessage(err), t('profileOperations.updateErrorTitle'));
|
notify.error(extractErrorMessage(err), t('profileOperations.updateErrorTitle'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export {
|
|||||||
useGetMyProfileQuery,
|
useGetMyProfileQuery,
|
||||||
useUpdateMyProfileMutation,
|
useUpdateMyProfileMutation,
|
||||||
useUpdateMyAddressMutation,
|
useUpdateMyAddressMutation,
|
||||||
|
useUpdateMyAccountTypeMutation,
|
||||||
PROFILE_FIELDS,
|
PROFILE_FIELDS,
|
||||||
PROFILE_FIELD_SECTION,
|
PROFILE_FIELD_SECTION,
|
||||||
} from "./lib/hooks/useCurrentProfile";
|
} from "./lib/hooks/useCurrentProfile";
|
||||||
|
|||||||
@@ -119,6 +119,22 @@ const profileApi = baseApi
|
|||||||
query: ({ id, body }) => ({ url: `/profiles/${id}`, method: 'PUT', body }),
|
query: ({ id, body }) => ({ url: `/profiles/${id}`, method: 'PUT', body }),
|
||||||
invalidatesTags: ['CurrentProfile'],
|
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<
|
updateMyAddress: builder.mutation<
|
||||||
unknown,
|
unknown,
|
||||||
{ id: string; body: Record<string, unknown> }
|
{ id: string; body: Record<string, unknown> }
|
||||||
@@ -134,6 +150,7 @@ export const {
|
|||||||
useGetMyProfileQuery,
|
useGetMyProfileQuery,
|
||||||
useUpdateMyProfileMutation,
|
useUpdateMyProfileMutation,
|
||||||
useUpdateMyAddressMutation,
|
useUpdateMyAddressMutation,
|
||||||
|
useUpdateMyAccountTypeMutation,
|
||||||
} = profileApi;
|
} = profileApi;
|
||||||
export const currentProfileApi = profileApi;
|
export const currentProfileApi = profileApi;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user