export const routePatterns = { catalogue: '/', viewer: '/machines/:machineId', legacyViewer: '/viewer/:machineId', notFound: '*', } as const; export type RouteId = keyof typeof routePatterns; export interface NavigationRoute { id: RouteId; label: string; path: string; ariaLabel: string; } export const navigationRoutes: readonly NavigationRoute[] = [ { id: 'catalogue', label: 'Catalogue', path: routePatterns.catalogue, ariaLabel: 'Open machine catalogue', }, { id: 'viewer', label: 'Viewer', path: routePatterns.viewer, ariaLabel: 'Open mechanical system viewer', }, ]; export type RouteQueryValue = string | number | boolean | null | undefined; const serialiseQuery = (query?: URLSearchParams | Record): string => { if (!query) { return ''; } const params = query instanceof URLSearchParams ? query : new URLSearchParams( Object.entries(query).reduce>((accumulator, [key, value]) => { if (value !== null && value !== undefined && value !== '') { accumulator[key] = String(value); } return accumulator; }, {}), ); const serialised = params.toString(); return serialised ? `?${serialised}` : ''; }; export const buildCatalogueRoute = (query?: URLSearchParams | Record): string => `${routePatterns.catalogue}${serialiseQuery(query)}`; export const buildMachineRoute = ( machineId: string, query?: URLSearchParams | Record, ): string => `/machines/${encodeURIComponent(machineId)}${serialiseQuery(query)}`; export const buildLegacyViewerRoute = ( machineId: string, query?: URLSearchParams | Record, ): string => `/viewer/${encodeURIComponent(machineId)}${serialiseQuery(query)}`; export const isViewerPath = (pathname: string): boolean => /^\/(?:machines|viewer)\/[^/]+/.test(pathname); export const parseMachineIdFromPath = (pathname: string): string | undefined => { const match = pathname.match(/^\/(?:machines|viewer)\/([^/?#]+)/); return match?.[1] ? decodeURIComponent(match[1]) : undefined; }; export const routes = { catalogue: { id: 'catalogue', path: routePatterns.catalogue, build: buildCatalogueRoute, }, viewer: { id: 'viewer', path: routePatterns.viewer, build: buildMachineRoute, }, legacyViewer: { id: 'legacyViewer', path: routePatterns.legacyViewer, build: buildLegacyViewerRoute, }, notFound: { id: 'notFound', path: routePatterns.notFound, build: () => routePatterns.catalogue, }, } as const;