Commit Graph

21 Commits

Author SHA1 Message Date
Deeman
77d801e326 feat: auto-create Resend audiences per blueprint
Removes RESEND_AUDIENCE_WAITLIST env var. capture_waitlist_email() now
derives audience name from request.blueprints[0] (e.g. waitlist-auth,
waitlist-suppliers), lazily creates it via Resend API on first signup,
and caches the ID in a new resend_audiences table. Zero config beyond
RESEND_API_KEY — adding @waitlist_gate to any new blueprint auto-creates
its audience on first use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 23:41:45 +01:00
Deeman
05b7397687 Refactor waitlist mode with decorator pattern + helper function
Extract duplicated waitlist logic into two reusable abstractions:

1. @waitlist_gate(template, **context) decorator
   - Intercepts GET requests when WAITLIST_MODE=true
   - Passes through POST requests for form handling
   - Evaluates callable context at request time

2. capture_waitlist_email(email, intent, plan, email_intent) helper
   - Idempotent DB insertion (INSERT OR IGNORE)
   - Enqueues confirmation email only for new signups
   - Adds to Resend audience with silent error handling
   - Supports separate email_intent for supplier messaging

Applied to auth, suppliers, and planner routes, reducing ~80+ lines
of duplicated code. Added comprehensive tests (55 total, all passing)
and documentation (25KB guide in docs/WAITLIST.md).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 23:04:21 +01:00
Deeman
b108a53ef3 updates 2026-02-19 19:16:23 +01:00
Deeman
536eefffdb add Basic tier, monthly/yearly billing, and supplier detail redesign
- New Basic tier (€39/mo or €349/yr): verified directory listing with
  enquiry form, contact sidebar, services checklist, social links, no leads
- Monthly + yearly billing for all paid tiers; yearly defaults selected
  in signup wizard with CSS-only price toggle (no JS state)
- Redesigned supplier_detail.html: navy hero with court-grid pattern,
  two-column body+sidebar for Basic+, tier-adaptive CTA strips
- Supplier enquiry form: HTMX-powered, rate-limited 5/24h, email relayed
  via worker task; supplier_enquiries table tracks all submissions
- New supplier columns: services_offered, contact_role, linkedin_url,
  instagram_url, youtube_url (migration 0012)
- _lead_tier_required decorator restricts lead feed to growth/pro;
  Basic users see overview + listing tabs only
- Admin: basic tier in dropdown, new fields in form/detail + enquiry count
- setup_paddle.py: adds 4 new products with yearly interval support
- Webhook handler strips _monthly/_yearly suffixes, Basic gets 0 credits
  and is_verified=1; existing growth/pro webhooks unchanged
- Sort order: pro > growth > basic > free
- 572 tests pass (+2 new for basic tier + yearly webhook variants)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 15:03:21 +01:00
Deeman
07c7e61049 refactor migration system: single source of truth via replay
Eliminated dual-maintenance of schema.sql + versioned migrations.
All databases (fresh and existing) now replay migrations in order
starting from 0000_initial_schema.py. Removed _is_fresh_db() and
the fresh-DB fast-path that skipped migration execution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:23:28 +01:00
Deeman
0b8350c770 fix webhook crashes on null custom_data, migrate to SDK Verifier
Paddle sandbox sends lifecycle events (subscription.updated, etc.) with
"custom_data": null. The .get("custom_data", {}) default only applies
when the key is missing, not when the value is explicitly null, causing
AttributeError on the next .get() call. Also guarded subscription.activated
to skip when user_id is absent (was inserting user_id=0 → FK violation).

Replaced manual HMAC verification with paddle_billing.Notifications.Verifier
via a lightweight _WebhookRequest wrapper satisfying the SDK's Request Protocol.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:43:40 +01:00
Deeman
df8a747463 add credit system and supplier webhook test suites, remove dead test
24 tests for credits.py (balance, spend, unlock, refill, ledger) and
10 integration tests for supplier webhook handlers (credit packs, sticky
boosts, subscription activation, business plan purchase). Removed
test_mobile_nav_no_overflow which never asserted its JS result.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:02:09 +01:00
Deeman
4e61e9b1ab fix broken webhook signature verification and stale billing tests
Webhook handler called Verifier().verify() with raw bytes instead of a
request object, so signature verification always failed. Replaced with
manual HMAC check matching Paddle's ts=...;h1=... format. Updated tests
to produce correct signature format, mock the SDK instead of httpx for
manage/cancel routes, and expect JSON for overlay checkout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:49:23 +01:00
Deeman
61bf855103 add programmatic SEO content engine with article generation pipeline and tests
Adds a complete content generation system for producing SEO articles
at scale with embedded financial scenario widgets. Includes DB schema
(published_scenarios, article_templates, template_data, articles with
FTS5), bulk generation pipeline with staggered publish dates, admin CRUD
for templates/scenarios/articles, public markets hub with HTMX filtering,
catch-all article serving from pre-rendered static HTML, sitemap
integration, and 94 pytest tests covering the full stack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:40:11 +01:00
Deeman
b99cd3c7d8 fix quote form state loss, admin errors, UI polish; add seed data and playwright tests
Quote wizard _accumulated hidden inputs used double-quote attribute delimiters
which broke on tojson output containing literal " characters — all step data was
lost by step 9. Admin dashboard crashed on credit_ledger queries referencing
wrong column names (amount/entry_type vs delta/event_type). Also: opaque nav bar,
pricing card button alignment, newsletter boost replaced with card color boost,
admin CRUD for suppliers/leads, dev seed data script, and playwright quote wizard
tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:37:13 +01:00
Deeman
6a10f82b5d add double opt-in email verification for quote requests
Guest quote submissions now require email verification before the lead
goes live. The verification click also creates a user account and logs
them in. Logged-in users submitting with their own email skip verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:02:32 +01:00
Deeman
e0563d62ff polish nav, planner UX, country pills, and dev magic link
- Nav: Zillow-style centered logo, solid blue Sign In button
- Planner: center app at 72rem, center wizard steps/header/preview
- Country pills: UK/USA labels, remove Other, show permits slider
  inline under country so the effect is transparent and adjustable
