merge: fix map bubble styling + improve hover UX
This commit is contained in:
@@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
- **Custom 404/500 error pages** — styled error pages extending `base.html` with i18n support (EN/DE). The 404 page is context-aware: when the URL matches `/markets/{country}/{city}`, it shows a city-specific message with a link back to the country overview instead of a generic "page not found".
|
- **Custom 404/500 error pages** — styled error pages extending `base.html` with i18n support (EN/DE). The 404 page is context-aware: when the URL matches `/markets/{country}/{city}`, it shows a city-specific message with a link back to the country overview instead of a generic "page not found".
|
||||||
- **Map: city article indicators** — country overview map bubbles now differentiate cities with/without published articles. Cities without articles appear in muted gray and are not clickable, preventing dead-end navigations. The `/api/markets/<country>/cities.json` endpoint includes a `has_article` boolean per city.
|
- **Map: city article indicators** — country overview map bubbles now differentiate cities with/without published articles. All cities retain score-based colors (green/amber/red); non-article cities are visually receded with lower opacity, dashed borders, desaturated color, and default cursor (no click). Tooltips show scores for all cities — article cities get "Click to explore →", non-article cities get "Coming soon". The `/api/markets/<country>/cities.json` endpoint includes a `has_article` boolean per city.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- **Admin template preview maps** — Leaflet maps rendered blank because `article-maps.js` called `L.divIcon()` at the IIFE top level before Leaflet was dynamically loaded, crashing the script. Moved `VENUE_ICON` creation into the `script.onload` callback so it runs after Leaflet is available. Previous commit's `.card` `overflow: visible` fix remains (clips tile layers otherwise).
|
- **Admin template preview maps** — Leaflet maps rendered blank because `article-maps.js` called `L.divIcon()` at the IIFE top level before Leaflet was dynamically loaded, crashing the script. Moved `VENUE_ICON` creation into the `script.onload` callback so it runs after Leaflet is available. Previous commit's `.card` `overflow: visible` fix remains (clips tile layers otherwise).
|
||||||
|
|||||||
@@ -892,6 +892,18 @@
|
|||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Non-article city markers: faded + dashed border, no click affordance */
|
||||||
|
.pn-marker--muted {
|
||||||
|
opacity: 0.45;
|
||||||
|
border: 2px dashed rgba(255,255,255,0.6);
|
||||||
|
cursor: default;
|
||||||
|
filter: saturate(0.7);
|
||||||
|
}
|
||||||
|
.pn-marker--muted:hover {
|
||||||
|
transform: none;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.28);
|
||||||
|
}
|
||||||
|
|
||||||
/* Small fixed venue dot */
|
/* Small fixed venue dot */
|
||||||
.pn-venue {
|
.pn-venue {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
|
|||||||
@@ -19,11 +19,12 @@
|
|||||||
return '#DC2626';
|
return '#DC2626';
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeIcon(size, color) {
|
function makeIcon(size, color, muted) {
|
||||||
var s = Math.round(size);
|
var s = Math.round(size);
|
||||||
|
var cls = 'pn-marker' + (muted ? ' pn-marker--muted' : '');
|
||||||
return L.divIcon({
|
return L.divIcon({
|
||||||
className: '',
|
className: '',
|
||||||
html: '<div class="pn-marker" style="width:' + s + 'px;height:' + s + 'px;background:' + color + ';opacity:0.82;"></div>',
|
html: '<div class="' + cls + '" style="width:' + s + 'px;height:' + s + 'px;background:' + color + ';"></div>',
|
||||||
iconSize: [s, s],
|
iconSize: [s, s],
|
||||||
iconAnchor: [s / 2, s / 2],
|
iconAnchor: [s / 2, s / 2],
|
||||||
});
|
});
|
||||||
@@ -44,17 +45,20 @@
|
|||||||
if (!c.lat || !c.lon) return;
|
if (!c.lat || !c.lon) return;
|
||||||
var size = 10 + 36 * Math.sqrt((c.padel_venue_count || 1) / maxV);
|
var size = 10 + 36 * Math.sqrt((c.padel_venue_count || 1) / maxV);
|
||||||
var hasArticle = c.has_article !== false;
|
var hasArticle = c.has_article !== false;
|
||||||
var color = hasArticle ? scoreColor(c.market_score) : '#9CA3AF';
|
var color = scoreColor(c.market_score);
|
||||||
var pop = c.population >= 1000000
|
var pop = c.population >= 1000000
|
||||||
? (c.population / 1000000).toFixed(1) + 'M'
|
? (c.population / 1000000).toFixed(1) + 'M'
|
||||||
: (c.population >= 1000 ? Math.round(c.population / 1000) + 'K' : (c.population || ''));
|
: (c.population >= 1000 ? Math.round(c.population / 1000) + 'K' : (c.population || ''));
|
||||||
var tip = '<strong>' + c.city_name + '</strong><br>'
|
var tip = '<strong>' + c.city_name + '</strong><br>'
|
||||||
+ (c.padel_venue_count || 0) + ' venues'
|
+ (c.padel_venue_count || 0) + ' venues'
|
||||||
+ (pop ? ' · ' + pop : '');
|
+ (pop ? ' · ' + pop : '')
|
||||||
|
+ '<br><span style="color:' + color + ';font-weight:600;">Score ' + Math.round(c.market_score) + '/100</span>';
|
||||||
if (hasArticle) {
|
if (hasArticle) {
|
||||||
tip += '<br><span style="color:' + color + ';font-weight:600;">Score ' + Math.round(c.market_score) + '/100</span>';
|
tip += '<br><span style="color:#94A3B8;font-size:0.75rem;">Click to explore →</span>';
|
||||||
|
} else {
|
||||||
|
tip += '<br><span style="color:#94A3B8;font-size:0.75rem;">Coming soon</span>';
|
||||||
}
|
}
|
||||||
var marker = L.marker([c.lat, c.lon], { icon: makeIcon(size, color) })
|
var marker = L.marker([c.lat, c.lon], { icon: makeIcon(size, color, !hasArticle) })
|
||||||
.bindTooltip(tip, { className: 'map-tooltip', direction: 'top', offset: [0, -Math.round(size / 2)] })
|
.bindTooltip(tip, { className: 'map-tooltip', direction: 'top', offset: [0, -Math.round(size / 2)] })
|
||||||
.addTo(map);
|
.addTo(map);
|
||||||
if (hasArticle) {
|
if (hasArticle) {
|
||||||
|
|||||||
Reference in New Issue
Block a user