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:
co-authored by
Claude Opus 4.8
parent
6c4f4219f1
commit
9c7099f224
@@ -59,6 +59,28 @@ h1 {
|
|||||||
cursor: pointer;
|
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 {
|
#etat {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-1
@@ -2,6 +2,8 @@ import { recupererFluxCalendrier } from "./nextcloud.js";
|
|||||||
import { parserEvenements } from "./evenements.js";
|
import { parserEvenements } from "./evenements.js";
|
||||||
import { formaterDate, formaterLieu, trierParDebut } from "./presentation.js";
|
import { formaterDate, formaterLieu, trierParDebut } from "./presentation.js";
|
||||||
import { deposerEvenement } from "./transfert.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/";
|
const URL_NEXTCLOUD = "https://atelier-huit.frama.space/";
|
||||||
|
|
||||||
@@ -49,6 +51,8 @@ function rendreEvenements(evenements) {
|
|||||||
function rendreEvenement(evenement) {
|
function rendreEvenement(evenement) {
|
||||||
const item = document.createElement("li");
|
const item = document.createElement("li");
|
||||||
item.className = "evenement";
|
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");
|
const infos = document.createElement("div");
|
||||||
infos.className = "evenement-infos";
|
infos.className = "evenement-infos";
|
||||||
@@ -135,10 +139,63 @@ async function charger() {
|
|||||||
// « Illegal invocation » dans le navigateur.
|
// « Illegal invocation » dans le navigateur.
|
||||||
const res = await recupererFluxCalendrier((u, o) => fetch(u, o));
|
const res = await recupererFluxCalendrier((u, o) => fetch(u, o));
|
||||||
if (res.ok) {
|
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 {
|
} else {
|
||||||
rendreErreur(res.erreur);
|
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();
|
charger();
|
||||||
|
|||||||
Reference in New Issue
Block a user