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:
co-authored by
Claude Opus 4.8
parent
69f12b0519
commit
4215d4478f
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { v_0_2_26 as current } from './v_0_2_26'
|
||||
export { v_0_2_32 as current } from './v_0_2_32'
|
||||
import { v_0_1_0 } from './v_0_1_0'
|
||||
import { v_0_2_0 } from './v_0_2_0'
|
||||
import { v_0_2_1 } from './v_0_2_1'
|
||||
@@ -25,4 +25,10 @@ import { v_0_2_22 } from './v_0_2_22'
|
||||
import { v_0_2_23 } from './v_0_2_23'
|
||||
import { v_0_2_24 } from './v_0_2_24'
|
||||
import { v_0_2_25 } from './v_0_2_25'
|
||||
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]
|
||||
import { v_0_2_26 } from './v_0_2_26'
|
||||
import { v_0_2_27 } from './v_0_2_27'
|
||||
import { v_0_2_28 } from './v_0_2_28'
|
||||
import { v_0_2_29 } from './v_0_2_29'
|
||||
import { v_0_2_30 } from './v_0_2_30'
|
||||
import { v_0_2_31 } from './v_0_2_31'
|
||||
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]
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const v_0_2_27 = VersionInfo.of({
|
||||
version: '0.2.27:0',
|
||||
releaseNotes: {
|
||||
en_US:
|
||||
'Entity overview now shows a full Valuation history — every quarter on record with its NAV, status, and signed date — instead of just the latest quarter. Investor portal now shows, per fund/SPV, gain/loss (amount and % vs paid-in) on the capital account and the % of commitment distributed.',
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {},
|
||||
down: async ({ effects }) => {},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const v_0_2_28 = VersionInfo.of({
|
||||
version: '0.2.28:0',
|
||||
releaseNotes: {
|
||||
en_US:
|
||||
"Investor gain/loss now measures total value (current NAV + distributions received) against paid-in capital, so an LP who has taken distributions no longer shows a false loss.",
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {},
|
||||
down: async ({ effects }) => {},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const v_0_2_29 = VersionInfo.of({
|
||||
version: '0.2.29:0',
|
||||
releaseNotes: {
|
||||
en_US:
|
||||
"New 'Reset Fund Partners' service action (and a 'Clear all partners' button on a fund's Partners tab) removes all of a fund's capital-account statements and investor access grants — for undoing a wrong members import, e.g. one fund's roster loaded into another. Investor accounts and holdings/NAV are left intact. Reset Fund Holdings still only clears holdings; this covers the partner side.",
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {},
|
||||
down: async ({ effects }) => {},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const v_0_2_30 = VersionInfo.of({
|
||||
version: '0.2.30:0',
|
||||
releaseNotes: {
|
||||
en_US:
|
||||
"Investor portal: the capital block now reads 'Current Capital Balance' and shows only the percentage gain (green) or loss (red) beneath it — the redundant dollar 'gain/loss vs paid-in' line was removed.",
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {},
|
||||
down: async ({ effects }) => {},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const v_0_2_31 = VersionInfo.of({
|
||||
version: '0.2.31:0',
|
||||
releaseNotes: {
|
||||
en_US:
|
||||
"Entities view: the 'GP Entities and Management Companies' section is now 'Management Entities' and gains a new 'Carry Vehicle' entity type (with both Partners and Assets tabs) for carry vehicles like Ten31 EP LLC. Investor capital chart: the Distributions line only appears once distributions have actually been made.",
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {},
|
||||
down: async ({ effects }) => {},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const v_0_2_32 = VersionInfo.of({
|
||||
version: '0.2.32:0',
|
||||
releaseNotes: {
|
||||
en_US:
|
||||
"Investor portal polish: a portfolio summary card totals commitment, paid-in, distributions and current balance across funds; gain/loss is labeled 'net of paid-in'; headline figures show whole dollars; documents group by year with a 'New' badge since the investor's last visit. Ten31 brand palette (navy/mint from the logo) replaces the orange accents. New members from the eNAV import now start with the default password 'Ten31Portal' (login enabled), and a new 'Enable Investor Logins' action converts existing no-login accounts. Password changes now require at least 8 characters. Login page shows a Portal@ten31.xyz contact line.",
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {},
|
||||
down: async ({ effects }) => {},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user