ada5d06dad
FieldsPage kept the selected field definition in component state, so reload lost the selection, fields couldn't be linked/shared, and back/forward didn't navigate selections — inconsistent with /vocabularies/:id and /objects/:id. Move selection into the URL: the route becomes /fields/:key? (optional segment), FieldList selection navigates, cancel/done navigates back to /fields, and the page derives the selected def from the already-cached field-defs query. An unknown or stale key (e.g. after deleting the selected field) falls back to the create form. Tests: deep link opens the locked edit form, select→cancel round-trips through the URL, unknown key falls back to create. Closes #72 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
93 lines
3.3 KiB
TypeScript
93 lines
3.3 KiB
TypeScript
import { lazy, Suspense } from "react";
|
|
import { createBrowserRouter, createRoutesFromElements, Navigate, Outlet, Route, RouterProvider } from "react-router-dom";
|
|
|
|
import { NavigationBridge } from "./shell/navigation-bridge";
|
|
import { RequireAuth } from "./auth/require-auth";
|
|
import { LoginPage } from "./auth/login-page";
|
|
import { AppShell } from "./shell/app-shell";
|
|
import { ObjectsPage } from "./objects/objects-page";
|
|
import { ObjectDetail } from "./objects/object-detail";
|
|
import { SearchPage } from "./search/search-page";
|
|
import { SelectSearchPrompt } from "./search/select-search-prompt";
|
|
import { VocabulariesPage } from "./vocab/vocabularies-page";
|
|
import { VocabularyTerms } from "./vocab/vocabulary-terms";
|
|
import { SelectVocabularyPrompt } from "./vocab/select-vocabulary-prompt";
|
|
import { AuthoritiesPage } from "./authorities/authorities-page";
|
|
import { FormSkeleton, ListSkeleton } from "@/components/ui/skeletons";
|
|
|
|
const ObjectNewPage = lazy(() =>
|
|
import("./objects/object-new-page").then((m) => ({ default: m.ObjectNewPage })),
|
|
);
|
|
|
|
const ObjectEditForm = lazy(() =>
|
|
import("./objects/object-edit-form").then((m) => ({ default: m.ObjectEditForm })),
|
|
);
|
|
|
|
const FieldsPage = lazy(() =>
|
|
import("./fields/fields-page").then((m) => ({ default: m.FieldsPage })),
|
|
);
|
|
|
|
function RootLayout() {
|
|
return (
|
|
<>
|
|
<NavigationBridge />
|
|
<Outlet />
|
|
</>
|
|
);
|
|
}
|
|
|
|
const router = createBrowserRouter(
|
|
createRoutesFromElements(
|
|
<Route element={<RootLayout />}>
|
|
<Route path="/login" element={<LoginPage />} />
|
|
<Route element={<RequireAuth />}>
|
|
<Route element={<AppShell />}>
|
|
<Route
|
|
path="/objects/new"
|
|
element={
|
|
<Suspense fallback={<div className="mx-auto max-w-2xl"><FormSkeleton /></div>}>
|
|
<ObjectNewPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route path="/objects" element={<ObjectsPage />}>
|
|
<Route path=":id" element={<ObjectDetail />} />
|
|
<Route
|
|
path=":id/edit"
|
|
element={
|
|
<Suspense fallback={<div className="mx-auto max-w-2xl"><FormSkeleton /></div>}>
|
|
<ObjectEditForm />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
</Route>
|
|
<Route path="/vocabularies" element={<VocabulariesPage />}>
|
|
<Route index element={<SelectVocabularyPrompt />} />
|
|
<Route path=":id" element={<VocabularyTerms />} />
|
|
</Route>
|
|
<Route path="/search" element={<SearchPage />}>
|
|
<Route index element={<SelectSearchPrompt />} />
|
|
<Route path=":id" element={<ObjectDetail />} />
|
|
</Route>
|
|
<Route path="/authorities" element={<Navigate to="/authorities/person" replace />} />
|
|
<Route path="/authorities/:kind" element={<AuthoritiesPage />} />
|
|
<Route
|
|
path="/fields/:key?"
|
|
element={
|
|
<Suspense fallback={<ListSkeleton />}>
|
|
<FieldsPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route path="/" element={<Navigate to="/objects" replace />} />
|
|
</Route>
|
|
</Route>
|
|
<Route path="*" element={<Navigate to="/objects" replace />} />
|
|
</Route>,
|
|
),
|
|
);
|
|
|
|
export function App() {
|
|
return <RouterProvider router={router} />;
|
|
}
|