fixed rebalance ui, helper feature redistruvbtion
All checks were successful
Deploy / deploy (push) Successful in 57s
Security Tests / security-non-db (push) Successful in 19s
Security Tests / security-db (push) Successful in 23s

This commit is contained in:
2026-03-11 22:11:15 -05:00
parent d39928a3f7
commit e6dac3f344

View File

@@ -71,22 +71,33 @@ export default function RebalancePage() {
push("err", "Amount exceeds available budget");
return;
}
const baseSum = sum(others.map((o) => o.targetCents)) || others.length;
const next = rows.map((r) => ({ ...r }));
let distributed = 0;
others.forEach((o) => {
const share = Math.floor((remaining * (o.targetCents || 1)) / baseSum);
const idx = next.findIndex((n) => n.id === o.id);
next[idx].targetCents = share;
distributed += share;
const weightSum = others.reduce((s, o) => s + (o.percent || 1), 0) || others.length;
const provisional = others.map((o) => {
const exact = (remaining * (o.percent || 1)) / weightSum;
const base = Math.floor(exact);
return { id: o.id, base, frac: exact - base };
});
let distributed = provisional.reduce((s, p) => s + p.base, 0);
let leftover = remaining - distributed;
provisional
.slice()
.sort((a, b) => b.frac - a.frac)
.forEach((p) => {
if (leftover > 0) {
p.base += 1;
leftover -= 1;
}
});
const next = rows.map((r) => ({ ...r }));
provisional.forEach((p) => {
const idx = next.findIndex((n) => n.id === p.id);
if (idx >= 0) next[idx].targetCents = p.base;
});
const leftover = remaining - distributed;
if (leftover > 0) {
const firstIdx = next.findIndex((n) => n.id !== adjustId);
if (firstIdx >= 0) next[firstIdx].targetCents += leftover;
}
const targetIdx = next.findIndex((n) => n.id === adjustId);
next[targetIdx].targetCents = desired;
if (targetIdx >= 0) next[targetIdx].targetCents = desired;
setRows(next);
};
@@ -115,28 +126,28 @@ export default function RebalancePage() {
if (isLoading || !data) return <div className="muted">Loading</div>;
return (
<div className="space-y-4">
<div className="space-y-6">
<header className="space-y-1">
<h2 className="text-lg font-semibold">Rebalance variable pool</h2>
<h2 className="text-2xl font-semibold">Rebalance variable pool</h2>
<p className="text-sm muted">
Redistribute your current variable pool without changing future income percentages.
</p>
</header>
<div className="card p-4 flex flex-wrap gap-4 items-center justify-between">
<div className="text-sm">
<div className="font-semibold">Available</div>
<div className="text-xl font-mono">${(available / 100).toFixed(2)}</div>
<div className="card glass p-5 flex flex-wrap gap-6 items-center justify-between">
<div className="space-y-1">
<div className="text-xs uppercase tracking-wide muted">Available</div>
<div className="text-2xl font-mono font-semibold">${(available / 100).toFixed(2)}</div>
</div>
<div className="text-sm">
<div>Totals</div>
<div className="font-mono">
<div className="space-y-1">
<div className="text-xs uppercase tracking-wide muted">Totals</div>
<div className="text-sm font-mono">
${(total / 100).toFixed(2)} / ${(available / 100).toFixed(2)}
</div>
</div>
<div className="text-sm">
<div>Savings total</div>
<div className="font-mono">${(savingsTotal / 100).toFixed(2)}</div>
<div className="space-y-1">
<div className="text-xs uppercase tracking-wide muted">Savings total</div>
<div className="text-sm font-mono">${(savingsTotal / 100).toFixed(2)}</div>
</div>
<label className="flex items-center gap-2 text-sm">
<input
@@ -148,12 +159,12 @@ export default function RebalancePage() {
</label>
</div>
<div className="card p-4 space-y-3">
<div className="flex flex-wrap gap-2 items-end">
<div className="flex flex-col">
<label className="text-xs muted">Adjust one category</label>
<div className="card p-5 space-y-4">
<div className="flex flex-wrap gap-3 items-end">
<div className="flex flex-col gap-1">
<label className="text-xs uppercase tracking-wide muted">Adjust one category</label>
<select
className="input"
className="input w-56"
value={adjustId}
onChange={(e) => setAdjustId(e.target.value)}
>
@@ -164,10 +175,10 @@ export default function RebalancePage() {
))}
</select>
</div>
<div className="flex flex-col">
<label className="text-xs muted">Amount</label>
<div className="flex flex-col gap-1">
<label className="text-xs uppercase tracking-wide muted">Amount</label>
<input
className="input"
className="input w-40"
type="number"
min={0}
step="0.01"
@@ -176,32 +187,33 @@ export default function RebalancePage() {
placeholder="0.00"
/>
</div>
<button className="btn" type="button" onClick={applyAdjustOne}>
<button className="btn primary" type="button" onClick={applyAdjustOne}>
Apply helper
</button>
</div>
<div className="overflow-auto">
<div className="overflow-auto rounded-2xl border border-[--color-border]/50 bg-[--color-panel]">
<table className="w-full text-sm">
<thead>
<thead className="bg-[--color-surface]">
<tr className="text-left">
<th className="py-2">Category</th>
<th className="py-2">Current</th>
<th className="py-2">Percent</th>
<th className="py-2">Target</th>
<th className="py-3 px-4 text-xs uppercase tracking-wide muted">Category</th>
<th className="py-3 px-4 text-xs uppercase tracking-wide muted">Current</th>
<th className="py-3 px-4 text-xs uppercase tracking-wide muted">Percent</th>
<th className="py-3 px-4 text-xs uppercase tracking-wide muted">Target</th>
</tr>
</thead>
<tbody>
{rows.map((row) => (
<tr key={row.id} className="border-t border-[--color-panel]">
<td className="py-2 font-medium">
{row.name} {row.isSavings ? <span className="text-xs text-emerald-500">Savings</span> : null}
<tr key={row.id} className="border-t border-[--color-border]/30">
<td className="py-3 px-4 font-medium flex items-center gap-2">
{row.name}
{row.isSavings ? <span className="badge badge-ghost">Savings</span> : null}
</td>
<td className="py-2 font-mono">${(row.balanceCents / 100).toFixed(2)}</td>
<td className="py-2">{row.percent}%</td>
<td className="py-2">
<td className="py-3 px-4 font-mono">${(row.balanceCents / 100).toFixed(2)}</td>
<td className="py-3 px-4">{row.percent}%</td>
<td className="py-3 px-4">
<CurrencyInput
className="w-32"
className="input w-32"
valueCents={row.targetCents}
onChange={(cents) =>
setRows((prev) =>
@@ -238,7 +250,7 @@ export default function RebalancePage() {
Apply rebalance
</button>
<button
className="btn"
className="btn ghost"
type="button"
onClick={async () => {
setForceSavings(false);