feat: show calendar fetch state on the list page

Orchestrate the feed retrieval from the list page: loading / success
(event count) / actionable French error states with a Réessayer button
and an Ouvrir Nextcloud link when the session is missing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Pierre Martin
2026-06-30 00:07:43 +02:00
co-authored by Claude Opus 4.8
parent 1432aba561
commit b27bb478bf
3 changed files with 91 additions and 7 deletions
+22
View File
@@ -27,3 +27,25 @@ h1 {
color: #666;
font-style: italic;
}
#etat {
margin-bottom: 1.5rem;
}
.etat-neutre {
color: #666;
}
.etat-erreur {
color: #b3261e;
}
#etat a {
display: inline-block;
margin-right: 1rem;
}
#etat button {
font: inherit;
cursor: pointer;
}
+1
View File
@@ -9,6 +9,7 @@
<body>
<main>
<h1>Écho du Huit</h1>
<div id="etat" role="status" aria-live="polite"></div>
<ul id="liste"></ul>
</main>
<script type="module" src="liste.js"></script>
+68 -7
View File
@@ -1,8 +1,69 @@
// Squelette : la récupération des deux sources (Atelier du Huit, ville de
// Cugnaux) arrivera dans des tâches ultérieures. Pour l'instant, état vide.
const liste = document.querySelector("#liste");
import {
compterEvenements,
recupererFluxCalendrier,
} from "./nextcloud.js";
const vide = document.createElement("li");
vide.className = "liste-vide";
vide.textContent = "Aucune donnée pour l'instant.";
liste.append(vide);
const URL_NEXTCLOUD = "https://atelier-huit.frama.space/";
// Mapping type d'erreur → message FR non technique et actionnable.
const MESSAGES_ERREUR = {
"non-connecte":
"Tu n'es pas connecté·e à Nextcloud. Ouvre Nextcloud dans un onglet, connecte-toi, puis reviens et clique sur Réessayer.",
reseau:
"Impossible de joindre Nextcloud. Vérifie ta connexion internet, puis réessaie.",
serveur:
"Nextcloud a renvoyé une erreur inattendue. Réessaie dans un moment.",
};
const etat = document.querySelector("#etat");
function rendreNeutre(texte) {
etat.replaceChildren();
const p = document.createElement("p");
p.className = "etat-neutre";
p.textContent = texte;
etat.append(p);
}
function rendreSucces(flux) {
const nombre = compterEvenements(flux);
rendreNeutre(`${nombre} événement${nombre > 1 ? "s" : ""} lu${nombre > 1 ? "s" : ""}`);
}
function rendreErreur(typeErreur) {
etat.replaceChildren();
const message = document.createElement("p");
message.className = "etat-erreur";
message.textContent = MESSAGES_ERREUR[typeErreur];
etat.append(message);
if (typeErreur === "non-connecte") {
const lien = document.createElement("a");
lien.href = URL_NEXTCLOUD;
lien.target = "_blank";
lien.rel = "noopener";
lien.textContent = "Ouvrir Nextcloud";
etat.append(lien);
}
const reessayer = document.createElement("button");
reessayer.type = "button";
reessayer.textContent = "Réessayer";
reessayer.addEventListener("click", charger);
etat.append(reessayer);
}
async function charger() {
rendreNeutre("Lecture du calendrier…");
// Wrapper obligatoire : un `fetch` nu détaché de `window` lève
// « Illegal invocation » dans le navigateur.
const res = await recupererFluxCalendrier((u, o) => fetch(u, o));
if (res.ok) {
rendreSucces(res.flux);
} else {
rendreErreur(res.erreur);
}
}
charger();