Personen koennen einen Namen bekommen (Plan-Einstellungen)
Deploy App / deploy (push) Successful in 1m52s

Person A/B erhalten ein optionales Namensfeld (Migration: Person.name nullable).
Fallback im UI bleibt "Person A"/"Person B". Angezeigt in Grundprofil-Box, Zeitachse,
Matrix-Elementzuordnung und Element-Erstelldialog; Szenario-Kopie uebernimmt den Namen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 08:32:19 +02:00
parent 56f8140af5
commit 4483980169
8 changed files with 40 additions and 17 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ export async function POST(
inflationRateDefault: source.inflationRateDefault,
parentPlanId: source.id,
persons: {
create: source.persons.map((p) => ({ role: p.role, age: p.age, retirementAge: p.retirementAge })),
create: source.persons.map((p) => ({ role: p.role, name: p.name, age: p.age, retirementAge: p.retirementAge })),
},
},
});
+1
View File
@@ -5,6 +5,7 @@ import { getCurrentUserId } from "@/lib/session";
const personSchema = z.object({
role: z.enum(["PERSON_A", "PERSON_B"]),
name: z.string().max(60).nullish(),
age: z.number().int().min(0).max(120),
retirementAge: z.number().int().min(30).max(100),
});
+12 -4
View File
@@ -1,19 +1,19 @@
"use client";
import { NumberField, SelectField } from "@/components/FormField";
import { NumberField, SelectField, TextField } from "@/components/FormField";
import type { HouseholdType, PersonRole } from "@/lib/types";
export interface ProfileDraft {
householdType: HouseholdType;
inflationRateDefault: number;
persons: { role: PersonRole; age: number; retirementAge: number }[];
persons: { role: PersonRole; name: string; age: number; retirementAge: number }[];
}
export function emptyProfileDraft(): ProfileDraft {
return {
householdType: "SINGLE",
inflationRateDefault: 1.5,
persons: [{ role: "PERSON_A", age: 35, retirementAge: 65 }],
persons: [{ role: "PERSON_A", name: "", age: 35, retirementAge: 65 }],
};
}
@@ -32,7 +32,7 @@ export function PlanProfileFields({
} else {
const persons =
draft.persons.length < 2
? [...draft.persons, { role: "PERSON_B" as PersonRole, age: 35, retirementAge: 65 }]
? [...draft.persons, { role: "PERSON_B" as PersonRole, name: "", age: 35, retirementAge: 65 }]
: draft.persons;
onChange({ ...draft, householdType: type, persons });
}
@@ -63,6 +63,14 @@ export function PlanProfileFields({
<div className="col-span-2 text-xs font-semibold uppercase tracking-wide text-faint">
{draft.householdType === "COUPLE" ? (person.role === "PERSON_A" ? "Person A" : "Person B") : "Ihre Angaben"}
</div>
<div className="col-span-2">
<TextField
label="Name (optional)"
value={person.name}
placeholder={person.role === "PERSON_A" ? "Person A" : "Person B"}
onChange={(v) => updatePerson(index, { name: v })}
/>
</div>
<NumberField
label="Aktuelles Alter"
value={person.age}
+21 -12
View File
@@ -106,9 +106,16 @@ export function PlanView({
return cols;
}, [computed.phases]);
// Anzeigename einer Person: eigener Name, sonst Fallback "Person A"/"Person B".
function personLabel(role: string): string {
const p = plan.persons.find((x) => x.role === role);
if (p?.name && p.name.trim()) return p.name.trim();
return role === "PERSON_A" ? "Person A" : "Person B";
}
const personAxes = plan.persons.map((p) => ({
role: p.role,
label: p.role === "PERSON_A" ? "Person A" : "Person B",
label: personLabel(p.role),
currentAge: p.age,
retirementAge: p.retirementAge,
color: p.role === "PERSON_A" ? "var(--person-a)" : "var(--person-b)",
@@ -242,7 +249,7 @@ export function PlanView({
<span className="text-xs font-semibold uppercase tracking-wide text-faint">Grundprofil (Plan)</span>
{plan.persons.map((p) => (
<span key={p.role} className="text-xs text-muted">
{plan.householdType === "COUPLE" ? (p.role === "PERSON_A" ? "Person A" : "Person B") : "Person"}: {p.age} J., Pension {p.retirementAge}
{personLabel(p.role)}: {p.age} J., Pension {p.retirementAge}
</span>
))}
<span className="text-xs text-muted">Inflation {plan.inflationRateDefault}%</span>
@@ -347,9 +354,7 @@ export function PlanView({
<td className="sticky left-0 z-10 border-b border-r border-border bg-surface px-3 py-1.5">
<div className="truncate text-xs font-medium text-fg">{el.name}</div>
{el.ownerRole && el.ownerRole !== "HOUSEHOLD" && (
<div className="text-[10px] text-faint">
{el.ownerRole === "PERSON_A" ? "Person A" : "Person B"}
</div>
<div className="text-[10px] text-faint">{personLabel(el.ownerRole)}</div>
)}
</td>
{columns.map((col) => {
@@ -675,23 +680,27 @@ function AddElementDialog({
const needsPerson = PERSON_ONLY_CATEGORIES.includes(category);
const isCouple = plan.householdType === "COUPLE";
const pLabel = (role: string) => {
const p = plan.persons.find((x) => x.role === role);
return p?.name && p.name.trim() ? p.name.trim() : role === "PERSON_A" ? "Person A" : "Person B";
};
const ownerOptions = needsPerson
? isCouple
? [
{ value: "PERSON_A", label: "Person A" },
{ value: "PERSON_B", label: "Person B" },
{ value: "PERSON_A", label: pLabel("PERSON_A") },
{ value: "PERSON_B", label: pLabel("PERSON_B") },
]
: [{ value: "PERSON_A", label: "Person A" }]
: [{ value: "PERSON_A", label: pLabel("PERSON_A") }]
: isCouple
? [
{ value: "HOUSEHOLD", label: "Gemeinsam" },
{ value: "PERSON_A", label: "Person A" },
{ value: "PERSON_B", label: "Person B" },
{ value: "PERSON_A", label: pLabel("PERSON_A") },
{ value: "PERSON_B", label: pLabel("PERSON_B") },
]
: [
{ value: "HOUSEHOLD", label: "Gemeinsam" },
{ value: "PERSON_A", label: "Person A" },
{ value: "PERSON_A", label: pLabel("PERSON_A") },
];
// Kontext fuer die Phase-1-Felder des neuen Elements.
@@ -864,7 +873,7 @@ function PlanSettingsDialog({ plan, onClose, onSaved }: { plan: PlanInput; onClo
const [draft, setDraft] = useState<ProfileDraft>({
householdType: plan.householdType,
inflationRateDefault: plan.inflationRateDefault,
persons: plan.persons.map((p) => ({ role: p.role, age: p.age, retirementAge: p.retirementAge })),
persons: plan.persons.map((p) => ({ role: p.role, name: p.name ?? "", age: p.age, retirementAge: p.retirementAge })),
});
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
+1
View File
@@ -34,6 +34,7 @@ export function toPlanInput(plan: PlanWithRelations): PlanInput {
persons: plan.persons.map((p) => ({
id: p.id,
role: p.role,
name: p.name,
age: p.age,
retirementAge: p.retirementAge,
})),
+1
View File
@@ -11,6 +11,7 @@ export type PersonRole = "PERSON_A" | "PERSON_B";
export interface PersonInput {
id: string;
role: PersonRole;
name: string | null;
age: number;
retirementAge: number;
}