0.2.35: exited positions show history clipped at the exit date
The eNAV keeps producing statements after an exit (quarterly for funds, event-driven for SPVs), so an unfiltered graph would crash to zero. Now the exited card gets its collapsible History back — chart + table showing the capital journey up to exited_on only. Post-exit statements stay recorded (audit trail; Undo restores full history) but are never plotted. History section extracted into a shared HistorySection component; toggle counts "statements" not "quarters" (SPV statements are event-driven). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6313d781b4
commit
2c87ab499a
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ten31portal-startos",
|
"name": "ten31portal-startos",
|
||||||
"version": "0.2.34",
|
"version": "0.2.35",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run check && rm -rf ./javascript && ncc build startos/index.ts -o ./javascript",
|
"build": "npm run check && rm -rf ./javascript && ncc build startos/index.ts -o ./javascript",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export { v_0_2_34 as current } from './v_0_2_34'
|
export { v_0_2_35 as current } from './v_0_2_35'
|
||||||
import { v_0_1_0 } from './v_0_1_0'
|
import { v_0_1_0 } from './v_0_1_0'
|
||||||
import { v_0_2_0 } from './v_0_2_0'
|
import { v_0_2_0 } from './v_0_2_0'
|
||||||
import { v_0_2_1 } from './v_0_2_1'
|
import { v_0_2_1 } from './v_0_2_1'
|
||||||
@@ -33,4 +33,5 @@ import { v_0_2_30 } from './v_0_2_30'
|
|||||||
import { v_0_2_31 } from './v_0_2_31'
|
import { v_0_2_31 } from './v_0_2_31'
|
||||||
import { v_0_2_32 } from './v_0_2_32'
|
import { v_0_2_32 } from './v_0_2_32'
|
||||||
import { v_0_2_33 } from './v_0_2_33'
|
import { v_0_2_33 } from './v_0_2_33'
|
||||||
export const other = [v_0_1_0, v_0_2_0, v_0_2_1, v_0_2_3, v_0_2_4, v_0_2_5, v_0_2_6, v_0_2_7, v_0_2_8, v_0_2_9, v_0_2_10, v_0_2_11, v_0_2_12, v_0_2_13, v_0_2_14, v_0_2_15, v_0_2_16, v_0_2_17, v_0_2_18, v_0_2_19, v_0_2_20, v_0_2_21, v_0_2_22, v_0_2_23, v_0_2_24, v_0_2_25, v_0_2_26, v_0_2_27, v_0_2_28, v_0_2_29, v_0_2_30, v_0_2_31, v_0_2_32, v_0_2_33]
|
import { v_0_2_34 } from './v_0_2_34'
|
||||||
|
export const other = [v_0_1_0, v_0_2_0, v_0_2_1, v_0_2_3, v_0_2_4, v_0_2_5, v_0_2_6, v_0_2_7, v_0_2_8, v_0_2_9, v_0_2_10, v_0_2_11, v_0_2_12, v_0_2_13, v_0_2_14, v_0_2_15, v_0_2_16, v_0_2_17, v_0_2_18, v_0_2_19, v_0_2_20, v_0_2_21, v_0_2_22, v_0_2_23, v_0_2_24, v_0_2_25, v_0_2_26, v_0_2_27, v_0_2_28, v_0_2_29, v_0_2_30, v_0_2_31, v_0_2_32, v_0_2_33, v_0_2_34]
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { VersionInfo } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
|
export const v_0_2_35 = VersionInfo.of({
|
||||||
|
version: '0.2.35:0',
|
||||||
|
releaseNotes: {
|
||||||
|
en_US:
|
||||||
|
"Exited positions now show the member's capital history up to the exit date: the collapsible History chart/table returns on the exited card, clipped at the exit — statements the eNAV keeps producing after the exit are recorded but never plotted, so the line ends at the exit instead of crashing to zero. History toggle now counts 'statements' (SPVs have event-driven statements, funds quarterly).",
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
up: async ({ effects }) => {},
|
||||||
|
down: async ({ effects }) => {},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// - content-hashed /assets/* are cache-first (immutable, safe forever)
|
// - content-hashed /assets/* are cache-first (immutable, safe forever)
|
||||||
// - /api/* is never cached
|
// - /api/* is never cached
|
||||||
// Bump CACHE on each release so old entries are purged.
|
// Bump CACHE on each release so old entries are purged.
|
||||||
const CACHE = 'ten31-portal-0.2.34'
|
const CACHE = 'ten31-portal-0.2.35'
|
||||||
|
|
||||||
self.addEventListener('install', () => self.skipWaiting())
|
self.addEventListener('install', () => self.skipWaiting())
|
||||||
|
|
||||||
|
|||||||
@@ -256,18 +256,13 @@ function CapitalBlock({
|
|||||||
() => [...accounts].sort((a, b) => a.as_of_date.localeCompare(b.as_of_date)),
|
() => [...accounts].sort((a, b) => a.as_of_date.localeCompare(b.as_of_date)),
|
||||||
[accounts],
|
[accounts],
|
||||||
);
|
);
|
||||||
const chartPoints: CapitalPoint[] = history.map((a) => ({
|
|
||||||
date: a.as_of_date,
|
|
||||||
value: a.ending_balance_cents,
|
|
||||||
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);
|
|
||||||
|
|
||||||
// A sold/transferred stake: the fund's books show $0 with no distribution, which is not a
|
// A sold/transferred stake: the fund's books show $0 with no distribution, which is not a
|
||||||
// loss. Show a quiet Exited badge and keep the documents; no balance, no gain/loss.
|
// loss. Show a quiet Exited badge and their capital journey UP TO the exit — statements
|
||||||
|
// the eNAV keeps producing after that date are recorded but never plotted, so the line
|
||||||
|
// ends at the exit instead of crashing to zero.
|
||||||
if (latest.exited_on) {
|
if (latest.exited_on) {
|
||||||
|
const clipped = history.filter((a) => a.as_of_date <= latest.exited_on!);
|
||||||
return (
|
return (
|
||||||
<div className={divider ? "mt-6 pt-5 border-t border-gray-100" : "mt-3"}>
|
<div className={divider ? "mt-6 pt-5 border-t border-gray-100" : "mt-3"}>
|
||||||
{label && <p className="text-sm font-medium text-gray-700">{label}</p>}
|
{label && <p className="text-sm font-medium text-gray-700">{label}</p>}
|
||||||
@@ -279,6 +274,7 @@ function CapitalBlock({
|
|||||||
<p className="text-sm text-gray-400 mt-2">
|
<p className="text-sm text-gray-400 mt-2">
|
||||||
This position was sold or transferred. Documents remain available below.
|
This position was sold or transferred. Documents remain available below.
|
||||||
</p>
|
</p>
|
||||||
|
<HistorySection history={clipped} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -314,7 +310,26 @@ function CapitalBlock({
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{history.length > 1 && (
|
<HistorySection history={history} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapsible capital-over-time chart + statement table. Collapsed by default so the portal
|
||||||
|
// opens clean; the investor expands the trend per fund. SPVs get sparse event-driven points,
|
||||||
|
// funds quarterly ones — the chart plots whatever statements exist.
|
||||||
|
function HistorySection({ history }: { history: CapitalAccount[] }) {
|
||||||
|
const [showChart, setShowChart] = useState(false);
|
||||||
|
if (history.length < 2) return null;
|
||||||
|
|
||||||
|
const chartPoints: CapitalPoint[] = history.map((a) => ({
|
||||||
|
date: a.as_of_date,
|
||||||
|
value: a.ending_balance_cents,
|
||||||
|
paidIn: a.contributions_cents,
|
||||||
|
distributions: a.distributions_cents,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -325,7 +340,7 @@ function CapitalBlock({
|
|||||||
<span className={`transition-transform ${showChart ? "rotate-90" : ""}`}>▸</span>
|
<span className={`transition-transform ${showChart ? "rotate-90" : ""}`}>▸</span>
|
||||||
History
|
History
|
||||||
<span className="normal-case font-normal text-gray-400">
|
<span className="normal-case font-normal text-gray-400">
|
||||||
· {history.length} quarters
|
· {history.length} statements
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
{showChart && (
|
{showChart && (
|
||||||
@@ -354,8 +369,6 @@ function CapitalBlock({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Bumped each release so the running build is visible in the UI.
|
// Bumped each release so the running build is visible in the UI.
|
||||||
// If the number shown in the app doesn't match the installed s9pk version,
|
// If the number shown in the app doesn't match the installed s9pk version,
|
||||||
// the new frontend isn't actually being served.
|
// the new frontend isn't actually being served.
|
||||||
export const APP_VERSION = "0.2.34";
|
export const APP_VERSION = "0.2.35";
|
||||||
|
|||||||
Reference in New Issue
Block a user