import * as React from "react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { formatCurrency } from "@/lib/format-currency";
export type ExpenseCategoryData = {
label: string;
color: string;
monthlyAmounts: number[];
};
export type ApplicantExpensesSectionProps = {
applicantName: string;
/** Three states: loading, access granted, access not granted. */
state: "loading" | "connected" | "pending-access";
/** Last 12 month labels (e.g. "Jan", "Feb" …). Required when state = "connected". */
monthLabels?: string[];
/** Per-category breakdown. Required when state = "connected". */
categories?: ExpenseCategoryData[];
/** Total spend over the loaded period. */
totalAmount?: number;
onConnectMore?: () => void;
onAddManually?: () => void;
onSendRequest?: () => void;
className?: string;
};
export function ApplicantExpensesSection({
applicantName,
state,
monthLabels = [],
categories = [],
totalAmount = 0,
onConnectMore,
onAddManually,
onSendRequest,
className,
}: ApplicantExpensesSectionProps) {
return (
{applicantName}'s Expenses
{state === "loading" &&
}
{state === "connected" && (
)}
{state === "pending-access" && (
)}
);
}
// ─── Sub-states ───────────────────────────────────────────────────────────────
function LoadingState() {
return (
);
}
function ConnectedState({
monthLabels,
categories,
totalAmount,
onConnectMore,
onAddManually,
}: {
monthLabels: string[];
categories: ExpenseCategoryData[];
totalAmount: number;
onConnectMore?: () => void;
onAddManually?: () => void;
}) {
const maxValue = Math.max(
...monthLabels.map((_, mi) =>
categories.reduce((sum, cat) => sum + (cat.monthlyAmounts[mi] ?? 0), 0),
),
1,
);
return (
{formatCurrency(totalAmount)}
Total latest 12 months
{onAddManually && (
)}
{/* Mini stacked bar chart */}
{monthLabels.length > 0 && categories.length > 0 && (
{monthLabels.map((month, mi) => {
const colTotal = categories.reduce(
(sum, cat) => sum + (cat.monthlyAmounts[mi] ?? 0),
0,
);
const heightPct = (colTotal / maxValue) * 100;
return (
{categories.map((cat) => {
const segPct =
colTotal > 0
? ((cat.monthlyAmounts[mi] ?? 0) / colTotal) * 100
: 0;
return (
);
})}
{month}
);
})}
)}
{/* Legend */}
{categories.length > 0 && (
{categories.map((cat) => (
))}
)}
);
}
function PendingAccessState({
applicantName,
onSendRequest,
}: {
applicantName: string;
onSendRequest?: () => void;
}) {
return (
Requires access to banking data
Send {applicantName} an invitation to connect their bank accounts.
);
}
// ─── Icons ────────────────────────────────────────────────────────────────────
function SpinnerIcon({ className }: { className?: string }) {
return (
);
}
function LockIcon({ className }: { className?: string }) {
return (
);
}