wulf-pulse/.planning/PROJECT.md

12 KiB
Raw Blame History

Pulse Mobile Shell Redesign

What This Is

A ground-up redesign of Pulse's /mobile/* shell — the manager-on-the-go view of the existing Pulse PSA dashboard. It replaces the current mobile layout, swaps the standalone /mobile/nav page for a Sheet drawer, restyles Dashboard / Tickets / Finance, and adds two new mobile surfaces (Analyzer feed, Engagement). Built on the existing Pulse codebase — same routes, same data, phone-first layouts.

Core Value

A manager can open Pulse on their phone and, in under 30 seconds, see the state of the business and triage tickets — without ever needing to switch to desktop for read-only awareness.

Requirements

Validated

  • /mobile route shell with sticky header and bottom nav — existing
  • /mobile/dashboard, /mobile/tickets, /mobile/tickets/[id], /mobile/finance routes — existing
  • ✓ Standalone /mobile/nav page (to be replaced by drawer) — existing
  • ✓ Authentication, sync workers, analyzer pipeline, Engagement data sources — existing in desktop app and reused here
  • ✓ PWA scaffolding (manifest, viewport-fit=cover, pt-safe/pb-safe utilities) — Validated in Phase 1: PWA Scaffolding (PWA-01..04)
  • ✓ New /mobile/layout.tsx shell — sticky header (WulfMark + Bell placeholder
    • avatar), 5-cell bottom nav (Dashboard/Tickets/Finance/Analyzer + More), shared MoreDrawer Sheet — Validated in Phase 2: Mobile Shell + More Drawer (SHELL-01..06, NAV-01..03)
  • ✓ More drawer (right Sheet) replaces /mobile/nav — Mobile sections, Full site links (with ExternalLink hint), Account + Sign out — Validated in Phase 2: Mobile Shell + More Drawer (DRAWER-01..06)
  • ✓ Dashboard restyle — 2×2 KPI grid, Needs Attention strip (horizontal scroll), worker/backup status row, no charts. Backed by single /api/mobile/dashboard endpoint returning kpis/needsAttention/workers. Validated in Phase 3: Dashboard Restyle (DASH-01..04)
  • ✓ Tickets restyle — collapsible URL-synced filter strip, priority-stripe rows, cursor-based infinite scroll (~25/page) via IntersectionObserver, Load more fallback, detail page header reskinned. Backed by /api/mobile/tickets with cursor pagination + exported MobileTicket / MobileTicketListResponse types. Validated in Phase 4: Tickets Restyle (TICK-01..07)
  • ✓ Finance restyle — /mobile/finance adopts new Card + typography scale, KPI 2×2 grid, 3-cell aging row, Collapsible invoice/payment lists with FinanceRow, stacked Top Customers + monthly revenue (no chart). API/data shape unchanged. Validated in Phase 5: Finance Restyle (FIN-01..02)
  • ✓ Analyzer feed (NEW) — /mobile/analyzer read-only most-recent-first stream of completed AI ticket analyses, with cursor-based infinite scroll (~25/page), kiosk_settings company scoping, and a phone-friendly summary view at /mobile/analyzer/[id] (Summary / Next Step / Next Step Rationale + "View full analysis" link to desktop). New endpoint /api/mobile/analyzer/feed; existing /api/analyzer/analyses/[id] reused for detail. No editing, no re-run, no prompt-tuning on mobile. Validated in Phase 6: Analyzer Feed (ANL-01..06)
  • ✓ Engagement overview (NEW) — /mobile/engagement phone-first refactor reachable from the More drawer (NOT the bottom bar). Sticky 3-chip period selector (7d/30d/90d), 4 stacked summary cards (active users / total Graph hours / total Autotask hours / hours-per-active-user), single-series SVG sparkline (no recharts), sortable per-employee list (Hours/Name/Utilization) with client-side search and IntersectionObserver infinite scroll. Two new endpoints: /api/mobile/engagement/summary (totals) and /api/mobile/engagement/trend (daily-hours time-series). Existing /api/engagement/users reused as-is. Read-only on mobile. Validated in Phase 7: Engagement Overview (ENG-01..05, ENG-09)
  • ✓ Per-user IANA timezone — user.timezone column (default 'UTC'), session.user.timezone exposed via Better Auth additionalFields, GET/PUT /api/me/timezone (IANA-validated; allowlist includes UTC/Etc/UTC/GMT), shared getUserTimezone(session) server helper, shared useUserTimezone() client hook (single source of truth — 52 client files migrated), all 6 affected read paths (/api/dashboard/{overview,trends}, /api/mobile/{dashboard,finance,engagement/summary,engagement/trend}) switched to (value AT TIME ZONE 'UTC') AT TIME ZONE $tz day-boundary math. /api/mobile/finance hardened with requireAuth(). Storage UTC unchanged. Engagement _snapshots-derived metrics keep UTC bucketing (documented carve-out). Validated in Phase 7.1: User Timezone Fix (TZ-01..04)
  • ✓ Engagement user profile (NEW) — /mobile/engagement/[userId] real-page profile replacing the desktop modal pattern. Server-side photo proxy at /api/mobile/engagement/user/[userId]/photo (MsGraphClient.getUserPhotoBytes(), Cache-Control: private, max-age=3600, neutral 404/502/503). Client page composes six new components — Skeleton, Header (photo with onError → initials fallback), MetricGrid (2×2), Breakdown (Time/Communication/ Meetings with conditional Zoom + after-hours), RecentEntries, RecentMeetings. Reuses /api/engagement/user/[userId] verbatim. Period chips (D7/D30/D90) refetch on change. Scroll restoration on back is partial (sessionStorage shim added but does not restore reliably; accepted as a known limitation). Validated in Phase 8: Engagement User Profile (ENG-06..08)
  • ✓ User profile & preferences (NEW) — /mobile/profile page with four sections: Timezone (Combobox + 400ms debounce + live clock), Theme (light/dark/system radio rows persisted to user.theme with cross-device sync via ThemeSessionBridge + desktop ThemeToggle write-through), Notifications matrix (event_key × channel_type, opt-in default), and Channels (personal Teams webhook + ntfy topic with QR code, per-channel test-send, inline 400-error display). Three new migrations: 084 (user.theme), 085 (notification_channels.owner_user_id + partial unique index), 086 (notify_event_keys + user_event_subscriptions). API surface at /api/me/{theme,channels,channels/[type],channels/[type]/test,notification-subscriptions} all session-scoped via requireAuth(); shared lib/services/personal-channels.ts centralizes validation, ntfy topic minting, and test-send. Notify pipeline step extended with routing: explicit | event-key | hybrid and 5 fallback reasons (user_muted, event_key_unknown, recipient_unresolvable, channel_missing, channel_send_failed); fully backward-compatible with existing global notify configs. Admin surfaces: channels page Owner column
    • filter, event-keys CRUD at /admin/workflow/event-keys, executions browser at /admin/workflow/executions with "Show only fallbacks" filter. Validated in Phase 9: User Profile & Preferences (PROF-01..04, TZ-CHOOSER-01..02, THEME-01..05, CHAN-01..07, SUB-01..04, ROUTE-01..07)

