feat(web): object detail + two-pane page + app routing

Implements the navigable SPA shell: object detail pane showing
inventory-minimum fields, flexible fields (via Record<string,unknown>
cast) and visibility badge; ObjectsPage two-pane layout; BrowserRouter
wired through RequireAuth+AppShell; QueryClient provided in main.tsx.
Consolidates ObjectList NavLink to use isActive function form, removing
manual useParams highlight.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 23:13:52 +02:00
parent d6fe0b0597
commit 859f41dcb9
8 changed files with 228 additions and 13 deletions
+48
View File
@@ -0,0 +1,48 @@
import { expect, test } from "vitest";
import { screen } from "@testing-library/react";
import { http, HttpResponse } from "msw";
import { Routes, Route } from "react-router-dom";
import { server } from "../test/server";
import { renderApp } from "../test/render";
import { ObjectDetail } from "./object-detail";
function tree() {
return (
<Routes>
<Route path="/objects/:id" element={<ObjectDetail />} />
</Routes>
);
}
test("renders inventory-minimum fields, flexible values and visibility", async () => {
// override so the object carries a flexible field value (schema types fields as
// Record<string,never>, so return a plain object literal here)
server.use(
http.get("/api/admin/objects/:id", () =>
HttpResponse.json({
id: "11111111-1111-1111-1111-111111111111",
object_number: "LM-0042",
object_name: "Amphora",
number_of_objects: 1,
brief_description: "Storage jar",
current_location: "Vault 3",
current_owner: null,
recorder: null,
recording_date: null,
visibility: "public",
fields: { material: "Bronze" },
}),
),
);
renderApp(tree(), { route: "/objects/11111111-1111-1111-1111-111111111111" });
expect(await screen.findByText("Amphora")).toBeInTheDocument();
expect(screen.getByText("Vault 3")).toBeInTheDocument();
expect(screen.getByText("Bronze")).toBeInTheDocument(); // flexible field value
expect(screen.getByText("Public")).toBeInTheDocument();
});
test("shows a not-found state for a missing object", async () => {
server.use(http.get("/api/admin/objects/:id", () => new HttpResponse(null, { status: 404 })));
renderApp(tree(), { route: "/objects/does-not-exist" });
expect(await screen.findByText(/object not found/i)).toBeInTheDocument();
});