Skip to content

Commit

Permalink
feat: use route masks for selectively hiding state that is being stor…
Browse files Browse the repository at this point in the history
…ed in the URL (#414)

* fix: loading not taking up the full page

* chore: remove the `defaultPendingComponent`

* refactor: cleanup the search params for the dashboard

* feat: route mask to hide the dashboard's widget pickers state

* refactor: use `<Link>` elements instead of buttons for the summary tabs

* feat: route mask to hide the agreement summary's tab state

* feat: route mask to hide the report page's selected category

* chore: better comments

* cleanup

* refactor: simplify the definitions for `parseSearchWith` and `stringifySearchWith`

* refactor: do not use the function caller

* refactor: remove `params: true`
  • Loading branch information
SeanCassiere authored Aug 9, 2024
1 parent 9cf03f8 commit 5088008
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 45 deletions.
50 changes: 35 additions & 15 deletions src/lib/config/tanstack-router.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from "react";
import { QueryClientProvider } from "@tanstack/react-query";
import {
createRouteMask,
createRouter as createTanStackRouter,
parseSearchWith,
stringifySearchWith,
} from "@tanstack/react-router";
import * as JSURL2 from "jsurl2";

import { CacheDocumentFocusChecker } from "@/components/cache-buster";
import { icons } from "@/components/ui/icons";
import { TooltipProvider } from "@/components/ui/tooltip";

import { GlobalDialogProvider } from "@/lib/context/modals";
Expand All @@ -18,27 +18,47 @@ import { queryClient } from "@/lib/config/tanstack-query";
import { routeTree } from "@/route-tree.gen";

export function createRouter() {
const routeMasks = [
// hide the widget picker modal's state
createRouteMask({
routeTree,
from: "/",
to: "/",
search: { show_widget_picker: undefined },
}),

// hide the selected summary tab's state of the agreement summary page
createRouteMask({
routeTree,
from: "/agreements/$agreementId/summary",
to: "/agreements/$agreementId/summary",
params: true,
search: { summary_tab: undefined },
}),

// hide the selected category state of the reports page
createRouteMask({
routeTree,
from: "/reports",
to: "/reports",
search: { category: undefined },
}),
];

const router = createTanStackRouter({
routeTree,
routeMasks,
defaultPreload: "intent",
defaultPreloadStaleTime: 0,
defaultViewTransition: true,
defaultPendingComponent: function RouterPendingComponent() {
<div className="grid min-h-full w-full place-items-center">
<icons.Loading className="h-24 w-24 animate-spin text-foreground" />
</div>;
},
parseSearch: parseSearchWith((value) => JSURL2.parse(value)),
stringifySearch: stringifySearchWith(
(value) => JSURL2.stringify(value),
(value) => JSURL2.parse(value)
),
trailingSlash: "never",
context: {
queryClient,
auth: undefined!, // will be set by an AuthWrapper
auth: undefined!, // will be set when passing it into the RouterProvider
},
trailingSlash: "never",
Wrap: function ({ children }) {
parseSearch: parseSearchWith(JSURL2.parse),
stringifySearch: stringifySearchWith(JSURL2.stringify, JSURL2.parse),
Wrap: function WrapComponent({ children }) {
return (
<QueryClientProvider client={queryClient}>
<GlobalDialogProvider>
Expand All @@ -47,7 +67,7 @@ export function createRouter() {
</QueryClientProvider>
);
},
InnerWrap: function ({ children }) {
InnerWrap: function InnerWrapComponent({ children }) {
return (
<React.Fragment>
<CacheDocumentFocusChecker />
Expand Down
1 change: 0 additions & 1 deletion src/lib/schemas/dashboard/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export * from "./dashboardWidgetItem";
export * from "./dashboardStats";
export * from "./searchFilters";
export * from "./vehicleStatusCounts";
export * from "./message";
export * from "./sales-status";
6 changes: 0 additions & 6 deletions src/lib/schemas/dashboard/searchFilters.ts

This file was deleted.

17 changes: 15 additions & 2 deletions src/routes/-components/full-screen-loading-spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import type { ComponentPropsWithoutRef } from "react";

import { icons } from "@/components/ui/icons";

export function FullPageLoadingSpinner() {
import { cn } from "@/lib/utils";

export function FullPageLoadingSpinner({
className,
...props
}: ComponentPropsWithoutRef<"div">) {
return (
<div className="grid min-h-full w-full place-items-center">
<div
className={cn(
"grid min-h-full w-full flex-1 place-items-center",
className
)}
{...props}
>
<icons.Loading className="h-24 w-24 animate-spin text-foreground" />
</div>
);
Expand Down
5 changes: 2 additions & 3 deletions src/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import type { QueryClient } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import {
createRootRouteWithContext,
Expand Down Expand Up @@ -28,16 +29,14 @@ import { useDocumentTitle } from "@/lib/hooks/useDocumentTitle";

import { titleMaker } from "@/lib/utils/title-maker";

import type { queryClient } from "@/lib/config/tanstack-query";

import { cn } from "@/lib/utils";

import { Container } from "./-components/container";
import { FeatureTogglesDialog } from "./-components/feature-toggles-dialog";
import { PageNotFound } from "./-components/page-not-found";

export interface MyRouterContext {
queryClient: typeof queryClient;
queryClient: QueryClient;
auth: AuthContextProps;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from "react";
import { useSuspenseQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import { createFileRoute, Link } from "@tanstack/react-router";
import { z } from "zod";

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
Expand Down Expand Up @@ -41,8 +41,6 @@ const SummarySignatureCard = React.lazy(
);

function Component() {
const navigate = Route.useNavigate();

const currentTab = Route.useSearch({
select: (s) => {
if (s.summary_tab && ["vehicle", "rental"].includes(s.summary_tab)) {
Expand Down Expand Up @@ -158,14 +156,6 @@ function Component() {
isCheckedIn,
]);

const setCurrentTab = (name: string) => {
navigate({
search: (s) => ({ ...s, summary_tab: name }),
resetScroll: false,
replace: true,
});
};

return (
<Container as="div">
<div className="mb-6 grid max-w-full grid-cols-1 gap-4 px-2 sm:px-4 lg:grid-cols-12">
Expand Down Expand Up @@ -197,14 +187,20 @@ function Component() {
)}

{tabsConfig.length >= 1 ? (
<Tabs value={currentTab} onValueChange={setCurrentTab}>
<Tabs defaultValue={currentTab} key={`tab-${currentTab}`}>
<TabsList className="w-full sm:max-w-max">
{tabsConfig.map((tab, idx) => (
<TabsTrigger
key={`tab-summary-trigger-${idx}`}
value={tab.id}
asChild
>
{tab.label}
<Link
search={(s) => ({ ...s, summary_tab: tab.id })}
resetScroll={false}
>
{tab.label}
</Link>
</TabsTrigger>
))}
</TabsList>
Expand Down
10 changes: 5 additions & 5 deletions src/routes/_auth/(dashboard)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { createFileRoute, Link } from "@tanstack/react-router";
import { useAuth } from "react-oidc-context";
import { z } from "zod";

import { EmptyState } from "@/components/empty-state";
import { Badge } from "@/components/ui/badge";
Expand All @@ -28,10 +29,7 @@ import { useDocumentTitle } from "@/lib/hooks/useDocumentTitle";
import { usePermission } from "@/lib/hooks/usePermission";
import { useScreenSetting } from "@/lib/hooks/useScreenSetting";

import {
DashboardSearchQuerySchema,
type DashboardWidgetItemParsed,
} from "@/lib/schemas/dashboard";
import type { DashboardWidgetItemParsed } from "@/lib/schemas/dashboard";
import {
fetchDashboardMessagesOptions,
fetchDashboardRentalStatisticsOptions,
Expand Down Expand Up @@ -59,7 +57,9 @@ import WidgetGrid from "./-components/widget-grid";
import WidgetPicker from "./-components/widget-picker";

export const Route = createFileRoute("/_auth/(dashboard)/")({
validateSearch: (search) => DashboardSearchQuerySchema.parse(search),
validateSearch: z.object({
show_widget_picker: z.boolean().default(false).optional(),
}),
beforeLoad: ({ context }) => {
const auth = getAuthFromRouterContext(context);
return {
Expand Down

0 comments on commit 5088008

Please sign in to comment.