diff --git a/.gitignore b/.gitignore index 5a64b4c..d6ae58d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ /result *.db .direnv/ -/prixdugaz diff --git a/go.mod b/go.mod index d47486e..351c584 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/Polensky/prixdugaz +module github.com/polen/prixdugaz go 1.25.0 diff --git a/static/map.js b/static/map.js index e4c56ed..5b175d8 100644 --- a/static/map.js +++ b/static/map.js @@ -4,54 +4,20 @@ (function () { 'use strict'; - // ── URL param helpers ────────────────────────────────────── - function getParams() { - return new URLSearchParams(window.location.search); - } - - function updateURL() { - const p = new URLSearchParams(); - if (currentFuel !== 'regular') p.set('fuel', currentFuel); - if (currentRegion !== '') p.set('region', currentRegion); - if (clusterMode !== 'avg') p.set('cluster', clusterMode); - if (!showCostco) p.set('costco', '0'); - const slider = document.getElementById('price-slider'); - if (slider && parseFloat(slider.value) < parseFloat(slider.max)) { - p.set('price', slider.value); - } - if (map) { - const c = map.getCenter(); - p.set('lat', c.lat.toFixed(5)); - p.set('lng', c.lng.toFixed(5)); - p.set('zoom', map.getZoom()); - } - const qs = p.toString(); - history.replaceState(null, '', qs ? '?' + qs : window.location.pathname); - } - - // Read initial state from URL params (fall back to defaults). - const _p = getParams(); - let currentFuel = ['regular','super','diesel'].includes(_p.get('fuel')) ? _p.get('fuel') : 'regular'; - let currentRegion = _p.get('region') || ''; - let clusterMode = ['min','avg','max'].includes(_p.get('cluster')) ? _p.get('cluster') : 'avg'; - let showCostco = _p.get('costco') !== '0'; - let _initPrice = _p.has('price') ? parseFloat(_p.get('price')) : null; - let _initLat = _p.has('lat') ? parseFloat(_p.get('lat')) : null; - let _initLng = _p.has('lng') ? parseFloat(_p.get('lng')) : null; - let _initZoom = _p.has('zoom') ? parseInt(_p.get('zoom'), 10) : null; - + let currentFuel = 'regular'; + let currentRegion = ''; + let clusterMode = 'avg'; // 'min' | 'avg' | 'max' + let showCostco = true; let allStations = window.__stations || []; let allMarkers = []; let visibleSet = new Set(); let minPrice = 0, maxPrice = 300; let sliderTimer = null; - let mapMoveTimer = null; const stationDeltas = window.__deltas || {}; // ── Map setup (deferred to init section below) ───────────── let map; let clusterGroup; - let locateMarker = null; // ── Colour helpers ───────────────────────────────────────── function priceColor(price, min, max) { @@ -254,14 +220,13 @@ document.getElementById('region-select').addEventListener('change', function () { currentRegion = this.value; rebuildMarkers(true); - updateURL(); }); document.getElementById('price-slider').addEventListener('input', function () { const val = parseFloat(this.value); document.getElementById('slider-label').textContent = val.toFixed(1) + '¢/L'; clearTimeout(sliderTimer); - sliderTimer = setTimeout(() => { applyPriceFilter(val); updateURL(); }, 80); + sliderTimer = setTimeout(() => applyPriceFilter(val), 80); }); document.querySelectorAll('.cluster-btn').forEach(btn => { @@ -271,7 +236,6 @@ document.querySelectorAll('.cluster-btn').forEach(b => b.classList.toggle('active', b.dataset.mode === clusterMode)); if (allStations.length) clusterGroup.refreshClusters(); - updateURL(); }); }); @@ -282,39 +246,14 @@ document.querySelectorAll('.fuel-btn[data-fuel]').forEach(b => b.classList.toggle('active', b.dataset.fuel === currentFuel)); rebuildMarkers(); - updateURL(); }); }); document.getElementById('costco-toggle').addEventListener('change', function () { showCostco = this.checked; rebuildMarkers(); - updateURL(); }); - // ── Locate-me button ─────────────────────────────────── - var locateBtn = document.getElementById('locate-btn'); - if (locateBtn) { - locateBtn.addEventListener('click', function () { - if (!navigator.geolocation) return; - navigator.geolocation.getCurrentPosition(function (pos) { - var latlng = [pos.coords.latitude, pos.coords.longitude]; - map.setView(latlng, 14); - if (locateMarker) { - locateMarker.setLatLng(latlng); - } else { - var dotIcon = L.divIcon({ - html: '
', - className: '', - iconSize: [20, 20], - iconAnchor: [10, 10], - }); - locateMarker = L.marker(latlng, { icon: dotIcon, pane: 'locatePane' }).addTo(map); - } - }); - }); - } - // ── Mobile filter panel toggle ───────────────────────── var filterToggle = document.getElementById('filter-toggle'); var sliderPanel = document.getElementById('slider-panel'); @@ -343,18 +282,10 @@ const loadingEl = document.getElementById('loading'); try { // Create map and cluster group now that the DOM is ready. - const initCenter = (_initLat !== null && _initLng !== null) ? [_initLat, _initLng] : [46.8, -71.2]; - const initZoom = _initZoom !== null ? _initZoom : 7; - map = L.map('map', { zoomControl: false }).setView(initCenter, initZoom); - map.createPane('locatePane').style.zIndex = 650; + map = L.map('map', { zoomControl: false }).setView([46.8, -71.2], 7); L.control.zoom({ position: 'topright' }).addTo(map); - var lastUpdatedText = (document.getElementById('last-updated') || {}).textContent || ''; - var lastUpdatedMobile = document.getElementById('last-updated-mobile'); - if (lastUpdatedMobile && lastUpdatedText) { - lastUpdatedMobile.textContent = lastUpdatedText; - } L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap | Données: Régie de l\'énergie du Québec' + (lastUpdatedText ? ' | ' + lastUpdatedText : ''), + attribution: '© OpenStreetMap | Données: Régie de l\'énergie du Québec', maxZoom: 18, }).addTo(map); @@ -393,46 +324,11 @@ loadingEl.style.display = 'none'; - // Apply initial state from URL params to UI controls. - if (currentFuel !== 'regular') { - document.querySelectorAll('.fuel-btn[data-fuel]').forEach(b => - b.classList.toggle('active', b.dataset.fuel === currentFuel)); - } - if (clusterMode !== 'avg') { - document.querySelectorAll('.cluster-btn').forEach(b => - b.classList.toggle('active', b.dataset.mode === clusterMode)); - } - if (!showCostco) { - const ct = document.getElementById('costco-toggle'); - if (ct) ct.checked = false; - } - if (currentRegion) { - const rs = document.getElementById('region-select'); - if (rs) rs.value = currentRegion; - } - rebuildMarkers(); - - // Override slider value from URL after rebuildMarkers set it. - if (_initPrice !== null) { - const slider = document.getElementById('price-slider'); - if (slider && _initPrice < parseFloat(slider.max)) { - slider.value = _initPrice; - document.getElementById('slider-label').textContent = _initPrice.toFixed(1) + '¢/L'; - applyPriceFilter(_initPrice, false); - } - } - map.addLayer(clusterGroup); bindControls(); - // Update URL on map move/zoom. - map.on('moveend zoomend', function () { - clearTimeout(mapMoveTimer); - mapMoveTimer = setTimeout(updateURL, 200); - }); - // Expose for use after htmx page-swap back to the map page. window.__mapInvalidate = function () { setTimeout(() => map.invalidateSize(), 50); diff --git a/static/style.css b/static/style.css index 9bd4d3c..d70adbd 100644 --- a/static/style.css +++ b/static/style.css @@ -13,7 +13,7 @@ } /* ── Layout ─────────────────────────────────────────── */ -html, body { height: 100%; margin: 0; padding: 0; overflow-x: hidden; } +html, body { height: 100%; margin: 0; padding: 0; } /* ── Top nav ─────────────────────────────────────────── */ #topnav { @@ -47,17 +47,6 @@ html, body { height: 100%; margin: 0; padding: 0; overflow-x: hidden; } color: var(--primary-foreground); } #topnav nav a svg { flex-shrink: 0; } -.github-link { - margin-left: auto; - display: flex; align-items: center; - color: var(--muted-foreground); - padding: 5px 6px; border-radius: 6px; - transition: background 0.12s, color 0.12s; -} -.github-link:hover { - background: var(--muted); - color: var(--foreground); -} /* ── App shell ───────────────────────────────────────── */ #app { @@ -70,7 +59,7 @@ html, body { height: 100%; margin: 0; padding: 0; overflow-x: hidden; } } /* ── Map page ────────────────────────────────────────── */ -#page-map { display: flex; flex-direction: column; flex: 1; position: relative; overflow: hidden; } +#page-map { display: flex; flex-direction: column; flex: 1; position: relative; } #map { flex: 1; min-height: 0; } /* Slider + fuel panel */ @@ -96,11 +85,6 @@ html, body { height: 100%; margin: 0; padding: 0; overflow-x: hidden; } } #visible-count { margin-top: 6px; font-size: 11px; color: var(--muted-foreground); } -/* Last-updated: only shown inside the mobile filter drawer */ -#last-updated-mobile { - display: none; -} - /* Map overlay panels — sit above the Leaflet tiles */ .map-panel { background: var(--card); @@ -133,54 +117,15 @@ html, body { height: 100%; margin: 0; padding: 0; overflow-x: hidden; } font-size: 10px; color: var(--muted-foreground); } +#last-updated { + position: absolute; bottom: 24px; right: 8px; + padding: 5px 10px; + z-index: 1000; font-size: 11px; + color: var(--muted-foreground); +} + .pin-icon { filter: drop-shadow(0 1px 3px rgba(0,0,0,0.35)); } -#locate-btn { - position: absolute; bottom: 34px; right: 8px; - display: flex; - align-items: center; - justify-content: center; - width: 30px; - height: 30px; - padding: 0; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--card); - color: var(--card-foreground); - box-shadow: 0 2px 8px rgba(0,0,0,0.15); - cursor: pointer; - z-index: 1000; -} -#locate-btn:hover { background: var(--muted); } - -/* Locate-me blue dot + pulse ring */ -.locate-dot { - position: relative; - width: 20px; - height: 20px; -} -.locate-dot-inner { - position: absolute; - inset: 4px; - border-radius: 50%; - background: #2563eb; - border: 2.5px solid #fff; - box-shadow: 0 1px 4px rgba(0,0,0,0.4); - z-index: 1; -} -.locate-dot-pulse { - position: absolute; - inset: 0; - border-radius: 50%; - background: #2563eb; - opacity: 0.35; - animation: locate-pulse 1.8s ease-out infinite; -} -@keyframes locate-pulse { - 0% { transform: scale(0.6); opacity: 0.5; } - 100% { transform: scale(2.2); opacity: 0; } -} - .cluster-info-tip { position: relative; display: inline-flex; @@ -320,9 +265,6 @@ html, body { height: 100%; margin: 0; padding: 0; overflow-x: hidden; } @media (max-width: 768px) { #filter-toggle { display: flex; } - #filter-toggle, - #locate-btn { bottom: 24px; } - #slider-panel { position: fixed; top: auto; left: 0; right: 0; bottom: 0; @@ -375,15 +317,4 @@ html, body { height: 100%; margin: 0; padding: 0; overflow-x: hidden; } .cluster-info-tip { display: none; } .cluster-hint { display: block; } .cluster-row { margin-bottom: 0; } - - /* Hide Leaflet attribution bar on mobile */ - .leaflet-control-attribution { display: none; } - - /* Show last-updated inside the filter drawer */ - #last-updated-mobile { - display: block; - margin-top: 6px; - font-size: 11px; - color: var(--muted-foreground); - } } diff --git a/templates/layout.html b/templates/layout.html index 7490b36..540d47e 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -3,7 +3,7 @@ - Prix du Gaz + Essence Québec @@ -51,11 +51,6 @@ Statistiques - - - - - diff --git a/templates/map.html b/templates/map.html index e341357..66636dd 100644 --- a/templates/map.html +++ b/templates/map.html @@ -55,7 +55,6 @@ -
-
Régulier (¢/L)
@@ -67,10 +66,7 @@
- - +
{{.LastUpdated}}