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,85 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { SheetFooter } from "@/components/ui/sheet";
|
||||
import { hiveReasonText } from "@/lib/hive/reason-text";
|
||||
import { describeHiveOps } from "@/lib/hive/describe";
|
||||
import type { HiveCatalog } from "@/lib/hive/types";
|
||||
import { HiveSheetShell } from "./hive-sheet-shell";
|
||||
import type { HiveConflict } from "./use-hive-mutation";
|
||||
import { SAVE_FAILED_TEXT } from "./use-sheet-submit";
|
||||
|
||||
export interface HiveConflictSheetProps {
|
||||
conflict: HiveConflict | null;
|
||||
catalog: HiveCatalog;
|
||||
onResolve: (action: "retry" | "discard") => Promise<void>;
|
||||
}
|
||||
|
||||
/** Spec section 11: the hive changed meanwhile; the own ops are shown on the current state. */
|
||||
export function HiveConflictSheet({ conflict, catalog, onResolve }: HiveConflictSheetProps) {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
async function resolve(action: "retry" | "discard") {
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
try {
|
||||
await onResolve(action);
|
||||
} catch {
|
||||
setError(SAVE_FAILED_TEXT);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
const preview = conflict?.preview;
|
||||
const failedOp = conflict && preview && !preview.ok ? conflict.ops.slice(preview.opIndex, preview.opIndex + 1) : [];
|
||||
|
||||
return (
|
||||
<HiveSheetShell
|
||||
open={conflict !== null}
|
||||
onOpenChange={(open) => {
|
||||
// Closing counts as discard, but not while a request is running.
|
||||
if (!open && !busy) void resolve("discard");
|
||||
}}
|
||||
title="Die Beute wurde inzwischen geändert"
|
||||
description="Jemand anderes hat den Aufbau verändert. Deine Änderung wurde noch nicht gespeichert."
|
||||
>
|
||||
{conflict && preview ? (
|
||||
<div className="flex flex-col gap-4 px-4 pb-4">
|
||||
{preview.ok ? (
|
||||
<p className="text-sm">
|
||||
<span className="font-medium">Deine Änderung auf dem neuen Stand: </span>
|
||||
{describeHiveOps(conflict.ops, conflict.current.state, catalog)}
|
||||
</p>
|
||||
) : (
|
||||
<p role="alert" className="text-sm text-destructive">
|
||||
Deine Änderung passt nicht mehr:{" "}
|
||||
{describeHiveOps(failedOp, conflict.current.state, catalog) || "Schritt " + (preview.opIndex + 1)}.
|
||||
<span className="mt-1 block text-xs text-muted-foreground">{hiveReasonText(preview.reason)}</span>
|
||||
</p>
|
||||
)}
|
||||
{error ? (
|
||||
<p role="alert" className="text-sm text-destructive">
|
||||
{error}
|
||||
</p>
|
||||
) : null}
|
||||
<SheetFooter className="p-0">
|
||||
<Button
|
||||
type="button"
|
||||
size="touch"
|
||||
disabled={!preview.ok || busy}
|
||||
onClick={() => void resolve("retry")}
|
||||
>
|
||||
Erneut speichern
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="touch" disabled={busy} onClick={() => void resolve("discard")}>
|
||||
Verwerfen
|
||||
</Button>
|
||||
</SheetFooter>
|
||||
</div>
|
||||
) : null}
|
||||
</HiveSheetShell>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user