Compare commits

..

4 Commits

Author SHA1 Message Date
Quentin Rouiller 9d701701e6 v5.0.8 — Correctifs 2026-04-21 12:53:22 +02:00
Quentin Rouiller 77c68dbe83 v5.0.7 — Correctifs 2026-04-21 12:50:36 +02:00
Quentin Rouiller d4fc8ff250 v5.0.6 — Correctifs 2026-04-21 12:46:58 +02:00
Quentin Rouiller 3996e3fb4f v5.0.5 — Correctifs admin/UX 2026-04-21 12:42:50 +02:00
3 changed files with 31 additions and 13 deletions
+14 -1
View File
@@ -131,7 +131,17 @@ async function fetchXhr2(origin, phpsessid, actionId) {
async function fetchFicheHtml(origin, phpsessid, formLink) { async function fetchFicheHtml(origin, phpsessid, formLink) {
const url = `${origin}/index.php?${formLink}&PHPSESSID=${encodeURIComponent(phpsessid)}`; const url = `${origin}/index.php?${formLink}&PHPSESSID=${encodeURIComponent(phpsessid)}`;
console.log("[bg] fetchFicheHtml →", url.substring(0, 120)); console.log("[bg] fetchFicheHtml →", url.substring(0, 120));
const r = await fetch(url, { credentials: "include" }); // v5.0.8 : EasyVista retourne maintenant un <script> de redirection si on
// fait la requête sans Referer. Probablement une protection CSRF ajoutée
// récemment. On ajoute un Referer qui simule une navigation depuis la
// page principale du planning.
const r = await fetch(url, {
credentials: "include",
headers: {
"Referer": `${origin}/index.php?eventName=HelpDesk_PlanningItem`,
"X-Requested-With": "XMLHttpRequest"
}
});
if (!r.ok) { if (!r.ok) {
const err = new Error("HTTP " + r.status); const err = new Error("HTTP " + r.status);
err.kind = classifyHttpStatus(r.status); err.kind = classifyHttpStatus(r.status);
@@ -140,6 +150,9 @@ async function fetchFicheHtml(origin, phpsessid, formLink) {
} }
const html = await r.text(); const html = await r.text();
console.log("[bg] fiche status =", r.status, "| taille =", html.length); console.log("[bg] fiche status =", r.status, "| taille =", html.length);
if (html.length < 500) {
console.warn("[bg] ⚠ fiche très courte, contenu =", JSON.stringify(html));
}
return html; return html;
} }
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Planification", "name": "Planification",
"version": "5.0.4", "version": "5.0.8",
"description": "Vue claire et rapide du planning des techniciens EasyVista. Regroupe interventions et réservations par tech, affiche horaires, contact, lieu, catégorie et statut en un coup d'œil.", "description": "Vue claire et rapide du planning des techniciens EasyVista. Regroupe interventions et réservations par tech, affiche horaires, contact, lieu, catégorie et statut en un coup d'œil.",
"permissions": ["activeTab", "scripting", "storage", "tabs", "alarms"], "permissions": ["activeTab", "scripting", "storage", "tabs", "alarms"],
"host_permissions": [ "host_permissions": [
+16 -11
View File
@@ -1955,11 +1955,19 @@ async function loadForDate(isoDate, opts = {}) {
) )
); );
// v5.0.6 : logs détaillés pour diagnostiquer pourquoi le fetch ne se
// lance pas.
const totalIv = merged.techs.reduce((s, t) => s + (t.interventions || []).length, 0);
const totalInterIv = merged.techs.reduce((s, t) =>
s + (t.interventions || []).filter(i => i.type === "AL-Intervention").length, 0);
const notFetched = merged.techs.reduce((s, t) =>
s + (t.interventions || []).filter(i => i.type === "AL-Intervention" && !i.ficheFetched).length, 0);
console.log(`[load] merged : ${merged.techs.length} techs, ${totalIv} iv totales, ${totalInterIv} interventions réelles, ${notFetched} sans fiche`);
console.log(`[load] needFetch = ${needFetch} | doStatusRefresh = ${!!opts.doStatusRefresh} | forceRefetch = ${!!opts.forceRefetch} | aborted = ${isRefreshAborted(myToken)}`);
// v4.3.2 : avant de lancer le fetch des fiches (lourd, ~460 KB chacune), // v4.3.2 : avant de lancer le fetch des fiches (lourd, ~460 KB chacune),
// on fait d'abord un batch xhr2 (léger, ~2-5 KB chacun) pour récupérer // on fait d'abord un batch xhr2 (léger, ~2-5 KB chacun) pour récupérer
// les vraies infos contact/lieu de toutes les interventions en parallèle. // les vraies infos contact/lieu de toutes les interventions en parallèle.
// Comme ça les cartes s'enrichissent en 1-3 secondes au lieu d'attendre
// que l'utilisateur les survole une par une.
if (!isRefreshAborted(myToken)) { if (!isRefreshAborted(myToken)) {
await prefetchAllXhr2(merged.techs, myToken, opts.doStatusRefresh); await prefetchAllXhr2(merged.techs, myToken, opts.doStatusRefresh);
} }
@@ -1968,13 +1976,10 @@ async function loadForDate(isoDate, opts = {}) {
const tFiches = performance.now(); const tFiches = performance.now();
const nFiches = merged.techs.flatMap(t=>t.interventions).filter(i=>i.type==="AL-Intervention").length; const nFiches = merged.techs.flatMap(t=>t.interventions).filter(i=>i.type==="AL-Intervention").length;
console.log(`[load] début fetch des ${nFiches} fiches (statuts)…`); console.log(`[load] début fetch des ${nFiches} fiches (statuts)…`);
// forceAll : uniquement si refresh manuel (bouton "rafraichir").
// À la navigation normale entre dates, on ne refetch que les iv non
// encore enrichies (ficheFetched=false) — ça reprend là où on s'était
// arrêté si un refresh précédent a été interrompu par un changement de
// date.
await refreshStatuses(merged.techs, isoDate, { forceAll: !!opts.doStatusRefresh, myToken }); await refreshStatuses(merged.techs, isoDate, { forceAll: !!opts.doStatusRefresh, myToken });
console.log(`[load] fiches finies en ${Math.round(performance.now() - tFiches)} ms`); console.log(`[load] fiches finies en ${Math.round(performance.now() - tFiches)} ms`);
} else {
console.log(`[load] PAS DE FETCH : needFetch=${needFetch}, doStatusRefresh=${!!opts.doStatusRefresh}, aborted=${isRefreshAborted(myToken)}`);
} }
// 6. Sauvegarder dans le cache (une seule fois, après que tout soit enrichi) // 6. Sauvegarder dans le cache (une seule fois, après que tout soit enrichi)
@@ -3942,16 +3947,16 @@ function buildCard(tech, isoDate) {
// bouton visible). Contient : détail période + bouton supprimer si // bouton visible). Contient : détail période + bouton supprimer si
// c'est une absence supprimable (actionId réel, pas pompier récurrent). // c'est une absence supprimable (actionId réel, pas pompier récurrent).
if (ab.actionId && !ab.isPompier && !ab._recurring) { if (ab.actionId && !ab.isPompier && !ab._recurring) {
// On attache le tooltip sur la CARD ENTIÈRE (cardEl) — comme ça // On attache le tooltip sur la CARD ENTIÈRE (card) — comme ça
// survoler n'importe où sur la zone grisée "absent" le déclenche. // survoler n'importe où sur la zone grisée "absent" le déclenche.
const ivCopy = { const ivCopy = {
...ab, ...ab,
type: "AL-Absence" // force pour buildTooltipHTML type: "AL-Absence" // force pour buildTooltipHTML
}; };
cardEl.addEventListener("mouseenter", (e) => { card.addEventListener("mouseenter", (e) => {
showTooltip(e, ivCopy, cardEl); showTooltip(e, ivCopy, card);
}); });
cardEl.addEventListener("mouseleave", () => { card.addEventListener("mouseleave", () => {
hideTooltip(); hideTooltip();
}); });
} }