V3: 3 Farbschemata, Grundprofil auf Plan-Ebene, Erstellungs-Popups, integer-Zahlenfelder mit Beschleunigungs-Spinner, Carry-Forward des Zielwerts, gefuehrter Uebergang
Deploy App / deploy (push) Successful in 1m51s
Deploy App / deploy (push) Successful in 1m51s
- Theming: semantische CSS-Tokens + 3 waehlbare Schemata (Hell/Dunkel/Warm/Sunset), Umschalter im Profil-Menue, FOUC-frei via Inline-Script, localStorage; Klassen-Sweep aller Komponenten, Recharts aus Tokens - Datenmodell: Household entfaellt; Plan traegt Haushaltsform/Personen/Inflation selbst (Person -> planId, Plan -> userId); destruktive Migration (TRUNCATE); Onboarding/HouseholdSettings entfernt; Plan-Erstellung & -Einstellungen mit Profilfeldern - Popups: Element-Erstellung mit Inline-Feldern (geteilte ElementPhaseFields/ElementTransitionFields), Phase- und Plan-Popups mit Direkteingabe - Zahlenfelder: 1'000er-Runden entfernt (floorToThousand/roundToHundred weg), integer MoneyInput mit beschleunigendem Press-and-Hold-Spinner, 0-Bug-Fix, harte Live-Caps - Quote: Amortisation + Tilgung neu quotenwirksam; Restquote sichtbar (sinkt beim Verteilen); Invest-Deckel = verfuegbares Kapital + fortgeschriebener Zielwert - Carry-Forward: Startwert der Folgephase = Zielwert der Vorphase minus Uebergangs-Bezug (live abgeleitet); optionale Zusatzinvestition aus verfuegbarem Kapital - Matrix: Zelle zeigt Start -> Ziel; Uebergangs-Spaltenkopf mit "n offen"-Badge + gefuehrtem Pruef-Panel Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+14
-27
@@ -2,9 +2,10 @@ import { Prisma } from "@/generated/prisma/client";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { phaseDataSchema, transitionDataSchema } from "@/lib/elements";
|
||||
import type { PhaseData, TransitionData } from "@/lib/elements";
|
||||
import type { HouseholdInput, PlanInput } from "@/lib/types";
|
||||
import type { PlanInput } from "@/lib/types";
|
||||
|
||||
export const planInclude = {
|
||||
persons: { orderBy: { role: "asc" } },
|
||||
phases: { orderBy: { sequenceNumber: "asc" } },
|
||||
elements: {
|
||||
orderBy: { orderIndex: "asc" },
|
||||
@@ -13,21 +14,6 @@ export const planInclude = {
|
||||
} satisfies Prisma.PlanInclude;
|
||||
|
||||
export type PlanWithRelations = Prisma.PlanGetPayload<{ include: typeof planInclude }>;
|
||||
export type HouseholdWithPersons = Prisma.HouseholdGetPayload<{ include: { persons: true } }>;
|
||||
|
||||
export function toHouseholdInput(household: HouseholdWithPersons): HouseholdInput {
|
||||
return {
|
||||
id: household.id,
|
||||
householdType: household.householdType,
|
||||
inflationRateDefault: household.inflationRateDefault,
|
||||
persons: household.persons.map((p) => ({
|
||||
id: p.id,
|
||||
role: p.role,
|
||||
age: p.age,
|
||||
retirementAge: p.retirementAge,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function parsePhaseData(raw: unknown): PhaseData {
|
||||
const parsed = phaseDataSchema.safeParse(raw);
|
||||
@@ -43,8 +29,14 @@ export function toPlanInput(plan: PlanWithRelations): PlanInput {
|
||||
return {
|
||||
id: plan.id,
|
||||
name: plan.name,
|
||||
retirementAgeA: plan.retirementAgeA,
|
||||
retirementAgeB: plan.retirementAgeB,
|
||||
householdType: plan.householdType,
|
||||
inflationRateDefault: plan.inflationRateDefault,
|
||||
persons: plan.persons.map((p) => ({
|
||||
id: p.id,
|
||||
role: p.role,
|
||||
age: p.age,
|
||||
retirementAge: p.retirementAge,
|
||||
})),
|
||||
phases: plan.phases.map((phase) => ({
|
||||
id: phase.id,
|
||||
sequenceNumber: phase.sequenceNumber,
|
||||
@@ -70,15 +62,10 @@ export function toPlanInput(plan: PlanWithRelations): PlanInput {
|
||||
};
|
||||
}
|
||||
|
||||
// Liefert den Haushalt des eingeloggten Benutzers (pro Konto genau einer).
|
||||
export async function getHouseholdOrNull(userId: string): Promise<HouseholdWithPersons | null> {
|
||||
return prisma.household.findFirst({ where: { userId }, include: { persons: true } });
|
||||
}
|
||||
|
||||
// Laedt einen Plan inkl. Phasen + Elemente, aber nur wenn er dem Benutzer gehoert.
|
||||
// Laedt einen Plan inkl. Profil + Phasen + Elemente, aber nur wenn er dem Benutzer gehoert.
|
||||
export async function getOwnedPlan(planId: string, userId: string) {
|
||||
return prisma.plan.findFirst({
|
||||
where: { id: planId, household: { userId } },
|
||||
where: { id: planId, userId },
|
||||
include: planInclude,
|
||||
});
|
||||
}
|
||||
@@ -86,13 +73,13 @@ export async function getOwnedPlan(planId: string, userId: string) {
|
||||
// Laedt eine Phase (Basisdaten), aber nur wenn sie dem Benutzer gehoert.
|
||||
export async function getOwnedPhase(phaseId: string, userId: string) {
|
||||
return prisma.phase.findFirst({
|
||||
where: { id: phaseId, plan: { household: { userId } } },
|
||||
where: { id: phaseId, plan: { userId } },
|
||||
});
|
||||
}
|
||||
|
||||
// Laedt ein Element (Basisdaten), aber nur wenn es dem Benutzer gehoert.
|
||||
export async function getOwnedElement(elementId: string, userId: string) {
|
||||
return prisma.financialElement.findFirst({
|
||||
where: { id: elementId, plan: { household: { userId } } },
|
||||
where: { id: elementId, plan: { userId } },
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user