Bundles several in-progress efforts that were sitting uncommitted:
- User queue-preferences (migration 087, API route, popover component)
- QBO invoice soft-delete (migration 088) and AR diagnostics route
- Dashboard/mobile engagement route and page adjustments
- Docker Compose log-rotation config
- One-off ticket/RMM investigation scripts (scripts/)
- Planning docs: phase verification/pattern notes, mobile shell design spec
- .gitignore: exclude local scratch financial/inventory data and Claude Code
worktree/local-settings runtime state (never meant for version control)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
- app/mobile/finance/page.tsx: thread tz through fmtDate, setLastSync, monthLabel — 3 formatter callsites now pass timeZone
- app/mobile/tickets/[id]/page.tsx: thread tz through fmtDate (5 callsites) and TimelineCard prop
- All toLocaleDateString / toLocaleString calls in both files now render in user.timezone, not browser local zone
- Resolves TZ-02 on the directly-reported bug surface (mobile finance + ticket detail)
- Replace AR Hero gradient + standalone Revenue YTD with 4 KpiCardMobile tiles (2x2 grid)
- Drop bar chart; add monthly-revenue stacked list (D-09 / DASH-04 precedent)
- Add shadcn Collapsible for Open Invoices and Recent Payments (D-15, D-16)
- Aging row uses locked amber/orange/destructive palette (D-08)
- Top AR by Customer rendered as stacked list with proportion bars (D-07)
- toast.success + toast.error on sync outcomes (D-17, D-18)
- FinanceSkeleton on initial load; destructive retry card on error (D-17, D-18)
- D-19 empty state when total_ar===0 and open_invoices.length===0
- D-23: no page H1; header controls row with aria-labels (D-14)
- No font-medium, no font-bold, no raw red/yellow/green Tailwind classes
- DB: inserted qbo-sync-2am (0 2 * * *) and qbo-sync-4pm (0 16 * * *) schedules
- Mobile finance: 'Sync QBO' button triggers POST /api/qbo/sync incremental,
polls /api/qbo/sync GET until lastSync timestamp changes (max 90s),
then reloads finance data
- Shows last sync timestamp below page title
- Separate refresh-only button (↻) for quick display refresh without re-syncing
- Sync status message shown during polling