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
@@ -1,5 +1,7 @@
|
||||
"""Document upload, listing, download, and deletion with per-account access control."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlmodel import Session, select
|
||||
@@ -58,7 +60,25 @@ def list_documents(
|
||||
query = query.where(Document.investor_user_id == investor_user_id)
|
||||
rows = session.exec(query.order_by(Document.created_at.desc())).all() # type: ignore[union-attr]
|
||||
visible = [d for d in rows if _can_view(user, d, session)]
|
||||
return [DocumentResponse.model_validate(d, from_attributes=True) for d in visible]
|
||||
|
||||
# Badge docs that arrived since the investor's previous visit. Badges show on the first
|
||||
# page-load after new documents arrive; the watermark advances at most once per 30 minutes
|
||||
# so rapid refetches don't rewrite the row. First ever visit (no watermark) badges nothing —
|
||||
# everything would be "new".
|
||||
seen_before = user.docs_seen_at if user.role == UserRole.investor else None
|
||||
if user.role == UserRole.investor:
|
||||
now = datetime.utcnow()
|
||||
if user.docs_seen_at is None or (now - user.docs_seen_at) > timedelta(minutes=30):
|
||||
user.docs_seen_at = now
|
||||
session.add(user)
|
||||
session.commit()
|
||||
|
||||
return [
|
||||
DocumentResponse.model_validate(d, from_attributes=True).model_copy(
|
||||
update={"is_new": seen_before is not None and d.created_at > seen_before}
|
||||
)
|
||||
for d in visible
|
||||
]
|
||||
|
||||
|
||||
@router.post("", status_code=201)
|
||||
|
||||
Reference in New Issue
Block a user