The soft reset to 77073ba inadvertently staged deletions of all phase 2
and 3 artifacts. This commit restores them from their source commits so
subsequent task commits build on the complete prior-phase foundation:
- components/mobile/{BottomNav,HeaderBar,KpiCardMobile,MoreDrawer,NeedsAttentionStrip,WorkerStatusRow}
- app/mobile/layout.tsx, dashboard/page.tsx, analyzer/page.tsx
- app/api/mobile/dashboard/route.ts
- All .planning/** files from phases 01-04
- CLAUDE.md, app/layout.tsx, app/styles/brand.css, public/manifest.json
- KpiCardMobile: phone-sized KPI card with label/value/caption; tone='attention' adds destructive left border
- NeedsAttentionStrip: horizontal-scroll strip of compact attention cards; renders null when items=[]
- WorkerStatusRow: 3-cell status row with emerald/amber/destructive status dots, each linking to desktop admin
- All three are pure presentational client components; no fetch, no recharts, no new state libraries
- Exports MobileDashboardResponse, KpiResponse, AttentionResponse, WorkerResponse interfaces
- Single Promise.all with 6 parameterless queries (KPI, failed backups, stalled workflows, analyzer, RMM, backup success)
- Ticket KPIs exclude out-of-scope companies via company_scope filter
- SLA breaches tone='attention' when value > 0
- Worker status rules: down if fail_1h>0 and in_flight=0, warn if fail_1h>0, otherwise ok
- Backup status: ok >= 95%, warn >= 80%, down otherwise
The planner agent's commit (d597415) was created from a worktree branch
based on master rather than the current feature branch HEAD. The soft-reset
safety check staged a deletion of ROADMAP.md (which exists in the feature
branch but not master), and that deletion was bundled into the agent's
commit. The plan files themselves were never added to the index.
Recovery: stage the on-disk ROADMAP.md (with the planner's Phase 3 updates)
plus the two new PLAN.md files, and commit them cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Standalone nav page replaced by MoreDrawer Sheet component
- Visiting /mobile/nav now returns Next.js 404 per DRAWER-06 spec
- No redirect needed — URL was never bookmarked-worthy
- Replace legacy 3-tab layout with new shell components from Plan 01
- Single useState(drawerOpen) shared between avatar and More button triggers
- SHELL-05: main content padded pb-[calc(theme(spacing.16)+env(safe-area-inset-bottom))]
- max-w-lg mx-auto container, no legacy /mobile/nav reference
- Minimal server component with coming soon card
- No data fetching — Phase 6 (ANL-01..06) owns the real implementation
- Sparkles icon consistent with BottomNav tab icon
- Resolves NAV-02: Analyzer tab in bottom nav has a valid route
Closes the orphaned PWA-04 requirement flagged by 01-VERIFICATION.md.
Single edit to app/styles/brand.css adds @utility pt-safe and
@utility pb-safe; production build (load-bearing for Tailwind 4
@utility syntax) compiled successfully and emitted both classes
into the production CSS bundle.
- Document manifest fields, viewport export shape, and verification results
- Record key decisions (theme_color #0075AD, background_color #FFFFFF,
metadata.manifest API choice, icon reuse)
- No deviations from plan
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add metadata.manifest = "/manifest.json" so Next.js emits
<link rel="manifest"> automatically (PWA-02)
- Add separate viewport export (Next 16 API) with viewportFit: "cover"
so Phase 2 can paint behind the home indicator (PWA-03)
- Include width: "device-width", initialScale: 1, and themeColor
light/dark pair for clean mobile rendering
- RootLayout body unchanged
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds company-level opt-out scoping so white-label / subcontract clients
(TTG, LEC, PER, VCF, Trivium Packaging, TNT Pizza, etc.) can be excluded
from Wulf's own dashboard KPIs and ticket analytics without affecting
per-company drill-down views.
- migration 082: company_scope table (opt-out; absent row = in scope)
- GET/PATCH /api/admin/company-scope[/companyId] — list + upsert
- /admin/client-scope — searchable company list with Switch per row,
type filter, and in/out scope filter; excluded rows are dimmed
- dashboard overview KPIs now exclude out-of-scope company tickets
- analyzer /tickets query excludes out-of-scope when no specific
client is selected (explicit per-company selection still works)
- "Client Scope" tile added to admin Tools & Data section
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Radix NavigationMenu uses a single shared viewport centered under
the full nav list. Opening a right-side submenu (Analyzer, Admin)
positioned the dropdown panel far to the left, so moving the mouse to
reach it crossed other triggers and closed it mid-way.
Switch all items-with-children to DropdownMenu, which positions each
panel directly under its own trigger. Keeps the same visual style
(navigationMenuTriggerStyle, ChevronDown, item layout with icon +
description) and active-state highlighting.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bucket the integration list under category headers (PSA, RMM,
Documentation, Security, …) following a canonical order, with
unrecognized categories appended after. Rows within a bucket sort
alphabetically by name.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The page was rendering its own sticky <header> on top of the global
AppNavigation, which obscured the top nav and duplicated the
ThemeToggle and a redundant "Back to Dashboard" link.
Replace with PageHeader (title, description, breadcrumbs, actions slot
holding Refresh / Bulk-admin / Export) and switch the main container to
the conventional px-6 py-6.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The reconciler can emit the same configuration_item id multiple times
when more than one match rule fires (e.g. exact_serial AND
hostname_in_company). The page rendered each occurrence as a separate
row keyed on ciId, producing React duplicate-key errors.
Dedupe client-side keeping the strongest confidence (exact_serial > mac
> hostname_in_company > other) and use the deduped count in the badge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>