Active

No active hypotheses — all planned milestone phases validated.

Out of Scope

  • Service worker / offline cache / push notifications — deferred until a clear offline use-case lands
  • Tablet breakpoint (md:max-w-2xl) — noted as follow-up, keep max-w-lg
  • Real notification list behind the Bell icon — placeholder only this iteration
  • Mobile editing on Engagement (user detail) or Analyzer (re-run, prompt edits) — read-only on mobile by design
  • Charts / recharts on the mobile Dashboard — not earning their weight on small widths
  • Restyling or replacing the desktop pages reachable from the More drawer — desktop pages stay as they are

Context

  • Brownfield project. Pulse is a Next.js 16 + React 19 PSA dashboard for Wulf Consulting. Existing codebase fully mapped at .planning/codebase/*.md. See CLAUDE.md, ARCHITECTURE.md, DESIGN.md in repo root.
  • Audience. Wulf Consulting managers using Pulse on iOS/Android during the workday — the shell is for status checks and triage, not full editing.
  • Mobile is not a replacement for desktop. Pages where mobile editing isn't justified link out via the More drawer with an ExternalLink hint.
  • Source of truth for this work. docs/superpowers/specs/2026-05-03-mobile-shell-design.md — 8-section spec with explicit build order. Phases should follow it.
  • Existing mobile shell is small. Current app/mobile/layout.tsx has 3 bottom-nav tabs and a /mobile/nav page. This redesign rebuilds it in place — no /mobile-v2, no parallel routes.
  • Engagement and Analyzer pages on desktop are large (~1300 + ~650 lines for engagement; analyzer pipeline already has a desktop UI). Mobile surfaces reuse the data sources but build phone-first layouts from scratch.

Constraints

  • Tech stack: Next.js 16 App Router, React 19, Tailwind 4, shadcn/ui — match existing Pulse conventions (no new state libraries, no SWR/react-query, no ORM, no Zod in API routes unless required)
  • Routes: Keep all existing /mobile/* paths. Replace files in place. No new top-level routes outside /mobile/.
  • Build order: Each spec step ships independently — no big-bang merge. Phase boundaries should let each step land on master cleanly.
  • No service worker / no offline: don't introduce next-pwa or a custom SW in this iteration.
  • Auth: existing Better Auth + middleware handles /mobile/*. No new auth surface.
  • Data sources: reuse existing endpoints where possible; add /api/mobile/* only when an existing list endpoint doesn't return the needed shape (e.g., analyzer feed).

Key Decisions

Decision Rationale Outcome
Rebuild /mobile in place (no /mobile-v2) Spec §2 — keep canonical URLs, avoid parallel maintenance burden — Pending
Bottom nav = 4 tabs + More (5 cells), Engagement in More Spec §3.1, §6.5 — managers don't check Engagement as often as the four primary surfaces — Pending
Ship each spec step as its own phase Spec §8 — independent ship reduces merge risk and keeps reviews focused — Pending
Bell icon is a placeholder only Spec §5.1 — real notification list deferred; keep keyboard-accessible button so future phase can wire it — Pending
No service worker in this iteration Spec §4 — defer until a clear offline use-case lands — Pending
Engagement mobile is a real refactor, not a thin adaptation Spec §6.5 — desktop's wide tables and modals don't translate; build phone-first from same data sources — Pending
Mobile user-detail is a page, not a modal Spec §6.5 — back gesture needs real navigation history — Pending

Evolution

This document evolves at phase transitions and milestone boundaries.

After each phase transition (via /gsd-transition):

  1. Requirements invalidated? → Move to Out of Scope with reason
  2. Requirements validated? → Move to Validated with phase reference
  3. New requirements emerged? → Add to Active
  4. Decisions to log? → Add to Key Decisions
  5. "What This Is" still accurate? → Update if drifted

After each milestone (via /gsd-complete-milestone):

  1. Full review of all sections
  2. Core Value check — still the right priority?
  3. Audit Out of Scope — reasons still valid?
  4. Update Context with current state

Last updated: 2026-05-11 — Phase 9 complete (User Profile & Preferences)