Commit Graph

3 Commits

Author SHA1 Message Date
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
JasonFraser 6a259a5012 Phase 2: refactor legacy app.py into core/industries/config structure
Implements CLAUDE.md Build Order Phase 2 — same scraping/extraction
logic as the legacy app.py, reorganized into the three-layer structure
and upgraded per §10's explicit instructions:
- core/scraper.py — playwright-stealth replaces the manual
  add_init_script stealth block; smart proxy (Webshare→Bright Data)
  wired via core/proxy_service.py with block-detection + auto-escalation;
  check_robots_txt() gate added per §30
- core/extractor.py — OpenRouter model cycling, merging the legacy
  JSON-repair fallback chain with the spec's env-driven free/paid
  model list and Sentry breadcrumb on paid-fallback use
- core/ai_fetch.py — AI web fetch fallback, ported unchanged
- industries/adventure_travel/{fields,prompt}.py ported unchanged;
  schema.py added as the future V2 custom-fields attachment point
- config/client.py — gspread Sheet write-back, ported unchanged
- services/analysis_service.py now sources CACHE_STALENESS_HOURS from
  core/scraper.py, its canonical location per §7

Dropped the legacy's commented-out Groq/Gemini alternates — OpenRouter
is the sole active provider and the dead code added nothing.

requirements.txt: relaxed playwright to >=1.55.0 and pinned
playwright-stealth==1.0.6 + setuptools<81 — Python 3.14 has no
prebuilt wheels for the originally-listed pins, and playwright-stealth
2.x replaced the stealth_async(page) API §10 documents verbatim with a
class-based one.

192 tests, 94% coverage. Also fixes a real bug surfaced by this phase:
several test helpers mutated core.extractor / industries.adventure_travel.prompt
via raw setattr instead of monkeypatch.setattr, silently corrupting
shared module state across tests once those modules became real.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 12:44:35 -04:00
JasonFraser f7aaafa533 Phase 1: data foundation — schema, repositories, services, routes, jobs
Implements CLAUDE.md Build Order Phase 1 end to end:
- PocketBase migration covering all 15 MVP-scope collections
- Repository layer (11 repos) with real PocketBase-backed + in-memory
  mock implementations for every collection
- Service layer (licence, trip-finder, battlecard, embedding, brief,
  alert, analysis, onboarding, billing, auth) — Phase 2 scraping/
  extraction dependencies are lazy-imported so this layer is fully
  testable ahead of the app.py refactor
- Flask API (api.py) covering every MVP route from the spec, with
  X-API-Key auth, Flask-Limiter rate limits, and structured JSON errors
- RQ job queue (jobs.py) with per-competitor failure isolation and
  duration-based Gotify alerting
- 142 tests, 95% coverage, 100% on licence validation / seat
  management / Stripe webhook dispatch per CLAUDE.md's testing floor

/sheet/push and /sheet/tabs intentionally return structured 501s until
the Apps Script Web App deployment exists (Phase 4).

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