diff --git a/CHANGELOG.md b/CHANGELOG.md index 20339b6..55a7452 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Added +- **Geo headers on city/region hubs** — Cloudflare geo headers (`CF-IPCountry`, `CF-IPCity`) now used across location-based pages. Opportunity map pre-selects and auto-loads the user's country. Country overview maps highlight the user's city with a blue ring (best-effort CF-IPCity name match). `window.__GEO` JS global injected via `base.html` for client-side map code. + +### Fixed +- **Opportunity map color scale** — low-score bubbles used blue (`#3B82F6`) instead of red (`#DC2626`), inconsistent with the unified `scoreColor()` scale used everywhere else. Fixed in `oppColor()`, legend, and `article-maps.js` tooltip colors. Thresholds aligned to ≥60/30/\<30. + ### Changed - **Opportunity Score v5 → v6** — calibrates for saturated markets (Spain avg dropped from ~78 to ~50-60 range). Density ceiling lowered from 8 → 5/100k (Spain at 6-16/100k now hits zero-gap). Supply deficit weight increased from 35 → 40 pts. Addressable market reduced from 25 → 20 pts. Market validation inverted → "market headroom": high country avg maturity now reduces opportunity (saturated market = less room for new entrants). - **Markets page map legend** — bubble map now has a visual legend explaining size = venue count, color = Market Score. Opportunity score tooltip color unified to same green/amber/red scale (was using blue for low scores, inconsistent). diff --git a/web/src/padelnomics/app.py b/web/src/padelnomics/app.py index d2e64c1..9bbd640 100644 --- a/web/src/padelnomics/app.py +++ b/web/src/padelnomics/app.py @@ -246,6 +246,8 @@ def create_app() -> Quart: "csrf_token": get_csrf_token, "ab_variant": getattr(g, "ab_variant", None), "ab_tag": getattr(g, "ab_tag", None), + "user_country": g.get("user_country", ""), + "user_city": g.get("user_city", ""), "lang": effective_lang, "t": get_translations(effective_lang), "v": _ASSET_VERSION, diff --git a/web/src/padelnomics/public/routes.py b/web/src/padelnomics/public/routes.py index 476415c..7497305 100644 --- a/web/src/padelnomics/public/routes.py +++ b/web/src/padelnomics/public/routes.py @@ -79,12 +79,23 @@ async def opportunity_map(): if not await is_flag_enabled("maps", default=True): abort(404) countries = await fetch_analytics(""" - SELECT DISTINCT country_slug, country_name_en + SELECT DISTINCT country_slug, country_name_en, country_code FROM serving.location_profiles WHERE city_slug IS NOT NULL ORDER BY country_name_en """) - return await render_template("opportunity_map.html", countries=countries) + user_cc = g.get("user_country", "") + selected_slug = "" + if user_cc: + for c in countries: + if c["country_code"] == user_cc: + selected_slug = c["country_slug"] + break + countries = sorted( + countries, + key=lambda c: (0 if c["country_code"] == user_cc else 1, c["country_name_en"]), + ) + return await render_template("opportunity_map.html", countries=countries, selected_slug=selected_slug) @bp.route("/opportunity-map/data") diff --git a/web/src/padelnomics/public/templates/opportunity_map.html b/web/src/padelnomics/public/templates/opportunity_map.html index a255a36..1105528 100644 --- a/web/src/padelnomics/public/templates/opportunity_map.html +++ b/web/src/padelnomics/public/templates/opportunity_map.html @@ -30,7 +30,7 @@ hx-trigger="change"> {% for c in countries %} - + {% endfor %} @@ -41,9 +41,9 @@