feat: badge published events with their direct mairie link

Additive enrichment after the list renders: never blocks the list, and a
scraping failure leaves it intact without badge. The 'Créer' button is
removed for published events (anti-duplicate).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Pierre Martin
2026-07-17 18:18:15 +02:00
co-authored by Claude Opus 4.8
parent 6c4f4219f1
commit 9c7099f224
2 changed files with 80 additions and 1 deletions
+22
View File
@@ -59,6 +59,28 @@ h1 {
cursor: pointer;
}
.evenement-publication {
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 0.25rem;
}
.evenement-publie {
align-self: flex-end;
padding: 0.15rem 0.5rem;
border-radius: 0.75rem;
background: #1a7f37;
color: #fff;
font-size: 0.8rem;
font-weight: 600;
}
.evenement-lien-mairie {
font-size: 0.85rem;
}
#etat {
margin-bottom: 1.5rem;
}
+58 -1
View File
@@ -2,6 +2,8 @@ import { recupererFluxCalendrier } from "./nextcloud.js";
import { parserEvenements } from "./evenements.js";
import { formaterDate, formaterLieu, trierParDebut } from "./presentation.js";
import { deposerEvenement } from "./transfert.js";
import { calculerPeriode, extraireCartes, recupererAgendaPublie } from "./agenda-mairie.js";
import { indexerCartes, trouverPublication } from "./publication.js";
const URL_NEXTCLOUD = "https://atelier-huit.frama.space/";
@@ -49,6 +51,8 @@ function rendreEvenements(evenements) {
function rendreEvenement(evenement) {
const item = document.createElement("li");
item.className = "evenement";
// Ancre pour retrouver le <li> lors de l'annotation « publié » (phase 2).
item.dataset.uid = evenement.uid;
const infos = document.createElement("div");
infos.className = "evenement-infos";
@@ -135,10 +139,63 @@ async function charger() {
// « Illegal invocation » dans le navigateur.
const res = await recupererFluxCalendrier((u, o) => fetch(u, o));
if (res.ok) {
rendreEvenements(trierParDebut(parserEvenements(res.flux, new Date())));
const evenements = trierParDebut(parserEvenements(res.flux, new Date()));
rendreEvenements(evenements);
// Enrichissement additif : la liste est déjà rendue, l'agenda ne la
// bloque jamais. Un échec de scraping laisse la liste intacte, sans badge.
// Fire-and-forget : le `.catch` garantit qu'aucune erreur imprévue
// (indexation, annotation) ne devienne une rejection non gérée.
annoterPublications(evenements).catch((erreur) =>
console.error("Écho du Huit : annotation des publications interrompue.", erreur),
);
} else {
rendreErreur(res.erreur);
}
}
// Phase 2 : marque « publié » les événements retrouvés sur l'agenda mairie et y
// pose leur lien direct. Dégradation gracieuse : agenda injoignable ou structure
// changée → on ne touche à rien (aucun faux positif, biais précision v1).
async function annoterPublications(evenements) {
if (evenements.length === 0) return;
const periode = calculerPeriode(evenements, new Date());
// Agenda public : fetch SANS credentials.
const res = await recupererAgendaPublie((u, o) => fetch(u, o), extraireCartes, periode);
if (!res.ok) return;
const index = indexerCartes(res.cartes);
for (const evenement of evenements) {
const publication = trouverPublication(evenement, index);
if (!publication.publie) continue;
const item = liste.querySelector(`[data-uid="${CSS.escape(evenement.uid)}"]`);
if (item) annoterItem(item, publication.lien);
}
}
// Badge « Publié » + lien direct ; le bouton « Créer » est retiré (anti-doublon :
// un événement déjà publié n'a pas à être re-soumis). textContent uniquement,
// le lien vient d'une source externe.
function annoterItem(item, lien) {
item.querySelector(".evenement-creer")?.remove();
const publication = document.createElement("div");
publication.className = "evenement-publication";
const badge = document.createElement("span");
badge.className = "evenement-publie";
badge.textContent = "Publié";
const voir = document.createElement("a");
voir.className = "evenement-lien-mairie";
voir.href = lien;
voir.target = "_blank";
voir.rel = "noopener";
voir.textContent = "Voir sur le site mairie";
publication.append(badge, voir);
item.append(publication);
}
charger();