0.2.25: batch historical eNAV backfill + collapsible investor chart
Add POST /api/import/capital-accounts/batch: upload several of a fund's eNAV workbooks at once; each file's ALLOC SI roster is auto-matched to existing members (by fund-admin investor ID, else name/username) and their capital statement is saved at that file's own as-of date, building trend-lines without replacing the latest figures. Members not already in the portal are skipped and reported per file (never created). Capital statements only -- holdings/NAV are untouched. One bad file (wrong password, no ALLOC SI, unreadable date) is reported per-file and does not abort the rest. Import page gains a "Backfill historical capital" batch section (fund picker, multi-file .xlsx input, shared password, per-file results table). Investor portal "Capital over time" chart is now collapsed by default and expands per fund (first login opens clean); applies to InvestorHome and the admin Investor View via the shared component. Tests: backend/tests/test_capital_batch.py (2). Full suite 17 passed; frontend tsc clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
33776d42f4
commit
099459b2f3
@@ -1,4 +1,4 @@
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { api, type CapitalAccount, type Entity, type PortalDocument } from "../api";
|
||||
import { categoryLabel, formatDate, formatMoneyExact } from "../format";
|
||||
import CapitalChart, { type CapitalPoint } from "../components/CapitalChart";
|
||||
@@ -125,6 +125,8 @@ function CapitalBlock({
|
||||
paidIn: a.contributions_cents,
|
||||
distributions: a.distributions_cents,
|
||||
}));
|
||||
// Collapsed by default so the portal opens clean; the investor expands the trend per fund.
|
||||
const [showChart, setShowChart] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={divider ? "mt-6 pt-5 border-t border-gray-100" : "mt-3"}>
|
||||
@@ -150,7 +152,20 @@ function CapitalBlock({
|
||||
|
||||
{history.length > 1 && (
|
||||
<div className="mt-5">
|
||||
<h3 className="text-xs font-medium text-gray-500 uppercase mb-2">Capital over time</h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowChart((v) => !v)}
|
||||
aria-expanded={showChart}
|
||||
className="flex items-center gap-1.5 text-xs font-medium text-gray-500 uppercase hover:text-gray-700"
|
||||
>
|
||||
<span className={`transition-transform ${showChart ? "rotate-90" : ""}`}>▸</span>
|
||||
Capital over time
|
||||
<span className="normal-case font-normal text-gray-400">
|
||||
({history.length} quarters{showChart ? "" : " — show"})
|
||||
</span>
|
||||
</button>
|
||||
{showChart && (
|
||||
<div className="mt-3">
|
||||
<CapitalChart points={chartPoints} />
|
||||
<table className="w-full text-sm mt-4">
|
||||
<thead className="text-gray-400 text-left">
|
||||
@@ -172,6 +187,8 @@ function CapitalBlock({
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user