Rewrite context routes to use useQueryLoader pattern
Replace loaderFromQueryLoader/loadQuery in route definitions with useQueryLoader inside dedicated Loader components, matching the compliance-page pattern. Use index routes instead of redirect hacks. Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import { Outlet } from "react-router";
|
||||
|
||||
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
||||
|
||||
export default function ContextLayout() {
|
||||
export default function ContextLayoutLoader() {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const prefix = `/organizations/${organizationId}/context`;
|
||||
@@ -21,7 +21,7 @@ export default function ContextLayout() {
|
||||
)}
|
||||
/>
|
||||
<Tabs>
|
||||
<TabLink to={`${prefix}/overview`}>{__("Context")}</TabLink>
|
||||
<TabLink to={prefix} end>{__("Context")}</TabLink>
|
||||
<TabLink to={`${prefix}/meetings`}>{__("Meetings")}</TabLink>
|
||||
</Tabs>
|
||||
<Outlet />
|
||||
@@ -1,11 +1,15 @@
|
||||
import { type PreloadedQuery, usePreloadedQuery } from "react-relay";
|
||||
import { useEffect } from "react";
|
||||
import { type PreloadedQuery, usePreloadedQuery, useQueryLoader } from "react-relay";
|
||||
import { graphql } from "relay-runtime";
|
||||
|
||||
import type { ContextPageLoaderQuery } from "#/__generated__/core/ContextPageLoaderQuery.graphql";
|
||||
import { LinkCardSkeleton } from "#/components/skeletons/LinkCardSkeleton";
|
||||
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
||||
import { CoreRelayProvider } from "#/providers/CoreRelayProvider";
|
||||
|
||||
import ContextPage from "./ContextPage";
|
||||
|
||||
export const contextPageQuery = graphql`
|
||||
const contextPageQuery = graphql`
|
||||
query ContextPageLoaderQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
... on Organization {
|
||||
@@ -15,12 +19,31 @@ export const contextPageQuery = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<ContextPageLoaderQuery>;
|
||||
};
|
||||
function ContextPageQueryLoader() {
|
||||
const organizationId = useOrganizationId();
|
||||
const [queryRef, loadQuery] = useQueryLoader<ContextPageLoaderQuery>(contextPageQuery);
|
||||
|
||||
export default function ContextPageLoader(props: Props) {
|
||||
const data = usePreloadedQuery(contextPageQuery, props.queryRef);
|
||||
useEffect(() => {
|
||||
if (!queryRef) {
|
||||
loadQuery({ organizationId });
|
||||
}
|
||||
});
|
||||
|
||||
if (!queryRef) return <LinkCardSkeleton />;
|
||||
|
||||
return <ContextPageInner queryRef={queryRef} />;
|
||||
}
|
||||
|
||||
function ContextPageInner({ queryRef }: { queryRef: PreloadedQuery<ContextPageLoaderQuery> }) {
|
||||
const data = usePreloadedQuery(contextPageQuery, queryRef);
|
||||
|
||||
return <ContextPage organization={data.organization} />;
|
||||
}
|
||||
|
||||
export default function ContextPageLoader() {
|
||||
return (
|
||||
<CoreRelayProvider>
|
||||
<ContextPageQueryLoader />
|
||||
</CoreRelayProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useEffect } from "react";
|
||||
import { useQueryLoader } from "react-relay";
|
||||
import { useParams } from "react-router";
|
||||
|
||||
import type { MeetingDetailPageQuery } from "#/__generated__/core/MeetingDetailPageQuery.graphql";
|
||||
import { PageSkeleton } from "#/components/skeletons/PageSkeleton";
|
||||
import { CoreRelayProvider } from "#/providers/CoreRelayProvider";
|
||||
|
||||
import MeetingDetailPage, { meetingDetailPageQuery } from "./MeetingDetailPage";
|
||||
|
||||
function MeetingDetailPageQueryLoader() {
|
||||
const { meetingId } = useParams<{ meetingId: string }>();
|
||||
const [queryRef, loadQuery] = useQueryLoader<MeetingDetailPageQuery>(meetingDetailPageQuery);
|
||||
|
||||
useEffect(() => {
|
||||
if (!queryRef && meetingId) {
|
||||
loadQuery({ meetingId });
|
||||
}
|
||||
});
|
||||
|
||||
if (!queryRef) return <PageSkeleton />;
|
||||
|
||||
return <MeetingDetailPage queryRef={queryRef} />;
|
||||
}
|
||||
|
||||
export default function MeetingDetailPageLoader() {
|
||||
return (
|
||||
<CoreRelayProvider>
|
||||
<MeetingDetailPageQueryLoader />
|
||||
</CoreRelayProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useEffect } from "react";
|
||||
import { useQueryLoader } from "react-relay";
|
||||
|
||||
import type { MeetingsPageQuery } from "#/__generated__/core/MeetingsPageQuery.graphql";
|
||||
import { LinkCardSkeleton } from "#/components/skeletons/LinkCardSkeleton";
|
||||
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
||||
import { CoreRelayProvider } from "#/providers/CoreRelayProvider";
|
||||
|
||||
import MeetingsPage, { meetingsPageQuery } from "./MeetingsPage";
|
||||
|
||||
function MeetingsPageQueryLoader() {
|
||||
const organizationId = useOrganizationId();
|
||||
const [queryRef, loadQuery] = useQueryLoader<MeetingsPageQuery>(meetingsPageQuery);
|
||||
|
||||
useEffect(() => {
|
||||
if (!queryRef) {
|
||||
loadQuery({ organizationId });
|
||||
}
|
||||
});
|
||||
|
||||
if (!queryRef) return <LinkCardSkeleton />;
|
||||
|
||||
return <MeetingsPage queryRef={queryRef} />;
|
||||
}
|
||||
|
||||
export default function MeetingsPageLoader() {
|
||||
return (
|
||||
<CoreRelayProvider>
|
||||
<MeetingsPageQueryLoader />
|
||||
</CoreRelayProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,113 +1,46 @@
|
||||
import { lazy } from "@probo/react-lazy";
|
||||
import {
|
||||
type AppRoute,
|
||||
loaderFromQueryLoader,
|
||||
withQueryRef,
|
||||
} from "@probo/routes";
|
||||
import { Fragment } from "react";
|
||||
import { loadQuery } from "react-relay";
|
||||
import { type LoaderFunctionArgs, redirect } from "react-router";
|
||||
import type { AppRoute } from "@probo/routes";
|
||||
|
||||
import type { ContextPageLoaderQuery } from "#/__generated__/core/ContextPageLoaderQuery.graphql";
|
||||
import type { MeetingDetailPageQuery } from "#/__generated__/core/MeetingDetailPageQuery.graphql";
|
||||
import type { MeetingsPageQuery } from "#/__generated__/core/MeetingsPageQuery.graphql";
|
||||
import { LinkCardSkeleton } from "#/components/skeletons/LinkCardSkeleton";
|
||||
import { PageSkeleton } from "#/components/skeletons/PageSkeleton";
|
||||
import { coreEnvironment } from "#/environments";
|
||||
import { contextPageQuery } from "#/pages/organizations/context/ContextPageLoader";
|
||||
import { meetingDetailPageQuery } from "#/pages/organizations/meetings/MeetingDetailPage";
|
||||
import { meetingsPageQuery } from "#/pages/organizations/meetings/MeetingsPage";
|
||||
|
||||
const meetingTabs = (prefix: string) => {
|
||||
return [
|
||||
{
|
||||
path: `${prefix}`,
|
||||
loader: ({
|
||||
params: { organizationId, meetingId },
|
||||
}: LoaderFunctionArgs) => {
|
||||
const basePath = `/organizations/${organizationId}/context/meetings/${meetingId}`;
|
||||
const redirectPath = `${basePath}/minutes`;
|
||||
// eslint-disable-next-line
|
||||
throw redirect(redirectPath);
|
||||
},
|
||||
Component: Fragment,
|
||||
},
|
||||
{
|
||||
path: `${prefix}minutes`,
|
||||
Fallback: LinkCardSkeleton,
|
||||
Component: lazy(
|
||||
() => import("../pages/organizations/meetings/tabs/MeetingMinutesTab"),
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const contextRoutes = [
|
||||
{
|
||||
path: "context",
|
||||
Fallback: PageSkeleton,
|
||||
Component: lazy(
|
||||
() => import("#/pages/organizations/context/ContextLayout"),
|
||||
() => import("#/pages/organizations/context/ContextLayoutLoader"),
|
||||
),
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
loader: ({
|
||||
params: { organizationId },
|
||||
}: LoaderFunctionArgs) => {
|
||||
// eslint-disable-next-line
|
||||
throw redirect(`/organizations/${organizationId}/context/overview`);
|
||||
},
|
||||
Component: Fragment,
|
||||
},
|
||||
{
|
||||
path: "overview",
|
||||
Fallback: PageSkeleton,
|
||||
loader: loaderFromQueryLoader(({ organizationId }) =>
|
||||
loadQuery<ContextPageLoaderQuery>(
|
||||
coreEnvironment,
|
||||
contextPageQuery,
|
||||
{ organizationId },
|
||||
),
|
||||
),
|
||||
Component: withQueryRef(
|
||||
lazy(
|
||||
() => import("#/pages/organizations/context/ContextPageLoader"),
|
||||
),
|
||||
index: true,
|
||||
Fallback: LinkCardSkeleton,
|
||||
Component: lazy(
|
||||
() => import("#/pages/organizations/context/ContextPageLoader"),
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "meetings",
|
||||
Fallback: PageSkeleton,
|
||||
loader: loaderFromQueryLoader(({ organizationId }) =>
|
||||
loadQuery<MeetingsPageQuery>(
|
||||
coreEnvironment,
|
||||
meetingsPageQuery,
|
||||
{ organizationId },
|
||||
),
|
||||
),
|
||||
Component: withQueryRef(
|
||||
lazy(
|
||||
() => import("#/pages/organizations/meetings/MeetingsPage"),
|
||||
),
|
||||
Fallback: LinkCardSkeleton,
|
||||
Component: lazy(
|
||||
() => import("#/pages/organizations/meetings/MeetingsPageLoader"),
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "meetings/:meetingId",
|
||||
Fallback: PageSkeleton,
|
||||
loader: loaderFromQueryLoader(({ meetingId }) =>
|
||||
loadQuery<MeetingDetailPageQuery>(
|
||||
coreEnvironment,
|
||||
meetingDetailPageQuery,
|
||||
{ meetingId },
|
||||
),
|
||||
Component: lazy(
|
||||
() => import("#/pages/organizations/meetings/MeetingDetailPageLoader"),
|
||||
),
|
||||
Component: withQueryRef(
|
||||
lazy(
|
||||
() => import("../pages/organizations/meetings/MeetingDetailPage"),
|
||||
),
|
||||
),
|
||||
children: [...meetingTabs("")],
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
Fallback: LinkCardSkeleton,
|
||||
Component: lazy(
|
||||
() => import("#/pages/organizations/meetings/tabs/MeetingMinutesTab"),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user