feat: OG png + sentry + dept api + drom notice + registre canicule
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
Quick wins : - public/og-image.png (1200x630, via sharp depuis le SVG, build via pnpm build) SVG ne fonctionne pas pour Open Graph (Slack/Discord/X/FB). - @sentry/astro intégré conditionnellement (skip si SENTRY_DSN absent → no-op). GIT_COMMIT_SHA en var pour le release tag dans GlitchTip si voulu. - /api/vigilance/dept/[code] : JSON enrichi (phenomenon label + color name) pour J et J1, CORS *, Cache-Control 5min. 404 si code unknown. - JSON-LD enrichi : @graph WebSite + Service avec isBasedOn Dataset + license LOv2. - Lien retour vigilance.meteofrance.fr visible sous la carte. DROM (97x / 976) : - 5 entrées ajoutées dans departements.ts (région "DROM"). - /departement/[code] DROM : bannière "Vigilance Outre-mer non couverte par cette source open data" + bouton vers vigilance.meteofrance.fr. - Home : ligne sous la carte listant les 5 DROM + lien retour. - L'API /api/vigilance/dept/<DROM> retourne quand même un JSON 200 (arrays vides). Registre canicule : - Page /conseils/registre-canicule : qui, quoi, comment s'inscrire au CCAS. - Numéro vert 0 800 06 66 66. - Bannière mise en avant en haut de /conseils. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
89e48c18e4
commit
87d173684c
13 changed files with 1595 additions and 35 deletions
|
|
@ -2,6 +2,10 @@ import { defineConfig } from 'astro/config';
|
||||||
import node from '@astrojs/node';
|
import node from '@astrojs/node';
|
||||||
import tailwind from '@astrojs/tailwind';
|
import tailwind from '@astrojs/tailwind';
|
||||||
import sitemap from '@astrojs/sitemap';
|
import sitemap from '@astrojs/sitemap';
|
||||||
|
import sentry from '@sentry/astro';
|
||||||
|
|
||||||
|
const sentryDsn = process.env.SENTRY_DSN;
|
||||||
|
const release = process.env.GIT_COMMIT_SHA || 'dev';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
output: 'server',
|
output: 'server',
|
||||||
|
|
@ -14,6 +18,19 @@ export default defineConfig({
|
||||||
!page.includes('/departement/'),
|
!page.includes('/departement/'),
|
||||||
// /departement/* est dynamique pour les 96 dépts — généré dans /sitemap-departements.xml.ts à part.
|
// /departement/* est dynamique pour les 96 dépts — généré dans /sitemap-departements.xml.ts à part.
|
||||||
}),
|
}),
|
||||||
|
// Sentry / GlitchTip — opt-in via env. Si SENTRY_DSN absent, intégration omise (no-op).
|
||||||
|
...(sentryDsn
|
||||||
|
? [
|
||||||
|
sentry({
|
||||||
|
dsn: sentryDsn,
|
||||||
|
environment: process.env.NODE_ENV ?? 'production',
|
||||||
|
release,
|
||||||
|
tracesSampleRate: 0.1,
|
||||||
|
// GlitchTip est OK avec source maps mais on les omet pour éviter le upload.
|
||||||
|
sourceMapsUploadOptions: { enabled: false },
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
],
|
],
|
||||||
server: { host: '0.0.0.0', port: 4321 },
|
server: { host: '0.0.0.0', port: 4321 },
|
||||||
site: 'https://info-canicule.nocleus.com',
|
site: 'https://info-canicule.nocleus.com',
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build",
|
"build": "node scripts/build-og-image.mjs && astro build",
|
||||||
|
"build:og": "node scripts/build-og-image.mjs",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"start": "node ./dist/server/entry.mjs",
|
"start": "node ./dist/server/entry.mjs",
|
||||||
"astro": "astro",
|
"astro": "astro",
|
||||||
|
|
@ -16,6 +17,7 @@
|
||||||
"@astrojs/node": "^9.2.2",
|
"@astrojs/node": "^9.2.2",
|
||||||
"@astrojs/sitemap": "^3.6.0",
|
"@astrojs/sitemap": "^3.6.0",
|
||||||
"@astrojs/tailwind": "^6.0.2",
|
"@astrojs/tailwind": "^6.0.2",
|
||||||
|
"@sentry/astro": "^10.53.1",
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"astro": "^5.7.0",
|
"astro": "^5.7.0",
|
||||||
"ioredis": "^5.6.0",
|
"ioredis": "^5.6.0",
|
||||||
|
|
@ -24,6 +26,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@types/node": "^22.10.5",
|
"@types/node": "^22.10.5",
|
||||||
|
"sharp": "^0.34.5",
|
||||||
"typescript": "^5.7.2"
|
"typescript": "^5.7.2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.0.0"
|
"packageManager": "pnpm@10.0.0"
|
||||||
|
|
|
||||||
1272
pnpm-lock.yaml
generated
1272
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
BIN
public/og-image.png
Normal file
BIN
public/og-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
20
scripts/build-og-image.mjs
Normal file
20
scripts/build-og-image.mjs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// Convertit public/og-image.svg en public/og-image.png (1200×630).
|
||||||
|
// Lancé manuellement ou via `pnpm prebuild` quand le SVG bouge.
|
||||||
|
|
||||||
|
import sharp from 'sharp';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { resolve, dirname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const SVG = resolve(__dirname, '../public/og-image.svg');
|
||||||
|
const PNG = resolve(__dirname, '../public/og-image.png');
|
||||||
|
|
||||||
|
const buf = readFileSync(SVG);
|
||||||
|
await sharp(buf, { density: 144 })
|
||||||
|
.resize(1200, 630, { fit: 'fill' })
|
||||||
|
.png({ compressionLevel: 9 })
|
||||||
|
.toFile(PNG);
|
||||||
|
|
||||||
|
console.log(`Wrote ${PNG}`);
|
||||||
|
|
@ -14,7 +14,7 @@ const {
|
||||||
title = 'Info Canicule — Vigilance météo France en temps réel',
|
title = 'Info Canicule — Vigilance météo France en temps réel',
|
||||||
description = 'Suivi gratuit et sans publicité des alertes Vigilance Météo France (canicule, orages, tempêtes), avec carte interactive par département et conseils officiels.',
|
description = 'Suivi gratuit et sans publicité des alertes Vigilance Météo France (canicule, orages, tempêtes), avec carte interactive par département et conseils officiels.',
|
||||||
canonical,
|
canonical,
|
||||||
ogImage = '/og-image.svg',
|
ogImage = '/og-image.png',
|
||||||
noindex = false,
|
noindex = false,
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
|
|
@ -25,20 +25,43 @@ const umamiSrc = process.env.UMAMI_SRC ?? 'https://analytics.nocleus.com/script.
|
||||||
|
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
|
'@graph': [
|
||||||
|
{
|
||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
|
'@id': `${SITE}/#website`,
|
||||||
name: 'Info Canicule',
|
name: 'Info Canicule',
|
||||||
url: SITE,
|
url: SITE,
|
||||||
description,
|
description,
|
||||||
inLanguage: 'fr-FR',
|
inLanguage: 'fr-FR',
|
||||||
publisher: {
|
publisher: { '@type': 'Person', name: 'Florian Bouchet' },
|
||||||
'@type': 'Person',
|
|
||||||
name: 'Florian Bouchet',
|
|
||||||
},
|
|
||||||
potentialAction: {
|
potentialAction: {
|
||||||
'@type': 'SearchAction',
|
'@type': 'SearchAction',
|
||||||
target: `${SITE}/departement/{code}`,
|
target: `${SITE}/departement/{code}`,
|
||||||
'query-input': 'required name=code',
|
'query-input': 'required name=code',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'@type': 'Service',
|
||||||
|
'@id': `${SITE}/#service`,
|
||||||
|
name: 'Info Canicule',
|
||||||
|
serviceType: 'Service d\'information météorologique grand public',
|
||||||
|
areaServed: { '@type': 'Country', name: 'France' },
|
||||||
|
audience: { '@type': 'PeopleAudience', audienceType: 'Grand public, personnes fragiles' },
|
||||||
|
provider: {
|
||||||
|
'@type': 'Person',
|
||||||
|
name: 'Florian Bouchet',
|
||||||
|
url: `${SITE}/a-propos`,
|
||||||
|
},
|
||||||
|
isBasedOn: {
|
||||||
|
'@type': 'Dataset',
|
||||||
|
name: 'Vigilance Météo France',
|
||||||
|
url: 'https://vigilance.meteofrance.fr/',
|
||||||
|
creator: { '@type': 'Organization', name: 'Météo-France' },
|
||||||
|
license: 'https://www.etalab.gouv.fr/licence-ouverte-open-licence/',
|
||||||
|
},
|
||||||
|
isAccessibleForFree: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export const ADVICE: Record<PhenomenonId, PhenomenonAdvice> = {
|
||||||
title: 'Veiller sur les proches',
|
title: 'Veiller sur les proches',
|
||||||
items: [
|
items: [
|
||||||
"Prendre des nouvelles des personnes isolées (voisins âgés, malades, sans-abri)",
|
"Prendre des nouvelles des personnes isolées (voisins âgés, malades, sans-abri)",
|
||||||
"S'inscrire ou inscrire un proche fragile sur le registre canicule de sa mairie",
|
"Inscrire un proche fragile sur le registre canicule de sa mairie — voir page dédiée",
|
||||||
'En cas de symptôme inquiétant (maux de tête, vertiges, nausées, fièvre) : alerter rapidement',
|
'En cas de symptôme inquiétant (maux de tête, vertiges, nausées, fièvre) : alerter rapidement',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,20 @@ export const DEPARTEMENTS: Departement[] = [
|
||||||
{ code: '94', name: 'Val-de-Marne', region: 'Île-de-France' },
|
{ code: '94', name: 'Val-de-Marne', region: 'Île-de-France' },
|
||||||
{ code: '95', name: "Val-d'Oise", region: 'Île-de-France' },
|
{ code: '95', name: "Val-d'Oise", region: 'Île-de-France' },
|
||||||
{ code: '99', name: 'Andorre (zone Vigilance)', region: 'Hors France' },
|
{ code: '99', name: 'Andorre (zone Vigilance)', region: 'Hors France' },
|
||||||
|
// DROM — non couverts par le dataset Opendatasoft actuel.
|
||||||
|
// Vigilance DROM dispo via l'API Météo France officielle uniquement (TODO).
|
||||||
|
{ code: '971', name: 'Guadeloupe', region: 'DROM' },
|
||||||
|
{ code: '972', name: 'Martinique', region: 'DROM' },
|
||||||
|
{ code: '973', name: 'Guyane', region: 'DROM' },
|
||||||
|
{ code: '974', name: 'La Réunion', region: 'DROM' },
|
||||||
|
{ code: '976', name: 'Mayotte', region: 'DROM' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const DROM_CODES = new Set(['971', '972', '973', '974', '976']);
|
||||||
|
export function isDrom(code: string): boolean {
|
||||||
|
return DROM_CODES.has(code);
|
||||||
|
}
|
||||||
|
|
||||||
const BY_CODE = new Map(DEPARTEMENTS.map((d) => [d.code, d]));
|
const BY_CODE = new Map(DEPARTEMENTS.map((d) => [d.code, d]));
|
||||||
|
|
||||||
export function getDepartement(code: string): Departement | undefined {
|
export function getDepartement(code: string): Departement | undefined {
|
||||||
|
|
|
||||||
62
src/pages/api/vigilance/dept/[code].ts
Normal file
62
src/pages/api/vigilance/dept/[code].ts
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import { getVigilanceSnapshot, alertsForDepartement } from '../../../../lib/vigilance';
|
||||||
|
import { getDepartement } from '../../../../lib/departements';
|
||||||
|
import { PHENOMENA, COLORS } from '../../../../lib/phenomena';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
// JSON par département (J + J1) — CORS *, réutilisable sous Licence Ouverte 2.0.
|
||||||
|
// Ex : GET /api/vigilance/dept/75 → alertes Paris
|
||||||
|
export const GET: APIRoute = async ({ params }) => {
|
||||||
|
const codeRaw = params.code;
|
||||||
|
if (!codeRaw) {
|
||||||
|
return new Response(JSON.stringify({ error: 'missing_code' }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const code = codeRaw.toUpperCase();
|
||||||
|
const dept = getDepartement(code);
|
||||||
|
if (!dept) {
|
||||||
|
return new Response(JSON.stringify({ error: 'unknown_departement', code }), {
|
||||||
|
status: 404,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const snap = await getVigilanceSnapshot();
|
||||||
|
const today = alertsForDepartement(snap, code, 'J');
|
||||||
|
const tomorrow = alertsForDepartement(snap, code, 'J1');
|
||||||
|
const enrich = (a: ReturnType<typeof alertsForDepartement>[0]) => ({
|
||||||
|
phenomenonId: a.phenomenonId,
|
||||||
|
phenomenon: PHENOMENA[a.phenomenonId].label,
|
||||||
|
colorId: a.colorId,
|
||||||
|
color: COLORS[a.colorId].name,
|
||||||
|
beginTime: a.beginTime,
|
||||||
|
endTime: a.endTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
departement: { code, name: dept.name, region: dept.region },
|
||||||
|
productDatetime: snap.productDatetime,
|
||||||
|
fetchedAt: snap.fetchedAt,
|
||||||
|
today: today.map(enrich),
|
||||||
|
tomorrow: tomorrow.map(enrich),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(body), {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
'Cache-Control': 'public, max-age=300',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: 'fetch_failed', detail: (e as Error).message }),
|
||||||
|
{ status: 502, headers: { 'Content-Type': 'application/json' } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -21,6 +21,17 @@ const phenomenaList = Object.values(PHENOMENA).sort((a, b) =>
|
||||||
Recommandations à appliquer en cas d'alerte Vigilance, par type de phénomène.
|
Recommandations à appliquer en cas d'alerte Vigilance, par type de phénomène.
|
||||||
Sources : Météo France, santé.gouv.fr, gouvernement.fr.
|
Sources : Météo France, santé.gouv.fr, gouvernement.fr.
|
||||||
</p>
|
</p>
|
||||||
|
<div class="mt-4 rounded-lg border border-canicule-200 bg-canicule-50 p-4">
|
||||||
|
<p class="text-sm font-semibold text-canicule-900">
|
||||||
|
🛡️ Aider une personne fragile : <a href="/conseils/registre-canicule" class="font-bold underline">
|
||||||
|
registre canicule de la mairie
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="mt-1 text-sm text-canicule-800">
|
||||||
|
Dispositif communal gratuit qui permet à un proche âgé, malade ou isolé d'être contacté en
|
||||||
|
cas d'alerte. Inscription en quelques minutes via le CCAS.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
108
src/pages/conseils/registre-canicule.astro
Normal file
108
src/pages/conseils/registre-canicule.astro
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
---
|
||||||
|
import Base from '../../layouts/Base.astro';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Base
|
||||||
|
title="Registre canicule — s'inscrire pour soi ou un proche fragile"
|
||||||
|
description="Le registre canicule est un dispositif communal gratuit qui permet d'être contacté par la mairie en cas d'épisode caniculaire. Comment s'y inscrire."
|
||||||
|
>
|
||||||
|
<section class="bg-gradient-to-b from-canicule-50 to-white">
|
||||||
|
<div class="container-tight py-10">
|
||||||
|
<a href="/conseils" class="text-sm text-canicule-700">← Tous les conseils</a>
|
||||||
|
<h1 class="mt-2 text-3xl font-bold sm:text-4xl">Registre canicule</h1>
|
||||||
|
<p class="mt-2 max-w-2xl text-slate-600">
|
||||||
|
Un dispositif communal, gratuit et confidentiel, qui permet d'être contacté par sa mairie en
|
||||||
|
cas d'alerte canicule. Conçu en priorité pour les personnes fragiles et isolées.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container-tight py-8 prose prose-slate max-w-none">
|
||||||
|
<h2>À qui ça s'adresse</h2>
|
||||||
|
<p>Le registre nominatif communal est ouvert à toute personne qui le souhaite, mais il est
|
||||||
|
particulièrement utile pour :</p>
|
||||||
|
<ul>
|
||||||
|
<li>Les <strong>personnes âgées de 65 ans et plus</strong> vivant à domicile</li>
|
||||||
|
<li>Les <strong>personnes en situation de handicap</strong></li>
|
||||||
|
<li>Les <strong>personnes adultes isolées</strong> (sans aidant proche)</li>
|
||||||
|
<li>Les personnes <strong>fragilisées par une maladie chronique</strong></li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Vous pouvez inscrire un proche (parent, voisin) avec son accord, ou demander une inscription
|
||||||
|
d'office pour une personne dont vous estimez qu'elle est en danger.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Comment ça se passe en cas d'alerte</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Quand un département passe en vigilance orange ou rouge canicule, le plan « Plan National
|
||||||
|
Canicule » (PNC) s'enclenche en mairie.</li>
|
||||||
|
<li>Les personnes inscrites sur le registre sont <strong>appelées</strong> ou
|
||||||
|
<strong>visitées</strong> par des agents communaux ou bénévoles (Croix-Rouge, ADMR…).</li>
|
||||||
|
<li>L'objectif : vérifier qu'elles vont bien, qu'elles ont de quoi s'hydrater et rester au
|
||||||
|
frais, et alerter si besoin (médecin traitant, 15, famille).</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Comment s'inscrire</h2>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
Contacter le <strong>CCAS</strong> (Centre Communal d'Action Sociale) de votre commune, ou
|
||||||
|
directement la mairie. Numéros disponibles via l'annuaire officiel des mairies :
|
||||||
|
<a href="https://lannuaire.service-public.fr/navigation/mairie" rel="noopener">
|
||||||
|
lannuaire.service-public.fr/navigation/mairie
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Demander le <strong>formulaire d'inscription au registre canicule</strong> (parfois appelé
|
||||||
|
« registre des personnes vulnérables » ou « plan canicule »).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Remplir le formulaire avec : nom, âge, adresse, téléphone, personne à prévenir, médecin
|
||||||
|
traitant, éventuelles particularités (isolement, mobilité, etc.).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Retourner le formulaire à la mairie. <strong>Gratuit et confidentiel</strong> — les données
|
||||||
|
ne servent qu'au plan canicule, ne sont pas partagées commercialement.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Bon à savoir</h2>
|
||||||
|
<ul>
|
||||||
|
<li>L'inscription est <strong>valable pour toute la saison estivale</strong> (juin à
|
||||||
|
septembre), à renouveler chaque année si besoin.</li>
|
||||||
|
<li>Vous pouvez vous <strong>désinscrire à tout moment</strong> par simple courrier ou appel.</li>
|
||||||
|
<li>
|
||||||
|
Cadre légal : article L121-6-1 du Code de l'action sociale et des familles + circulaire
|
||||||
|
annuelle relative au Plan National Canicule.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="not-prose my-6 rounded-lg border border-canicule-200 bg-canicule-50 p-4">
|
||||||
|
<p class="text-sm font-semibold text-canicule-900">📞 Plateforme téléphonique canicule</p>
|
||||||
|
<p class="mt-1 text-sm text-canicule-800">
|
||||||
|
Numéro vert <a href="tel:0800066666" class="font-mono font-bold">0 800 06 66 66</a> (gratuit
|
||||||
|
depuis un poste fixe). Activé pendant les épisodes de canicule pour informer et conseiller.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Pour aller plus loin</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://www.sante.gouv.fr/sante-et-environnement/risques-climatiques/article/plan-national-canicule-pnc" rel="noopener">
|
||||||
|
Plan National Canicule (santé.gouv.fr)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://solidarites.gouv.fr/sites/solidarite/files/migration/Brochure_canicule_grand_public_2024.pdf" rel="noopener">
|
||||||
|
Brochure officielle Canicule grand public (PDF)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://meteofrance.com/comprendre-meteo/temperature-et-chaleur/canicule" rel="noopener">
|
||||||
|
Comprendre une canicule (Météo France)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</Base>
|
||||||
|
|
@ -3,7 +3,7 @@ import Base from '../../layouts/Base.astro';
|
||||||
import VigilanceChip from '../../components/VigilanceChip.astro';
|
import VigilanceChip from '../../components/VigilanceChip.astro';
|
||||||
import VigilanceLegend from '../../components/VigilanceLegend.astro';
|
import VigilanceLegend from '../../components/VigilanceLegend.astro';
|
||||||
import { getVigilanceSnapshot, alertsForDepartement } from '../../lib/vigilance';
|
import { getVigilanceSnapshot, alertsForDepartement } from '../../lib/vigilance';
|
||||||
import { getDepartement } from '../../lib/departements';
|
import { getDepartement, isDrom } from '../../lib/departements';
|
||||||
import { PHENOMENA, COLOR_LABEL } from '../../lib/phenomena';
|
import { PHENOMENA, COLOR_LABEL } from '../../lib/phenomena';
|
||||||
import { ADVICE, EMERGENCY_NUMBERS } from '../../lib/advice';
|
import { ADVICE, EMERGENCY_NUMBERS } from '../../lib/advice';
|
||||||
import { getClimatoForDepartement } from '../../lib/climato';
|
import { getClimatoForDepartement } from '../../lib/climato';
|
||||||
|
|
@ -18,20 +18,26 @@ if (!dept) {
|
||||||
return new Response('Département introuvable', { status: 404 });
|
return new Response('Département introuvable', { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const drom = isDrom(dept.code);
|
||||||
|
|
||||||
let snapshot;
|
let snapshot;
|
||||||
let error: string | null = null;
|
let error: string | null = null;
|
||||||
|
if (!drom) {
|
||||||
try {
|
try {
|
||||||
snapshot = await getVigilanceSnapshot();
|
snapshot = await getVigilanceSnapshot();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = (e as Error).message;
|
error = (e as Error).message;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let climato = null;
|
let climato = null;
|
||||||
|
if (!drom) {
|
||||||
try {
|
try {
|
||||||
climato = await getClimatoForDepartement(dept.code);
|
climato = await getClimatoForDepartement(dept.code);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('climato fetch failed for', dept.code, (e as Error).message);
|
console.warn('climato fetch failed for', dept.code, (e as Error).message);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const today = snapshot ? alertsForDepartement(snapshot, dept.code, 'J') : [];
|
const today = snapshot ? alertsForDepartement(snapshot, dept.code, 'J') : [];
|
||||||
const tomorrow = snapshot ? alertsForDepartement(snapshot, dept.code, 'J1') : [];
|
const tomorrow = snapshot ? alertsForDepartement(snapshot, dept.code, 'J1') : [];
|
||||||
|
|
@ -52,10 +58,30 @@ const adviceFor = highest && ADVICE[highest.phenomenonId];
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="container-tight py-8">
|
<section class="container-tight py-8">
|
||||||
{error && <p class="text-red-700">Données indisponibles : {error}</p>}
|
{drom && (
|
||||||
|
<div class="rounded-lg border border-amber-200 bg-amber-50 p-4">
|
||||||
|
<p class="font-semibold text-amber-900">Vigilance Outre-mer non couverte par cette source</p>
|
||||||
|
<p class="mt-1 text-sm text-amber-800">
|
||||||
|
Les départements et régions d'Outre-mer disposent de leur propre dispositif Vigilance, géré
|
||||||
|
par les centres météorologiques locaux de Météo France. Ces données ne sont pas (encore)
|
||||||
|
rediffusées en open data au même format que la métropole.
|
||||||
|
</p>
|
||||||
|
<p class="mt-3">
|
||||||
|
<a
|
||||||
|
href="https://vigilance.meteofrance.fr/"
|
||||||
|
rel="noopener"
|
||||||
|
class="inline-flex items-center gap-1 rounded bg-amber-700 px-4 py-2 text-sm font-semibold text-white no-underline hover:bg-amber-800"
|
||||||
|
>
|
||||||
|
Voir la Vigilance officielle Outre-mer →
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!drom && error && <p class="text-red-700">Données indisponibles : {error}</p>}
|
||||||
|
|
||||||
{
|
{
|
||||||
!error && today.length === 0 && (
|
!drom && !error && today.length === 0 && (
|
||||||
<div class="rounded border border-green-200 bg-green-50 p-4">
|
<div class="rounded border border-green-200 bg-green-50 p-4">
|
||||||
<p class="font-semibold text-green-800">Aucune vigilance particulière aujourd'hui.</p>
|
<p class="font-semibold text-green-800">Aucune vigilance particulière aujourd'hui.</p>
|
||||||
<p class="text-sm text-green-700">Le département est en niveau vert pour tous les phénomènes.</p>
|
<p class="text-sm text-green-700">Le département est en niveau vert pour tous les phénomènes.</p>
|
||||||
|
|
@ -64,7 +90,7 @@ const adviceFor = highest && ADVICE[highest.phenomenonId];
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!error && today.length > 0 && (
|
!drom && !error && today.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<h2 class="mb-3 text-xl font-semibold">Alertes en cours</h2>
|
<h2 class="mb-3 text-xl font-semibold">Alertes en cours</h2>
|
||||||
<ul class="space-y-3">
|
<ul class="space-y-3">
|
||||||
|
|
@ -100,7 +126,7 @@ const adviceFor = highest && ADVICE[highest.phenomenonId];
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!error && tomorrow.length > 0 && (
|
!drom && !error && tomorrow.length > 0 && (
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<h2 class="mb-3 text-xl font-semibold">Prévision pour demain</h2>
|
<h2 class="mb-3 text-xl font-semibold">Prévision pour demain</h2>
|
||||||
<ul class="space-y-2">
|
<ul class="space-y-2">
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,22 @@ const productDate = snapshot?.productDatetime
|
||||||
<DepartementGrid colorsByDept={colorsByDept} />
|
<DepartementGrid colorsByDept={colorsByDept} />
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
<p class="mt-4 text-center text-xs text-slate-500">
|
||||||
|
Source officielle :
|
||||||
|
<a href="https://vigilance.meteofrance.fr/" rel="noopener" class="text-canicule-700 font-medium">
|
||||||
|
vigilance.meteofrance.fr
|
||||||
|
</a>
|
||||||
|
— toujours s'y référer en cas d'urgence.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-center text-xs text-slate-500">
|
||||||
|
<strong>Outre-mer non couvert</strong> par cette source open data :
|
||||||
|
<a href="/departement/971" class="text-canicule-700">Guadeloupe</a> ·
|
||||||
|
<a href="/departement/972" class="text-canicule-700">Martinique</a> ·
|
||||||
|
<a href="/departement/973" class="text-canicule-700">Guyane</a> ·
|
||||||
|
<a href="/departement/974" class="text-canicule-700">La Réunion</a> ·
|
||||||
|
<a href="/departement/976" class="text-canicule-700">Mayotte</a> →
|
||||||
|
<a href="https://vigilance.meteofrance.fr/" rel="noopener" class="text-canicule-700">vigilance.meteofrance.fr</a>
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue