Commit Graph

2 Commits

Author SHA1 Message Date
JasonFraser 965028e6c2 Add Excel export, rewrite Sheet pull to transposed layout, enrich battlecards
- SyncToSheet.vue: add "Download Excel" export alongside Copy rows,
  using exceljs (dynamically imported to avoid bundling ~940KB into
  AnalysePage) instead of xlsx (unpatched high-severity CVEs)
- appscript/Sync.gs: rewrite the Sheet-pull layout from column-per-field/
  row-per-competitor to the transposed layout (fields down column A,
  one column per competitor) — matches the dashboard's Copy rows/Excel
  export and the real legacy client reference sheet, which the old
  layout was never actually reconciled against. Row detection is now
  label-based and self-bootstraps a blank template on first pull.
- CLAUDE.md: correct §15 to document the transposed layout instead of
  the stale column-per-field description
- battlecard_service.py: replace two hardcoded placeholder strings
  (positioning_summary, recent_changes) with real computed data —
  actual price_history-derived recent changes, and richer product
  summaries (service levels, target audience, activities). Raise
  BATTLECARD_PRODUCT_LIMIT 10->50 and loosen prompt word caps so the
  richer input isn't compressed away
- BattlecardCard.vue: render pricing_observation, generated but never
  displayed before
- pb_migrations/009: fix battlecards.generated_at to stamp on update,
  not just create, so regenerated battlecards show an accurate
  "Updated" timestamp

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 23:08:06 -04:00
JasonFraser 11610b8b4a Phase 3: Vue 3 + Nuxt UI v4 dashboard
Implements CLAUDE.md Build Order Phase 3 — the 7-page dashboard,
matching the approved bettersight-dashboard.html mockup's design
system (dot-grid motif, dark sidebar, card shadows, badges) exactly.

Scope note: nuxt-ui-templates/dashboard-vue (the template CLAUDE.md
says to clone) ships entirely in TypeScript, which directly conflicts
with §18's "JavaScript only, no .ts files" rule. Per user decision,
converted the template to plain JS and rebuilt the dashboard chrome
(sidebar/topbar) as custom components matching the mockup instead of
Nuxt UI's own dashboard components, since the mockup is the approved
design (§25) and Nuxt UI's dashboard components have their own default
look that would fight against it.

- Core plumbing: pocketbase.js client, api_service.js (X-API-Key +
  JWT + 401-refresh interceptor), auth_store/analysis_store/
  notifications_store (Pinia), 5 repositories, explicit vue-router
  (v4, not v5 — matching CLAUDE.md's stack table) with an auth guard
- Shared components: programmatic DotGrid.vue, StatCard, EmptyState,
  CompetitorForm, SeatManager, BattlecardCard, SubscriptionCard,
  CsvUpload+ImportPreviewModal, FeedItem, AlertCard
- All 7 pages: Login (+Google OAuth, forgot password), ResetPassword,
  Account (subscription, upgrade prompt, seats, onboarding), Competitors
  (CSV bulk import), Analyse (full find-urls → confirm → poll → results
  → confidence → push-to-sheet flow with all 4 JobProgress terminal
  states), Activity (stat row, feed, alerts, onboarding tour trigger),
  Battlecards
- Shepherd.js 5-step onboarding tour ported verbatim from §21
- Sentry init (prod-only) + global error handler in main.js

Two backend gaps surfaced and fixed while building this:
- Added GET /account/me (dashboard had no way to learn its own
  tenant_id after login) and fixed GET /account/<tenant_id> to verify
  the session's tenant matches the requested one (previously any valid
  X-API-Key could read any tenant's account data)
- Added GET /alerts and PATCH /alerts/<id>/read (no route existed to
  list or acknowledge alerts, needed by the sidebar badge and
  ActivityPage's alerts panel)

Deliberately deferred: the mockup's Price Position sparkline/corridor
card has no defined data contract anywhere in CLAUDE.md — omitted
rather than backed with fabricated numbers. /sheet/push and
/sheet/tabs still return their Phase-1 structured 501 pending Phase 4.

Verified: full production build succeeds, dev server serves the login
page correctly (screenshotted, zero console errors), and the router's
auth guard correctly redirects an unauthenticated session to
/login?return=... with no console errors. Backend: 202 tests still
passing after the two route additions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 13:16:31 -04:00