feat: member portal Sprint 1 — dashboard with MemberShell layout and TowerUser profile fields

- Add avatar, hometown, currentLocation, interests, language, digestPreference, directoryVisible fields to TowerUser
- Migration: 20260617200000_add_tower_user_profile_fields
- GET /my/dashboard API endpoint with stats (groups, messages, consents)
- BFF route /api/my/dashboard
- MemberShell 3-column layout (220px nav + flex-1 main + 280px rail)
- Dashboard page: hero card, stat cards, group list, interests chips
- Sidebar returns null for /my paths (MemberShell owns the nav)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 15:44:58 +05:30
parent 622737fdca
commit 566690bd04
8 changed files with 283 additions and 75 deletions
@@ -0,0 +1,7 @@
ALTER TABLE "TowerUser" ADD COLUMN "avatar" TEXT;
ALTER TABLE "TowerUser" ADD COLUMN "hometown" TEXT;
ALTER TABLE "TowerUser" ADD COLUMN "currentLocation" TEXT;
ALTER TABLE "TowerUser" ADD COLUMN "interests" TEXT[] NOT NULL DEFAULT '{}';
ALTER TABLE "TowerUser" ADD COLUMN "language" TEXT NOT NULL DEFAULT 'en';
ALTER TABLE "TowerUser" ADD COLUMN "digestPreference" TEXT NOT NULL DEFAULT 'daily';
ALTER TABLE "TowerUser" ADD COLUMN "directoryVisible" BOOLEAN NOT NULL DEFAULT true;
+15 -8
View File
@@ -365,14 +365,21 @@ enum MemberOptOutReason {
// Hashed identity: SHA-256 of E.164 phone number (pepper via JWT_SECRET).
model TowerUser {
id String @id @default(cuid())
tenantId String
tenant Tenant @relation(fields: [tenantId], references: [id])
phoneHash String
jid String
displayName String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
id String @id @default(cuid())
tenantId String
tenant Tenant @relation(fields: [tenantId], references: [id])
phoneHash String
jid String
displayName String?
avatar String?
hometown String?
currentLocation String?
interests String[]
language String @default("en")
digestPreference String @default("daily")
directoryVisible Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
consents ConsentRecord[]
optOuts MemberOptOut[]