0.2.27-0.2.32: LP portal polish, brand palette, default investor logins

Cumulative checkpoint since 0.2.26:
- 0.2.27/28: entity valuation-history table; investor gain/loss = NAV +
  distributions vs paid-in
- 0.2.29: Reset Fund Partners (endpoint, Partners-tab button, CLI, action)
- 0.2.30: "Current Capital Balance" label, %-only gain/loss
- 0.2.31: Management Entities rename, Carry Vehicle type, chart
  distributions-line gate
- 0.2.32: LP-facing polish pass
  * Ten31 brand palette from the logo (navy/mint); orange retired
  * portfolio summary card across funds; gain labeled "net of paid-in"
  * whole-dollar headline figures; "History · N quarters" toggle
  * documents grouped by year with a "New" badge (users.docs_seen_at)
  * eNAV-created members start on default password with login enabled;
    enable-investor-logins CLI + StartOS action for existing accounts
  * password minimum raised to 8 chars; login help line (Portal@ten31.xyz)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jonathan Kirkwood
2026-07-03 08:40:40 -05:00
co-authored by Claude Opus 4.8
parent 69f12b0519
commit 4215d4478f
47 changed files with 935 additions and 111 deletions
+85
View File
@@ -414,12 +414,97 @@ const resetHoldingsAction = Action.withInput(
},
)
// ============================================
// Action: Reset Fund Partners
// ============================================
const resetPartnersInputSpec = InputSpec.of({
name: Value.text({
name: 'Fund Name',
description: 'Exact name of the fund whose partners to clear (see List Funds)',
default: '',
required: true,
placeholder: 'Low Time Preference Fund III, LP',
}),
})
const resetPartnersAction = Action.withInput(
'reset-partners',
{
name: 'Reset Fund Partners',
description:
"Remove every partner from a fund — deletes its investor capital-account statements and their access grants to it. Use to undo a wrong members import (e.g. another fund's roster loaded into this one). Investor accounts themselves are kept, and holdings/NAV are not affected (use Reset Fund Holdings for those).",
warning:
"This permanently deletes this fund's capital-account statements and removes investors' access to it. Investor accounts are kept. Re-import the correct roster afterward to repopulate.",
allowedStatuses: 'only-running',
group: null,
visibility: 'enabled',
},
resetPartnersInputSpec,
async () => ({ name: '' }),
async ({ input, effects }) => {
try {
const result = await runCli(effects, ['reset-partners', '--name', input.name], 'reset-partners-task')
if (result.exitCode !== 0) {
return errorResult(result.stderr?.toString() || 'Failed to clear partners')
}
return {
version: '1' as const,
title: 'Partners Cleared',
message: result.stdout?.toString() || `Cleared partners from ${input.name}.`,
result: null,
}
} catch (e: any) {
return errorResult(`Failed to clear partners: ${e.message || e}`)
}
},
)
// ============================================
// Action: Enable Investor Logins
// ============================================
const enableInvestorLoginsAction = Action.withoutInput(
'enable-investor-logins',
{
name: 'Enable Investor Logins',
description:
'Give every investor account that has no login yet the default password (Ten31Portal) and enable sign-in. Accounts that can already sign in are not touched; investors change their own password in the portal.',
warning: 'Every converted account gets the same well-known default password until the investor changes it.',
allowedStatuses: 'only-running',
group: null,
visibility: 'enabled',
},
async ({ effects }) => {
try {
const result = await runCli(effects, ['enable-investor-logins'], 'enable-investor-logins-task')
if (result.exitCode !== 0) {
return errorResult(result.stderr?.toString() || 'Failed to enable investor logins')
}
return {
version: '1' as const,
title: 'Investor Logins Enabled',
message: 'Send each investor their username; they sign in with the default password and change it.',
result: {
type: 'single' as const,
value: result.stdout?.toString() || 'Nothing to do.',
copyable: true,
qr: false,
masked: false,
},
}
} catch (e: any) {
return errorResult(`Failed to enable investor logins: ${e.message || e}`)
}
},
)
export const actions = sdk.Actions.of()
.addAction(createUserAction)
.addAction(resetPasswordAction)
.addAction(showAdminPasswordAction)
.addAction(listUsersAction)
.addAction(enableInvestorLoginsAction)
.addAction(deleteUserAction)
.addAction(dedupeAction)
.addAction(listFundsAction)
.addAction(resetHoldingsAction)
.addAction(resetPartnersAction)