Bestandsaufnahme: Kategorienamen, Sammel-Speichern, Matrix-Spalte Start
Deploy App / deploy (push) Successful in 1m39s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 11:37:16 +02:00
parent 42d5585969
commit 2f6b788bd4
5 changed files with 454 additions and 98 deletions
+45 -10
View File
@@ -11,7 +11,7 @@
import { useState } from "react";
import { ArrowLeft, ArrowRight, Check, Lock } from "lucide-react";
import { Button, Modal } from "@/components/ui";
import { Button, Modal, useConfirm } from "@/components/ui";
import { ASSISTANT_STEPS, stepBlockedReason } from "@/lib/assistant";
import type { PlanInput } from "@/lib/types";
@@ -188,6 +188,12 @@ export function AssistantStepDialog({
isDone,
onToggleDone,
onClose,
// Gibt es ungespeicherte Eingaben? Der Dialog fragt dann beim Schliessen nach, statt sie
// still zu verwerfen -- ohne Speichern-Knopf je Feld wäre das sonst die einzige Stelle,
// an der Arbeit verloren gehen kann.
dirty = false,
// Wird beim Abschliessen aufgerufen und schreibt, was das Werkzeug gesammelt hat.
onCommit,
children,
}: {
plan: PlanInput;
@@ -195,6 +201,8 @@ export function AssistantStepDialog({
isDone: boolean;
onToggleDone: (done: boolean) => void;
onClose: () => void;
dirty?: boolean;
onCommit?: () => Promise<void>;
// Das Werkzeug des Schritts. Fehlt es, ist der Schritt reine Information.
children?: React.ReactNode;
}) {
@@ -202,30 +210,57 @@ export function AssistantStepDialog({
const explainer = STEP_EXPLAINERS[stepIndex];
const blocked = stepBlockedReason(plan, stepIndex);
const [showTool, setShowTool] = useState(false);
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
const confirm = useConfirm();
const canGuide = step.guided && !blocked && !!children;
// Schliessen mit ungespeicherten Eingaben: nachfragen statt still verwerfen.
async function closeGuarded() {
if (!dirty) return onClose();
const ok = await confirm({
title: "Eingaben verwerfen?",
message:
"Du hast Angaben erfasst, die noch nicht gespeichert sind. Schliesst du jetzt, gehen sie verloren. " +
"Mit «Schritt abschliessen» werden sie übernommen.",
confirmLabel: "Verwerfen",
danger: true,
});
if (ok) onClose();
}
async function finish() {
setSaving(true);
setError(null);
try {
if (onCommit) await onCommit();
onToggleDone(true);
onClose();
} catch (e) {
setError(e instanceof Error ? e.message : "Speichern fehlgeschlagen.");
} finally {
setSaving(false);
}
}
return (
<Modal
title={`Schritt ${stepIndex + 1}: ${step.title}`}
subtitle={showTool ? undefined : step.lead}
onClose={onClose}
onClose={closeGuarded}
xwide
>
{showTool ? (
<div className="flex flex-col gap-4">
{children}
{error && <p className="text-sm text-danger">{error}</p>}
<div className="flex items-center justify-between border-t border-border pt-3">
<Button variant="ghost" onClick={() => setShowTool(false)}>
<ArrowLeft className="h-4 w-4" /> Zurück zur Erklärung
</Button>
<Button
onClick={() => {
onToggleDone(true);
onClose();
}}
>
<Check className="h-4 w-4" /> Schritt abschliessen
<Button disabled={saving} onClick={finish}>
<Check className="h-4 w-4" /> {saving ? "Wird gespeichert…" : "Schritt abschliessen"}
</Button>
</div>
</div>
@@ -270,7 +305,7 @@ export function AssistantStepDialog({
variant="ghost"
onClick={() => {
onToggleDone(!isDone);
onClose();
void closeGuarded();
}}
>
{isDone ? "Wieder als offen markieren" : "Selbst erledigen"}