added api logic, vitest, minimal testing ui
This commit is contained in:
32
web/src/components/charts/FixedFundingBars.tsx
Normal file
32
web/src/components/charts/FixedFundingBars.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from "recharts";
|
||||
|
||||
export type FixedItem = { name: string; funded: number; remaining: number };
|
||||
|
||||
export default function FixedFundingBars({ data }: { data: FixedItem[] }) {
|
||||
if (!data.length) {
|
||||
return (
|
||||
<div className="card">
|
||||
<h3 className="text-sm mb-2" style={{ color: "var(--color-sage)" }}>Fixed Plan Funding</h3>
|
||||
<div className="muted text-sm">No fixed plans yet.</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="card">
|
||||
<h3 className="text-sm mb-2" style={{ color: "var(--color-sage)" }}>Fixed Plan Funding</h3>
|
||||
<div className="chart-lg">
|
||||
<ResponsiveContainer>
|
||||
<BarChart data={data} stackOffset="expand">
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#1f2937" />
|
||||
<XAxis dataKey="name" stroke="#94a3b8" />
|
||||
<YAxis tickFormatter={(v) => `${Math.round(Number(v) * 100)}%`} stroke="#94a3b8" />
|
||||
<Tooltip formatter={(v: number) => `${Math.round(Number(v) * 100)}%`} contentStyle={{ background: "#111827", border: "1px solid #111827", borderRadius: 12, color: "#E7E3D7" }} />
|
||||
<Legend />
|
||||
<Bar dataKey="funded" stackId="a" fill="#165F46" name="Funded" />
|
||||
<Bar dataKey="remaining" stackId="a" fill="#374151" name="Remaining" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
35
web/src/components/charts/VariableAllocationDonut.tsx
Normal file
35
web/src/components/charts/VariableAllocationDonut.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
// web/src/components/charts/VariableAllocationDonut.tsx
|
||||
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend } from "recharts";
|
||||
|
||||
export type VariableSlice = { name: string; value: number; isSavings: boolean };
|
||||
|
||||
export default function VariableAllocationDonut({ data }: { data: VariableSlice[] }) {
|
||||
const total = data.reduce((s, d) => s + d.value, 0);
|
||||
if (!data.length || total === 0) {
|
||||
return (
|
||||
<div className="card">
|
||||
<h3 className="text-sm mb-2" style={{ color: "var(--color-sage)" }}>Variable Allocation</h3>
|
||||
<div className="muted text-sm">No categories configured yet.</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const fillFor = (s: boolean) => (s ? "#165F46" : "#374151");
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<h3 className="text-sm mb-2" style={{ color: "var(--color-sage)" }}>Variable Allocation</h3>
|
||||
<div className="chart-md">
|
||||
<ResponsiveContainer>
|
||||
<PieChart>
|
||||
<Pie data={data} dataKey="value" nameKey="name" innerRadius={70} outerRadius={100} stroke="#0B1020" strokeWidth={2}>
|
||||
{data.map((d, i) => <Cell key={i} fill={fillFor(d.isSavings)} />)}
|
||||
</Pie>
|
||||
<Tooltip formatter={(v: number) => `${v}%`} contentStyle={{ background: "#111827", border: "1px solid #111827", borderRadius: 12, color: "#E7E3D7" }} />
|
||||
<Legend verticalAlign="bottom" height={24} />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user