Mobile App — Dashboard (Tenant)
The Dashboard app ships on two platforms from a single codebase: web (Next.js) and mobile (React Native). The mobile app targets tenant users (clients) who need to review, approve, and manage their marketing pipeline on the go.
Audience: Dashboard/client users only. For the DM reviewer mobile app, see dm-mobile.md.
Technology
| Layer | Technology |
|---|---|
| Framework | Expo bare workflow + React Native 0.83 |
| Navigation | React Navigation v7 (bottom tabs + native stack) |
| State / data fetching | TanStack Query v5 |
| Token storage | expo-secure-store (iOS Keychain / Android Keystore) |
| Biometrics | expo-local-authentication |
| Push notifications | Expo Notifications (FCM + APNs) — Phase 4 |
| Auth | JWT (access + refresh tokens) stored in SecureStore |
| Real-time | SSE via react-native-sse |
| Offline cache | react-native-mmkv + TanStack Query persist |
Monorepo placement
apps/
dashboard/ # Next.js web app
dashboard-mobile/ # React Native app (Expo bare) — this app
dm-mobile/ # React Native app for DM reviewers (see dm-mobile.md)Shared code (between web and mobile):
packages/common— format utilities, badge colours
Not shared — diverges per platform:
- UI components (web: shadcn/ui + Tailwind; mobile: React Native StyleSheet)
- Navigation (web: Next.js App Router; mobile: React Navigation)
- Data fetching (web: Server Actions; mobile:
/mobile/v1Fastify endpoints)
Feature Parity
The mobile app exposes the same user actions as the web dashboard. No features are downgraded to view-only unless noted.
| Feature | Mobile behaviour |
|---|---|
| Approve context / strategy / plan | Full approve flow with confirmation |
| Request revision | Bottom sheet with free-text notes → AI regenerates |
| Strategy status change | Full status picker: Draft / Pending Review / Approved / Rejected |
| View timeline | Content | Timeline | Details tab strip on all 3 pipeline screens |
| Strategy version history | Version picker pill in header |
| Edit content | Not on mobile — revision request covers the use case |
| DM plan editing (goals/templates) | Not on mobile — DM portal is web-only |
Screen Availability
| Screen | Web dashboard ID | Mobile | Notes |
|---|---|---|---|
| Home | D1 | ✅ MVP | Simplified stat cards; deep-link into other screens |
| Marketing Strategy | D2 | ✅ MVP | Content | Timeline | Details tabs; full status picker; revision request |
| Goals | D3 | ✅ MVP | Progress bars, monthly targets; read-only |
| Leads | D4 | ✅ MVP | View + status changes |
| Deliverables | D5 | ✅ MVP | Read-only; period picker + expandable rows |
| Activities | D6 | ✅ MVP | List view only (no calendar/kanban/grouped) |
| Activity Detail | D6 detail | ✅ MVP | Read-only output view |
| Channels | D7 | ✅ MVP | View status; connect via system browser |
| Reports | D8 | 🔜 Phase 2 | Monthly performance snapshot |
| Report Detail | D9 | 🔜 Phase 2 | Full report document |
| Business Info / Vault | D10 | 🔜 Phase 2 | Subscriptions, invoices, email preferences |
| Users | D11 | ⬜ | Management task; not on mobile |
| Agent Chat | D12 | ✅ MVP | AI assistant; streaming responses via SSE |
| Client Context | D12a | ✅ MVP | Content | Timeline | Details; approve + revision request |
| Profile / Settings | D13 | ✅ MVP | Name, email, theme switcher (light/dark/system), sign out |
| Agent Detail | D14 | ⬜ | Too detailed for mobile |
| Social Posts | D15 | ✅ MVP | Status tabs; approve/reject |
| Social Post Detail | D15b | ✅ MVP | Full preview + approve/reject with notes |
| Approvals Queue | — | ✅ MVP | All pending approvals (blog, generic content) |
| Approval Review | — | ✅ MVP | Scrollable Markdown + pinned action bar |
| Deliverable Plan | — | ✅ MVP | Content | Timeline | Details; status-aware action bar; 4-status flow |
| Notifications | — | ✅ MVP | Bell icon; full history; mark-as-read |
| Blog Requests | — | ✅ MVP | View + approve |
| Social Requests | — | ✅ MVP | View + approve |
Deliverable Plan — 4-status flow
The plan goes through two approval stages before execution begins:
pending_review → DM (web portal) reviews and edits goals/templates
dm_approved → Client can see the plan and approve it (mobile or web dashboard)
approved → Plan locked; Activity Planner enqueued; execution begins
archived → Superseded plan (read-only history)Mobile behaviour per status:
| Status | Mobile shows |
|---|---|
pending_review | ”Your account manager is reviewing the plan” — auto-refresh every 15s |
dm_approved | Full goals + templates + Approve Plan button |
approved | Read-only + “Approved” badge + Timeline tab |
Navigation Structure (MVP)
Header (persistent across all tabs)
└── 🔔 Bell icon → Notifications screen (full history + mark-as-read)
Bottom Tab Bar
├── 🏠 Home (D1 — Home)
│ Stat cards deep-link into other screens
├── ✅ Approvals (Approvals Queue — blog + generic content)
│ └── /[id] (Approval Review)
├── 📋 Activities (D6 — Activities list)
│ └── /[id] (Activity Detail — read-only output)
├── 💬 Chat (D12 — Agent Chat with SSE streaming)
└── ⚙️ More
├── Client Context (D12a — Content|Timeline|Details tabs + approve + revision)
├── Strategy (D2 — Content|Timeline|Details + status picker + versions)
├── Deliverable Plan (Goals + Templates + Timeline; 4-status aware)
├── Social Posts (D15 list → D15b detail — separate from Approvals tab)
├── Blog Requests (view + approve)
├── Social Requests (view + approve)
├── Leads (D4 — view + status changes)
├── Channels (D7 — view + connect via system browser)
└── Profile (theme switcher, account info, sign out)API Layer
The mobile app calls a dedicated /mobile/v1 Fastify router. All routes require a valid tenant JWT.
Base URL: http(s)://<api-host>/mobile/v1
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /home | Dashboard stats (stage, goals, activities, approvals) |
| GET | /activities | Paginated activity list |
| GET | /activities/:id | Activity detail + output |
| GET | /approvals | Pending approvals queue |
| GET | /approvals/:id | Single approval item by ID |
| POST | /approvals/:id/resolve | Approve or reject an approval |
| GET | /context | Client context (status, content, version) |
| POST | /context/approve | Approve context → triggers strategy writer |
| POST | /context/revision | Request revision with notes → AI regenerates |
| GET | /context/logs | Context activity timeline |
| GET | /strategy | Strategy (status, content, version) |
| PATCH | /strategy/status | Update strategy status |
| POST | /strategy/revision | Request revision → AI regenerates |
| GET | /strategy/logs | Strategy activity timeline |
| GET | /strategy/versions | All strategy versions |
| GET | /deliverables | Deliverable plan + goals + templates + active period |
| POST | /deliverables/approve | Approve plan → enqueues activity planner |
| GET | /deliverables/logs | Deliverable plan activity timeline |
| GET | /goals | Active goals list |
| GET | /social-posts | Social posts list |
| GET | /leads | Leads list |
| PATCH | /leads/:id/status | Update lead status |
| GET | /channels | Connected channels |
| GET | /blog-requests | Blog posts awaiting client review |
| GET | /social-requests | Social posts awaiting client review |
| GET | /notifications | Notification history |
| PATCH | /notifications/read | Mark all notifications as read |
Push Notifications
| Event | Notification | Deep link target |
|---|---|---|
| New approval pending (high risk) | ”🔴 High-risk content needs your review” | Approval Review |
| New approval pending (normal) | ”📝 Content ready to review” | Approval Review |
| Agent completed a task | ”✅ SEO Specialist completed keyword research” | Activity Detail |
| Agent error / escalation | ”⚠️ Agent hit an error — tap to review” | Activity Detail |
| New social post ready | ”📱 Social post ready for approval” | Social Post Detail |
| Deliverable plan ready | ”📋 Your plan is ready to review” | Deliverable Plan |
Implementation:
@react-native-firebase/messagingon the client (not Expo Push) — direct FCM viagoogle-services.json- Firebase Admin SDK (
@leadmetrics/provider-firebase) on the server — uses Application Default Credentials - FCM token stored in
push_device_tokentable (unique per token, withuserId+tenantId) registerForPushNotifications()called after login;unregisterPushNotifications()called before logout- Email co-dispatch: every email sent by
apps/servers/notificationsalso fires push to the same recipients (best-effort, non-blocking) - Admin broadcast:
POST /admin/v1/push/broadcast— superadmin only, sends to all registered tokens - Tenant notify:
POST /admin/v1/push/tenants/:tenantId/notify— sends to all members of a tenant - Stale token pruning: tokens that fail FCM delivery are automatically deleted from the DB
Required setup:
- Firebase project → download
google-services.json→ place atandroid/app/google-services.json - Run
gcloud auth application-default loginas the Firebase project owner for ADC - Set
GOOGLE_CLOUD_PROJECT=<firebase-project-id>inapps/api/.envandapps/servers/notifications/.env— the project ID must match theproject_idingoogle-services.json
Mobile-Specific UX Notes
Action bar pattern
Status-aware action bar pinned to bottom of pipeline screens (Context, Strategy, Deliverable Plan):
pending_review→ Approve + Request Revision buttonsgenerating→ Loading indicator “AI is working…”approved→ Read-only badge
Revision request
Bottom sheet slides up with a text input for notes. Submitting triggers AI regeneration with the feedback as context.
Strategy version picker
Version pill button in the header (e.g. “v3 ▾”) opens a modal list of all past versions. Tapping a version loads its content.
Tab strip (Content | Timeline | Details)
All three pipeline screens (Context, Strategy, Deliverable Plan) use a 3-tab layout:
- Content — full markdown view of the document
- Timeline — chronological activity log (generated, approved, revised, status changes)
- Details — metadata (status, version, dates, cost estimate)
OAuth channel connect
“Connect Channel” opens the system browser — not an in-app WebView — to comply with OAuth provider restrictions (Google, LinkedIn).
Offline behaviour
TanStack Query caches last-fetched data for offline viewing. Approval submissions are queued locally and retried on reconnect.
Auth Flow
- First launch: email + password login (same credentials as web)
- On success: biometric (Face ID / fingerprint) prompt to enable quick unlock
- Subsequent opens: biometric unlock (no password re-entry)
- Token storage:
expo-secure-store(iOS Keychain / Android Keystore) - Token refresh: automatic on 401 — queued requests resume with new token
Development
cd apps/dashboard-mobile
pnpm start # Expo Metro dev server
pnpm android # Android Emulator (requires Android SDK)
pnpm ios # iOS Simulator (requires Xcode, macOS only)