From 5f8b765d79ed9b37d2830b91b7e6eef02eb7051d Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 26 May 2026 01:57:35 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Vigilance=20API=20officielle=20+=20tab?= =?UTF-8?q?=201=20an=20+=20logo=20SVG=20+=20map=20=C3=A0=205xl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. lib/vigilance.ts : provider Météo France officielle via DPVigilance/v1 /cartevigilance/encours. Map vers VigilanceAlert (1 par dept × phen × J/J1) en prenant phenomenon_max_color_id (pire de la journée). Filtre les domains non-dept (FRA national, sub-côtes XX10, etc). - Auto-pick : MF si key dispo, sinon opendatasoft. Override VIGILANCE_PROVIDER. - Fallback opendatasoft si MF échoue (cache key différent pour pas polluer). - Devrait fixer le lag de plusieurs heures observé sur Opendatasoft. 2. lib/climato.ts : fetch latest + previous (~4 MB compressé), garder 365j max en cache 24h. Permet l'onglet "1 an" sur la page dept. 3. TemperatureChartInteractive : onglet "1 an" (apparaît si > 30j dispos), série days365 + normales365 sérialisés au SSR. 4. Base.astro : logo header et footer utilisent au lieu d'un emoji 🌡️. 5. index.astro : wrapper map repassé à container-tight (max-w-5xl) — la version 1400px était trop grande, on revient à la largeur du reste du site. Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.tmpl | 4 +- .../TemperatureChartInteractive.astro | 42 ++++++-- src/layouts/Base.astro | 7 +- src/lib/climato.ts | 99 ++++++++++--------- src/lib/vigilance.ts | 91 ++++++++++++++++- src/pages/departement/[code].astro | 7 +- src/pages/index.astro | 2 +- 7 files changed, 189 insertions(+), 63 deletions(-) diff --git a/.env.tmpl b/.env.tmpl index abe6386..7d9473c 100644 --- a/.env.tmpl +++ b/.env.tmpl @@ -5,7 +5,9 @@ PUBLIC_SITE_URL=https://info-canicule.nocleus.com # Valkey shared infra — DB 0 (isolation par préfixe `info-canicule:*` via ACL) REDIS_URL=redis://info-canicule:{{ pass://Infra/Valkey — info-canicule/password }}@valkey:6379/0 -VIGILANCE_PROVIDER=opendatasoft +# Provider Vigilance : `meteofrance` (API officielle, frais) ou `opendatasoft` (no-auth, lag possible). +# Vide = auto (MF si METEOFRANCE_API_KEY défini, sinon opendatasoft). +VIGILANCE_PROVIDER= VIGILANCE_CACHE_TTL=900 # Umami analytics (RGPD, auto-hébergé — analytics.nocleus.com) diff --git a/src/components/TemperatureChartInteractive.astro b/src/components/TemperatureChartInteractive.astro index deae348..1d02c4f 100644 --- a/src/components/TemperatureChartInteractive.astro +++ b/src/components/TemperatureChartInteractive.astro @@ -7,18 +7,20 @@ interface Props { hourly?: HourlySeries | null; // 24h days7: DayObservation[]; days30: DayObservation[]; - normales7?: NormalePoint[]; // 1 normale par jour (même longueur que days7) - normales30?: NormalePoint[]; // idem pour 30j - normaleHourly?: NormalePoint | null; // 1 seule normale (jour courant) pour overlay 24h + days365: DayObservation[]; + normales7?: NormalePoint[]; + normales30?: NormalePoint[]; + normales365?: NormalePoint[]; + normaleHourly?: NormalePoint | null; // 1 normale (jour courant) pour overlay 24h stationLabel?: string | null; } -const { hourly, days7, days30, normales7 = [], normales30 = [], normaleHourly = null, stationLabel } = Astro.props; +const { + hourly, days7, days30, days365, + normales7 = [], normales30 = [], normales365 = [], + normaleHourly = null, stationLabel, +} = Astro.props; -// Sérialiser les 3 séries pour le JS client (toggle + hover). -// Pour les normales : -// - 24h : 1 seul point répété (la normale du jour courant) → ligne horizontale -// - 7j / 30j : 1 normale par jour (courbe qui suit la saison) const serialize = { hourly: hourly?.observations.map((o) => ({ t: o.time, tx: o.t, tn: null })) ?? [], days7: days7.map((d, i) => ({ @@ -29,10 +31,15 @@ const serialize = { t: d.date, tx: d.tx, tn: d.tn, normTx: normales30[i]?.tx ?? null, normTn: normales30[i]?.tn ?? null, })), + days365: days365.map((d, i) => ({ + t: d.date, tx: d.tx, tn: d.tn, + normTx: normales365[i]?.tx ?? null, normTn: normales365[i]?.tn ?? null, + })), normaleHourly: normaleHourly ? { tx: normaleHourly.tx, tn: normaleHourly.tn } : null, }; const hasHourly = (hourly?.observations.length ?? 0) > 0; +const hasYear = days365.length > 30; const defaultPeriod = hasHourly ? '24h' : '7j'; --- @@ -68,10 +75,21 @@ const defaultPeriod = hasHourly ? '24h' : '7j'; role="tab" data-period="30j" aria-selected={false} - class="period-tab px-3 py-1 text-xs font-medium aria-selected:bg-canicule-600 aria-selected:text-white" + class:list={['period-tab px-3 py-1 text-xs font-medium aria-selected:bg-canicule-600 aria-selected:text-white', hasYear ? 'border-r border-slate-200' : '']} > 30 jours + {hasYear && ( + + )} @@ -117,7 +135,11 @@ const defaultPeriod = hasHourly ? '24h' : '7j'; } function render(period) { - const series = data[period === '24h' ? 'hourly' : period === '7j' ? 'days7' : 'days30']; + const seriesKey = period === '24h' ? 'hourly' + : period === '7j' ? 'days7' + : period === '30j' ? 'days30' + : 'days365'; + const series = data[seriesKey]; if (!series || series.length === 0) { svg.innerHTML = 'Aucune donnée disponible'; return; diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro index 938160b..47d9f96 100644 --- a/src/layouts/Base.astro +++ b/src/layouts/Base.astro @@ -103,7 +103,7 @@ const jsonLd = {
- 🌡️ + Info Canicule