feat: hive card with SVG, outline, edit sheets and catalog settings (Beute L2)
- lib/hive: frontend port of the hive domain (ops, validator, facts, layout, follow-ups, describe texts, reason texts) running the backend fixtures, API client - hive card on the colony page (same height as Stammdaten): SVG view, accessible outline tree, frame/part sheets with optimistic save, conflict sheet, history with past states, init from template or another colony, inspection draft mode - follow-up toasts prefilling feeding and treatment forms (treatment end only with an open treatment) - settings page "Beutensysteme" (hive-systems:manage) - NumberStepper decimal steps, reactive motion level hook, holo tokens - spec: owner design decision 2026-10-16 (glass hologram, focus mode) The SVG view is the fallback; the 3D scene follows in L3. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
0091b3d28b
commit
c608f5d405
87 files changed
+9493
-43
No files matched your search
@@ -0,0 +1,69 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { ApiError, apiFetch } from "@/lib/auth/api-client";
|
||||
import {
|
||||
createHive,
|
||||
fetchHive,
|
||||
fetchHiveAt,
|
||||
fetchHiveHistory,
|
||||
fetchHiveSystems,
|
||||
fetchHiveTemplate,
|
||||
hiveConflictOf,
|
||||
hiveErrorCode,
|
||||
hiveKeys,
|
||||
postHiveChange,
|
||||
} from "./api";
|
||||
|
||||
vi.mock("@/lib/auth/api-client", async (importOriginal) => {
|
||||
const original = await importOriginal<typeof import("@/lib/auth/api-client")>();
|
||||
return { ...original, apiFetch: vi.fn() };
|
||||
});
|
||||
|
||||
describe("hive api", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(apiFetch).mockReset();
|
||||
vi.mocked(apiFetch).mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("reads systems, setup, template, history and a day", async () => {
|
||||
await fetchHiveSystems();
|
||||
await fetchHive("c 1");
|
||||
await fetchHiveTemplate("s1");
|
||||
await fetchHiveHistory("c1");
|
||||
await fetchHiveHistory("c1", "abc_-");
|
||||
await fetchHiveAt("c1", "2026-10-01");
|
||||
expect(vi.mocked(apiFetch).mock.calls.map((call) => call[0])).toEqual([
|
||||
"/hive-systems",
|
||||
"/colonies/c%201/hive",
|
||||
"/hive-systems/s1/template",
|
||||
"/colonies/c1/hive/changes",
|
||||
"/colonies/c1/hive/changes?cursor=abc_-",
|
||||
"/colonies/c1/hive/at?day=2026-10-01",
|
||||
]);
|
||||
});
|
||||
|
||||
it("sends the idempotency key on create and change", async () => {
|
||||
const state = { schemaVersion: 1 as const, stack: [] };
|
||||
await createHive("c1", { systemId: "s", state, occurredOn: "2026-10-01" }, "key-1");
|
||||
await postHiveChange("c1", { occurredOn: "2026-10-01", source: "CARD", ops: [] }, "key-2");
|
||||
const [create, change] = vi.mocked(apiFetch).mock.calls;
|
||||
expect(create[0]).toBe("/colonies/c1/hive");
|
||||
expect(create[1]).toMatchObject({ method: "POST", headers: { "Idempotency-Key": "key-1" } });
|
||||
expect(change[0]).toBe("/colonies/c1/hive/changes");
|
||||
expect(change[1]).toMatchObject({ method: "POST", headers: { "Idempotency-Key": "key-2" } });
|
||||
});
|
||||
|
||||
it("has the query keys of the plan", () => {
|
||||
expect(hiveKeys.systems).toEqual(["hive-systems"]);
|
||||
expect(hiveKeys.detail("c1")).toEqual(["colonies", "c1", "hive"]);
|
||||
expect(hiveKeys.history("c1")).toEqual(["colonies", "c1", "hive", "history"]);
|
||||
});
|
||||
|
||||
it("extracts the HIVE_CONFLICT body and error codes", () => {
|
||||
const body = { code: "HIVE_CONFLICT", opIndex: 1, reason: "RULE_ROOF_TOP: x", current: { version: 3, state: {} } };
|
||||
expect(hiveConflictOf(new ApiError(409, "Conflict", body))).toEqual(body);
|
||||
expect(hiveConflictOf(new ApiError(409, "Conflict", { code: "HIVE_EXISTS" }))).toBeNull();
|
||||
expect(hiveConflictOf(new Error("x"))).toBeNull();
|
||||
expect(hiveErrorCode(new ApiError(404, "nf", { code: "HIVE_NOT_FOUND" }))).toBe("HIVE_NOT_FOUND");
|
||||
expect(hiveErrorCode(new ApiError(500, "x"))).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user