The admin article preview iframe was missing Leaflet CSS/JS and had
scripts blocked by the sandbox policy, so map shortcodes rendered as
empty divs.
- Extract inline map script to static/js/article-maps.js (shared
between article_detail.html and admin preview)
- Replace f-string preview doc with a proper Jinja template that
includes Leaflet assets
- Add allow-scripts to iframe sandbox on both initial load and HTMX
preview updates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`plan --auto-apply` only detects SQL model changes and won't re-run
for new data. `run prod` evaluates missing cron intervals and picks
up newly extracted data — matching the fix already applied to the
supervisor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Grid children default to min-width:auto, letting the Chart.js canvas
push the container wider than its grid track. Adding min-width:0 and
overflow:hidden constrains charts to their column width.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add scripts/check_pipeline.py: read-only diagnostic for pricing pipeline
row counts, date range analysis, HAVING filter impact, join coverage
- Add description field to all 12 workflows in workflows.toml
- Parse and display descriptions on extraction status cards
- Show spinner + "Running" state with blue-tinted card border
- Display start time with "running..." text for active extractions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In prod the package is installed in a venv, so __file__.parents[4] doesn't
reach the repo root. Use CWD (repo root in both dev and prod via systemd
WorkingDirectory) with REPO_ROOT env var override.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
_extract_line_items() was returning [] for all checkout sessions, which
meant _handle_transaction_completed never processed credit packs, sticky
boosts, or business plan PDF purchases. Now fetches line items from the
Stripe API using the session ID, with a fallback to embedded line_items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- dev_run.sh: also remove app.db-shm and app.db-wal on reset to fix
SQLite disk I/O error from stale WAL/SHM files
- articles bulk: add checkboxes to grouped rows (data-ids holds all
variant IDs); checking a group selects EN+DE together
- restore select-all checkbox in grouped <th>
- add toggleArticleGroupSelect() JS function
- fix htmx:afterSwap to re-check group checkboxes correctly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 4 (Project Phase) required location_status server-side but had no
visual "*" indicator and no error message when submitting without a
selection. All other steps already had both.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The CRO homepage overhaul (f4f8a45) introduced url_for('quote.wizard')
in landing.html, but that endpoint never existed — the actual route is
leads.quote_request. This broke CI runs #99–#109.
Also adds landing_vs_col_us to i18n allowlist (brand name, same in both
languages).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace L.circleMarker with L.divIcon + .pn-marker CSS class (white
border, box-shadow, hover scale) matching the beanflows growing
conditions map pattern. Dark .map-tooltip CSS override (no arrow,
dark navy background). Small venue dots use .pn-venue class.
Add _require_maps_flag() to all 4 API endpoints (default=True so
dev works without seeding the flag row). Gate /opportunity-map route
the same way.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New route GET /<lang>/opportunity-map renders a full-width Leaflet map
with a country selector. On country change, fetches
/api/opportunity/{slug}.json and renders opportunity circles
(color-coded by score, sized by population) plus existing-venue gray
reference dots from /api/markets/{country}/cities.json.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New serving model: city_venue_locations joins dim_venues + dim_cities
to expose lat/lon/court_count per venue for the city dot map endpoint.
pseo_city_costs_de.sql: add c.lat, c.lon so city-cost articles have
city coordinates for the #city-map data attributes.
city-cost-de.md.jinja: add #city-map div (both DE and EN sections)
after the stats strip. Leaflet init handled by article_detail.html.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add #country-map div to country-overview.md.jinja (both DE/EN).
article_detail.html: always include Leaflet CSS, conditionally load
Leaflet JS only when #country-map or #city-map divs are present.
Initializes country city-bubble map and city venue-dot map from
/api/markets/{slug}/cities.json and /api/markets/{country}/{city}/venues.json.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Leaflet map to /markets with country-level bubbles sized by
total_venues and colored by avg_market_score. Click navigates to
country overview page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Self-host Leaflet 1.9.4 JS/CSS/images in static/vendor/leaflet/.
Create api.py blueprint with 4 JSON endpoints for map data.
Register api_bp at /api in app.py (before content catch-all).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add bulk selection checkboxes and action bars to the articles and leads
admin pages, replicating the existing supplier bulk pattern.
Articles: publish, unpublish, toggle noindex, rebuild, delete (with
confirmation dialog). Leads: set status, set heat. Both re-render the
results partial after action via HTMX, preserving current filters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stripe API 2026-02+ moved current_period_end from subscription to
subscription items. Add _get_period_end() helper that falls back to
items[0].current_period_end when the subscription-level field is None.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add customer.subscription.created → subscription.activated mapping in
stripe.parse_webhook so direct API subscription creation also creates DB rows
- Add customer.subscription.created to setup_stripe.py enabled_events
- Pin PAYMENT_PROVIDER=paddle and STRIPE_WEBHOOK_SECRET="" in test conftest
so billing tests don't hit real Stripe API when env has Stripe keys
- Add 8 unit tests for stripe.parse_webhook covering all event types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stripe Python SDK doesn't accept request_options as a kwarg to create/retrieve/modify.
Timeouts are handled by the global max_network_retries setting.
Also gracefully handle webhook endpoint creation failure for localhost URLs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mirrors setup_paddle.py structure:
- Creates 17 products + prices in Stripe (same keys, same prices)
- Writes to payment_products table with provider='stripe'
- Registers webhook endpoint at /billing/webhook/stripe
- tax_behavior='exclusive' (price + VAT on top, EU standard)
- Supports --sync flag to re-populate from existing Stripe products
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New _payment_js.html: conditionally loads Paddle.js or nothing (Stripe
uses server-side Checkout Session). Provides startCheckout() helper.
- All checkout templates use _payment_js.html instead of _paddle.html
- export.html, signup_step_4.html: Paddle.Checkout.open() → startCheckout()
- dashboard_boosts.html: inline onclick → buyItem() with server round-trip
- New /billing/checkout/item endpoint for single-item purchases (boosts, credits)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add @slugify SQLMesh macro (STRIP_ACCENTS + ß→ss) replacing broken
inline REGEXP_REPLACE that dropped non-ASCII chars (Düsseldorf → d-sseldorf)
- Apply @slugify to dim_venues, dim_cities, dim_locations
- Fix Python slugify() to pre-replace ß→ss before NFKD normalization
- Add language prefix to B2B article market links (/markets/germany → /de/markets/germany)
- Change country overview top-5 ranking: venue count (not raw market_score)
for top cities, population for top opportunity cities
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Curly quotes (U+201C/U+201D) were used as JSON key/value delimiters
on line 894 of both locale files, making them invalid JSON.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Task 1: Hero, features, FAQ, final CTA, supplier matching, meta/SEO
strings all rewritten. New keys added for proof strip, struggling-
moments section, and "Why Padelnomics" comparison section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the auto-escaped `{{ body_html }}` div (showed raw HTML tags)
with a sandboxed `<iframe srcdoc>` pattern matching the email preview.
Both the initial page load and the HTMX live-update endpoint now build
a full `preview_doc` document embedding the public CSS and wrapping
content in `<div class="article-body">` — pixel-perfect against the
live article, admin styles fully isolated.
Also:
- Delete ~65 lines of redundant `.preview-body` custom CSS
- Add "Meta ▾" toolbar toggle to collapse/expand metadata strip
- Add word count footer in the editor pane (updates on input)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two fixes:
- _find_article_md() scans _ARTICLES_DIR for files whose frontmatter
slug matches, so padel-halle-bauen-de.md is found for slug
'padel-halle-bauen'. The previous exact-name lookup missed any file
where the filename ≠ slug (e.g. {slug}-{lang}.md naming convention).
- Editor pane: replace dark navy background with warm off-white (#FEFDFB)
and dark text so it reads like a document, not a code editor.
Bug: article_edit GET was passing raw .md file content (including YAML
frontmatter) to the body textarea. Articles synced from disk via
_sync_static_articles() had their frontmatter bled into the editor,
making it look like content was missing or garbled.
Fix: strip frontmatter (using existing _FRONTMATTER_RE) before setting
body, consistent with how _rebuild_article() already does it.
Also switch to _ARTICLES_DIR (absolute) instead of relative path.
New: split editor layout — compact metadata strip at top, dark
monospace textarea on the left, live rendered preview on the right
(HTMX, 500ms debounce). Initial preview server-rendered on page load.
New POST /admin/articles/preview endpoint returns the preview partial.
Lift admin_client fixture from 7 duplicate definitions into conftest.py.
Add mock_send_email fixture, replacing 60 inline patch() blocks across
test_emails.py, test_waitlist.py, and test_businessplan.py. Net -197 lines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>