import React, { useEffect, useState } from "react"; import { Pencil, X } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "./button"; import { Input } from "./input"; import { MoneyInputWithSlider } from "./money-input-with-slider"; import { Toggle } from "./toggle"; import { Sheet, SheetContent, SheetTitle } from "./sheet"; /** * Mobile bottom-sheet organism for creating or editing a borrowing scenario. * * Sections (top-to-bottom): * - Header: editable scenario name + close button * - Content (scrollable): * - Objective selector (Buy a Home / Buy an Investment / Refinance) * - Applicants selector (1 / 2) * - Dependants selector (0 – 3) * - MoneyInputWithSlider fields (income, expenses, debt, desired loan) * - Footer: Cancel + Create/Update Scenario buttons * * On mobile the sheet fills the viewport width. On larger screens it is * capped at max-w-2xl and the slider fields flow in a 2-column grid so * the drawer does not stretch the page layout. */ // ─── Types ──────────────────────────────────────────────────────────────────── export type ScenarioObjective = "buy-home" | "buy-investment" | "refinance"; export interface ScenarioDrawerValues { name: string; objective: ScenarioObjective; /** 1 = sole applicant, 2 = two applicants */ applicants: 1 | 2; /** Number of dependants (0–3) */ dependants: number; /** Main applicant after-tax monthly income in dollars */ mainApplicantMonthlyIncome: number; /** Main applicant monthly rental income in dollars */ mainApplicantMonthlyRentalIncome: number; /** Co-applicant after-tax monthly income in dollars (applicants = 2 only) */ coApplicantMonthlyIncome: number; /** Co-applicant monthly rental income in dollars (applicants = 2 only) */ coApplicantMonthlyRentalIncome: number; /** Combined essential expenses per month in dollars */ monthlyExpense: number; /** Combined debt repayments per month in dollars */ monthlyDebtRepayment: number; /** Estimated rental income (buy-investment objective only) */ estimatedRentalIncome: number; /** Target loan amount in dollars */ desiredLoanAmount: number; } export interface ScenarioDrawerProps { open: boolean; onOpenChange: (open: boolean) => void; /** Initial field values — re-applied whenever the drawer opens */ defaultValues?: Partial; /** true = show "Update Scenario" footer button, false = "Create Scenario" */ isEditMode?: boolean; /** Called with the final values when the user clicks Save */ onSave: (values: ScenarioDrawerValues) => void; /** Called when the user clicks Cancel or the close button */ onCancel?: () => void; className?: string; } // ─── Constants ──────────────────────────────────────────────────────────────── const DEFAULT_VALUES: ScenarioDrawerValues = { name: "New Scenario", objective: "buy-home", applicants: 1, dependants: 0, mainApplicantMonthlyIncome: 0, mainApplicantMonthlyRentalIncome: 0, coApplicantMonthlyIncome: 0, coApplicantMonthlyRentalIncome: 0, monthlyExpense: 0, monthlyDebtRepayment: 0, estimatedRentalIncome: 0, desiredLoanAmount: 750_000, }; const OBJECTIVE_OPTIONS: { value: ScenarioObjective; label: string }[] = [ { value: "buy-home", label: "Buy a Home or Move" }, { value: "buy-investment", label: "Buy an Investment" }, { value: "refinance", label: "Refinance My Loan" }, ]; const APPLICANT_OPTIONS: { value: 1 | 2; label: string }[] = [ { value: 1, label: "Just Me" }, { value: 2, label: "Me + One" }, ]; const DEPENDANT_OPTIONS: { value: number; label: string }[] = [ { value: 0, label: "None" }, { value: 1, label: "1" }, { value: 2, label: "2" }, { value: 3, label: "3" }, ]; // ─── Internal sub-components ────────────────────────────────────────────────── /** * Horizontally connected row of Toggle buttons acting as a single-select group. * Uses the WealthX `Toggle` variant="outline" so the pressed state renders * with `bg-primary/10 + inset-ring-primary` per the DS token. */ function OptionGroup({ label, options, value, onChange, }: { label: string; options: { value: T; label: string }[]; value: T; onChange: (v: T) => void; }) { return (
{label}
{options.map((opt, idx) => ( { if (on) onChange(opt.value); }} className={cn( "h-auto flex-1 whitespace-normal px-3 py-2 text-center text-sm leading-tight", // Connect buttons: collapse shared borders idx > 0 && "border-l-0", )} > {opt.label} ))}
); } // ─── Main component ─────────────────────────────────────────────────────────── export function ScenarioDrawer({ open, onOpenChange, defaultValues, isEditMode = false, onSave, onCancel, className, }: ScenarioDrawerProps) { const [values, setValues] = useState({ ...DEFAULT_VALUES, ...defaultValues, }); const [editingName, setEditingName] = useState(false); // Re-seed internal state whenever the drawer opens with new defaultValues useEffect(() => { if (open) { setValues({ ...DEFAULT_VALUES, ...defaultValues }); } }, [open]); // intentional: only react to open changing, not defaultValues const set = ( key: K, value: ScenarioDrawerValues[K], ) => setValues((prev) => ({ ...prev, [key]: value })); const handleCancel = () => { onCancel?.(); onOpenChange(false); }; const handleSave = () => { onSave(values); onOpenChange(false); }; const isTwoApplicants = values.applicants === 2; const isInvestment = values.objective === "buy-investment"; const expenseLabel = isTwoApplicants ? "Combined" : "Estimated"; return ( {/* * showCloseButton={false}: disable the Sheet's built-in absolute close * button — the drawer renders its own X in the header row. */} {/* ── Header ─────────────────────────────────────────────────────── */}
{isEditMode ? "Edit Scenario" : "Create Scenario"} {editingName ? ( set("name", e.target.value)} onBlur={() => setEditingName(false)} onKeyDown={(e) => e.key === "Enter" && setEditingName(false)} className="h-8 text-base font-semibold" /> ) : ( )}
{/* ── Scrollable content ──────────────────────────────────────────── */}
{/* Selectors */} set("objective", v)} /> set("applicants", v)} /> set("dependants", v)} /> {/* Income sliders — 2-col grid on md+ */}
set("mainApplicantMonthlyIncome", v)} /> set("mainApplicantMonthlyRentalIncome", v)} /> {isTwoApplicants && ( <> set("coApplicantMonthlyIncome", v)} /> set("coApplicantMonthlyRentalIncome", v)} /> )}
{/* Expense sliders — 2-col grid on md+ */}
set("monthlyExpense", v)} /> set("monthlyDebtRepayment", v)} /> {isInvestment && ( set("estimatedRentalIncome", v)} /> )}
{/* Desired Loan Amount */}
set("desiredLoanAmount", v)} />
{/* ── Footer ──────────────────────────────────────────────────────── */}
); }