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,79 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describeHiveOps } from "./describe";
|
||||
import { inlineCatalog, part, templateState } from "./test-utils";
|
||||
import type { HiveOp, HiveState } from "./types";
|
||||
|
||||
describe("describeHiveOps", () => {
|
||||
const catalog = inlineCatalog();
|
||||
|
||||
function stateWithInsert(): HiveState {
|
||||
const state = templateState();
|
||||
state.stack[0].inserts = [part("fi", "floor-insert")];
|
||||
return state;
|
||||
}
|
||||
|
||||
it("describes removed inserts and added parts", () => {
|
||||
const ops: HiveOp[] = [
|
||||
{ op: "removePart", id: "fi" },
|
||||
{ op: "addPart", part: part("b2", "box"), at: 2 },
|
||||
];
|
||||
expect(describeHiveOps(ops, stateWithInsert(), catalog)).toBe("Bausperre entfernt, Zarge hinzugefügt");
|
||||
});
|
||||
|
||||
it("uses the label of a part when it has one", () => {
|
||||
const state = templateState();
|
||||
state.stack[1].label = "Honigraum 1";
|
||||
expect(describeHiveOps([{ op: "removePart", id: "b" }], state, catalog)).toBe("Honigraum 1 entfernt");
|
||||
expect(describeHiveOps([{ op: "movePart", id: "b", to: 2 }], state, catalog)).toBe("Honigraum 1 verschoben");
|
||||
});
|
||||
|
||||
it("describes insert ops", () => {
|
||||
expect(
|
||||
describeHiveOps([{ op: "addInsert", hostId: "f", part: part("fi", "floor-insert") }], templateState(), catalog),
|
||||
).toBe("Bausperre eingelegt");
|
||||
expect(
|
||||
describeHiveOps([{ op: "addInsert", hostId: "b", part: part("e", "evaporator") }], templateState(), catalog),
|
||||
).toBe("Verdunster eingesetzt");
|
||||
});
|
||||
|
||||
it("describes setState with unit and option labels", () => {
|
||||
const state = templateState();
|
||||
state.stack.splice(2, 0, part("fe", "feeder"));
|
||||
expect(describeHiveOps([{ op: "setState", id: "fe", key: "syrupLiters", value: 2.5 }], state, catalog)).toBe(
|
||||
"Flüssigfutter 2,5 l",
|
||||
);
|
||||
expect(describeHiveOps([{ op: "setState", id: "l", key: "feedHole", value: "OPEN" }], state, catalog)).toBe(
|
||||
"Futterloch offen",
|
||||
);
|
||||
});
|
||||
|
||||
it("describes setFrame, setFrames and setLabel", () => {
|
||||
const state = templateState();
|
||||
expect(describeHiveOps([{ op: "setFrame", boxId: "b", slot: 4, type: "BROOD" }], state, catalog)).toBe(
|
||||
"Rähmchen 4 in Zarge: Brutwabe",
|
||||
);
|
||||
expect(describeHiveOps([{ op: "setFrames", boxId: "b", frames: [] }], state, catalog)).toBe(
|
||||
"Rähmchen in Zarge neu gesetzt",
|
||||
);
|
||||
expect(describeHiveOps([{ op: "setLabel", id: "b", label: "Brutraum" }], state, catalog)).toBe(
|
||||
"Zarge heißt jetzt „Brutraum“",
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves parts added earlier in the same batch", () => {
|
||||
const ops: HiveOp[] = [
|
||||
{ op: "addPart", part: { ...part("n", "box"), label: "Aufsatz" }, at: 2 },
|
||||
{ op: "setFrame", boxId: "n", slot: 1, type: "HONEY" },
|
||||
];
|
||||
expect(describeHiveOps(ops, templateState(), catalog)).toBe(
|
||||
"Aufsatz hinzugefügt, Rähmchen 1 in Aufsatz: Honigwabe",
|
||||
);
|
||||
});
|
||||
|
||||
it("shortens more than three ops", () => {
|
||||
const ops: HiveOp[] = [1, 2, 3, 4, 5].map((slot) => ({ op: "setFrame", boxId: "b", slot, type: "BROOD" }));
|
||||
expect(describeHiveOps(ops, templateState(), catalog)).toBe(
|
||||
"Rähmchen 1 in Zarge: Brutwabe, Rähmchen 2 in Zarge: Brutwabe, Rähmchen 3 in Zarge: Brutwabe und 2 weitere",
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user