Move route utils to packages/routes

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2025-12-04 16:09:36 +04:00
parent 2fc6143409
commit 57caa9d28f
28 changed files with 299 additions and 189 deletions

View File

@@ -13,10 +13,11 @@
},
"dependencies": {
"@hookform/resolvers": "^5.0.1",
"@probo/helpers": "1.0.0",
"@probo/helpers": "^1.0.0",
"@probo/hooks": "1.0.0",
"@probo/i18n": "1.0.0",
"@probo/react-lazy": "1.0.0",
"@probo/routes": "^1.0.0",
"@probo/ui": "1.0.0",
"@probo/vendors": "0.0.1",
"@tanstack/react-query": "^5.76.1",

View File

@@ -2,15 +2,11 @@ import {
createBrowserRouter,
Navigate,
redirect,
useLoaderData,
useRouteError,
type LoaderFunction,
type LoaderFunctionArgs,
type RouteObject,
} from "react-router";
import { MainLayout } from "./layouts/MainLayout";
import { AuthLayout, CenteredLayout, CenteredLayoutSkeleton } from "@probo/ui";
import { Fragment, Suspense, type ComponentType, type LazyExoticComponent } from "react";
import { Fragment } from "react";
import {
relayEnvironment,
UnAuthenticatedError,
@@ -18,8 +14,7 @@ import {
ForbiddenError,
} from "./providers/RelayProviders";
import { PageSkeleton } from "./components/skeletons/PageSkeleton.tsx";
import { loadQuery, type EnvironmentProviderOptions, type PreloadedQuery } from "react-relay";
import { useCleanup } from "./hooks/useDelayedEffect.ts";
import { loadQuery } from "react-relay";
import { riskRoutes } from "./routes/riskRoutes.ts";
import { measureRoutes } from "./routes/measureRoutes.ts";
import { documentsRoutes } from "./routes/documentsRoutes.ts";
@@ -40,42 +35,7 @@ import { snapshotsRoutes } from "./routes/snapshotsRoutes.ts";
import { continualImprovementRoutes } from "./routes/continualImprovementRoutes.ts";
import { processingActivityRoutes } from "./routes/processingActivityRoutes.ts";
import { lazy } from "@probo/react-lazy";
import type { OperationType } from "relay-runtime";
export function withQueryRef<
TQuery extends OperationType,
TEnvironmentProviderOptions = EnvironmentProviderOptions
>(
Component: LazyExoticComponent<ComponentType<{ queryRef: PreloadedQuery<TQuery, TEnvironmentProviderOptions> }>>,
) {
return () => {
const { queryRef, dispose } = useLoaderData();
useCleanup(dispose, 1000);
return <Component queryRef={queryRef} />
}
}
export function loaderFromQueryLoader<
TQuery extends OperationType,
TEnvironmentProviderOptions = EnvironmentProviderOptions
>(
queryLoader: (params: Record<string, string>) => PreloadedQuery<TQuery, TEnvironmentProviderOptions>
): LoaderFunction {
return ({ params }: LoaderFunctionArgs) => {
const query = queryLoader(params as Record<string, string>);
return {
queryRef: query,
dispose: query.dispose,
};
}
}
export type AppRoute = Omit<RouteObject, "children"> & {
children?: AppRoute[];
fallback?: ComponentType;
}
import { loaderFromQueryLoader, routeFromAppRoute, withQueryRef, type AppRoute } from "@probo/routes";
/**
* Top level error boundary
@@ -132,7 +92,7 @@ const routes = [
{
path: "/",
Component: CenteredLayout,
fallback: CenteredLayoutSkeleton,
Fallback: CenteredLayoutSkeleton,
ErrorBoundary: ErrorBoundary,
children: [
{
@@ -171,7 +131,7 @@ const routes = [
},
{
path: "settings",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(
({ organizationId }) =>
loadQuery(relayEnvironment, organizationViewQuery, {
@@ -234,34 +194,4 @@ const routes = [
},
] satisfies AppRoute[];
/**
* Wrap components with suspense to handle lazy loading & relay loading states
*/
function routeTransformer({
Component,
fallback: FallbackComponent,
children,
...rest
}: AppRoute): RouteObject {
let route: RouteObject = { ...rest };
if (Component && FallbackComponent) {
const OriginalComponent = Component as ComponentType;
route = {
...route,
Component: () => (
<Suspense fallback={<FallbackComponent />}>
<OriginalComponent />
</Suspense>
),
};
}
return {
...route,
children: children?.map(routeTransformer),
} as RouteObject;
}
export const router = createBrowserRouter(routes.map(routeTransformer));
export const router = createBrowserRouter(routes.map(routeFromAppRoute));

View File

@@ -5,12 +5,12 @@ import { lazy } from "@probo/react-lazy";
import { assetsQuery, assetNodeQuery } from "../hooks/graph/AssetGraph";
import type { AssetGraphListQuery } from "/hooks/graph/__generated__/AssetGraphListQuery.graphql";
import type { AssetGraphNodeQuery } from "/hooks/graph/__generated__/AssetGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
export const assetRoutes = [
{
path: "assets",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<AssetGraphListQuery>(relayEnvironment, assetsQuery, {
organizationId: organizationId,
@@ -21,7 +21,7 @@ export const assetRoutes = [
},
{
path: "snapshots/:snapshotId/assets",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<AssetGraphListQuery>(relayEnvironment, assetsQuery, {
organizationId: organizationId,
@@ -32,7 +32,7 @@ export const assetRoutes = [
},
{
path: "assets/:assetId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ assetId }) =>
loadQuery<AssetGraphNodeQuery>(relayEnvironment, assetNodeQuery, { assetId }),
),
@@ -42,7 +42,7 @@ export const assetRoutes = [
},
{
path: "snapshots/:snapshotId/assets/:assetId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ assetId }) =>
loadQuery<AssetGraphNodeQuery>(relayEnvironment, assetNodeQuery, { assetId }),
),

View File

@@ -5,12 +5,12 @@ import { lazy } from "@probo/react-lazy";
import { auditsQuery, auditNodeQuery } from "../hooks/graph/AuditGraph";
import type { AuditGraphListQuery } from "/hooks/graph/__generated__/AuditGraphListQuery.graphql";
import type { AuditGraphNodeQuery } from "/hooks/graph/__generated__/AuditGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
export const auditRoutes = [
{
path: "audits",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<AuditGraphListQuery>(relayEnvironment, auditsQuery, { organizationId }),
),
@@ -20,7 +20,7 @@ export const auditRoutes = [
},
{
path: "audits/:auditId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ auditId }) =>
loadQuery<AuditGraphNodeQuery>(relayEnvironment, auditNodeQuery, { auditId }),
),
@@ -28,4 +28,4 @@ export const auditRoutes = [
() => import("/pages/organizations/audits/AuditDetailsPage")
)),
},
];
] satisfies AppRoute[];

View File

@@ -5,12 +5,12 @@ import { lazy } from "@probo/react-lazy";
import { continualImprovementsQuery, continualImprovementNodeQuery } from "/hooks/graph/ContinualImprovementGraph";
import type { ContinualImprovementGraphListQuery } from "/hooks/graph/__generated__/ContinualImprovementGraphListQuery.graphql";
import type { ContinualImprovementGraphNodeQuery } from "/hooks/graph/__generated__/ContinualImprovementGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
export const continualImprovementRoutes = [
{
path: "continual-improvements",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<ContinualImprovementGraphListQuery>(relayEnvironment, continualImprovementsQuery, {
organizationId,
@@ -23,7 +23,7 @@ export const continualImprovementRoutes = [
},
{
path: "snapshots/:snapshotId/continual-improvements",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<ContinualImprovementGraphListQuery>(relayEnvironment, continualImprovementsQuery, {
organizationId,
@@ -36,7 +36,7 @@ export const continualImprovementRoutes = [
},
{
path: "continual-improvements/:improvementId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ improvementId }) =>
loadQuery<ContinualImprovementGraphNodeQuery>(relayEnvironment, continualImprovementNodeQuery, {
continualImprovementId: improvementId!,
@@ -48,7 +48,7 @@ export const continualImprovementRoutes = [
},
{
path: "snapshots/:snapshotId/continual-improvements/:improvementId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ improvementId }) =>
loadQuery<ContinualImprovementGraphNodeQuery>(relayEnvironment, continualImprovementNodeQuery, {
continualImprovementId: improvementId!,

View File

@@ -5,12 +5,12 @@ import { lazy } from "@probo/react-lazy";
import { dataQuery, datumNodeQuery } from "../hooks/graph/DatumGraph";
import type { DatumGraphListQuery } from "/hooks/graph/__generated__/DatumGraphListQuery.graphql";
import type { DatumGraphNodeQuery } from "/hooks/graph/__generated__/DatumGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
export const dataRoutes = [
{
path: "data",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<DatumGraphListQuery>(relayEnvironment, dataQuery, {
organizationId: organizationId,
@@ -23,7 +23,7 @@ export const dataRoutes = [
},
{
path: "snapshots/:snapshotId/data",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<DatumGraphListQuery>(relayEnvironment, dataQuery, {
organizationId,
@@ -36,7 +36,7 @@ export const dataRoutes = [
},
{
path: "data/:dataId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ dataId }) =>
loadQuery<DatumGraphNodeQuery>(relayEnvironment, datumNodeQuery, { dataId }),
),
@@ -46,7 +46,7 @@ export const dataRoutes = [
},
{
path: "snapshots/:snapshotId/data/:dataId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ dataId }) =>
loadQuery<DatumGraphNodeQuery>(relayEnvironment, datumNodeQuery, { dataId }),
),

View File

@@ -7,7 +7,7 @@ import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { redirect, type LoaderFunctionArgs } from "react-router";
import { lazy } from "@probo/react-lazy";
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { DocumentGraphListQuery } from "/hooks/graph/__generated__/DocumentGraphListQuery.graphql";
import type { DocumentGraphNodeQuery } from "/hooks/graph/__generated__/DocumentGraphNodeQuery.graphql";
@@ -26,7 +26,7 @@ const documentTabs = (prefix: string) => {
},
{
path: `${prefix}description`,
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import(
@@ -36,7 +36,7 @@ const documentTabs = (prefix: string) => {
},
{
path: `${prefix}controls`,
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("../pages/organizations/documents/tabs/DocumentControlsTab"),
@@ -44,7 +44,7 @@ const documentTabs = (prefix: string) => {
},
{
path: `${prefix}signatures`,
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("../pages/organizations/documents/tabs/DocumentSignaturesTab"),
@@ -56,7 +56,7 @@ const documentTabs = (prefix: string) => {
export const documentsRoutes = [
{
path: "documents",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<DocumentGraphListQuery>(relayEnvironment, documentsQuery, { organizationId: organizationId! }),
),
@@ -66,7 +66,7 @@ export const documentsRoutes = [
},
{
path: "documents/:documentId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ documentId }) =>
loadQuery<DocumentGraphNodeQuery>(relayEnvironment, documentNodeQuery, { documentId: documentId! }),
),

View File

@@ -9,7 +9,7 @@ import {
import { Fragment } from "react";
import { lazy } from "@probo/react-lazy";
import { ControlSkeleton } from "../components/skeletons/ControlSkeleton";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { FrameworkGraphListQuery } from "/hooks/graph/__generated__/FrameworkGraphListQuery.graphql";
import type { FrameworkGraphNodeQuery } from "/hooks/graph/__generated__/FrameworkGraphNodeQuery.graphql";
import type { FrameworkGraphControlNodeQuery } from "/hooks/graph/__generated__/FrameworkGraphControlNodeQuery.graphql";
@@ -17,7 +17,7 @@ import type { FrameworkGraphControlNodeQuery } from "/hooks/graph/__generated__/
export const frameworkRoutes = [
{
path: "frameworks",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<FrameworkGraphListQuery>(relayEnvironment, frameworksQuery, { organizationId: organizationId! }),
),
@@ -27,7 +27,7 @@ export const frameworkRoutes = [
},
{
path: "frameworks/:frameworkId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ frameworkId }) =>
loadQuery<FrameworkGraphNodeQuery>(relayEnvironment, frameworkNodeQuery, { frameworkId: frameworkId! }),
),
@@ -41,7 +41,7 @@ export const frameworkRoutes = [
},
{
path: "controls/:controlId",
fallback: ControlSkeleton,
Fallback: ControlSkeleton,
loader: loaderFromQueryLoader(({ controlId }) =>
loadQuery<FrameworkGraphControlNodeQuery>(relayEnvironment, frameworkControlNodeQuery, { controlId: controlId! }),
),

View File

@@ -6,14 +6,14 @@ import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { redirect } from "react-router";
import { lazy } from "@probo/react-lazy";
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { MeasureGraphListQuery } from "/hooks/graph/__generated__/MeasureGraphListQuery.graphql";
import type { MeasureGraphNodeQuery } from "/hooks/graph/__generated__/MeasureGraphNodeQuery.graphql";
export const measureRoutes = [
{
path: "measures",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<MeasureGraphListQuery>(relayEnvironment, measuresQuery, { organizationId: organizationId! }),
),
@@ -27,7 +27,7 @@ export const measureRoutes = [
},
{
path: "measures/:measureId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ measureId }) =>
loadQuery<MeasureGraphNodeQuery>(relayEnvironment, measureNodeQuery, { measureId: measureId! }),
),
@@ -42,21 +42,21 @@ export const measureRoutes = [
},
{
path: "risks",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/measures/tabs/MeasureRisksTab.tsx")
),
},
{
path: "tasks",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/measures/tabs/MeasureTasksTab.tsx")
),
},
{
path: "controls",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("/pages/organizations/measures/tabs/MeasureControlsTab.tsx")
@@ -64,7 +64,7 @@ export const measureRoutes = [
},
{
path: "evidences/:evidenceId?",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("/pages/organizations/measures/tabs/MeasureEvidencesTab.tsx")

View File

@@ -9,7 +9,7 @@ import { lazy } from "@probo/react-lazy";
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
import type { MeetingGraphListQuery } from "/hooks/graph/__generated__/MeetingGraphListQuery.graphql";
import type { MeetingGraphNodeQuery } from "/hooks/graph/__generated__/MeetingGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
const meetingTabs = (prefix: string) => {
return [
@@ -24,7 +24,7 @@ const meetingTabs = (prefix: string) => {
},
{
path: `${prefix}minutes`,
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import(
@@ -38,7 +38,7 @@ const meetingTabs = (prefix: string) => {
export const meetingsRoutes = [
{
path: "meetings",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<MeetingGraphListQuery>(relayEnvironment, meetingsQuery, { organizationId }),
),
@@ -48,7 +48,7 @@ export const meetingsRoutes = [
},
{
path: "meetings/:meetingId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ meetingId }) =>
loadQuery<MeetingGraphNodeQuery>(relayEnvironment, meetingNodeQuery, { meetingId }),
),
@@ -57,5 +57,5 @@ export const meetingsRoutes = [
)),
children: [...meetingTabs("")],
},
];
] satisfies AppRoute[];

View File

@@ -5,12 +5,12 @@ import { lazy } from "@probo/react-lazy";
import { nonconformitiesQuery, nonconformityNodeQuery } from "../hooks/graph/NonconformityGraph";
import type { NonconformityGraphListQuery } from "/hooks/graph/__generated__/NonconformityGraphListQuery.graphql";
import type { NonconformityGraphNodeQuery } from "/hooks/graph/__generated__/NonconformityGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
export const nonconformityRoutes = [
{
path: "nonconformities",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<NonconformityGraphListQuery>(relayEnvironment, nonconformitiesQuery, {
organizationId,
@@ -23,7 +23,7 @@ export const nonconformityRoutes = [
},
{
path: "snapshots/:snapshotId/nonconformities",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<NonconformityGraphListQuery>(relayEnvironment, nonconformitiesQuery, {
organizationId,
@@ -36,7 +36,7 @@ export const nonconformityRoutes = [
},
{
path: "nonconformities/:nonconformityId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ nonconformityId }) =>
loadQuery<NonconformityGraphNodeQuery>(relayEnvironment, nonconformityNodeQuery, {
nonconformityId,
@@ -48,7 +48,7 @@ export const nonconformityRoutes = [
},
{
path: "snapshots/:snapshotId/nonconformities/:nonconformityId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ nonconformityId }) =>
loadQuery<NonconformityGraphNodeQuery>(relayEnvironment, nonconformityNodeQuery, {
nonconformityId: nonconformityId

View File

@@ -5,12 +5,12 @@ import { lazy } from "@probo/react-lazy";
import { obligationsQuery, obligationNodeQuery } from "/hooks/graph/ObligationGraph";
import type { ObligationGraphListQuery } from "/hooks/graph/__generated__/ObligationGraphListQuery.graphql";
import type { ObligationGraphNodeQuery } from "/hooks/graph/__generated__/ObligationGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
export const obligationRoutes = [
{
path: "obligations",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<ObligationGraphListQuery>(relayEnvironment, obligationsQuery, {
organizationId,
@@ -23,7 +23,7 @@ export const obligationRoutes = [
},
{
path: "snapshots/:snapshotId/obligations",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<ObligationGraphListQuery>(relayEnvironment, obligationsQuery, {
organizationId,
@@ -36,7 +36,7 @@ export const obligationRoutes = [
},
{
path: "obligations/:obligationId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ obligationId }) =>
loadQuery<ObligationGraphNodeQuery>(relayEnvironment, obligationNodeQuery, {
obligationId,
@@ -48,7 +48,7 @@ export const obligationRoutes = [
},
{
path: "snapshots/:snapshotId/obligations/:obligationId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ obligationId }) =>
loadQuery<ObligationGraphNodeQuery>(relayEnvironment, obligationNodeQuery, {
obligationId,
@@ -58,4 +58,4 @@ export const obligationRoutes = [
() => import("/pages/organizations/obligations/ObligationDetailsPage")
)),
},
];
] satisfies AppRoute[];

View File

@@ -7,14 +7,14 @@ import {
peopleNodeQuery,
} from "/hooks/graph/PeopleGraph";
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { PeopleGraphPaginatedQuery } from "/hooks/graph/__generated__/PeopleGraphPaginatedQuery.graphql";
import type { PeopleGraphNodeQuery } from "/hooks/graph/__generated__/PeopleGraphNodeQuery.graphql";
export const peopleRoutes = [
{
path: "people",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<PeopleGraphPaginatedQuery>(relayEnvironment, paginatedPeopleQuery, { organizationId: organizationId! }),
),
@@ -22,7 +22,7 @@ export const peopleRoutes = [
},
{
path: "people/:peopleId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ peopleId }) =>
loadQuery<PeopleGraphNodeQuery>(relayEnvironment, peopleNodeQuery, { peopleId: peopleId! }),
),
@@ -32,21 +32,21 @@ export const peopleRoutes = [
children: [
{
path: "tasks",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/people/tabs/PeopleTasksTab")
),
},
{
path: "role",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/people/tabs/PeopleRoleTab")
),
},
{
path: "profile",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/people/tabs/PeopleProfileTab")
),

View File

@@ -3,14 +3,14 @@ import { relayEnvironment } from "/providers/RelayProviders";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { lazy } from "@probo/react-lazy";
import { processingActivitiesQuery, processingActivityNodeQuery } from "/hooks/graph/ProcessingActivityGraph";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { ProcessingActivityGraphListQuery } from "/hooks/graph/__generated__/ProcessingActivityGraphListQuery.graphql";
import type { ProcessingActivityGraphNodeQuery } from "/hooks/graph/__generated__/ProcessingActivityGraphNodeQuery.graphql";
export const processingActivityRoutes = [
{
path: "processing-activities",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<ProcessingActivityGraphListQuery>(relayEnvironment, processingActivitiesQuery, {
organizationId: organizationId!,
@@ -23,7 +23,7 @@ export const processingActivityRoutes = [
},
{
path: "snapshots/:snapshotId/processing-activities",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<ProcessingActivityGraphListQuery>(relayEnvironment, processingActivitiesQuery, {
organizationId: organizationId!,
@@ -36,7 +36,7 @@ export const processingActivityRoutes = [
},
{
path: "processing-activities/:activityId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ activityId }) =>
loadQuery<ProcessingActivityGraphNodeQuery>(relayEnvironment, processingActivityNodeQuery, {
processingActivityId: activityId!,
@@ -48,7 +48,7 @@ export const processingActivityRoutes = [
},
{
path: "snapshots/:snapshotId/processing-activities/:activityId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ activityId }) =>
loadQuery<ProcessingActivityGraphNodeQuery>(relayEnvironment, processingActivityNodeQuery, {
processingActivityId: activityId!,

View File

@@ -8,13 +8,13 @@ import { redirect } from "react-router";
import { lazy } from "@probo/react-lazy";
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
import type { RiskGraphNodeQuery } from "/hooks/graph/__generated__/RiskGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { RiskGraphListQuery } from "/hooks/graph/__generated__/RiskGraphListQuery.graphql";
export const riskRoutes = [
{
path: "risks",
fallback: RisksPageSkeleton,
Fallback: RisksPageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<RiskGraphListQuery>(relayEnvironment, risksQuery, {
organizationId: organizationId!,
@@ -25,7 +25,7 @@ export const riskRoutes = [
},
{
path: "snapshots/:snapshotId/risks",
fallback: RisksPageSkeleton,
Fallback: RisksPageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<RiskGraphListQuery>(relayEnvironment, risksQuery, {
organizationId: organizationId!,
@@ -36,7 +36,7 @@ export const riskRoutes = [
},
{
path: "risks/:riskId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ riskId }) =>
loadQuery<RiskGraphNodeQuery>(relayEnvironment, riskNodeQuery, { riskId: riskId! }),
),
@@ -51,35 +51,35 @@ export const riskRoutes = [
},
{
path: "overview",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/risks/tabs/RiskOverviewTab.tsx")
),
},
{
path: "measures",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/risks/tabs/RiskMeasuresTab.tsx")
),
},
{
path: "documents",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/risks/tabs/RiskDocumentsTab.tsx")
),
},
{
path: "controls",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/risks/tabs/RiskControlsTab.tsx")
),
},
{
path: "obligations",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/risks/tabs/RiskObligationsTab.tsx")
),
@@ -88,7 +88,7 @@ export const riskRoutes = [
},
{
path: "snapshots/:snapshotId/risks/:riskId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ riskId }) =>
loadQuery<RiskGraphNodeQuery>(relayEnvironment, riskNodeQuery, { riskId: riskId! }),
),
@@ -103,7 +103,7 @@ export const riskRoutes = [
},
{
path: "overview",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/risks/tabs/RiskOverviewTab.tsx")
),

View File

@@ -5,12 +5,12 @@ import { snapshotsQuery, snapshotNodeQuery } from "/hooks/graph/SnapshotGraph";
import { lazy } from "@probo/react-lazy";
import type { SnapshotGraphListQuery } from "/hooks/graph/__generated__/SnapshotGraphListQuery.graphql";
import type { SnapshotGraphNodeQuery } from "/hooks/graph/__generated__/SnapshotGraphNodeQuery.graphql";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
export const snapshotsRoutes = [
{
path: "snapshots",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<SnapshotGraphListQuery>(relayEnvironment, snapshotsQuery, { organizationId }),
),
@@ -20,7 +20,7 @@ export const snapshotsRoutes = [
},
{
path: "snapshots/:snapshotId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ snapshotId }) =>
loadQuery<SnapshotGraphNodeQuery>(relayEnvironment, snapshotNodeQuery, { snapshotId }),
),

View File

@@ -3,12 +3,12 @@ import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { loadQuery } from "react-relay";
import { relayEnvironment } from "/providers/RelayProviders";
import { tasksQuery } from "/hooks/graph/TaskGraph";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { TaskGraphQuery } from "/hooks/graph/__generated__/TaskGraphQuery.graphql";
export const taskRoutes = [
{
path: "tasks",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<TaskGraphQuery>(relayEnvironment, tasksQuery, {
organizationId,

View File

@@ -5,12 +5,12 @@ import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
import { lazy } from "@probo/react-lazy";
import { trustCenterQuery } from "../hooks/graph/TrustCenterGraph";
import type { TrustCenterGraphQuery } from "/hooks/graph/__generated__/TrustCenterGraphQuery.graphql";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
export const trustCenterRoutes = [
{
path: "trust-center",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<TrustCenterGraphQuery>(relayEnvironment, trustCenterQuery, { organizationId }, { fetchPolicy: "network-only" }),
),
@@ -20,49 +20,49 @@ export const trustCenterRoutes = [
children: [
{
index: true,
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/trustCenter/TrustCenterOverviewTab")
),
},
{
path: "trust-by",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/trustCenter/TrustCenterReferencesTab")
),
},
{
path: "audits",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/trustCenter/TrustCenterAuditsTab")
),
},
{
path: "documents",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/trustCenter/TrustCenterDocumentsTab")
),
},
{
path: "files",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/trustCenter/TrustCenterFilesTab")
),
},
{
path: "vendors",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/trustCenter/TrustCenterVendorsTab")
),
},
{
path: "access",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("/pages/organizations/trustCenter/TrustCenterAccessTab")
),

View File

@@ -4,14 +4,14 @@ import { relayEnvironment } from "/providers/RelayProviders";
import { PageSkeleton } from "/components/skeletons/PageSkeleton";
import { vendorNodeQuery, vendorsQuery } from "/hooks/graph/VendorGraph";
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "/routes";
import { loaderFromQueryLoader, withQueryRef, type AppRoute } from "@probo/routes";
import type { VendorGraphListQuery } from "/hooks/graph/__generated__/VendorGraphListQuery.graphql";
import type { VendorGraphNodeQuery } from "/hooks/graph/__generated__/VendorGraphNodeQuery.graphql";
export const vendorRoutes = [
{
path: "vendors",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId }) =>
loadQuery<VendorGraphListQuery>(relayEnvironment, vendorsQuery, {
organizationId: organizationId!,
@@ -22,7 +22,7 @@ export const vendorRoutes = [
},
{
path: "snapshots/:snapshotId/vendors",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ organizationId, snapshotId }) =>
loadQuery<VendorGraphListQuery>(relayEnvironment, vendorsQuery, {
organizationId: organizationId!,
@@ -33,7 +33,7 @@ export const vendorRoutes = [
},
{
path: "vendors/:vendorId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ vendorId }) =>
loadQuery<VendorGraphNodeQuery>(relayEnvironment, vendorNodeQuery, {
vendorId: vendorId!,
@@ -45,14 +45,14 @@ export const vendorRoutes = [
children: [
{
path: "overview",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("../pages/organizations/vendors/tabs/VendorOverviewTab")
),
},
{
path: "certifications",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import(
@@ -62,7 +62,7 @@ export const vendorRoutes = [
},
{
path: "compliance",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("../pages/organizations/vendors/tabs/VendorComplianceTab")
@@ -70,7 +70,7 @@ export const vendorRoutes = [
},
{
path: "risks",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import(
@@ -80,7 +80,7 @@ export const vendorRoutes = [
},
{
path: "contacts",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("../pages/organizations/vendors/tabs/VendorContactsTab")
@@ -88,7 +88,7 @@ export const vendorRoutes = [
},
{
path: "services",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("../pages/organizations/vendors/tabs/VendorServicesTab")
@@ -98,7 +98,7 @@ export const vendorRoutes = [
},
{
path: "snapshots/:snapshotId/vendors/:vendorId",
fallback: PageSkeleton,
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ vendorId }) =>
loadQuery<VendorGraphNodeQuery>(relayEnvironment, vendorNodeQuery, {
vendorId: vendorId!,
@@ -110,14 +110,14 @@ export const vendorRoutes = [
children: [
{
path: "overview",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() => import("../pages/organizations/vendors/tabs/VendorOverviewTab")
),
},
{
path: "certifications",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import(
@@ -127,7 +127,7 @@ export const vendorRoutes = [
},
{
path: "compliance",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("../pages/organizations/vendors/tabs/VendorComplianceTab")
@@ -135,7 +135,7 @@ export const vendorRoutes = [
},
{
path: "risks",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import(
@@ -145,7 +145,7 @@ export const vendorRoutes = [
},
{
path: "contacts",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("../pages/organizations/vendors/tabs/VendorContactsTab")
@@ -153,7 +153,7 @@ export const vendorRoutes = [
},
{
path: "services",
fallback: LinkCardSkeleton,
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("../pages/organizations/vendors/tabs/VendorServicesTab")

34
package-lock.json generated
View File

@@ -97,7 +97,6 @@
"babel-plugin-relay": "^19.0.0",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"graphql": "^16.11.0",
"prettier": "^3.5.3",
@@ -1997,6 +1996,10 @@
"resolved": "packages/react-lazy",
"link": true
},
"node_modules/@probo/routes": {
"resolved": "packages/routes",
"link": true
},
"node_modules/@probo/trust": {
"resolved": "apps/trust",
"link": true
@@ -12399,9 +12402,9 @@
"license": "MIT"
},
"node_modules/react": {
"version": "19.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"version": "19.2.1",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
"integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -12821,9 +12824,9 @@
}
},
"node_modules/react-router": {
"version": "7.9.6",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz",
"integrity": "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==",
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.0.tgz",
"integrity": "sha512-FVyCOH4IZ0eDDRycODfUqoN8ZSR2LbTvtx6RPsBgzvJ8xAXlMZNCrOFpu+jb8QbtZnpAd/cEki2pwE848pNGxw==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@@ -16124,6 +16127,23 @@
"react": ">18.0.0"
}
},
"packages/routes": {
"name": "@probo/routes",
"version": "1.0.0",
"license": "ISC",
"devDependencies": {
"@probo/prettier": "1.0.0",
"prettier": "^3.5.3",
"typescript": "^5.8.3",
"vitest": "^3.1.3"
},
"peerDependencies": {
"react": "^19.2.1",
"react-relay": "^19.0.0",
"react-router": "^7.10.0",
"relay-runtime": "^19.0.0"
}
},
"packages/tsconfig": {
"name": "@probo/tsconfig",
"version": "0.0.1",

View File

@@ -0,0 +1,40 @@
import { ComponentType, LazyExoticComponent } from "react";
import { EnvironmentProviderOptions, PreloadedQuery } from "react-relay";
import { LoaderFunction, LoaderFunctionArgs, RouteObject, useLoaderData } from "react-router";
import { OperationType } from "relay-runtime";
import { useCleanup } from "./useDelayedEffect";
export function withQueryRef<
TQuery extends OperationType,
TEnvironmentProviderOptions = EnvironmentProviderOptions
>(
Component: LazyExoticComponent<ComponentType<{ queryRef: PreloadedQuery<TQuery, TEnvironmentProviderOptions> }>>,
) {
return () => {
const { queryRef, dispose } = useLoaderData();
useCleanup(dispose, 1000);
return <Component queryRef={queryRef} />
}
}
export function loaderFromQueryLoader<
TQuery extends OperationType,
TEnvironmentProviderOptions = EnvironmentProviderOptions
>(
queryLoader: (params: Record<string, string>) => PreloadedQuery<TQuery, TEnvironmentProviderOptions>
): LoaderFunction {
return ({ params }: LoaderFunctionArgs) => {
const query = queryLoader(params as Record<string, string>);
return {
queryRef: query,
dispose: query.dispose,
};
}
}
export type AppRoute = Omit<RouteObject, "children"> & {
children?: AppRoute[];
fallback?: ComponentType;
}

View File

@@ -0,0 +1,19 @@
import { useEffect, useRef } from "react";
/**
* Hook to handle cleanup after a delay
*
* Used for disposing the graphQL query when the component unmounts
*/
export function useCleanup(callback: () => void, delay: number) {
const timer = useRef<ReturnType<typeof setTimeout>>(null);
useEffect(() => {
if (timer.current) {
clearTimeout(timer.current);
}
return () => {
timer.current = setTimeout(callback, delay);
};
}, [callback, delay]);
}

View File

@@ -0,0 +1,27 @@
{
"name": "@probo/routes",
"version": "1.0.0",
"description": "",
"main": "./src/index.ts",
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"format": "prettier --write ./src"
},
"prettier": "@probo/prettier",
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@probo/prettier": "1.0.0",
"prettier": "^3.5.3",
"typescript": "^5.8.3",
"vitest": "^3.1.3"
},
"peerDependencies": {
"react": "^19.2.1",
"react-relay": "^19.0.0",
"react-router": "^7.10.0",
"relay-runtime": "^19.0.0"
}
}

View File

@@ -0,0 +1,30 @@
import { ComponentType, Suspense } from "react";
import { RouteObject } from "react-router";
export type AppRoute = Omit<RouteObject, "children"> & {
children?: AppRoute[];
Fallback?: ComponentType;
}
export function routeFromAppRoute(appRoute: AppRoute): RouteObject {
const { Component, Fallback, children, ...rest } = appRoute;
let route = { ...rest } as RouteObject;
if (Component && Fallback) {
const OriginalComponent = Component as ComponentType;
route = {
...route,
Component: () => (
<Suspense fallback={<Fallback />}>
<OriginalComponent />
</Suspense>
),
};
}
return {
...route,
children: children?.map(routeFromAppRoute),
} as RouteObject;
}

View File

@@ -0,0 +1,2 @@
export { routeFromAppRoute, type AppRoute } from "./appRoute";
export { withQueryRef, loaderFromQueryLoader } from "./relay";

View File

@@ -0,0 +1,35 @@
import { ComponentType } from "react";
import { EnvironmentProviderOptions, PreloadedQuery } from "react-relay";
import { LoaderFunction, LoaderFunctionArgs, useLoaderData } from "react-router";
import { OperationType } from "relay-runtime";
import { useCleanup } from "@probo/hooks";
export function withQueryRef<
TQuery extends OperationType,
TEnvironmentProviderOptions = EnvironmentProviderOptions
>(
Component: ComponentType<{ queryRef: PreloadedQuery<TQuery, TEnvironmentProviderOptions> }>,
) {
return () => {
const { queryRef, dispose } = useLoaderData();
useCleanup(dispose, 1000);
return <Component queryRef={queryRef} />
}
}
export function loaderFromQueryLoader<
TQuery extends OperationType,
TEnvironmentProviderOptions = EnvironmentProviderOptions
>(
queryLoader: (params: Record<string, string>) => PreloadedQuery<TQuery, TEnvironmentProviderOptions>
): LoaderFunction {
return ({ params }: LoaderFunctionArgs) => {
const query = queryLoader(params as Record<string, string>);
return {
queryRef: query,
dispose: query.dispose,
};
}
}

View File

@@ -0,0 +1,6 @@
{
"compilerOptions": {
"lib": ["ES2021", "dom"],
"jsx": "react-jsx",
}
}