refactor: render repo stats directly in ssr if cache is available
This commit is contained in:
parent
66076e5841
commit
73c0511167
5 changed files with 21 additions and 8 deletions
|
|
@ -3,9 +3,11 @@ import { Archive } from "lucide-react";
|
||||||
import { getRepositoryStatsOptions } from "~/client/api-client/@tanstack/react-query.gen";
|
import { getRepositoryStatsOptions } from "~/client/api-client/@tanstack/react-query.gen";
|
||||||
import { ByteSize } from "~/client/components/bytes-size";
|
import { ByteSize } from "~/client/components/bytes-size";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card";
|
||||||
|
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repositoryShortId: string;
|
repositoryShortId: string;
|
||||||
|
initialStats?: GetRepositoryStatsResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const toSafeNumber = (value: number | undefined) => {
|
const toSafeNumber = (value: number | undefined) => {
|
||||||
|
|
@ -16,14 +18,15 @@ const toSafeNumber = (value: number | undefined) => {
|
||||||
return Math.max(0, value);
|
return Math.max(0, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CompressionStatsChart({ repositoryShortId }: Props) {
|
export function CompressionStatsChart({ repositoryShortId, initialStats }: Props) {
|
||||||
const {
|
const {
|
||||||
data: stats,
|
data: stats,
|
||||||
isLoading,
|
isPending,
|
||||||
error,
|
error,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
...getRepositoryStatsOptions({ path: { shortId: repositoryShortId } }),
|
...getRepositoryStatsOptions({ path: { shortId: repositoryShortId } }),
|
||||||
retry: false,
|
retry: false,
|
||||||
|
initialData: initialStats,
|
||||||
});
|
});
|
||||||
|
|
||||||
const storedSize = toSafeNumber(stats?.total_size);
|
const storedSize = toSafeNumber(stats?.total_size);
|
||||||
|
|
@ -40,7 +43,7 @@ export function CompressionStatsChart({ repositoryShortId }: Props) {
|
||||||
|
|
||||||
const hasStats = !!stats && (storedSize > 0 || uncompressedSize > 0 || snapshotsCount > 0);
|
const hasStats = !!stats && (storedSize > 0 || uncompressedSize > 0 || snapshotsCount > 0);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isPending) {
|
||||||
return (
|
return (
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<p className="text-sm text-muted-foreground">Loading compression statistics...</p>
|
<p className="text-sm text-muted-foreground">Loading compression statistics...</p>
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,18 @@ import { RepositoryInfoTabContent } from "../tabs/info";
|
||||||
import { RepositorySnapshotsTabContent } from "../tabs/snapshots";
|
import { RepositorySnapshotsTabContent } from "../tabs/snapshots";
|
||||||
import { useNavigate, useSearch } from "@tanstack/react-router";
|
import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||||
import type { BackupSchedule, Snapshot } from "~/client/lib/types";
|
import type { BackupSchedule, Snapshot } from "~/client/lib/types";
|
||||||
|
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
||||||
|
|
||||||
export default function RepositoryDetailsPage({
|
export default function RepositoryDetailsPage({
|
||||||
repositoryId,
|
repositoryId,
|
||||||
initialSnapshots,
|
initialSnapshots,
|
||||||
initialBackupSchedules,
|
initialBackupSchedules,
|
||||||
|
initialStats,
|
||||||
}: {
|
}: {
|
||||||
repositoryId: string;
|
repositoryId: string;
|
||||||
initialSnapshots?: Snapshot[];
|
initialSnapshots?: Snapshot[];
|
||||||
initialBackupSchedules?: BackupSchedule[];
|
initialBackupSchedules?: BackupSchedule[];
|
||||||
|
initialStats?: GetRepositoryStatsResponse;
|
||||||
}) {
|
}) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { tab } = useSearch({ from: "/(dashboard)/repositories/$repositoryId/" });
|
const { tab } = useSearch({ from: "/(dashboard)/repositories/$repositoryId/" });
|
||||||
|
|
@ -32,7 +35,7 @@ export default function RepositoryDetailsPage({
|
||||||
<TabsTrigger value="snapshots">Snapshots</TabsTrigger>
|
<TabsTrigger value="snapshots">Snapshots</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="info">
|
<TabsContent value="info">
|
||||||
<RepositoryInfoTabContent repository={data} />
|
<RepositoryInfoTabContent repository={data} initialStats={initialStats} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="snapshots">
|
<TabsContent value="snapshots">
|
||||||
<Suspense>
|
<Suspense>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "~/client/components/ui/alert-dialog";
|
} from "~/client/components/ui/alert-dialog";
|
||||||
import type { Repository } from "~/client/lib/types";
|
import type { Repository } from "~/client/lib/types";
|
||||||
|
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
||||||
import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime";
|
import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime";
|
||||||
import {
|
import {
|
||||||
cancelDoctorMutation,
|
cancelDoctorMutation,
|
||||||
|
|
@ -30,6 +31,7 @@ import { cn } from "~/client/lib/utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
|
initialStats?: GetRepositoryStatsResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getEffectiveLocalPath = (repository: Repository): string | null => {
|
const getEffectiveLocalPath = (repository: Repository): string | null => {
|
||||||
|
|
@ -37,7 +39,7 @@ const getEffectiveLocalPath = (repository: Repository): string | null => {
|
||||||
return repository.config.path;
|
return repository.config.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RepositoryInfoTabContent = ({ repository }: Props) => {
|
export const RepositoryInfoTabContent = ({ repository, initialStats }: Props) => {
|
||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
@ -232,7 +234,7 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
|
||||||
|
|
||||||
<DoctorReport repositoryStatus={repository.status} result={repository.doctorResult} />
|
<DoctorReport repositoryStatus={repository.status} result={repository.doctorResult} />
|
||||||
</Card>
|
</Card>
|
||||||
<CompressionStatsChart repositoryShortId={repository.shortId} />
|
<CompressionStatsChart repositoryShortId={repository.shortId} initialStats={initialStats} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AlertDialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
<AlertDialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export function getRouter() {
|
||||||
routeTree,
|
routeTree,
|
||||||
context: { queryClient },
|
context: { queryClient },
|
||||||
defaultPreload: "intent",
|
defaultPreload: "intent",
|
||||||
scrollRestoration: true,
|
scrollRestoration: false,
|
||||||
});
|
});
|
||||||
setupRouterSsrQueryIntegration({
|
setupRouterSsrQueryIntegration({
|
||||||
router,
|
router,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
import {
|
import {
|
||||||
getRepositoryOptions,
|
getRepositoryOptions,
|
||||||
|
getRepositoryStatsOptions,
|
||||||
listBackupSchedulesOptions,
|
listBackupSchedulesOptions,
|
||||||
listSnapshotsOptions,
|
listSnapshotsOptions,
|
||||||
} from "~/client/api-client/@tanstack/react-query.gen";
|
} from "~/client/api-client/@tanstack/react-query.gen";
|
||||||
|
|
@ -14,17 +15,20 @@ export const Route = createFileRoute("/(dashboard)/repositories/$repositoryId/")
|
||||||
loader: async ({ params, context }) => {
|
loader: async ({ params, context }) => {
|
||||||
const snapshotOptions = listSnapshotsOptions({ path: { shortId: params.repositoryId } });
|
const snapshotOptions = listSnapshotsOptions({ path: { shortId: params.repositoryId } });
|
||||||
const schedulesOptions = listBackupSchedulesOptions();
|
const schedulesOptions = listBackupSchedulesOptions();
|
||||||
|
const statsOptions = getRepositoryStatsOptions({ path: { shortId: params.repositoryId } });
|
||||||
|
|
||||||
const [res] = await Promise.all([
|
const [res] = await Promise.all([
|
||||||
context.queryClient.ensureQueryData(getRepositoryOptions({ path: { shortId: params.repositoryId } })),
|
context.queryClient.ensureQueryData(getRepositoryOptions({ path: { shortId: params.repositoryId } })),
|
||||||
prefetchOrSkip(context.queryClient, snapshotOptions),
|
prefetchOrSkip(context.queryClient, snapshotOptions),
|
||||||
prefetchOrSkip(context.queryClient, schedulesOptions),
|
prefetchOrSkip(context.queryClient, schedulesOptions),
|
||||||
|
prefetchOrSkip(context.queryClient, statsOptions),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...res,
|
...res,
|
||||||
snapshots: context.queryClient.getQueryData(snapshotOptions.queryKey),
|
snapshots: context.queryClient.getQueryData(snapshotOptions.queryKey),
|
||||||
backupSchedules: context.queryClient.getQueryData(schedulesOptions.queryKey),
|
backupSchedules: context.queryClient.getQueryData(schedulesOptions.queryKey),
|
||||||
|
stats: context.queryClient.getQueryData(statsOptions.queryKey),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
validateSearch: type({ tab: "string?" }),
|
validateSearch: type({ tab: "string?" }),
|
||||||
|
|
@ -47,13 +51,14 @@ export const Route = createFileRoute("/(dashboard)/repositories/$repositoryId/")
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const { repositoryId } = Route.useParams();
|
const { repositoryId } = Route.useParams();
|
||||||
const { snapshots, backupSchedules } = Route.useLoaderData();
|
const { snapshots, backupSchedules, stats } = Route.useLoaderData();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RepositoryDetailsPage
|
<RepositoryDetailsPage
|
||||||
repositoryId={repositoryId}
|
repositoryId={repositoryId}
|
||||||
initialSnapshots={snapshots}
|
initialSnapshots={snapshots}
|
||||||
initialBackupSchedules={backupSchedules}
|
initialBackupSchedules={backupSchedules}
|
||||||
|
initialStats={stats}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue