mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
101 lines
3.7 KiB
TypeScript
101 lines
3.7 KiB
TypeScript
import { Navigate, useLocation } from 'react-router-dom';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useGetMyOperatorTypesQuery } from '@ema-platform/api';
|
|
import { PageLoader } from '@ema-platform/ui';
|
|
|
|
/**
|
|
* Sends an applicant who has not said what they operate as to the step that
|
|
* asks. The logistics licence catalogue is keyed off that answer — it offers
|
|
* nothing without it, and the server refuses an application for a mode the
|
|
* profile does not hold — so it is asked once, up front.
|
|
*
|
|
* The gate is deliberately not portal-wide. Seafarer and vessel services are
|
|
* seeded `requiresOperatorMode: false` precisely because a seafarer holds no
|
|
* logistics mode of operation; gating them on this question locked the people
|
|
* those services exist for out of registration, sea records, certificates and
|
|
* examinations. Profile and support stay open for the same reason: someone who
|
|
* cannot answer the question yet must still reach their account and ask for
|
|
* help.
|
|
*/
|
|
const ALWAYS_ALLOWED = [
|
|
'/onboarding/operations',
|
|
'/profile',
|
|
'/support',
|
|
'/notifications',
|
|
// Seafarer services — no operator mode required by the licence types behind
|
|
// them (SEAFARER_REGISTRATION, CERTIFICATE_OF_COMPETENCY/PROFICIENCY).
|
|
'/seafarer-registration',
|
|
'/seafarer/records',
|
|
'/seafarer-registry',
|
|
'/exams',
|
|
'/certificates',
|
|
'/seaman-book',
|
|
'/endorsements',
|
|
'/documents',
|
|
// Vessel services — VESSEL_REGISTRATION is likewise mode-free.
|
|
'/vessel-registration',
|
|
// Waivers are filed by importers, who hold no logistics operating licence.
|
|
'/waiver',
|
|
];
|
|
|
|
/**
|
|
* Licence types seeded `requiresOperatorMode: false`.
|
|
*
|
|
* The wizard lives at one shared route (`/licensing/:typeCode/apply`), so the
|
|
* gate has to know which types behind it are mode-free — otherwise a seafarer
|
|
* or an importer reaches their service page and is bounced the moment they
|
|
* start the application. Mirrors the seeds; the server is still the authority,
|
|
* refusing a create for a mode the profile does not hold.
|
|
*/
|
|
const MODE_FREE_TYPE_KEYS = [
|
|
'SEAFARER_REGISTRATION',
|
|
'VESSEL_REGISTRATION',
|
|
'VESSEL_OWNERSHIP_TRANSFER',
|
|
'CERTIFICATE_OF_COMPETENCY',
|
|
'CERTIFICATE_OF_PROFICIENCY',
|
|
'ENDORSEMENT_COC',
|
|
'ENDORSEMENT_GOC',
|
|
'PRE_WAIVER',
|
|
'POST_WAIVER',
|
|
];
|
|
|
|
function isModeFreeLicensingRoute(pathname: string): boolean {
|
|
const match = pathname.match(/^\/licensing\/([^/]+)/);
|
|
return match ? MODE_FREE_TYPE_KEYS.includes(match[1]) : false;
|
|
}
|
|
|
|
export function RequireOperations({ children }: { children: React.ReactNode }) {
|
|
const { t } = useTranslation();
|
|
const { pathname } = useLocation();
|
|
const { data, isLoading, isFetching, isError } = useGetMyOperatorTypesQuery();
|
|
|
|
if (
|
|
ALWAYS_ALLOWED.some((path) => pathname.startsWith(path)) ||
|
|
isModeFreeLicensingRoute(pathname)
|
|
) {
|
|
return <>{children}</>;
|
|
}
|
|
|
|
if (isLoading) {
|
|
return <PageLoader label={t('onboarding.checkingProfile')} height={350} />;
|
|
}
|
|
|
|
// A failed lookup must not lock anyone out of the portal — the server still
|
|
// enforces the rule on create, so the worst case is a catalogue that offers
|
|
// more than it should for one session.
|
|
if (isError) return <>{children}</>;
|
|
|
|
if ((data?.items ?? []).length === 0) {
|
|
// An empty set while a request is in flight is not an answer. Saving the
|
|
// onboarding form invalidates this query and navigates to the dashboard in
|
|
// the same tick; deciding on the pre-save cache bounced the applicant
|
|
// straight back to the screen they had just completed.
|
|
if (isFetching) {
|
|
return <PageLoader label={t('onboarding.checkingProfile')} height={350} />;
|
|
}
|
|
return <Navigate to="/onboarding/operations" replace />;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|