mirror of
https://github.com/luxfi/financial.git
synced 2026-10-30 16:55:56 +00:00
Brand-new sibling Next.js app at app/investor/ for investor.lux.financial,
mirroring the layout/auth/build idiom of @luxbank/console while moving to
Next 15 + React 19 + Tailwind 4 + TypeScript strict.
Foundation (this commit):
- Edge middleware (src/middleware.ts) — verifies the IAM-issued JWT against
iam.luxfi.io's JWKS via jose, scopes to investorID + tenantID, and
injects X-Lux-* identity headers on every (authed) request.
- Gateway client library (src/lib/gateway/{transport,captable,transfer,
treasury,broker,audit}.ts) — typed REST surface routed through the
operator gateway (api.lux.financial) with investor scope. Zero direct
backend exposure to the browser; non-2xx responses throw GatewayError.
- Shell (src/components/shell/{Shell,Sidebar,TopBar}.tsx) — top bar with
brand + account menu, sidebar with grouped nav across all G-22 routes,
responsive grid layout. Brand interop via @luxbank/brand.
- Shared UI primitives (src/components/{data,forms}/) — Card, DataTable,
Notice, Stat, PageHeader, Button, Select.
- Sign-in/out (src/app/(unauthed)/{signin,signout}/) plus API routes for
OIDC callback and magic-link request.
Stage 10.1 — Account dashboard (src/app/(authed)/dashboard/):
- Portfolio NAV summary card with 24h/7d/30d deltas.
- Positions table per offering with quantity, cost basis, NAV, unrealized
P&L, lockup remaining, restrictions label.
- Cost-basis method toggle (FIFO / LIFO / specific-lot) drives a server
re-fetch via ?method=.
- Pending settlement panel with T+0/T+1/T+2 bucket badges and per-trade
side/price/gross.
- Server-side data fetched in parallel via Promise.allSettled so a
single backend hiccup degrades gracefully.
Stage 10.10 — Audit log (src/app/(authed)/activity/):
- Filterable timeline of investor-initiated actions (auth, document,
trade, withdrawal, profile, beneficiary, kyc, comms).
- Filters: category, outcome, since, until — URL-driven.
- Per-entry: timestamp, action verb, target ref, description, IP, UA,
outcome, optional error.
- CSV export — same-origin /api/audit/export.csv proxies the gateway
endpoint with the caller's identity and streams text/csv back.
Tests (vitest + happy-dom + @testing-library/react) — 29 tests across 8
files, all passing:
- auth/session — header extraction + SessionError surface.
- auth/sign-in-url — OIDC authorize + signout URL construction.
- gateway/transport — URL prefixing, identity-header injection, JSON
body + content-type, 204 handling, GatewayError mapping.
- gateway/audit — CSV row escaping (RFC 4180-aware column counter).
- format — currency / percent / number / date / signClass.
- dashboard — render of PortfolioSummaryCard, PositionsTable,
PendingSettlementsCard against typed fixtures.
- activity — render of AuditTimeline (multiple categories + outcomes).
- middleware — matcher regex shape.
Build: pnpm install + pnpm -F @luxbank/investor build → green; 8 routes
compiled (/, /dashboard, /activity, /signin, /signout,
/api/auth/callback, /api/auth/magic-link, /api/audit/export.csv) +
40 kB middleware bundle.
Contract for other G-22 agents:
- Middleware sets X-Lux-Sub / X-Lux-Investor-Id / X-Lux-Tenant-Id /
X-Lux-Email / X-Lux-Name / X-Lux-Exp on every (authed) request.
Read via headers() in any server component / route handler, or use
the readSessionFromHeaders helper.
- gateway clients are server-only (transport.ts lazy-imports
next/headers). Client components must call through a server action or
API route — never import @/lib/gateway directly in 'use client' files.
- New backend sub-clients append to src/lib/gateway/index.ts; they
reuse callGateway() from transport.ts (one path-prefix per service
→ captable / transfer / treasury / broker / audit). Add the entry
to src/components/shell/Sidebar.tsx for nav.
Source-of-design: Lux-Prior-IP
Source-ref: 96510a0caf
Clean-Team-Member: dev-agent (G-22a foundation + dashboard + audit)
Signed-off-by: dev <[email protected]>
24 lines
690 B
JSON
24 lines
690 B
JSON
{
|
|
"compilerOptions": {
|
|
"target": "ES2022",
|
|
"lib": ["dom", "dom.iterable", "esnext"],
|
|
"allowJs": true,
|
|
"skipLibCheck": true,
|
|
"forceConsistentCasingInFileNames": true,
|
|
"strict": true,
|
|
"noEmit": true,
|
|
"esModuleInterop": true,
|
|
"module": "esnext",
|
|
"moduleResolution": "Bundler",
|
|
"resolveJsonModule": true,
|
|
"isolatedModules": true,
|
|
"jsx": "preserve",
|
|
"incremental": true,
|
|
"plugins": [{ "name": "next" }],
|
|
"paths": { "@/*": ["./src/*"] },
|
|
"types": ["vitest/globals", "@testing-library/jest-dom"]
|
|
},
|
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
"exclude": ["node_modules", "out", ".next"]
|
|
}
|