Commit Graph

3 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 ad1d7f1815 Fix critical scrape-targeting bug and harden proxy, matching, and dashboard reliability
The highest-impact fix: run_research_job() only ever read entry['id'] from
confirmed competitors, silently discarding the Firecrawl-matched or
manually-swapped URL — every scrape fell back to the competitor's bare
root domain regardless of what was confirmed. This explains most of the
"unpredictable/wrong data" behavior seen against the old app.py. Threaded
confirmed_url through jobs.py -> analysis_service.py so the actually
confirmed page is what gets scraped.

Other fixes bundled in this pass:
- Proxy layer: Webshare rotating-gateway country code lowercased (was
  silently failing auth), Bright Data port corrected to 33335 (current
  cert), dedicated-IP candidate rotation added before falling back to the
  shared gateway/Bright Data escalation.
- Trip-intent matching: score_and_rank_urls() now hard-gates on
  destination keyword and excludes bare root-domain candidates, instead
  of letting duration/category signals alone push a wrong-country or
  homepage URL over the confidence threshold.
- force_refresh cache-bypass toggle added (opt-in, collapsed "Advanced
  options"), plus a "Skip this competitor" affordance for unmatched URLs.
- De-hardcoded "G Adventures" (the hackathon reference client) out of the
  extraction prompt — now uses the real tenant's company name.
- RunHistory/Apps-Script "latest results" queries filtered to
  triggered_by = "manual" so per-competitor content-hash bookkeeping rows
  no longer drown out real batch runs.
- Dashboard fixes: stuck-state bug on revisiting the Analyse page,
  AccountPage/BattlecardsPage restacked to single-column layout, Alerts
  moved to a header bell dropdown, invisible Export/Run Analysis icons
  (fill -> stroke), tour popup width mismatch, horizontal-scroll bug on
  cards (missing min-width: 0 in flex layout).
- New PocketBase migrations for scrape_runs (content_hash fields,
  tab_type, created_by_email) and products (departures, start_days).

335 backend tests passing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 17:33:05 -04:00
JasonFraser cc81038ba5 Initial commit: project spec, reference files, and dev plan
Adds CLAUDE.md (authoritative spec), the legacy app.py scraper and
approved dashboard mockup as reference material under z_starting_files/,
and the phased dev_plan.md for Phase 1+ implementation.
2026-06-30 16:07:46 -04:00