trails/apps/planner/app/routes/session.$id.tsx
Ullrich Schäfer 8fb7771223
Add withDb() helper for graceful DB error handling
Shared withDb() wrapper in @trails-cool/db catches database
connection errors and throws a 503 Response instead of crashing
the server. Re-throws React Router responses (redirects, data()).

Applied to Planner session routes — replaces manual try/catch.
Any route can use withDb(() => ...) for automatic 503 on DB failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:39:33 +01:00

70 lines
2.2 KiB
TypeScript

import { useParams, useSearchParams } from "react-router";
import type { Route } from "./+types/session.$id";
import { getSession } from "~/lib/sessions";
import { data } from "react-router";
import { withDb } from "@trails-cool/db";
import { ClientOnly } from "~/components/ClientOnly";
import { lazy, Suspense } from "react";
const SessionView = lazy(() =>
import("~/components/SessionView").then((m) => ({ default: m.SessionView })),
);
export function meta(_args: Route.MetaArgs) {
return [{ title: "trails.cool Planner — Session" }];
}
export async function loader({ params }: Route.LoaderArgs) {
return withDb(async () => {
const session = await getSession(params.id);
if (!session) {
throw data({ error: "Session not found" }, { status: 404 });
}
return data({
sessionId: session.id,
callbackUrl: session.callbackUrl ?? null,
callbackToken: session.callbackToken ?? null,
});
});
}
export default function SessionPage({ loaderData }: Route.ComponentProps) {
const { id } = useParams();
const [searchParams] = useSearchParams();
const returnUrl = searchParams.get("returnUrl") ?? undefined;
const waypointsParam = searchParams.get("waypoints");
let initialWaypoints: Array<{ lat: number; lon: number; name?: string }> | undefined;
if (waypointsParam) {
try { initialWaypoints = JSON.parse(waypointsParam); } catch { /* ignore */ }
}
return (
<div className="flex h-full flex-col">
<ClientOnly
fallback={
<div className="flex h-full items-center justify-center">
<p className="text-gray-500">Loading planner...</p>
</div>
}
>
{() => (
<Suspense
fallback={
<div className="flex h-full items-center justify-center">
<p className="text-gray-500">Loading planner...</p>
</div>
}
>
<SessionView
sessionId={id!}
callbackUrl={loaderData.callbackUrl ?? undefined}
callbackToken={loaderData.callbackToken ?? undefined}
returnUrl={returnUrl}
initialWaypoints={initialWaypoints}
/>
</Suspense>
)}
</ClientOnly>
</div>
);
}