Neues Feld Plan.initialCash (additiv, Default 0). computePlan startet die erste Phase
mit diesem Cash-Bestand statt 0. In der Matrix ist die Cash-Zelle der ersten Phase
klickbar und oeffnet ein Popup zum Setzen des Anfangswerts (PATCH /api/plans/{id}).
Szenario-Kopie uebernimmt den Wert. Golden-Test ergaenzt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
} from "@/components/ElementDetail";
|
||||
import { PhaseDetail } from "@/components/PhaseDetail";
|
||||
import { PlanProfileFields, type ProfileDraft } from "@/components/PlanProfileFields";
|
||||
import { MoneyField } from "@/components/FormField";
|
||||
import { api } from "@/lib/api-client";
|
||||
import { formatChf } from "@/lib/format";
|
||||
import {
|
||||
@@ -94,6 +95,7 @@ export function PlanView({
|
||||
const [reviewFromPhaseId, setReviewFromPhaseId] = useState<string | null>(null);
|
||||
const [editTransition, setEditTransition] = useState<{ elementId: string; fromPhaseId: string } | null>(null);
|
||||
const [editPhaseCell, setEditPhaseCell] = useState<{ elementId: string; phaseId: string } | null>(null);
|
||||
const [showCashInit, setShowCashInit] = useState(false);
|
||||
|
||||
const columns = useMemo<Column[]>(() => {
|
||||
const cols: Column[] = [];
|
||||
@@ -332,13 +334,16 @@ export function PlanView({
|
||||
</span>
|
||||
<span className="text-[10px] text-faint">verfuegbares Kapital</span>
|
||||
</td>
|
||||
{columns.map((col) =>
|
||||
col.kind === "phase" ? (
|
||||
{columns.map((col) => {
|
||||
const isFirst = col.kind === "phase" && col.phase.sequenceNumber === 1;
|
||||
return col.kind === "phase" ? (
|
||||
<td
|
||||
key={`cash-${col.phase.id}`}
|
||||
onClick={isFirst ? () => setShowCashInit(true) : undefined}
|
||||
title={isFirst ? "Cash-Anfangswert bearbeiten" : undefined}
|
||||
className={`border-b border-r border-border px-2 py-1.5 text-center text-xs ${
|
||||
col.phase.cashNegative ? "font-semibold text-danger" : "text-fg"
|
||||
}`}
|
||||
isFirst ? "cursor-pointer hover:bg-accent-soft" : ""
|
||||
} ${col.phase.cashNegative ? "font-semibold text-danger" : "text-fg"}`}
|
||||
>
|
||||
<span className="whitespace-nowrap">
|
||||
{formatChf(col.phase.cashStart)} <span className="text-faint">→</span> {formatChf(col.phase.cashEnd)}
|
||||
@@ -348,8 +353,8 @@ export function PlanView({
|
||||
<td key={`cash-t-${col.fromPhase.id}`} className="border-b border-r border-border px-2 py-1.5 text-center text-[11px] text-faint">
|
||||
→
|
||||
</td>
|
||||
)
|
||||
)}
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
{CATEGORY_ORDER.map((cat) => {
|
||||
const els = elementsByCategory.get(cat)!;
|
||||
@@ -475,6 +480,17 @@ export function PlanView({
|
||||
/>
|
||||
)}
|
||||
|
||||
{showCashInit && (
|
||||
<CashInitialDialog
|
||||
plan={plan}
|
||||
onClose={() => setShowCashInit(false)}
|
||||
onSaved={() => {
|
||||
setShowCashInit(false);
|
||||
onChanged();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{reviewFromPhaseId && (() => {
|
||||
const fromPhase = computed.phases.find((p) => p.id === reviewFromPhaseId);
|
||||
if (!fromPhase) return null;
|
||||
@@ -1038,6 +1054,35 @@ function TransitionReviewDialog({
|
||||
);
|
||||
}
|
||||
|
||||
// --- Dialog: Cash-Anfangswert (erste Lebensphase) ---
|
||||
function CashInitialDialog({ plan, onClose, onSaved }: { plan: PlanInput; onClose: () => void; onSaved: () => void }) {
|
||||
const [value, setValue] = useState(plan.initialCash);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
async function save() {
|
||||
setSaving(true);
|
||||
setError(null);
|
||||
try {
|
||||
await api.patch(`/api/plans/${plan.id}`, { initialCash: value });
|
||||
onSaved();
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "Speichern fehlgeschlagen.");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogShell title="Cash-Anfangswert" onClose={onClose}>
|
||||
<p className="text-sm text-muted">Startbestand des Cash-Kontos zu Beginn der ersten Lebensphase.</p>
|
||||
<MoneyField label="Anfangswert (CHF)" value={value} onChange={setValue} />
|
||||
{error && <p className="text-sm text-danger">{error}</p>}
|
||||
<DialogActions saving={saving} onConfirm={save} onClose={onClose} confirmLabel="Speichern" />
|
||||
</DialogShell>
|
||||
);
|
||||
}
|
||||
|
||||
// --- Dialog: Element-Werte einer Lebensphase (per Klick auf eine Phasenzelle) ---
|
||||
function PhaseCellDialog({
|
||||
element,
|
||||
|
||||
Reference in New Issue
Block a user