Files
probo/apps/trust/src/routes.tsx
Bryan Frimin a3e3c0bdda Update trust webapp to work in both mode
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2025-10-09 15:06:41 +02:00

180 lines
4.7 KiB
TypeScript

import {
createBrowserRouter,
Navigate,
redirect,
type RouteObject,
useLoaderData,
useRouteError,
} from "react-router";
import { type ComponentType, Fragment, Suspense } from "react";
import {
relayEnvironment,
UnAuthenticatedError,
} from "./providers/RelayProviders";
import { loadQuery, type PreloadedQuery } from "react-relay";
import { useCleanup } from "./hooks/useDelayedEffect";
import { PageError } from "./components/PageError";
import { MainLayout } from "/layouts/MainLayout";
import {
currentTrustGraphQuery,
currentTrustDocumentsQuery,
currentTrustVendorsQuery,
} from "/queries/TrustGraph";
import { OverviewPage } from "/pages/OverviewPage";
import { DocumentsPage } from "/pages/DocumentsPage";
import { SubprocessorsPage } from "/pages/SubprocessorsPage";
import { AccessPage } from "./pages/AccessPage.tsx";
import { TabSkeleton } from "./components/Skeletons/TabSkeleton";
import { MainSkeleton } from "./components/Skeletons/MainSkeleton";
export type AppRoute = Omit<RouteObject, "Component" | "children"> & {
Component?: ComponentType<any>;
children?: AppRoute[];
fallback?: ComponentType;
queryLoader?: (params: any) => PreloadedQuery<any>;
};
/**
* Top level error boundary
*/
function ErrorBoundary({ error: propsError }: { error?: string }) {
const error = useRouteError() ?? propsError;
if (error instanceof UnAuthenticatedError) {
return <Navigate to="/auth/login" />;
}
return <PageError error={error?.toString()} />;
}
const routes = [
{
path: "/",
loader: async () => {
throw redirect("/overview");
},
Component: Fragment,
ErrorBoundary: ErrorBoundary,
},
// Custom domain routes (subdomain-based)
{
path: "/overview",
queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}),
Component: MainLayout,
fallback: MainSkeleton,
ErrorBoundary: ErrorBoundary,
children: [
{
path: "",
fallback: TabSkeleton,
Component: OverviewPage,
},
],
},
{
path: "/documents",
queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}),
Component: MainLayout,
fallback: MainSkeleton,
ErrorBoundary: ErrorBoundary,
children: [
{
path: "",
queryLoader: () =>
loadQuery(relayEnvironment, currentTrustDocumentsQuery, {}),
fallback: TabSkeleton,
Component: DocumentsPage,
},
],
},
{
path: "/subprocessors",
queryLoader: () => loadQuery(relayEnvironment, currentTrustGraphQuery, {}),
Component: MainLayout,
fallback: MainSkeleton,
ErrorBoundary: ErrorBoundary,
children: [
{
path: "",
queryLoader: () =>
loadQuery(relayEnvironment, currentTrustVendorsQuery, {}),
fallback: TabSkeleton,
Component: SubprocessorsPage,
},
],
},
{
path: "/access",
Component: AccessPage,
ErrorBoundary: ErrorBoundary,
},
// Fallback URL to the NotFound Page
{
path: "*",
Component: PageError,
},
] satisfies AppRoute[];
/**
* Wrap components with suspense to handle lazy loading & relay loading states
*/
function routeTransformer({
fallback: FallbackComponent,
queryLoader,
...route
}: AppRoute): RouteObject {
let result = { ...route };
if (FallbackComponent && route.Component) {
const OriginalComponent = route.Component;
result = {
...result,
Component: (props) => (
<Suspense fallback={<FallbackComponent />}>
<OriginalComponent {...props} />
</Suspense>
),
};
}
if (queryLoader && route.Component) {
const OriginalComponent = route.Component;
result = {
...result,
loader: ({ params }) => {
const query = queryLoader(params as Record<string, string>);
return {
queryRef: query,
dispose: query.dispose,
};
},
Component: () => {
const { queryRef, dispose } = useLoaderData();
useCleanup(dispose, 1000);
return (
<Suspense fallback={FallbackComponent ? <FallbackComponent /> : null}>
<OriginalComponent queryRef={queryRef} />
</Suspense>
);
},
};
}
return {
...result,
children: route.children?.map(routeTransformer),
} as RouteObject;
}
// Detect basename from current URL path
// If URL starts with /trust/{slug}, extract that as the basename
// Otherwise, use "/" for custom domains
function getBasename(): string {
const path = window.location.pathname;
const trustMatch = path.match(/^\/trust\/[^/]+/);
return trustMatch ? trustMatch[0] : "/";
}
export const router = createBrowserRouter(routes.map(routeTransformer), {
basename: getBasename(),
});