Release 0.2.22: capital chart, Investor View, GP stakes, doc folders

Snapshot commit bringing the uncommitted phase-2 work into version control
together with four new features and the 0.2.22 version bump.

New features:
- Investor capital-over-time chart (value, paid-in, distributions per
  quarter), rendered from existing capital-account history.
- Admin Investor View: read-only reconstruction of an investor's portal
  (GET /api/users/{id}/investor-view), reusing the investor portal UI.
- Document upload scoped to the selected fund's own investors, with an
  explicit upload-target confirmation to prevent mis-attaching.
- GP/mgmt entities gain an Assets tab listing their stakes in the funds
  they manage (new entity_stakes table + /api/entities/{id}/stakes).
- Edit-entity form (change type/status/etc.), so GP entities can be
  categorized correctly.

Verified: 11/11 backend tests pass; alembic upgrades to head b8c9d0e1f2a3;
frontend tsc + vite build clean; s9pk packs at 0.2.22:0 (x86_64).
Also: ignore .DS_Store and *.s9pk artifacts.
This commit is contained in:
Jonathan Kirkwood
2026-07-01 14:25:50 -05:00
parent 7fc78d7058
commit f0f8fd15c6
69 changed files with 5492 additions and 740 deletions
+31
View File
@@ -205,6 +205,25 @@ export interface AuditEntry {
created_at: string;
}
export interface EntityStake {
id: number;
holder_entity_id: number;
fund_entity_id: number;
fund_name: string | null;
fund_type: EntityType | null;
ownership_pct: number | null;
value_cents: number | null;
note: string | null;
created_at: string;
}
export interface InvestorView {
user: User;
entities: Entity[];
capital_accounts: CapitalAccount[];
documents: PortalDocument[];
}
// --- API helpers ---
export class ApiError extends Error {
@@ -270,6 +289,18 @@ export const api = {
updateEntity: (id: number, data: Partial<Entity>) =>
request<Entity>(`/api/entities/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
// Entity stakes (a GP/mgmt entity's interest in the funds it manages)
listStakes: (entityId: number) => request<EntityStake[]>(`/api/entities/${entityId}/stakes`),
createStake: (
entityId: number,
data: { fund_entity_id: number; ownership_pct?: number | null; value_dollars?: number | null; note?: string | null },
) => request<EntityStake>(`/api/entities/${entityId}/stakes`, { method: "POST", body: JSON.stringify(data) }),
deleteStake: (entityId: number, stakeId: number) =>
request<{ status: string }>(`/api/entities/${entityId}/stakes/${stakeId}`, { method: "DELETE" }),
// Investor View (admin read-only reconstruction of an investor's portal)
investorView: (userId: number) => request<InvestorView>(`/api/users/${userId}/investor-view`),
// Holdings
listHoldings: (entityId: number) =>
request<Holding[]>(`/api/entities/${entityId}/holdings`),