mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
The Fayda redirect URI registered for local testing is http://localhost:3000/callback, matched exactly by the provider, so the portal has to be the thing listening there. Its dev and preview servers move from 4200 to 3000 and the API moves to 3001. Every hardcoded fallback to http://localhost:3000/api follows — six copies of the same default across libs/api, libs/auth, the portal and the backoffice — otherwise a developer without a .env would have had the app calling itself. e2e is unaffected: it binds its own ports (3011/4302/4303) explicitly.
72 lines
2.8 KiB
TypeScript
72 lines
2.8 KiB
TypeScript
import type { Bilingual, LicenseCategory, LicenseTemplate, LicenseType } from '@ema-platform/api';
|
|
|
|
/** Same resolution — and same fallback — the shared RTK Query baseQuery uses. */
|
|
export const API_BASE_URL =
|
|
(import.meta as { env?: Record<string, string> }).env?.['VITE_BASE_API_URL'] ??
|
|
'http://localhost:3001/api';
|
|
|
|
export const STATUS_COLOR: Record<LicenseTemplate['status'], string> = {
|
|
DRAFT: 'gray',
|
|
PUBLISHED: 'teal',
|
|
ARCHIVED: 'dark',
|
|
};
|
|
|
|
/** Page options sent with every save and preview — A4, background printed. */
|
|
export function pageOptionsFor(landscape: boolean) {
|
|
return { format: 'A4' as const, landscape, printBackground: true };
|
|
}
|
|
|
|
/**
|
|
* Order the category groups appear in, and their fallback labels.
|
|
*
|
|
* Seafarer certification sits first: it holds over 50 of the 80-odd licence
|
|
* types, and it is what the designer is opened for most often. Any category
|
|
* not listed here still renders — it falls to the end under its own key —
|
|
* so a new category on the server does not silently hide its types.
|
|
*/
|
|
const CATEGORY_ORDER: { key: LicenseCategory; fallback: string }[] = [
|
|
{ key: 'MARITIME_PERSONNEL', fallback: 'Seafarer certification (CoC / CoP)' },
|
|
{ key: 'CARGO_FREIGHT', fallback: 'Cargo & freight' },
|
|
{ key: 'SHIPPING_AGENCY', fallback: 'Shipping agency' },
|
|
{ key: 'INVESTMENT', fallback: 'Investment & joint ventures' },
|
|
{ key: 'VESSEL_SERVICES', fallback: 'Vessel services' },
|
|
{ key: 'WAIVER_SERVICES', fallback: 'Waiver services' },
|
|
];
|
|
|
|
/**
|
|
* The licence-type picker's options, grouped by category.
|
|
*
|
|
* Grouping is what makes the picker usable at all: certificates of competency
|
|
* and proficiency are a different kind of thing from an operator's logistics
|
|
* licence, and a flat alphabetical list interleaves the two.
|
|
*/
|
|
export function groupedTypeOptions(
|
|
licenseTypes: LicenseType[],
|
|
localized: (value?: Bilingual) => string,
|
|
t: (key: string, fallback: string) => string,
|
|
) {
|
|
const byCategory = new Map<string, { value: string; label: string }[]>();
|
|
|
|
for (const type of licenseTypes) {
|
|
const option = { value: type.id, label: localized(type.name) || type.key };
|
|
const bucket = byCategory.get(type.category);
|
|
if (bucket) bucket.push(option);
|
|
else byCategory.set(type.category, [option]);
|
|
}
|
|
|
|
const known = CATEGORY_ORDER.map(({ key, fallback }) => ({
|
|
group: t(`designer.category.${key}`, fallback),
|
|
items: (byCategory.get(key) ?? []).sort((a, b) => a.label.localeCompare(b.label)),
|
|
}));
|
|
|
|
// Categories the client does not know about yet, so their types stay reachable.
|
|
const unknown = [...byCategory.entries()]
|
|
.filter(([key]) => !CATEGORY_ORDER.some((c) => c.key === key))
|
|
.map(([key, items]) => ({
|
|
group: key,
|
|
items: items.sort((a, b) => a.label.localeCompare(b.label)),
|
|
}));
|
|
|
|
return [...known, ...unknown].filter((group) => group.items.length > 0);
|
|
}
|