fix(dept): vert ≠ vigilance, conseils sur chaque alerte active
Some checks failed
Deploy info-canicule / deploy (push) Failing after 5s
Some checks failed
Deploy info-canicule / deploy (push) Failing after 5s
Le flux Météo France renvoie parfois des phénomènes explicitement au vert (colorId 1) : ils étaient traités comme des alertes, d'où un « Que faire maintenant ? » affiché sur un phénomène vert et un dropdown limité aux phénomènes absents du flux. - "Active" = vigilance jaune ou plus (colorId >= 2) ; le vert n'est jamais une alerte - Tout au vert → un seul encadré « Aucune vigilance active » - Dropdown regroupe désormais tous les phénomènes au vert (présents au vert dans le flux + absents) - Une vigilance présente → bloc complet « Que faire maintenant ? » sur chacune des alertes actives (au lieu de la première seulement) - Tableau Aujourd'hui vs Demain et suite : inchangés Au passage, fix typage FranceMap : Map par défaut typée pour ne pas perdre VigilanceAlert (5 erreurs astro check), imports inutilisés retirés. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9db5d4c204
commit
c170bbca7d
2 changed files with 55 additions and 67 deletions
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
import franceMap from '../data/france-map.json';
|
||||
import { COLORS, COLOR_LABEL, PHENOMENA } from '../lib/phenomena';
|
||||
import type { ColorId, PhenomenonId } from '../lib/phenomena';
|
||||
import { COLORS, PHENOMENA } from '../lib/phenomena';
|
||||
import type { ColorId } from '../lib/phenomena';
|
||||
import type { VigilanceAlert } from '../lib/vigilance';
|
||||
import { getDepartement } from '../lib/departements';
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ interface Props {
|
|||
alertsByDept?: Map<string, VigilanceAlert[]>;
|
||||
}
|
||||
|
||||
const { colorsByDept, alertsByDept = new Map() } = Astro.props;
|
||||
const { colorsByDept, alertsByDept = new Map<string, VigilanceAlert[]>() } = Astro.props;
|
||||
const entries = Object.entries(franceMap.paths) as [string, { d: string; name: string }][];
|
||||
|
||||
// Construire un objet { code: { name, color, alerts: [{phenLabel, colorName, colorId}] } }
|
||||
|
|
|
|||
|
|
@ -147,16 +147,20 @@ const j1Available = snapshot ? (ech === 'J' && hasJ1Period(snapshot)) : false;
|
|||
const today = snapshot ? alertsForDepartement(snapshot, dept.code, ech) : [];
|
||||
const tomorrow = j1Available ? alertsForDepartement(snapshot!, dept.code, 'J1') : [];
|
||||
|
||||
// Sort by severity desc, pick top alert for the hero block
|
||||
// Sort by severity desc. "Active" = vigilance jaune ou plus (colorId >= 2).
|
||||
// Le flux MF renvoie parfois des phénomènes explicitement au vert (colorId 1) —
|
||||
// ce ne sont PAS des alertes : ils rejoignent le dropdown "tous au vert", au même
|
||||
// titre que les phénomènes absents du flux. topAlert = la plus sévère des actives
|
||||
// (sert aux numéros d'urgence + carte "Kit" en bas de page).
|
||||
const sortedToday = [...today].sort((a, b) => b.colorId - a.colorId);
|
||||
const topAlert = sortedToday[0] ?? null;
|
||||
const otherAlerts = sortedToday.slice(1);
|
||||
const activeToday = sortedToday.filter((a) => a.colorId >= 2);
|
||||
const topAlert = activeToday[0] ?? null;
|
||||
|
||||
const adviceFor = topAlert ? ADVICE[topAlert.phenomenonId] : null;
|
||||
// First 3 action items as quick bullets inside the hero block
|
||||
const quickActions = adviceFor
|
||||
? adviceFor.blocks.flatMap((b) => b.items).slice(0, 3)
|
||||
: [];
|
||||
// First 3 action items, par phénomène, pour le bloc "Que faire maintenant ?"
|
||||
function quickActionsFor(phenId: PhenomenonId): string[] {
|
||||
const advice = ADVICE[phenId];
|
||||
return advice ? advice.blocks.flatMap((b) => b.items).slice(0, 3) : [];
|
||||
}
|
||||
|
||||
// Per-phenomenon color maps for the comparison table
|
||||
const PHENOM_IDS: PhenomenonId[] = [1, 2, 3, 5, 6, 8, 9];
|
||||
|
|
@ -179,9 +183,10 @@ const comparisonRows = PHENOM_IDS.map((id) => ({
|
|||
changed: j1Available && (todayByPhenom.get(id) ?? 1) !== (tomorrowByPhenom.get(id) ?? 1),
|
||||
}));
|
||||
|
||||
// Phenomena with no active alert today (green)
|
||||
const activePhenomIds = new Set(today.map((a) => a.phenomenonId));
|
||||
const inactivePhenomIds = PHENOM_IDS.filter((id) => !activePhenomIds.has(id));
|
||||
// Phénomènes au vert aujourd'hui = ceux sans alerte active (jaune+). Inclut aussi
|
||||
// les phénomènes absents du flux MF (réputés verts). Tous regroupés dans le dropdown.
|
||||
const activePhenomIds = new Set(activeToday.map((a) => a.phenomenonId));
|
||||
const greenPhenomIds = PHENOM_IDS.filter((id) => !activePhenomIds.has(id));
|
||||
|
||||
const productDate = snapshot?.productDatetime
|
||||
? new Date(snapshot.productDatetime).toLocaleString('fr-FR', {
|
||||
|
|
@ -204,10 +209,6 @@ const tomorrowLabel = tomorrow[0] ? labelDate(tomorrow[0].beginTime) : '';
|
|||
|
||||
// Glyphs matching the pill levels
|
||||
const GLYPHS: Record<ColorId, string> = { 1: '●', 2: '▲', 3: '◆', 4: '■' };
|
||||
|
||||
// Pre-computed display values for the hero alert block
|
||||
const topColorName = topAlert ? COLORS[topAlert.colorId].name : null;
|
||||
const topPhen = topAlert ? PHENOMENA[topAlert.phenomenonId] : null;
|
||||
---
|
||||
|
||||
<Base
|
||||
|
|
@ -266,80 +267,67 @@ const topPhen = topAlert ? PHENOMENA[topAlert.phenomenonId] : null;
|
|||
|
||||
{!drom && !error && (
|
||||
<>
|
||||
{/* All-green state */}
|
||||
{sortedToday.length === 0 && (
|
||||
{/* All-green state — aucune vigilance active (jaune+) */}
|
||||
{activeToday.length === 0 && (
|
||||
<div class="v-block v-vert" style="margin-bottom: 24px;">
|
||||
<h2 style="color: var(--v-vert-ink); font-size: clamp(1.2rem, 1rem + 0.8vw, 1.6rem);">
|
||||
● Aucune vigilance active {todayLabel ? `pour ${todayLabel}` : "aujourd'hui"}
|
||||
</h2>
|
||||
<p style="color: var(--v-vert-ink); margin-top: 10px; line-height: 1.5;">
|
||||
Tous les phénomènes sont au vert pour {dept.name}. Restez attentif aux mises à jour Météo France.
|
||||
Tous les phénomènes sont au vert pour {dept.name} — rien de particulier à signaler.
|
||||
Restez attentif aux mises à jour Météo France.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hero — top alert */}
|
||||
{topAlert && topColorName && topPhen && (
|
||||
<div class={`v-block v-${topColorName}`} style="margin-bottom: 24px;">
|
||||
{/* Active alerts (jaune+) — bloc complet "Que faire maintenant ?" sur chacune */}
|
||||
{activeToday.map((a) => {
|
||||
const colorName = COLORS[a.colorId].name;
|
||||
const phen = PHENOMENA[a.phenomenonId];
|
||||
const advice = ADVICE[a.phenomenonId];
|
||||
const quickActions = quickActionsFor(a.phenomenonId);
|
||||
return (
|
||||
<div class={`v-block v-${colorName}`} style="margin-bottom: 24px;">
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 12px; flex-wrap: wrap;">
|
||||
<span style="font-size: 2rem; line-height: 1;" aria-hidden="true">{topPhen.emoji}</span>
|
||||
<h2 style={`color: var(--v-${topColorName}-ink); font-size: clamp(1.2rem, 1rem + 0.8vw, 1.6rem);`}>
|
||||
{GLYPHS[topAlert.colorId]} {topPhen.label} — {COLOR_LABEL[topAlert.colorId]}
|
||||
<span style="font-size: 2rem; line-height: 1;" aria-hidden="true">{phen.emoji}</span>
|
||||
<h2 style={`color: var(--v-${colorName}-ink); font-size: clamp(1.2rem, 1rem + 0.8vw, 1.6rem);`}>
|
||||
{GLYPHS[a.colorId]} {phen.label} — {COLOR_LABEL[a.colorId]}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{adviceFor && (
|
||||
{advice && (
|
||||
<div style="margin-top: 4px; padding: 16px 20px; background: var(--paper-2); border-radius: var(--r-md); color: var(--ink);">
|
||||
<strong style="font-size: 0.95rem;">Que faire maintenant ?</strong>
|
||||
<ul style="margin-top: 10px; padding-left: 18px; color: var(--ink-2); line-height: 1.6;">
|
||||
{quickActions.map((a) => <li>{a}</li>)}
|
||||
{quickActions.map((item) => <li>{item}</li>)}
|
||||
</ul>
|
||||
<a
|
||||
href={`/conseils/${topPhen.slug}`}
|
||||
href={`/conseils/${phen.slug}`}
|
||||
style="display: inline-flex; align-items: center; gap: 6px; margin-top: 12px; font-size: 0.88rem; font-weight: 600; color: var(--brand-deep); text-decoration: none;"
|
||||
>
|
||||
Voir le kit complet {topPhen.label} →
|
||||
Voir le kit complet {phen.label} →
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p style={`color: var(--v-${topColorName}-ink); margin-top: 14px; font-size: 0.85rem; opacity: 0.85;`}>
|
||||
Valide du {fmtTime(topAlert.beginTime)} au {fmtTime(topAlert.endTime)} — heure de Paris.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Other active alerts */}
|
||||
{otherAlerts.map((a) => {
|
||||
const colorName = COLORS[a.colorId].name;
|
||||
const phen = PHENOMENA[a.phenomenonId];
|
||||
return (
|
||||
<div class={`v-block v-${colorName}`} style="margin-bottom: 12px; padding: 16px;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<span style="font-size: 1.3rem;" aria-hidden="true">{phen.emoji}</span>
|
||||
<strong style={`color: var(--v-${colorName}-ink);`}>{phen.label}</strong>
|
||||
</div>
|
||||
<VigilanceChip colorId={a.colorId} showLevel />
|
||||
</div>
|
||||
<p style={`color: var(--v-${colorName}-ink); margin-top: 8px; font-size: 0.82rem; opacity: 0.8;`}>
|
||||
Du {fmtTime(a.beginTime)} au {fmtTime(a.endTime)}
|
||||
<p style={`color: var(--v-${colorName}-ink); margin-top: 14px; font-size: 0.85rem; opacity: 0.85;`}>
|
||||
Valide du {fmtTime(a.beginTime)} au {fmtTime(a.endTime)} — heure de Paris.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Collapsible: inactive phenomena (all green) */}
|
||||
{inactivePhenomIds.length > 0 && (
|
||||
{/* Collapsible: phénomènes au vert (sans vigilance) */}
|
||||
{greenPhenomIds.length > 0 && (
|
||||
<details class="ic-card" style="margin-bottom: 28px; padding: 16px;">
|
||||
<summary style="cursor: pointer; font-weight: 600; color: var(--ink-2); display: flex; align-items: center; justify-content: space-between; list-style: none;">
|
||||
<span>
|
||||
{inactivePhenomIds.length} phénomène{inactivePhenomIds.length > 1 ? 's' : ''} — tous au vert
|
||||
{greenPhenomIds.length} phénomène{greenPhenomIds.length > 1 ? 's' : ''} au vert — aucune vigilance
|
||||
</span>
|
||||
<span aria-hidden="true" style="color: var(--ink-mute); font-size: 0.8rem;">▼</span>
|
||||
</summary>
|
||||
<div class="grid gap-2" style="grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); margin-top: 14px;">
|
||||
{inactivePhenomIds.map((id) => (
|
||||
{greenPhenomIds.map((id) => (
|
||||
<div class="v-block v-vert" style="padding: 10px 14px; font-size: 0.9rem; display: flex; align-items: center; gap: 8px;">
|
||||
<span aria-hidden="true">{PHENOMENA[id].emoji}</span>
|
||||
<span style="color: var(--v-vert-ink);">{PHENOMENA[id].label}</span>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue