SPEC 013 — User Profile and Dashboard
| Field | Value |
|---|---|
| Status | DRAFT |
| Priority | P1 — Core Platform |
| Backend | equa-server/modules/persistence/ (entity definitions), equa-server/modules/api/src/endpoints/ (profile endpoints) |
| Frontend | equa-web/src/modules/profile/, equa-web/src/modules/user-dashboard/ |
1. Feature Purpose
The user profile and dashboard module provides the personal home experience for each Equa user. It aggregates holdings, organization memberships, personal information, wallet data, and account security settings into a single view. Users can edit their profile, manage blockchain wallet addresses, configure a 4-digit signing PIN, and view portfolio performance across all organizations they belong to.2. Current State (Verified)
2.1 User Dashboard Entry Point
| Detail | Value |
|---|---|
| File | equa-web/src/modules/user-dashboard/dashboard.tsx |
| Component | UserDashboard |
| Behavior | On load, checks user’s organizations; redirects to welcome if none, to single org if one, to org list if multiple |
| Data loader | loadOrganizations |
| State | Redux state.user |
2.2 Dashboard Component
| Detail | Value |
|---|---|
| File | equa-web/src/modules/profile/components/dashboard-component.tsx |
| Component | DashboardComponent |
| Sections | Holdings summary panels, Organizations grid, Personal Information panel, Address panel |
| Metrics | Total Investment, Total Current Value, Lifetime Growth (USD), Lifetime Growth (%) |
| Visualization | UserDashRainbowBar — color-coded bar chart of holdings by security |
| Organization display | Sorted alphabetically, capped at 3 in dashboard view |
| Color system | Per-holding colors loaded from GetColorsResponse, fallback palette: ['#F29D4A', '#EB5757', '#9B51E0', '#56CCF2', '#27AE60', '#F2C94C', '#2F80ED', '#FFFFFF'] |
2.3 Profile Form
| Detail | Value |
|---|---|
| File | equa-web/src/modules/profile/components/profile-form.tsx |
| Component | ProfileForm |
| Sections | Name & Profile Image, Personal Information, Address, Email, Profile Links, Website |
| Fields | firstName (required), lastName (required), photo (PNG/JPG/JPEG), phone, dateOfBirth (MM/DD/YYYY) |
| Profile links | LinkedIn, Facebook, Twitter, Instagram — all validated with isUrl |
| Address | Rendered via shared <Address> component |
2.4 Account Settings
| Detail | Value |
|---|---|
| File | equa-web/src/modules/profile/components/account-settings-form.tsx |
| Component | AccountSettingsForm |
| Purpose | 4-digit PIN creation for document signing |
| Validation | isPin() validator, numeric input with confirmation field |
| Input type | Protected input (protectedInputFieldComponent) |
2.5 Wallet Integration
| Detail | Value |
|---|---|
| File | equa-web/src/modules/profile/components/wallet-info.tsx |
| Component | WalletInfo |
| Categories | Wallet, Savings, Claimable, Debt, Networks, Protocols |
| Networks supported | Ethereum, Polygon, BSC |
| Net worth calculation | walletTotal + savingsTotal + claimTotal + vaultTotal - debtTotal |
| Token icons | Loaded from storage.googleapis.com/zapper-fi-assets/tokens/{network}/{address}.png |
| Other wallet components | WalletPort, WalletAddress, connect-wallet-modal, edit-wallet-address modal |
2.6 Pages Exported
| Page | File |
|---|---|
PortfolioPage | pages/portfolio-page.tsx |
NewHoldingPage | pages/new-holding-page.tsx |
EditHoldingPage | pages/edit-holding-page.tsx |
EditProfilePage | pages/edit-profile-page.tsx |
ViewProfilePage | pages/view-profile-page.tsx |
ViewAccountSettingsPage | pages/view-account-settings-page.tsx |
EditAccountSettingsPage | pages/edit-account-settings-page.tsx |
ViewCardPage | pages/view-card-page.tsx |
AddCardPage | pages/add-card-page.tsx |
EditCardPage | pages/edit-card-page.tsx |
UserDashboardPage | pages/user-dashboard.tsx |
WalletPortfolioPage | pages/wallet-portfolio.tsx |
WalletAddressBookPage | pages/wallet-address-book.tsx |
3. Data Model
Profiles
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| fullName | text | |
| homeAddress | Hash | FK to address table |
| website | text | |
| citext | ||
| dateOfBirth | date | |
| phone | text | |
| file | Hash | FK to files table (profile image) |
| links | jsonb | { linkedIn, facebook, twitter, instagram } |
Users
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| citext | UNIQUE | |
| username | text | |
| emailVerified | boolean | |
| acceptedTerms | boolean |
UserAccounts
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| pin | number | 4-digit signing PIN |
UserStates
| Column | Type | Constraints |
|---|---|---|
| user | uuid | PK, FK to Users |
| data | jsonb | Arbitrary user state blob |
Colors
| Column | Type | Constraints |
|---|---|---|
| user | uuid | Composite PK |
| target | text | Composite PK (holding/security ID) |
| value | text | Hex color code without # prefix |
WalletAddresses
| Column | Type | Constraints |
|---|---|---|
| address | Hash | PK |
| entity | Hash | FK to user or organization |
| globalName | text | ENS or equivalent display name |
| name | text | User-given label |
4. API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/v1/profile/:id | Session | Retrieve profile by user ID |
| PUT | /api/v1/profile/:id | Session | Update profile fields |
| POST | /api/v1/profile/:id/file | Session | Upload profile image (PNG/JPG/JPEG) |
| GET | /api/v1/account/:user | Session | Get account settings (PIN status) |
| PUT | /api/v1/account/:user | Session | Update account settings (PIN) |
| GET | /api/v1/colors/:user | Session | Get user color assignments |
| PUT | /api/v1/colors/:user | Session | Set color for a holding/security |
| GET | /api/v1/addresses/:entity | Session | List wallet addresses |
| POST | /api/v1/addresses/:entity | Session | Add a wallet address |
| PUT | /api/v1/addresses/:entity/:address | Session | Update wallet address label |
| DELETE | /api/v1/addresses/:entity/:address | Session | Remove wallet address |
| GET | /api/v1/holdings/expanded | Session | Get expanded holdings with portfolio data |
5. Frontend Components
Module: equa-web/src/modules/profile/
| Component | File | Purpose |
|---|---|---|
ProfileForm | components/profile-form.tsx | Form for editing name, image, phone, DOB, links, website |
AccountSettingsForm | components/account-settings-form.tsx | 4-digit PIN setup with confirmation |
DashboardComponent | components/dashboard-component.tsx | Main user dashboard with holdings, orgs, personal info |
WalletInfo | components/wallet-info.tsx | Net worth display with token categories |
WalletPort | components/wallet-port.tsx | Wallet portfolio display |
WalletAddress | components/wallet-address.tsx | Single wallet address display |
UserDashRainbowBar | components/user-dash-rainbow-bar.tsx | Color bar visualization of portfolio allocation |
Settings | components/settings.tsx | Settings page container |
ProfileModals | components/profile-modals.tsx | Modal dialogs for profile actions |
ProfileDetailsShared | components/profile-details-shared.tsx | Shared profile display layout |
HoldingForm | components/holding-form.tsx | Add/edit personal holding |
NewCardForm | components/new-card-form.tsx | Payment card entry form |
ConnectWalletModal | components/modal/connect-wallet-modal.tsx | Wallet connection flow |
EditWalletAddress | components/modal/edit-wallet-address.tsx | Edit wallet label modal |
SuccessModal | components/modal/success-modal.tsx | Confirmation after successful action |
AddressBookDrop | components/address-book-drop.tsx | Dropdown for saved addresses |
Module: equa-web/src/modules/user-dashboard/
| Component | File | Purpose |
|---|---|---|
UserDashboard | dashboard.tsx | Entry point — routes to welcome, org, or org list |
6. Business Rules
- Profile image must be PNG, JPG, or JPEG. Square images are recommended.
- PIN must be exactly 4 numeric digits. The confirmation PIN must match.
- Email field on profile is distinct from the login email on the Users table. Profile email is editable; login email requires verification.
- Wallet net worth is calculated as:
wallet + savings + claimable + vault - debt. - Color assignments persist per user and apply across the dashboard and portfolio views. If no color is assigned, the system cycles through the 8-color fallback palette.
- Dashboard redirects: zero orgs → welcome page; one org → that org’s page; multiple orgs → org list.
- Guest users see a limited dashboard with prompts to add personal information and organizations.
- Profile links (LinkedIn, Facebook, Twitter, Instagram, Website) are validated as URLs before save.
- Copy to clipboard is available for email and phone number fields on the dashboard.
7. Acceptance Criteria
- User can view their dashboard with holdings summary (Total Investment, Total Current Value, Lifetime Growth USD/%)
- User can edit profile: name, photo, phone, DOB, address, social links, website
- User can set and update their 4-digit signing PIN
- User can view, add, edit, and remove blockchain wallet addresses
- Wallet portfolio displays net worth with category breakdown (Wallet, Savings, Claimable, Debt)
- Dashboard shows up to 3 organizations alphabetically sorted
- Rainbow bar chart reflects portfolio allocation by security
- Color assignments persist across sessions
- Guest users see appropriate empty-state prompts
- Profile image upload accepts PNG/JPG/JPEG only
8. Risks
| Risk | Impact | Mitigation |
|---|---|---|
| Zapper-fi token icon URLs may be deprecated | Broken wallet token images | Cache icons locally or use a fallback icon service |
| PIN stored as plain number | Security exposure if DB is compromised | Hash PIN before storage (currently not hashed per code review) |
| No pagination on wallet addresses | Performance degradation with many addresses | Add pagination or virtual scrolling |
| Color palette limited to 8 fallback colors | Visual overlap with many securities | Extend palette or auto-generate distinct colors |
| Wallet data fetching from external blockchain APIs | Latency, availability | Implement caching layer with stale-while-revalidate |