import * as React from "react";
import { useState, useCallback } from "react";
import {
Baby,
Car,
DollarSign,
Gift,
Home,
Info,
ShoppingCart,
Shield,
Smartphone,
type LucideIcon,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { CurrencyInputWithSlider } from "@/components/ui/form-primitives";
// ---------------------------------------------------------------------------
// SharedEqualToggle — "No [Switch] Yes" row + label, used both as the
// per-item toggle here and the global toggle in ExpenseWorkDetails.
// ---------------------------------------------------------------------------
export type SharedEqualToggleProps = {
checked: boolean;
onCheckedChange?: (checked: boolean) => void;
label?: string;
ariaLabel?: string;
/** "stacked" = label above the switch row (per-item); "inline" = all on one row (global toggle). */
layout?: "inline" | "stacked";
};
export function SharedEqualToggle({
checked,
onCheckedChange,
label = "Expense shared equally",
ariaLabel,
layout = "inline",
}: SharedEqualToggleProps) {
const toggleRow = (
No
Yes
);
if (layout === "stacked") {
return (
{label}
{toggleRow}
);
}
return (
{label}
{toggleRow}
);
}
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export type ExpenseType =
| "housing"
| "transport"
| "groceries"
| "childcare"
| "insurance"
| "entertainment"
| "utilities"
| "other";
export type ExpenseDetailItemProps = {
/** Expense category type — drives the icon. Empty means "not chosen yet",
* which renders the category picker instead of the icon + label. */
type: ExpenseType | string;
/** Display label for the expense */
label: string;
/** Categories still available to pick — only used while `type` is unset. */
categoryOptions?: Array<{ type: ExpenseType | string; label: string }>;
/** Called when a category is chosen for a not-yet-typed item. */
onTypeSelect?: (type: string) => void;
/** Placeholder for the category picker. */
categoryPlaceholder?: string;
/** Optional tooltip description (shown via ⓘ icon) */
description?: string;
/** Current monthly expense amount */
amount: number;
/** Total of all expenses — used to calculate the progress bar ratio */
totalExpenses?: number;
/** Whether there is a co-applicant — shows the shared toggle when true */
hasCoApplicant?: boolean;
/** Whether this expense is shared between applicants */
isShared?: boolean;
/** Percentage attributed to the main applicant (0–100) */
sharedPercent?: number;
onAmountChange?: (amount: number) => void;
onSharedChange?: (shared: boolean) => void;
onSharedPercentChange?: (percent: number) => void;
onDelete?: () => void;
className?: string;
};
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const EXPENSE_ICON_MAP: Record = {
housing: Home,
transport: Car,
groceries: ShoppingCart,
childcare: Baby,
insurance: Shield,
entertainment: Gift,
utilities: Smartphone, // phone / internet / subscriptions
other: DollarSign,
};
// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------
/**
* ExpenseDetailItem — a single expense row in the loan wizard Expenses Details section.
*
* Left side: category icon + label + ⓘ, progress bar, optional shared toggle + percent.
* Right side: currency input with slider.
*
* Figma: WealthX-Backoffice---Mobile-App — node 19308:53715
*/
export function ExpenseDetailItem({
type,
label,
categoryOptions = [],
onTypeSelect,
categoryPlaceholder = "Choose Your Expense Item",
description,
amount,
totalExpenses,
hasCoApplicant = false,
isShared = false,
sharedPercent: initialPercent = 50,
onAmountChange,
onSharedChange,
onSharedPercentChange,
onDelete,
className,
}: ExpenseDetailItemProps) {
// A freshly added row has no category yet — it shows a picker until one is chosen.
const isNewItem = !type;
const Icon = EXPENSE_ICON_MAP[type] ?? DollarSign;
const fillPct =
totalExpenses && totalExpenses > 0
? Math.min(100, (amount / totalExpenses) * 100)
: 0;
const [percent, setPercent] = useState(String(initialPercent));
const handlePercentBlur = useCallback(() => {
const parsed = Math.max(0, Math.min(100, parseInt(percent, 10) || 0));
setPercent(String(parsed));
onSharedPercentChange?.(parsed);
}, [percent, onSharedPercentChange]);
return (
{isNewItem ? (
) : (
{label}
{description && (
)}
)}
{/* Full-width on mobile; capped at 274px (Figma left-column width) from sm+ */}
{hasCoApplicant && (
)}
{onDelete && (
)}
);
}