- Reset button: inline confirm (red "Sure? Reset") instead of alert
- Worker: print magic link to console when DEBUG=true for local dev

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 14:36:26 +01:00
Deeman
fc410920d8 add supplier tiers, directory redesign, CTA cleanup, and ROI fix
Phase 0 features: ungate planner, lead qualifier with heat scoring,
quote form (migrations 0002-0003), supplier directory with FTS5 search
(migration 0004), landing page redesign with ROI calculator and FAQ.

Phase 1 improvements: supplier tier system with Growth/Pro paid plans
(migration 0005), HTMX live directory search, three-tier card design,
Zillow-style sticky nav, "Get Matched" → "Get Quotes" CTA rename,
remove "Free" messaging site-wide, realistic ROI calculator defaults
(~3.9yr payback / ~26% ROI), mandatory form validation with 422 errors,
supplier pricing page with boost add-ons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 14:11:35 +01:00
Deeman
7cb41d91f2 fix planner toggle active state, improve space defaults
- Toggle buttons (Indoor/Outdoor, Rent/Buy) now visually update
  their active state on click
- Space requirement sliders start from minimum court size
  (200m² double, 120m² single) instead of 0
- Defaults updated to court + 2m buffer (336/240/312/216 m²)
- Reference dimensions panel shows standard court sizes
- Chart.js font updated from JetBrains Mono to Commit Mono

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:06:03 +01:00
Deeman
72077fdd46 migrate from Pico CSS to Tailwind CSS v4
Replace Pico CSS CDN with Tailwind v4 standalone CLI (no Node.js).
Brand theme with navy/electric/accent palette, component classes,
self-hosted Commit Mono font. Docker multi-stage CSS build.
Logo links to dashboard when logged in.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 14:45:32 +01:00
Deeman
97e3310998 add migration docs & tests, fix empty env var crash
- Expand migrate.py docstring with algorithm, protocol, and design decisions
- Add 20-test suite for migration framework (test_migrations.py)
- Fix: empty env vars (SECRET_KEY=) now fall back to defaults via _env() helper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 11:37:00 +01:00
Deeman
25d06a80d5 switch payment provider from LemonSqueezy to Paddle
Run copier update with payment_provider=paddle to switch all billing
integration: routes, config, schema columns, webhook handling, tests,
and CI deploy secrets. Add one-time migration script for renaming
lemonsqueezy_* columns to paddle_* in production DB.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:40:40 +01:00
Deeman
f562df8437 fix heading colors on dark-OS systems, increase logo size
The heading color override (bare h1 selector, specificity 0,0,1) was
losing to Pico's theme-scoped heading rules ([data-theme=light] h1,
specificity 0,1,1). On systems with prefers-color-scheme: dark, Pico's
dark heading colors (#f0f1f3 — near white) leaked through, making
headings invisible on the light background.

Fix: scope heading overrides under [data-theme="light"] to match Pico's
specificity. Also bump nav logo from 24px to 32px, footer to 24px.

Visual tests now run with color_scheme="dark" to catch this class of bug.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 00:16:31 +01:00
Deeman
6bba19f628 skip visual tests in CI — require explicit -m visual flag
Playwright visual tests need browser binaries (playwright install
chromium) which CI doesn't have. Mark them with pytest.mark.visual
and add addopts = "-m 'not visual'" so they're skipped by default.

Run locally with: uv run pytest -m visual tests/test_visual.py -v

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 23:59:53 +01:00
Deeman
dc685e8e7b fix logo sizing, heading colors, and add Playwright visual tests
- Trim logo background to transparent, use inline style height to
  override Pico's img { height: auto } — nav 24px, footer 20px
- Fix heading colors by setting --pico-h1-color through --pico-h6-color
  variables and --pico-color override on heading elements
- Add white-space:nowrap on nav CTA button to prevent wrapping
- Add Playwright visual test suite (11 tests): screenshots of landing,
  login, signup, mobile; assertions for light background, dark headings,
  logo presence, nav layout, no dark remnants
- Screenshots saved to tests/screenshots/ for manual review

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 23:56:39 +01:00
Deeman
9703651562 add hybrid calculator refactor and comprehensive billing test suite
Move planner financial model from client-side JS to server-side Python
(calculator.py + /planner/calculate endpoint). Add full test coverage:
227 calculator tests and 371 billing tests covering SQL helpers,
webhooks, routes, and subscription gating with Hypothesis fuzzing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:05:03 +01:00