Files
veye-lalwa/web/src/routes/+page.svelte
T

363 lines
8.9 KiB
Svelte
Raw Normal View History

<script lang="ts">
import BadgeStatut from '$composants/BadgeStatut.svelte';
import { dateLongue, joursRestants } from '$lib/format';
import type { PageData } from './$types';
const { data }: { data: PageData } = $props();
// L'encart « point de veille » tourne sur les sept analyses transversales.
let analyseCourante = $state(0);
const analyse = $derived(data.insights[analyseCourante]);
</script>
<svelte:head>
<title>Veille législative Gwadloup — tableau de bord</title>
<meta
name="description"
content="Suivi des lois françaises et de leurs effets sur les libertés des individus, des associations et des entreprises, avec un zoom Guadeloupe."
/>
</svelte:head>
<section class="hero">
<h1>Ki lwa ka chanjé lavi an nou ?</h1>
<p class="accroche">
{data.compteurs.total} textes suivis, {data.compteurs.sources} sources vérifiables.
Données arrêtées au {dateLongue(data.dateArrete)}.
</p>
<form class="recherche" action="/recherche" method="get" role="search">
<label class="visuellement-cache" for="q">Rechercher dans les textes suivis</label>
<input
id="q"
name="q"
type="search"
placeholder="chlordécone, associations, facturation électronique…"
autocomplete="off"
/>
<button class="bouton" type="submit"><span>Rechercher</span></button>
</form>
</section>
<section class="section">
<h2>Où en sont les textes</h2>
<div class="compteurs">
{#each data.compteurs.parStatut as ligne (ligne.valeur)}
<a class="compteur" href="/recherche?statut={ligne.valeur}">
<strong>{ligne.nombre}</strong>
<span>{ligne.libelle}</span>
</a>
{/each}
</div>
<div class="ligne raccourcis">
<a class="bouton bouton-discret" href="/recherche?statut=saisie_cc">
<span>{data.compteurs.devantCc} devant le Conseil constitutionnel</span>
</a>
<a class="bouton bouton-discret" href="/recherche?guadeloupe=forte">
<span>{data.compteurs.guadeloupeForte} de pertinence forte pour la Guadeloupe</span>
</a>
<a class="bouton bouton-discret" href="/recherche?confiance=low">
<span>{data.compteurs.aVerifier} à vérifier</span>
</a>
</div>
</section>
<div class="deux-colonnes">
<section class="section">
<h2>Prochaines échéances</h2>
{#if data.echeances.length}
<ol class="echeances">
{#each data.echeances as echeance (echeance.code)}
{@const jours = joursRestants(echeance.date_echeance, data.dateArrete)}
<li class="carte echeance" class:gwada={echeance.concerne_guadeloupe === 1}>
<div class="quand">
<time datetime={echeance.date_echeance}>{dateLongue(echeance.date_echeance)}</time>
{#if jours >= 0}<span class="dans">dans {jours} j</span>{/if}
</div>
<h3>
{#if echeance.texte_id}
<a href="/textes/{echeance.texte_id}">{echeance.libelle}</a>
{:else}
{echeance.libelle}
{/if}
</h3>
{#if echeance.description}<p>{echeance.description}</p>{/if}
{#if echeance.concerne_guadeloupe === 0}
<p class="hors-gwada">Ne concerne pas la Guadeloupe.</p>
{/if}
{#if echeance.note_guadeloupe}
<p class="note-gwada">{echeance.note_guadeloupe}</p>
{/if}
</li>
{/each}
</ol>
<p><a href="/calendrier">Voir tout le calendrier →</a></p>
{:else}
<p class="vide">Aucune échéance dans les 45 prochains jours.</p>
{/if}
</section>
<div class="colonne-secondaire">
<section class="section">
<h2>Point de veille</h2>
{#if analyse}
<article class="carte analyse">
<h3>{analyse.titre}</h3>
<p>{analyse.corps.slice(0, 460)}{analyse.corps.length > 460 ? '…' : ''}</p>
{#if analyse.implications}
<p class="implications"><strong>Implications —</strong> {analyse.implications}</p>
{/if}
<div class="ligne navigation-analyse">
<span class="position">{analyseCourante + 1} / {data.insights.length}</span>
<button
type="button"
class="bouton bouton-discret"
onclick={() =>
(analyseCourante = (analyseCourante + 1) % data.insights.length)}
>
<span>Analyse suivante</span>
</button>
</div>
</article>
{/if}
</section>
<section class="section">
<h2>Décisions attendues</h2>
<ul class="decisions">
{#each data.decisionsAttendues as decision (decision.numero_affaire)}
<li>
<span class="affaire">{decision.numero_affaire}</span>
{#if decision.texte_id}
<a href="/textes/{decision.texte_id}">{decision.libelle}</a>
{:else}
<span>{decision.libelle}</span>
{/if}
{#if decision.date_echeance}
<span class="quand-decision">{dateLongue(decision.date_echeance)}</span>
{/if}
</li>
{/each}
</ul>
</section>
<section class="section">
<h2>Derniers changements détectés</h2>
{#if data.changements.length}
<ul class="changements">
{#each data.changements as changement, i (i)}
<li>
{#if changement.texte_id}
<a href="/textes/{changement.texte_id}">{changement.titre ?? changement.texte_id}</a>
{:else}
<span>{changement.description}</span>
{/if}
{#if changement.champ}
<span class="diff">
{changement.champ} : {changement.ancienne_valeur ?? '—'}
<strong>{changement.nouvelle_valeur}</strong>
</span>
{/if}
</li>
{/each}
</ul>
{:else}
<p class="vide">
Aucun changement enregistré. Le pipeline n'a pas encore tourné en mode écriture —
lancer <code>make update</code>.
</p>
{/if}
{#if data.run}
<p class="run">
Dernier passage : {data.run.horodatage} ({data.run.mode}),
{data.run.ajouts} ajout(s), {data.run.modifications} modification(s).
</p>
{/if}
</section>
</div>
</div>
<style>
.hero {
padding-block: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--line);
}
.accroche {
color: var(--muted);
max-width: 60ch;
}
.recherche {
display: flex;
gap: 0.5rem;
max-width: 720px;
margin-top: var(--space-2);
}
.recherche input {
flex: 1;
background: var(--card-bg);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
color: var(--blanc-creme);
font: inherit;
padding: 0.6rem 0.8rem;
}
.recherche input:focus {
border-color: var(--or-oki);
}
.compteurs {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 0.6rem;
}
.compteur {
display: flex;
flex-direction: column;
gap: 0.15rem;
padding: 0.7rem 0.85rem;
background: var(--card-bg);
border: var(--border-card);
border-left: 4px solid var(--or-oki);
border-radius: var(--radius-md);
color: var(--blanc-creme);
text-decoration: none;
}
.compteur:hover {
background: var(--card-bg-survol);
}
.compteur strong {
font-family: var(--font-display);
font-size: 1.6rem;
line-height: 1;
color: var(--or-oki);
}
.compteur span {
font-size: 0.8rem;
color: var(--muted);
}
.raccourcis {
margin-top: var(--space-2);
}
.deux-colonnes {
display: grid;
grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
gap: var(--space-3);
}
@media (max-width: 900px) {
.deux-colonnes {
grid-template-columns: 1fr;
}
}
.echeances {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 0.6rem;
}
.echeance {
border-left-color: var(--muted-fort);
}
.echeance.gwada {
border-left-color: var(--turquoise-caraibes);
}
.echeance h3 {
font-size: 0.95rem;
text-transform: none;
margin: 0.2rem 0 0.3rem;
}
.echeance p {
font-size: 0.85rem;
color: var(--muted);
margin: 0 0 0.3rem;
}
.quand {
display: flex;
gap: 0.6rem;
align-items: baseline;
font-size: 0.8rem;
}
.quand time {
color: var(--or-oki);
font-weight: 600;
}
.dans {
color: var(--muted-fort);
}
.hors-gwada {
color: var(--rouge-oki) !important;
font-weight: 600;
}
.note-gwada {
color: var(--turquoise-caraibes) !important;
}
.analyse h3 {
text-transform: none;
font-size: 1rem;
}
.analyse p {
font-size: 0.87rem;
color: var(--muted);
}
.implications {
border-left: 2px solid var(--or-oki);
padding-left: 0.7rem;
}
.navigation-analyse {
justify-content: space-between;
}
.position {
font-size: 0.78rem;
color: var(--muted-fort);
font-variant-numeric: tabular-nums;
}
.decisions,
.changements {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 0.45rem;
font-size: 0.85rem;
}
.decisions li,
.changements li {
padding-bottom: 0.45rem;
border-bottom: 1px solid var(--line);
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
align-items: baseline;
}
.affaire {
font-family: var(--font-mono);
font-size: 0.78rem;
color: var(--orange-flamme);
flex: none;
}
.quand-decision,
.diff {
color: var(--muted-fort);
font-size: 0.78rem;
}
.vide,
.run {
font-size: 0.82rem;
color: var(--muted-fort);
}
code {
font-family: var(--font-mono);
font-size: 0.85em;
background: var(--card-bg);
padding: 0.1em 0.3em;
border-radius: 2px;
}
</style>