# 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.