# Mobile Shell Redesign — Design Spec
**Date:** 2026-05-03
**Scope:** `/mobile/*` shell, navigation, and per-page layouts on Pulse.
## 1. Goal & audience
Pulse's mobile shell exists for **managers on the go** — quick status checks,
triage decisions, and read-only awareness. It is not a full replacement for the
desktop app; pages where mobile editing isn't justified should link out to the
desktop equivalent.
Success means a manager can:
- See the state of the business at a glance (Dashboard).
- Triage and inspect tickets (Tickets).
- Read AR / invoice / payment status (Finance).
- Skim recent AI ticket analyses (Analyzer).
- Reach Engagement and sign-out without clutter on the primary nav.
## 2. Approach
**Rebuild `/mobile` in place.** Keep all existing `/mobile/*` route paths
(`/mobile/dashboard`, `/mobile/tickets`, `/mobile/tickets/[id]`,
`/mobile/finance`). Replace `/mobile/layout.tsx`, the four page files, and
delete `/mobile/nav` (its content moves into a Sheet drawer — see §3).
No new top-level routes. No parallel `/mobile-v2` directory. The existing pages
stay their canonical URLs throughout the redesign.
## 3. Navigation
### 3.1 Bottom tab bar (primary)
Four tabs, equal-width, fixed to the bottom of the viewport:
| Tab | Icon (lucide) | Route |
|------------|-------------------|---------------------|
| Dashboard | `LayoutDashboard` | `/mobile/dashboard` |
| Tickets | `Ticket` | `/mobile/tickets` |
| Finance | `DollarSign` | `/mobile/finance` |
| Analyzer | `Sparkles` | `/mobile/analyzer` |
Active state: `text-primary`; inactive: `text-muted-foreground`. Active
detection via `pathname.startsWith(href)`.
### 3.2 "More" Sheet drawer (secondary)
A fifth control on the bottom bar — `Menu` icon labelled **More** — opens a
shadcn `Sheet` (side="right", or "bottom" on phones; pick one and stay
consistent). The drawer **replaces the standalone `/mobile/nav` page entirely**.
Drawer contents, top to bottom:
1. **Mobile sections** — Engagement (`/mobile/engagement`, see §6.5).
2. **Full site** — link list to desktop pages that have no mobile view yet
(Quotes, Configuration Items, Backup Status, Ticket Digest, Admin / Sync).
Each link uses the `ExternalLink` icon hint to signal "leaves mobile shell".
3. **Account** — current user (avatar + email, read-only), then **Sign out**
(calls `signOut()` then `router.push('/auth/sign-in')`).
Delete `app/mobile/nav/page.tsx` after the drawer is in place.
## 4. PWA setup
- `public/manifest.json` — name "Pulse", short_name "Pulse", `display: "standalone"`,
`start_url: "/mobile"`, theme/background colors matching the dark and light
shells. Reference it from `app/layout.tsx` via ``.
- `viewport` meta in `app/layout.tsx` (or root metadata): include
`viewport-fit=cover` so the shell can paint behind the home indicator.
- **Safe-area insets** — both the sticky header and the bottom tab bar add
`env(safe-area-inset-top)` / `env(safe-area-inset-bottom)` padding. Use
Tailwind arbitrary values (`pt-[env(safe-area-inset-top)]`,
`pb-[env(safe-area-inset-bottom)]`) or a small utility class.
- **No service worker, no offline mode** in this iteration. Don't ship
`next-pwa` or a custom SW — deferred until a clear offline use-case lands.
- **Tablet** — `md:max-w-2xl mx-auto` wrapper is noted as a follow-up but is
**not** part of this spec's deliverable. Keep the current `max-w-lg`.
## 5. Shell (`app/mobile/layout.tsx`)
### 5.1 Header
Sticky top, `bg-background/95 backdrop-blur`, bottom border. Three slots:
- **Left:** Wulf mark (existing logo asset) + "Pulse" wordmark, linked to
`/mobile/dashboard`. Use the actual brand mark, not the text-only fallback.
- **Right (in order):**
- `Bell` icon button — **placeholder only**, no menu, no badge logic. Wire
to `aria-label="Notifications"` and an empty `onClick` so it's
keyboard-accessible. A future phase will turn this into a real list.
- Compact user avatar (`Avatar` from shadcn, size `h-7 w-7`). On tap,
opens the More drawer (so the avatar is the entry point alongside the
bottom-bar More button).
No page title in the header — pages render their own H1 in the content area.
### 5.2 Content area
`` between header and bottom nav, scrollable. Add bottom padding equal to
the bottom-nav height + safe-area inset so content doesn't hide under the bar.
### 5.3 Bottom nav
Fixed, full-width, `border-t bg-background`. Wraps in `max-w-lg mx-auto` so
content and nav share the same gutter. Five cells: 4 tabs (§3.1) + More (§3.2).
## 6. Pages
### 6.1 Dashboard (`/mobile/dashboard`)
Sections, top to bottom:
1. **2×2 KPI grid** — four primary metric cards. KPIs to choose from the
desktop dashboard's hero stats; final selection during build, but the layout
is fixed at 2×2 on phone widths.
2. **"Needs Attention" strip** — horizontal scroll of compact cards
surfacing items that require manager action (overdue tickets, failed
backups, stalled workflows). Each card opens its detail view directly.
3. **Backup / worker status** — compact status row showing the analyzer
worker, RMM worker, and backup-success-rate at a glance. Read-only; tap
opens the desktop admin page.
No charts on the mobile Dashboard in this iteration — recharts on small widths
isn't earning its weight.
### 6.2 Tickets (`/mobile/tickets`)
- **Collapsible filter strip** at top (`Collapsible` from shadcn, default
collapsed). When expanded: status, priority, queue, assigned-to-me toggle.
Filter state syncs to the URL query string so deep-linking works.
- **Priority-bar rows** — list rows have a left-edge color stripe by priority
(Critical/High/Medium/Low → red/orange/amber/slate). Body shows ticket #,
title, company, age, assignee. Single-tap opens detail.
- **Infinite scroll** — replace the current pagination with cursor-based
infinite scroll. Fetch in pages of ~25; trigger next page when the last row
enters the viewport (IntersectionObserver). Keep a "Load more" fallback button
for accessibility.
- **Detail page** — keep `/mobile/tickets/[id]/page.tsx` largely as-is; only
reskin the header to match the new shell (Wulf mark, breadcrumb back).
### 6.3 Finance (`/mobile/finance`)
Restyle of the existing page only. No new data, no new sections. Adopt the new
Card and typography scale, fix any spacing that breaks on small phones. If a
section currently relies on a wide table, swap it for a stacked list on mobile.
### 6.4 Analyzer (`/mobile/analyzer`) — NEW
**Read-only feed of recent AI ticket analyses.**
- List view: most-recent-first stream of analyses. Each row shows ticket #,
title, the analyzer's one-line summary, confidence badge, and a stage
indicator (Triage → Analyze → Deep Review). Tap opens a mobile summary view.
- Mobile summary view: renders **Summary**, **Next Step**, **Next Step
Rationale** (all already produced by the analyzer pipeline). Includes a "View
full analysis" link out to the desktop analyzer page.
- No editing, no re-run, no prompt tuning on mobile.
- Source data: existing `analyzer_analyses` rows via a new
`/api/mobile/analyzer/feed` endpoint (or reuse an existing list endpoint if
one already returns the right shape).
### 6.5 Engagement (`/mobile/engagement`) — NEW, accessed via More drawer
Engagement gets a **real mobile refactor**, not a thin adaptation. The desktop
page (`app/engagement/page.tsx`, ~1300 lines) and profile page
(`app/engagement/profile/page.tsx`, ~650 lines) both rely on wide tables, dense
charts, and modal patterns that don't translate to phone widths. Build the
mobile views from the same data sources but with phone-first layouts.
**Overview (`/mobile/engagement`):**
- Period selector chip row (today / 7d / 30d) — sticky just below the page H1.
- Summary cards stacked single-column (active users, total Graph hours, total
Autotask hours, hours-per-active-user). No 4-up grid; reading numbers across
a 4-up row on a phone is a non-starter.
- Per-employee list as stacked rows (avatar / initials, name, role, hours
bar). Sortable via a small control above the list (sort by hours, name,
utilization). Search input above the list.
- Charts: replace the desktop's wide bar/line charts with one compact "hours
trend" sparkline at the top of the list, period-scoped. No multi-series
chart on mobile in this iteration.
**User profile (`/mobile/engagement/profile?userId=...` or
`/mobile/engagement/[userId]`):**
- Reuses the existing profile data endpoints. Layout is single-column:
identity header → period selector → key metrics (compact) → activity
breakdown list → recent items.
- This **replaces** the desktop user-detail modal pattern on mobile —
tapping a row navigates to a real page, not a modal, so the shell's back
gesture works correctly.
- Pick exactly one of the two route shapes above during build (prefer the
`[userId]` segment form for shareable URLs); don't ship both.
Engagement is **not** on the bottom bar — it lives in the More drawer because
managers don't check it as often as the four primary surfaces.
## 7. Out of scope (explicit non-goals)
- Service worker, offline cache, push notifications.
- Tablet breakpoint (md:max-w-2xl) — noted, deferred.
- Real notification list behind the bell.
- Mobile editing on Engagement (user detail) or Analyzer (re-run, prompt edits).
- Charts / recharts on mobile Dashboard.
- Replacing or restyling the desktop pages reachable from the More drawer.
## 8. Build order
1. PWA scaffolding — `manifest.json`, viewport meta, safe-area utility.
2. New `app/mobile/layout.tsx` — header, More drawer (Sheet), 5-cell bottom nav.
Delete `app/mobile/nav/page.tsx` in the same change.
3. Dashboard restyle (2×2 grid, Needs Attention, status row).
4. Tickets restyle (filter Collapsible, priority-bar rows, infinite scroll).
Detail page header reskin only.
5. Finance restyle.
6. Analyzer page + `/api/mobile/analyzer/feed` endpoint.
7. Engagement Overview (`/mobile/engagement`) — phone-first layout from existing data sources.
8. Engagement user profile (`/mobile/engagement/[userId]`) — replaces the desktop modal pattern with a real mobile page.
Each step ships independently — no big-bang merge.