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
+11 -7
View File
@@ -21,6 +21,7 @@ const TYPE_LABELS: Record<string, string> = {
spv: "SPV",
gp: "GP",
mgmt_co: "Mgmt Co",
carry: "Carry Vehicle",
};
export default function EntitiesList() {
@@ -62,7 +63,9 @@ export default function EntitiesList() {
}
const funds = entities.filter((e) => e.type === "fund" || e.type === "spv");
const gps = entities.filter((e) => e.type === "gp" || e.type === "mgmt_co");
const gps = entities.filter(
(e) => e.type === "gp" || e.type === "mgmt_co" || e.type === "carry",
);
return (
<div className="space-y-8">
@@ -71,7 +74,7 @@ export default function EntitiesList() {
{isWriter && (
<button
onClick={() => setShowForm(true)}
className="px-4 py-2 bg-gray-900 text-white text-sm rounded hover:bg-gray-800"
className="px-4 py-2 bg-brand-900 text-white text-sm rounded hover:bg-brand-800"
>
Add Entity
</button>
@@ -89,7 +92,7 @@ export default function EntitiesList() {
)}
<EntityTable title="Funds and SPVs" rows={funds} />
<EntityTable title="GP Entities and Management Companies" rows={gps} />
<EntityTable title="Management Entities" rows={gps} />
</div>
);
}
@@ -155,7 +158,7 @@ function AddEntityForm({
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-orange-500 focus:border-transparent"
className="w-full px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-accent-500 focus:border-transparent"
placeholder="Low Time Preference Fund I, LLC"
required
/>
@@ -171,6 +174,7 @@ function AddEntityForm({
<option value="spv">SPV</option>
<option value="gp">GP</option>
<option value="mgmt_co">Mgmt Co</option>
<option value="carry">Carry Vehicle</option>
</select>
</div>
<div className="flex gap-3">
@@ -202,7 +206,7 @@ function AddEntityForm({
<button
type="submit"
disabled={saving}
className="px-4 py-2 bg-gray-900 text-white text-sm rounded hover:bg-gray-800 disabled:opacity-50"
className="px-4 py-2 bg-brand-900 text-white text-sm rounded hover:bg-brand-800 disabled:opacity-50"
>
{saving ? "Creating..." : "Create"}
</button>
@@ -261,7 +265,7 @@ function EntityTable({ title, rows }: { title: string; rows: EntityRow[] }) {
return (
<th className={`px-4 py-3 font-medium text-gray-600 select-none ${a}`}>
<button onClick={() => sortBy(k)} className={`w-full hover:text-gray-900 ${a}`}>
{label}<span className="text-orange-500">{arrow(k)}</span>
{label}<span className="text-accent-500">{arrow(k)}</span>
</button>
</th>
);
@@ -345,7 +349,7 @@ function EntityTable({ title, rows }: { title: string; rows: EntityRow[] }) {
<td className="px-4 py-3">
<Link
to={`/entities/${row.id}`}
className="text-gray-900 font-medium hover:text-orange-600"
className="text-gray-900 font-medium hover:text-accent-600"
>
{row.name}
</Link>