feat: tooltip carte + tri/group alertes + safelist couleurs + legal Nocleus
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run
- FranceMap : tooltip riche au hover (HTML overlay), liste les phénomènes
+ niveaux du département. Touch-friendly (1er tap = preview, 2e = clic).
- index.astro : layout refactored, carte toujours visible full-width centrée,
liste par région en details collapsible sous (plus de side-by-side cassé sur PC).
- Alertes actives groupées par département, triées par numéro asc (2A/2B après 19).
- Tailwind safelist vigilance-chip-{1..4} : les classes générées dynamiquement
n'étaient pas captées par le scanner statique → CSS absent en prod.
- Mentions légales : distinction explicite entre Nocleus (micro-entreprise
commerciale) et Info Canicule (projet perso non lucratif, hors cadre pro).
- Liens code source git.nocleus.com retirés partout (autres repos privés y sont
visibles) → code "disponible sur demande" par mail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
58053b72ed
commit
89e48c18e4
8 changed files with 287 additions and 81 deletions
|
|
@ -1,23 +1,56 @@
|
||||||
---
|
---
|
||||||
import franceMap from '../data/france-map.json';
|
import franceMap from '../data/france-map.json';
|
||||||
import { COLORS } from '../lib/phenomena';
|
import { COLORS, COLOR_LABEL, PHENOMENA } from '../lib/phenomena';
|
||||||
import type { ColorId } from '../lib/phenomena';
|
import type { ColorId, PhenomenonId } from '../lib/phenomena';
|
||||||
|
import type { VigilanceAlert } from '../lib/vigilance';
|
||||||
|
import { getDepartement } from '../lib/departements';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
colorsByDept: Map<string, ColorId>;
|
colorsByDept: Map<string, ColorId>;
|
||||||
|
alertsByDept?: Map<string, VigilanceAlert[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { colorsByDept } = Astro.props;
|
const { colorsByDept, alertsByDept = new Map() } = Astro.props;
|
||||||
const entries = Object.entries(franceMap.paths) as [string, { d: string; name: string }][];
|
const entries = Object.entries(franceMap.paths) as [string, { d: string; name: string }][];
|
||||||
|
|
||||||
|
// Construire un objet { code: { name, color, alerts: [{phenLabel, colorName, colorId}] } }
|
||||||
|
// transmis au JS du tooltip via JSON inline.
|
||||||
|
const tooltipData: Record<string, {
|
||||||
|
name: string;
|
||||||
|
region: string;
|
||||||
|
colorId: ColorId;
|
||||||
|
alerts: Array<{ phen: string; colorId: ColorId; colorName: string }>;
|
||||||
|
}> = {};
|
||||||
|
|
||||||
|
for (const [code] of entries) {
|
||||||
|
const dept = getDepartement(code);
|
||||||
|
if (!dept) continue;
|
||||||
|
const colorId = colorsByDept.get(code) ?? 1;
|
||||||
|
const list = (alertsByDept.get(code) ?? [])
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => b.colorId - a.colorId);
|
||||||
|
tooltipData[code] = {
|
||||||
|
name: dept.name,
|
||||||
|
region: dept.region,
|
||||||
|
colorId,
|
||||||
|
alerts: list.map((a) => ({
|
||||||
|
phen: PHENOMENA[a.phenomenonId].label,
|
||||||
|
colorId: a.colorId,
|
||||||
|
colorName: COLORS[a.colorId].name,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<svg
|
<div class="relative">
|
||||||
|
<svg
|
||||||
viewBox={franceMap.viewBox}
|
viewBox={franceMap.viewBox}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
role="img"
|
role="img"
|
||||||
aria-label="Carte des départements français colorée selon le niveau Vigilance"
|
aria-label="Carte des départements français colorée selon le niveau Vigilance"
|
||||||
class="h-auto w-full max-w-3xl"
|
class="h-auto w-full max-w-3xl mx-auto"
|
||||||
>
|
id="france-map"
|
||||||
|
>
|
||||||
<title>Carte Vigilance Météo France</title>
|
<title>Carte Vigilance Météo France</title>
|
||||||
{
|
{
|
||||||
entries.map(([code, dept]) => {
|
entries.map(([code, dept]) => {
|
||||||
|
|
@ -25,9 +58,10 @@ const entries = Object.entries(franceMap.paths) as [string, { d: string; name: s
|
||||||
const color = COLORS[colorId];
|
const color = COLORS[colorId];
|
||||||
return (
|
return (
|
||||||
<a href={`/departement/${code}`} class="cursor-pointer">
|
<a href={`/departement/${code}`} class="cursor-pointer">
|
||||||
<title>{`${dept.name} (${code}) — ${color.name}`}</title>
|
|
||||||
<path
|
<path
|
||||||
d={dept.d}
|
d={dept.d}
|
||||||
|
data-code={code}
|
||||||
|
data-name={dept.name}
|
||||||
fill={color.hex}
|
fill={color.hex}
|
||||||
stroke="#ffffff"
|
stroke="#ffffff"
|
||||||
stroke-width="0.8"
|
stroke-width="0.8"
|
||||||
|
|
@ -37,4 +71,131 @@ const entries = Object.entries(franceMap.paths) as [string, { d: string; name: s
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="map-tooltip"
|
||||||
|
class="pointer-events-none fixed z-50 hidden max-w-xs rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm shadow-lg"
|
||||||
|
role="tooltip"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="application/json" id="vigilance-tooltip-data" set:html={JSON.stringify(tooltipData)} />
|
||||||
|
|
||||||
|
<script is:inline>
|
||||||
|
(function () {
|
||||||
|
const dataEl = document.getElementById('vigilance-tooltip-data');
|
||||||
|
if (!dataEl) return;
|
||||||
|
const data = JSON.parse(dataEl.textContent || '{}');
|
||||||
|
const tooltip = document.getElementById('map-tooltip');
|
||||||
|
const map = document.getElementById('france-map');
|
||||||
|
if (!tooltip || !map) return;
|
||||||
|
|
||||||
|
const COLOR_LABEL = {
|
||||||
|
1: 'Pas de vigilance particulière',
|
||||||
|
2: 'Soyez attentif',
|
||||||
|
3: 'Soyez très vigilant',
|
||||||
|
4: 'Vigilance absolue',
|
||||||
|
};
|
||||||
|
const COLOR_HEX = { 1: '#5cb85c', 2: '#f6d800', 3: '#f08c1a', 4: '#d9534f' };
|
||||||
|
|
||||||
|
function colorChip(colorId, label) {
|
||||||
|
return (
|
||||||
|
'<span class="inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs" style="background:' +
|
||||||
|
COLOR_HEX[colorId] +
|
||||||
|
'; color:' +
|
||||||
|
(colorId >= 3 ? '#fff' : '#1e293b') +
|
||||||
|
'">' +
|
||||||
|
label +
|
||||||
|
'</span>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(code) {
|
||||||
|
const d = data[code];
|
||||||
|
if (!d) return '';
|
||||||
|
const header =
|
||||||
|
'<div class="font-semibold text-slate-900">' +
|
||||||
|
d.name +
|
||||||
|
' <span class="text-xs text-slate-500">(' +
|
||||||
|
code +
|
||||||
|
')</span></div>' +
|
||||||
|
'<div class="text-xs text-slate-500">' +
|
||||||
|
d.region +
|
||||||
|
'</div>';
|
||||||
|
let body;
|
||||||
|
if (d.alerts.length === 0) {
|
||||||
|
body = '<div class="mt-1">' + colorChip(1, 'Aucune vigilance') + '</div>';
|
||||||
|
} else {
|
||||||
|
body =
|
||||||
|
'<ul class="mt-1 space-y-1">' +
|
||||||
|
d.alerts
|
||||||
|
.map(function (a) {
|
||||||
|
return '<li>' + colorChip(a.colorId, a.phen) + '</li>';
|
||||||
|
})
|
||||||
|
.join('') +
|
||||||
|
'</ul>';
|
||||||
|
}
|
||||||
|
const hint = '<div class="mt-1 text-xs text-canicule-700">Cliquer pour le détail →</div>';
|
||||||
|
return header + body + hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function position(evt) {
|
||||||
|
const margin = 14;
|
||||||
|
const tw = tooltip.offsetWidth;
|
||||||
|
const th = tooltip.offsetHeight;
|
||||||
|
let x = evt.clientX + margin;
|
||||||
|
let y = evt.clientY + margin;
|
||||||
|
if (x + tw > window.innerWidth - 8) x = evt.clientX - tw - margin;
|
||||||
|
if (y + th > window.innerHeight - 8) y = evt.clientY - th - margin;
|
||||||
|
tooltip.style.left = x + 'px';
|
||||||
|
tooltip.style.top = y + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
function show(target, evt) {
|
||||||
|
const code = target.getAttribute('data-code');
|
||||||
|
if (!code) return;
|
||||||
|
tooltip.innerHTML = render(code);
|
||||||
|
tooltip.classList.remove('hidden');
|
||||||
|
tooltip.setAttribute('aria-hidden', 'false');
|
||||||
|
position(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
tooltip.classList.add('hidden');
|
||||||
|
tooltip.setAttribute('aria-hidden', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
map.addEventListener('mouseover', function (e) {
|
||||||
|
const t = e.target;
|
||||||
|
if (t && t.tagName === 'path' && t.getAttribute('data-code')) {
|
||||||
|
show(t, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
map.addEventListener('mousemove', function (e) {
|
||||||
|
if (!tooltip.classList.contains('hidden')) position(e);
|
||||||
|
});
|
||||||
|
map.addEventListener('mouseout', function (e) {
|
||||||
|
const t = e.target;
|
||||||
|
const rt = e.relatedTarget;
|
||||||
|
if (t && t.tagName === 'path') {
|
||||||
|
// Don't hide if moving between sibling paths
|
||||||
|
if (!rt || rt.tagName !== 'path') hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Touch devices: tap to show, second tap follows link.
|
||||||
|
map.addEventListener('touchstart', function (e) {
|
||||||
|
const t = e.target;
|
||||||
|
if (t && t.tagName === 'path' && t.getAttribute('data-code')) {
|
||||||
|
if (tooltip.dataset.code !== t.getAttribute('data-code')) {
|
||||||
|
e.preventDefault();
|
||||||
|
tooltip.dataset.code = t.getAttribute('data-code');
|
||||||
|
const rect = t.getBoundingClientRect();
|
||||||
|
show(t, { clientX: rect.left + rect.width / 2, clientY: rect.top });
|
||||||
|
setTimeout(function () { tooltip.dataset.code = ''; }, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { passive: false });
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ const jsonLd = {
|
||||||
publisher: {
|
publisher: {
|
||||||
'@type': 'Person',
|
'@type': 'Person',
|
||||||
name: 'Florian Bouchet',
|
name: 'Florian Bouchet',
|
||||||
url: 'https://nocleus.com',
|
|
||||||
},
|
},
|
||||||
potentialAction: {
|
potentialAction: {
|
||||||
'@type': 'SearchAction',
|
'@type': 'SearchAction',
|
||||||
|
|
@ -110,7 +109,7 @@ const jsonLd = {
|
||||||
<li><a href="/a-propos">À propos</a></li>
|
<li><a href="/a-propos">À propos</a></li>
|
||||||
<li><a href="/mentions-legales">Mentions légales</a></li>
|
<li><a href="/mentions-legales">Mentions légales</a></li>
|
||||||
<li><a href="/dependances">Dépendances</a></li>
|
<li><a href="/dependances">Dépendances</a></li>
|
||||||
<li><a href="/soutenir">Soutenir</a></li>
|
<li><a href="/soutenir">☕ Soutenir sur Ko-fi</a></li>
|
||||||
<li><a href="/api/vigilance">API JSON publique</a></li>
|
<li><a href="/api/vigilance">API JSON publique</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -124,10 +123,8 @@ const jsonLd = {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-4 text-xs text-slate-400">
|
<p class="mt-4 text-xs text-slate-400">
|
||||||
Un projet
|
Édité à titre personnel, sans but lucratif —
|
||||||
<a href="https://nocleus.com" class="text-canicule-700" rel="noopener">Nocleus</a>.
|
<a href="/mentions-legales" class="text-canicule-700">mentions légales</a>.
|
||||||
Code source :
|
|
||||||
<a href="https://git.nocleus.com/florian/info-canicule" class="text-canicule-700" rel="noopener">git.nocleus.com</a>.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
|
|
@ -77,9 +77,9 @@ export const prerender = false;
|
||||||
|
|
||||||
<h2>Code source</h2>
|
<h2>Code source</h2>
|
||||||
<p>
|
<p>
|
||||||
Le site est entièrement open source. Le code est disponible sur
|
Le code est disponible sur demande à
|
||||||
<a href="https://git.nocleus.com/florian/info-canicule" rel="noopener">git.nocleus.com/florian/info-canicule</a>
|
<a href="mailto:florian@nocleus.com">florian@nocleus.com</a> (contributions, signalements de
|
||||||
(instance Forgejo personnelle). Contributions, signalements de bugs et améliorations bienvenus.
|
bugs et améliorations bienvenus).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Contact</h2>
|
<h2>Contact</h2>
|
||||||
|
|
|
||||||
|
|
@ -206,10 +206,8 @@ const INFRA = [
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-sm text-slate-500">
|
<p class="text-sm text-slate-500">
|
||||||
Code source du site :
|
Code source du site disponible sur demande à
|
||||||
<a href="https://git.nocleus.com/florian/info-canicule" rel="noopener">
|
<a href="mailto:florian@nocleus.com">florian@nocleus.com</a>.
|
||||||
git.nocleus.com/florian/info-canicule
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import VigilanceLegend from '../components/VigilanceLegend.astro';
|
||||||
import VigilanceChip from '../components/VigilanceChip.astro';
|
import VigilanceChip from '../components/VigilanceChip.astro';
|
||||||
import { getVigilanceSnapshot, maxColorByDepartement, activeAlerts } from '../lib/vigilance';
|
import { getVigilanceSnapshot, maxColorByDepartement, activeAlerts } from '../lib/vigilance';
|
||||||
import { getDepartement } from '../lib/departements';
|
import { getDepartement } from '../lib/departements';
|
||||||
import { PHENOMENA, COLORS } from '../lib/phenomena';
|
import type { VigilanceAlert } from '../lib/vigilance';
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
|
|
@ -20,6 +20,29 @@ try {
|
||||||
|
|
||||||
const colorsByDept = snapshot ? maxColorByDepartement(snapshot, 'J') : new Map();
|
const colorsByDept = snapshot ? maxColorByDepartement(snapshot, 'J') : new Map();
|
||||||
const alertsToday = snapshot ? activeAlerts(snapshot, 2) : [];
|
const alertsToday = snapshot ? activeAlerts(snapshot, 2) : [];
|
||||||
|
|
||||||
|
// Group all today's alerts (any level) by department, for tooltip + active alerts list.
|
||||||
|
const alertsByDept = new Map<string, VigilanceAlert[]>();
|
||||||
|
if (snapshot) {
|
||||||
|
for (const a of snapshot.alerts) {
|
||||||
|
if (a.echeance !== 'J') continue;
|
||||||
|
if (a.colorId < 2) continue;
|
||||||
|
const list = alertsByDept.get(a.departement) ?? [];
|
||||||
|
list.push(a);
|
||||||
|
alertsByDept.set(a.departement, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tri des départements actifs par code (2A/2B inséré après 19).
|
||||||
|
function deptSortKey(code: string): string {
|
||||||
|
if (code === '2A') return '19.1';
|
||||||
|
if (code === '2B') return '19.2';
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
const activeDeptCodes = [...alertsByDept.keys()].sort((a, b) =>
|
||||||
|
deptSortKey(a).localeCompare(deptSortKey(b), 'en', { numeric: true }),
|
||||||
|
);
|
||||||
|
|
||||||
const canicule = alertsToday.filter((a) => a.phenomenonId === 6).length;
|
const canicule = alertsToday.filter((a) => a.phenomenonId === 6).length;
|
||||||
const orages = alertsToday.filter((a) => a.phenomenonId === 3).length;
|
const orages = alertsToday.filter((a) => a.phenomenonId === 3).length;
|
||||||
const orange = alertsToday.filter((a) => a.colorId >= 3).length;
|
const orange = alertsToday.filter((a) => a.colorId >= 3).length;
|
||||||
|
|
@ -82,11 +105,13 @@ const productDate = snapshot?.productDatetime
|
||||||
<h2 class="text-xl font-semibold text-slate-900">Niveau par département (aujourd'hui)</h2>
|
<h2 class="text-xl font-semibold text-slate-900">Niveau par département (aujourd'hui)</h2>
|
||||||
<VigilanceLegend />
|
<VigilanceLegend />
|
||||||
</div>
|
</div>
|
||||||
<div class="grid gap-8 lg:grid-cols-[2fr_1fr]">
|
<p class="mb-3 text-sm text-slate-500">
|
||||||
|
Survolez un département pour voir le détail des alertes, cliquez pour la page complète.
|
||||||
|
</p>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<FranceMap colorsByDept={colorsByDept} />
|
<FranceMap colorsByDept={colorsByDept} alertsByDept={alertsByDept} />
|
||||||
</div>
|
</div>
|
||||||
<details class="rounded border border-slate-200 bg-white p-4">
|
<details class="mt-6 rounded border border-slate-200 bg-white p-4">
|
||||||
<summary class="cursor-pointer font-medium text-slate-700">
|
<summary class="cursor-pointer font-medium text-slate-700">
|
||||||
Vue par région (liste)
|
Vue par région (liste)
|
||||||
</summary>
|
</summary>
|
||||||
|
|
@ -94,29 +119,37 @@ const productDate = snapshot?.productDatetime
|
||||||
<DepartementGrid colorsByDept={colorsByDept} />
|
<DepartementGrid colorsByDept={colorsByDept} />
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!error && alertsToday.length > 0 && (
|
!error && activeDeptCodes.length > 0 && (
|
||||||
<section class="border-t border-slate-200 bg-white">
|
<section class="border-t border-slate-200 bg-white">
|
||||||
<div class="container-tight py-8">
|
<div class="container-tight py-8">
|
||||||
<h2 class="mb-4 text-xl font-semibold text-slate-900">Alertes actives</h2>
|
<h2 class="mb-4 text-xl font-semibold text-slate-900">
|
||||||
|
Départements en alerte ({activeDeptCodes.length})
|
||||||
|
</h2>
|
||||||
|
<p class="mb-4 text-sm text-slate-500">
|
||||||
|
Triés par numéro de département. Un département peut cumuler plusieurs phénomènes (ex: canicule + orages).
|
||||||
|
</p>
|
||||||
<ul class="space-y-2">
|
<ul class="space-y-2">
|
||||||
{alertsToday
|
{activeDeptCodes.map((code) => {
|
||||||
.sort((a, b) => b.colorId - a.colorId)
|
const dept = getDepartement(code);
|
||||||
.slice(0, 50)
|
|
||||||
.map((a) => {
|
|
||||||
const dept = getDepartement(a.departement);
|
|
||||||
if (!dept) return null;
|
if (!dept) return null;
|
||||||
|
const alerts = (alertsByDept.get(code) ?? []).slice().sort((a, b) => b.colorId - a.colorId);
|
||||||
return (
|
return (
|
||||||
<li class="flex flex-wrap items-center justify-between gap-3 rounded border border-slate-200 px-3 py-2">
|
<li class="rounded border border-slate-200 px-3 py-2">
|
||||||
<a href={`/departement/${a.departement}`} class="font-medium no-underline">
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||||
{dept.name} ({a.departement})
|
<a href={`/departement/${code}`} class="font-medium no-underline">
|
||||||
|
<span class="font-mono text-slate-500">{code}</span> · {dept.name}
|
||||||
</a>
|
</a>
|
||||||
|
<div class="flex flex-wrap gap-1.5">
|
||||||
|
{alerts.map((a) => (
|
||||||
<VigilanceChip colorId={a.colorId} phenomenonId={a.phenomenonId} />
|
<VigilanceChip colorId={a.colorId} phenomenonId={a.phenomenonId} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -18,16 +18,22 @@ export const prerender = false;
|
||||||
<section class="container-tight py-8 prose prose-slate max-w-none">
|
<section class="container-tight py-8 prose prose-slate max-w-none">
|
||||||
<h2>Éditeur du site</h2>
|
<h2>Éditeur du site</h2>
|
||||||
<p>
|
<p>
|
||||||
Site édité à titre personnel, <strong>sans but lucratif</strong>, par :
|
Site édité <strong>à titre personnel, sans but lucratif</strong>, par :
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Florian Bouchet — développeur indépendant</li>
|
<li>Florian Bouchet — personne physique</li>
|
||||||
<li>Contact : <a href="mailto:florian@nocleus.com">florian@nocleus.com</a></li>
|
<li>Contact : <a href="mailto:florian@nocleus.com">florian@nocleus.com</a></li>
|
||||||
<li>Statut : personne physique (pas d'entreprise immatriculée, pas d'association)</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
Le site n'a aucune vocation commerciale. Aucun chiffre d'affaires, aucune publicité, aucune
|
Info Canicule n'a <strong>aucune vocation commerciale</strong> : aucune publicité, aucun chiffre
|
||||||
collecte de données à des fins de monétisation.
|
d'affaires, aucune collecte de données à des fins de monétisation.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Distinction importante</strong> : l'éditeur exerce par ailleurs une activité de
|
||||||
|
développement sous le statut de micro-entreprise « <em>Nocleus</em> ». Cette activité commerciale
|
||||||
|
est <strong>totalement indépendante</strong> du site Info Canicule, qui est édité hors cadre
|
||||||
|
professionnel, sur fonds propres, à titre purement personnel et bénévole. Aucun service de la
|
||||||
|
micro-entreprise n'est financé, promu ou rattaché à ce site.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Hébergement</h2>
|
<h2>Hébergement</h2>
|
||||||
|
|
@ -100,10 +106,10 @@ export const prerender = false;
|
||||||
|
|
||||||
<h2>Propriété intellectuelle</h2>
|
<h2>Propriété intellectuelle</h2>
|
||||||
<p>
|
<p>
|
||||||
Le code source du site est sous licence libre (
|
Le code source du site est disponible sur demande à
|
||||||
<a href="https://git.nocleus.com/florian/info-canicule" rel="noopener">repo Forgejo</a>
|
<a href="mailto:florian@nocleus.com">florian@nocleus.com</a>. Les données affichées sont sous
|
||||||
). Les données affichées sont sous Licence Ouverte 2.0 et réutilisables librement, y compris
|
Licence Ouverte 2.0 et réutilisables librement, y compris commercialement, à condition de citer
|
||||||
commercialement, à condition de citer Météo France et la licence.
|
Météo France et la licence.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
L'endpoint <code>/api/vigilance</code> diffuse le snapshot courant en JSON (CORS *), pour
|
L'endpoint <code>/api/vigilance</code> diffuse le snapshot courant en JSON (CORS *), pour
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,7 @@ export const prerender = false;
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<strong>Signaler un bug ou une typo</strong> : par mail à
|
<strong>Signaler un bug ou une typo</strong> : par mail à
|
||||||
<a href="mailto:florian@nocleus.com">florian@nocleus.com</a> ou via une issue sur le
|
<a href="mailto:florian@nocleus.com">florian@nocleus.com</a>.
|
||||||
<a href="https://git.nocleus.com/florian/info-canicule" rel="noopener">repo Forgejo</a>.
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Partager le site</strong> autour de vous, en particulier auprès de personnes fragiles
|
<strong>Partager le site</strong> autour de vous, en particulier auprès de personnes fragiles
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,18 @@ const require = createRequire(import.meta.url);
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||||
|
// Les classes vigilance-chip-{1..4} sont construites dynamiquement (`vigilance-chip-${colorId}`),
|
||||||
|
// donc le scanner statique ne les voit pas — on les force ici.
|
||||||
|
safelist: [
|
||||||
|
'vigilance-chip-1',
|
||||||
|
'vigilance-chip-2',
|
||||||
|
'vigilance-chip-3',
|
||||||
|
'vigilance-chip-4',
|
||||||
|
'bg-vigilance-vert',
|
||||||
|
'bg-vigilance-jaune',
|
||||||
|
'bg-vigilance-orange',
|
||||||
|
'bg-vigilance-rouge',
|
||||||
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue