attempt to add variable fixed epenses in onboaring
All checks were successful
Deploy / deploy (push) Successful in 58s
Security Tests / security-non-db (push) Successful in 21s
Security Tests / security-db (push) Successful in 23s

This commit is contained in:
2026-03-10 19:35:22 -05:00
parent 5fa82adc20
commit 479a5ff9d7

View File

@@ -16,6 +16,7 @@ type Step = 1 | 2 | 3 | 4 | 5 | 6;
type IncomeType = "regular" | "irregular";
type BudgetConservatism = "tight" | "moderate" | "relaxed" | "custom";
type FixedAmountMode = "fixed" | "estimated";
type VariableCat = {
id: string;
@@ -37,6 +38,7 @@ type FixedItem = {
id: string;
name: string;
amountCents: number; // absolute amount
amountMode: FixedAmountMode;
priority: number;
dueOn: string; // yyyy-mm-dd
frequency?: "one-time" | "weekly" | "biweekly" | "monthly";
@@ -139,6 +141,7 @@ export default function OnboardingPage() {
setFixeds(
s.fixeds.map((f: FixedItem) => ({
...f,
amountMode: f.amountMode === "estimated" ? "estimated" : "fixed",
schedule: f.schedule ?? defaultSchedule(),
autoPayEnabled: !!f.autoPayEnabled,
}))
@@ -317,6 +320,7 @@ export default function OnboardingPage() {
id: crypto.randomUUID(),
name: "",
amountCents: 0,
amountMode: "fixed",
priority: list.length + 1,
dueOn: getTodayInTimezone(userTimezone),
frequency: "monthly",
@@ -405,6 +409,8 @@ export default function OnboardingPage() {
const name = f.name.trim();
if (!name) continue; // skip empties just in case
try {
const amountMode = f.amountMode ?? "fixed";
const planAmountCents = Math.max(0, f.amountCents || 0);
const schedule = f.autoPayEnabled
? {
frequency: "monthly" as const,
@@ -420,8 +426,10 @@ export default function OnboardingPage() {
const created = await fixedPlansApi.create({
name,
totalCents: f.amountCents,
totalCents: planAmountCents,
fundedCents: 0,
amountMode,
estimatedCents: amountMode === "estimated" ? planAmountCents : null,
priority: i + 1,
dueOn: dueOnISO,
frequency: f.frequency,
@@ -520,7 +528,11 @@ export default function OnboardingPage() {
dashboardSnapshot.fixedPlans.map((p, i) => ({
id: p.id,
name: p.name,
amountCents: p.totalCents,
amountCents:
(p as any).amountMode === "estimated"
? Number((p as any).estimatedCents ?? p.totalCents)
: p.totalCents,
amountMode: (p as any).amountMode === "estimated" ? "estimated" : "fixed",
priority: i + 1,
dueOn: p.dueOn,
autoPayEnabled: false,
@@ -969,8 +981,8 @@ export default function OnboardingPage() {
<h2 className="section-title text-xl">Fixed Expenses</h2>
<p className="muted text-sm">
{incomeType === "regular"
? "Set amounts for recurring bills and expenses"
: "Set amounts for recurring bills and choose payment planning options"}
? "Set fixed or estimated amounts for recurring bills and expenses"
: "Set fixed or estimated amounts and choose payment planning options"}
</p>
</div>
@@ -1007,6 +1019,18 @@ export default function OnboardingPage() {
</button>
</div>
<div className="fixed-expense-fields">
<select
className="fixed-expense-select"
value={f.amountMode}
onChange={(e) =>
updateFixed(f.id, {
amountMode: (e.target.value as FixedAmountMode) || "fixed",
})
}
>
<option value="fixed">Fixed</option>
<option value="estimated">Estimated</option>
</select>
<CurrencyInput
className="fixed-expense-input"
valueCents={f.amountCents}
@@ -1030,6 +1054,11 @@ export default function OnboardingPage() {
<option value="monthly">Monthly</option>
</select>
</div>
{f.amountMode === "estimated" && (
<div className="text-xs muted mt-2">
Estimated bills can be true-upped later in Settings by applying the actual amount.
</div>
)}
<label className="fixed-expense-checkbox">
<input
@@ -1183,6 +1212,11 @@ export default function OnboardingPage() {
<span className="review-item-meta">
Due {formatDateInTimezone(f.dueOn, userTimezone)}
</span>
{f.amountMode === "estimated" && (
<span className="review-item-badge">
Estimated bill
</span>
)}
{f.autoPayEnabled && (
<span className="review-item-badge">
{incomeType === "irregular" ? "Payment plan" : "Auto-fund"}