Public Access
v2026.5.50 : texte des actions rétabli (format EasyVista du 29.09), catégories Roll Out et Déménagement
- Timeline et fiche : la cellule JSON qui porte ACTION_ID est repérée par son contenu, la description par le nom de sa colonne (EasyVista est passé de 14 à 13 cellules par action et le texte des actions ne s'affichait plus). - Format EasyVista non reconnu : avertissement unique dans la console ; fiche ou historique non chargé journalisé. - Réservations : catégories Roll Out et Déménagement, propres à la Planification (EasyVista reste « Evènements spéciaux ») ; catégorie inconnue conservée à l'édition ; proposée à la saisie du n° d'une intervention Roll Out. - firefox-updates.json : 2026.5.50, xpi signé par AMO.
This commit is contained in:
@@ -9,6 +9,28 @@
|
||||
|
||||
---
|
||||
|
||||
## v2026.5.50 — Texte des actions rétabli (format EasyVista du 29.09.2026), catégories Roll Out et Déménagement
|
||||
|
||||
> Correctif urgent : depuis le 29 septembre 2026, le texte complet des actions ne s'affichait plus. EasyVista renvoie désormais les actions avec une cellule de moins, et l'extension les lisait par position. Elle les retrouve maintenant par leur contenu. Cette version ajoute aussi deux catégories de réservation.
|
||||
|
||||
### Texte des actions
|
||||
|
||||
- **Texte complet des actions rétabli** dans les bulles et les fiches, avec l'ancien comme avec le nouveau format d'EasyVista.
|
||||
- **Plus robuste aux prochains changements** : la description est repérée par le nom de sa colonne, et l'identifiant de l'action par son contenu, plus par leur position.
|
||||
- **Changement de format signalé** : si EasyVista modifie encore la structure au point qu'aucune action ne soit lisible, un avertissement apparaît dans la console (une seule fois, sans aucune donnée personnelle). Les fiches ou historiques qui ne se chargent pas sont aussi signalés dans la console.
|
||||
|
||||
### Réservations
|
||||
|
||||
- **Deux nouvelles catégories : Roll Out et Déménagement.** Elles sont propres à la Planification : dans EasyVista, la réservation reste « Evènements spéciaux ». Elles sont partagées entre tous les postes par le dossier partagé, comme le n°, la ville et la rue.
|
||||
- **Catégorie proposée à la saisie du n°** : le n° d'une intervention Roll Out propose Roll Out.
|
||||
- **Catégorie inconnue conservée** : une catégorie posée par une version plus récente n'est plus effacée quand on modifie la réservation.
|
||||
|
||||
### Déploiement
|
||||
|
||||
- **Mettre à jour tous les postes le même jour.** Un poste resté en 2026.5.49 qui modifie une réservation Roll Out ou Déménagement effacerait cette catégorie sur tous les postes.
|
||||
|
||||
---
|
||||
|
||||
## v2026.5.49 — Sécurité hors session, onglet Email clarifié, lecture au défilement, affichage et synchronisation fiabilisés
|
||||
|
||||
> Cette version renforce la **sécurité** quand aucune session EasyVista n'est active, clarifie l'**onglet Email** (déplacement et annulation bien séparés, aperçu de la copie), améliore la **lecture du planning au défilement** (le nom du technicien, son compteur et la frise horaire restent visibles), et corrige en profondeur l'**affichage de la reconnexion** ainsi que plusieurs comportements de la synchronisation et des réservations.
|
||||
|
||||
@@ -9,7 +9,7 @@ Extension Chrome / Firefox pour visualiser de manière claire et rapide le plann
|
||||
- **Auteur** : Quentin (FRS), Technicien DGNSI — Canton de Vaud
|
||||
- **Public cible** : coordinateurs DGNSI qui pilotent dans EasyVista (`itsma.etat-de-vaud.ch` / `itsma.vd.ch`) le planning de l'équipe technicienne
|
||||
- **Démarrage projet** : jeudi 16 avril 2026
|
||||
- **Version actuelle** : [`v2026.5.49`](https://gitea.netaplaid.ch/FroSteel/Planification/releases/tag/v2026.5.49) (latest)
|
||||
- **Version actuelle** : [`v2026.5.50`](https://gitea.netaplaid.ch/FroSteel/Planification/releases/tag/v2026.5.50) (latest)
|
||||
- **Contact** : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact) ou [ouvrir une issue](https://gitea.netaplaid.ch/FroSteel/Planification/issues/new)
|
||||
- **Manifest** : V3 (Chrome/Edge/Firefox 140+)
|
||||
- **Format** : `.zip` (Chromium) + `.xpi` signé Mozilla (Firefox)
|
||||
@@ -121,7 +121,14 @@ Le numéro de **majeure** n'est **pas** un mois et **pas** un chiffre lié au ca
|
||||
|
||||
## Versions notables
|
||||
|
||||
### `v2026.5.49` (latest, 1 septembre 2026) — Sécurité hors session, onglet Email clarifié, lecture au défilement, affichage fiabilisé
|
||||
### `v2026.5.50` (latest, 29 septembre 2026) — Texte des actions rétabli, catégories Roll Out et Déménagement
|
||||
|
||||
- Correctif : depuis le 29 septembre 2026, EasyVista renvoie les actions avec une cellule de moins et le texte complet des actions ne s'affichait plus ; l'extension retrouve désormais ces informations par leur contenu et par le nom des colonnes, pour tenir aux prochains changements de format
|
||||
- Si EasyVista change encore de format, un avertissement clair apparaît dans la console (une seule fois)
|
||||
- Réservations : deux nouvelles catégories, Roll Out et Déménagement, propres à la Planification (dans EasyVista, la réservation reste « Evènements spéciaux ») ; partagées entre postes comme les autres informations de réservation
|
||||
- Mettre à jour tous les postes le même jour : un poste resté en 2026.5.49 effacerait ces catégories en modifiant la réservation
|
||||
|
||||
### `v2026.5.49` (1 septembre 2026) — Sécurité hors session, onglet Email clarifié, lecture au défilement, affichage fiabilisé
|
||||
|
||||
- Sans session EasyVista active : plus d'écriture dans le dossier partagé (cache local seul) ni de modification (déplacement, suppression, absence, réservation bloqués proprement)
|
||||
- Onglet Email séparé en deux sections (déplacement / annulation), copie (CC) commune avec aperçu en direct des adresses retenues, variables en colonnes
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
"addons": {
|
||||
"[email protected]": {
|
||||
"updates": [
|
||||
{
|
||||
"version": "2026.5.50",
|
||||
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.50/planification-v2026.5.50-firefox.xpi",
|
||||
"update_hash": "sha256:314ae3cba7e31d1f6eb89c419a666d49d156cd78a83c116de03f5e48b8a094df"
|
||||
},
|
||||
{
|
||||
"version": "2026.5.49",
|
||||
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.49/planification-v2026.5.49-firefox.xpi",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Planification",
|
||||
"version": "2026.5.49",
|
||||
"version": "2026.5.50",
|
||||
"description": "Vue claire et rapide du planning des techniciens EasyVista. Développé par Quentin — DGNSI, Canton de Vaud.",
|
||||
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl/El4I9gxcZkp5bnheg4ZBCKxckRLt9xQ5x+i6phEPYlydl6NapWlkbOyycUaeTZcpoEh/tKQnpMpoEc5jY8zCoRTwh/vfejVBZE6ANDG/dc3iix/1Z8OhRI4zMaRYQ+DQTjS5Q6l6wkcwzbQB85HZNhke/k1qIW6RCuBjeJYmES5WVDwzfCNVvaF6uQvdOCpLjrA3+LI6vUBEwaMg1hM42mC3dU0z9p7qzAby/dH3XpZD9HTFXLJL9/1zryN9rXNDpvZwYWnJ0hLTu5tfmURkvgKOwquX1KJy9vupmhUALRtS08Fplx/e3kUH3LD+coS1ExLwoOZ+1ORBUE9JCB8wIDAQAB",
|
||||
"permissions": [
|
||||
|
||||
+114
-24
@@ -6624,7 +6624,13 @@ async function submitAbsence(opts) {
|
||||
// ville, rue, catégorie) au créneau, propagées par le merge et affichées dans
|
||||
// la bulle. Les réservations créées directement dans EV restent inchangées.
|
||||
const RESERVATION_TYPE_GUID = "{E7432422-55CB-4DB9-8A26-619D036E2155}"; // Evènements spéciaux
|
||||
const RESERVATION_CATEGORIES = ["Installation", "Remplacement", "Récupération", "Incident"];
|
||||
// v2026.5.50 — « Roll Out » et « Déménagement » : catégories propres à la
|
||||
// Planification (méta du carnet, partagée par le dossier) ; côté EasyVista, la
|
||||
// réservation reste « Evènements spéciaux ».
|
||||
const RESERVATION_CATEGORIES = ["Installation", "Remplacement", "Récupération", "Incident", "Roll Out", "Déménagement"];
|
||||
// Celles que seule la Planification repère (texte de l'action) : la fiche EV,
|
||||
// qui ne connaît que le catalogue, ne doit pas les remplacer.
|
||||
const CATEGORIES_PROPRES_PLANIFICATION = ["Roll Out", "Déménagement"];
|
||||
// v2026.5.46 — GUID du type "Pompier" (utilisé par le popup pompier de la
|
||||
// modal Réservation). Doit rester aligné avec HOLIDAY_TYPES.
|
||||
const POMPIER_TYPE_GUID = "{60D70502-063D-45AD-9415-25C1C556105F}";
|
||||
@@ -6638,7 +6644,8 @@ function _resTypeColorKey(cat) {
|
||||
case "récupération":
|
||||
case "recuperation": return "recup";
|
||||
case "incident": return "incident";
|
||||
default: return null;
|
||||
case "roll out": return "rollout";
|
||||
default: return null; // dont « Déménagement » : couleur par défaut des réservations
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6653,7 +6660,11 @@ function _lookupInterventionInfo(num) {
|
||||
if (String(iv.ref || "").trim() === n || String(iv.actionId || "").trim() === n) {
|
||||
const cat = (iv.categoryLine || "").toLowerCase();
|
||||
let categorie = "";
|
||||
if (/install/.test(cat)) categorie = "Installation";
|
||||
// Une réf I… (incident) ne reçoit pas les catégories propres à la Planification.
|
||||
const refIncident = /^I\d/.test(iv.ref || "");
|
||||
if (!refIncident && isRollOut(iv)) categorie = "Roll Out";
|
||||
else if (!refIncident && /d[ée]m[ée]nag/.test(cat)) categorie = "Déménagement";
|
||||
else if (/install/.test(cat)) categorie = "Installation";
|
||||
else if (/remplac/.test(cat)) categorie = "Remplacement";
|
||||
else if (/r[ée]cup/.test(cat)) categorie = "Récupération";
|
||||
else if (/incident/.test(cat)) categorie = "Incident";
|
||||
@@ -6772,7 +6783,13 @@ async function _fetchReservationInfo(num) {
|
||||
const titM = html.match(/"dbFieldName"\s*:\s*"TITLE_FR"[^}]*?"value"\s*:\s*"([^"]{2,300})"/);
|
||||
if (titM) titreFiche = (decodeJsonString ? decodeJsonString(titM[1]) : titM[1]).trim();
|
||||
const titreCourt = deriveShortTitle({ ref: n, categoryLine: titreFiche });
|
||||
const categorie = RESERVATION_CATEGORIES.includes(titreCourt) ? titreCourt : "";
|
||||
let categorie = RESERVATION_CATEGORIES.includes(titreCourt) ? titreCourt : "";
|
||||
// Catégories propres à la Planification, repérées dans le titre de la fiche
|
||||
// (deriveShortTitle ne les connaît pas sans le texte de l'action).
|
||||
if (!categorie && titreCourt !== "Incident") {
|
||||
if (/roll\s*-?\s*out/i.test(titreFiche)) categorie = "Roll Out";
|
||||
else if (/d[ée]m[ée]nag/i.test(titreFiche)) categorie = "Déménagement";
|
||||
}
|
||||
let ville = "", rue = "";
|
||||
const locM = html.match(/"dbFieldName"\s*:\s*"[^"]*(?:LOCATION|LOCALISATION|ADDRESS|ADRESSE)[^"]*"[^}]*?"value"\s*:\s*"([^"]{3,160})"/i)
|
||||
|| html.match(/Localisation[^>]{0,40}>\s*([^<]{3,160})/i);
|
||||
@@ -6828,11 +6845,18 @@ function _wireResNumberLookup(numField, cibles) {
|
||||
if (num === lastNum) return;
|
||||
lastNum = num;
|
||||
LOG.info("reservation", `n° saisi : ${num} → recherche infos (planning + EV)`);
|
||||
apply(_lookupInterventionInfo(num)); // instantané (cache planning)
|
||||
const infoPlanning = _lookupInterventionInfo(num);
|
||||
apply(infoPlanning); // instantané (cache planning)
|
||||
// Roll Out / Déménagement trouvé par le planning pour CE n° : la fiche EV ne
|
||||
// le voit pas et ne doit pas le remplacer par une catégorie du catalogue.
|
||||
const categoriePropre = infoPlanning && CATEGORIES_PROPRES_PLANIFICATION.includes(infoPlanning.categorie)
|
||||
? infoPlanning.categorie : "";
|
||||
const seq = ++seqRef; // fetch EV réel en arrière-plan
|
||||
if (loadingBar) loadingBar.classList.remove("hidden");
|
||||
_fetchReservationInfo(num).then(info => {
|
||||
if (seq === seqRef) { apply(info); if (loadingBar) loadingBar.classList.add("hidden"); }
|
||||
if (seq !== seqRef) return;
|
||||
apply(info && categoriePropre ? { ...info, categorie: categoriePropre } : info);
|
||||
if (loadingBar) loadingBar.classList.add("hidden");
|
||||
});
|
||||
};
|
||||
numField.addEventListener("change", trigger);
|
||||
@@ -7064,6 +7088,12 @@ function showReservationEditModal(iv) {
|
||||
const catSelect = document.createElement("select"); catSelect.className = "modal-form-select";
|
||||
const emptyOpt = document.createElement("option"); emptyOpt.value = ""; emptyOpt.textContent = "—"; catSelect.appendChild(emptyOpt);
|
||||
RESERVATION_CATEGORIES.forEach(c => { const o = document.createElement("option"); o.value = c; o.textContent = c; if (c === iv.resCategorie) o.selected = true; catSelect.appendChild(o); });
|
||||
// Catégorie inconnue de cette version (posée par une version plus récente,
|
||||
// reçue du dossier) : gardée telle quelle, sinon l'enregistrer l'effacerait
|
||||
// sur tous les postes (l'édition la plus récente gagne à la fusion).
|
||||
if (iv.resCategorie && !RESERVATION_CATEGORIES.includes(iv.resCategorie)) {
|
||||
const o = document.createElement("option"); o.value = iv.resCategorie; o.textContent = iv.resCategorie; o.selected = true; catSelect.appendChild(o);
|
||||
}
|
||||
catGroup.appendChild(catSelect); grid.appendChild(catGroup);
|
||||
const villeField = _mkField("Ville / village", iv.resVille, "");
|
||||
const rueField = _mkField("Rue / chemin / route", iv.resRue, "");
|
||||
@@ -9801,10 +9831,10 @@ const RX_LOGIN_COMMENTAIRE = /(?:^|\n|<br\s*\/?>)\s*([a-z0-9_]{3,12})\s*:\s+(\S[
|
||||
|
||||
/**
|
||||
* Extrait toutes les actions d'une fiche en parsant les blocs "rows" du HTML.
|
||||
* Une action a 14 values. Indices stables :
|
||||
* Une action a 13 values (14 jusqu'au 28.09.2026). Indices stables :
|
||||
* [2] = Intervenant (ex: "Nom, Prénom" ou "EZV_WS_REST_USER")
|
||||
* [4] = Type d'action (ex: "AL-Intervention", "Ajout d'informations")
|
||||
* [13] = JSON stringifié contenant ACTION_ID, AM_DONE_BY_ID, …
|
||||
* dernière cellule = JSON stringifié contenant ACTION_ID, AM_DONE_BY_ID, …
|
||||
*
|
||||
* v2026.5.45 — la position des dates et de la description varie selon le
|
||||
* layout EV (anciennement [8]/[9]/[11], aujourd'hui [6]/[7]/[9] sur les
|
||||
@@ -9850,26 +9880,30 @@ function parseAllActionsFromFicheHtml(html) {
|
||||
// Une "vraie" action a 14 valeurs. On se contente de 12 minimum
|
||||
// pour avoir au moins la description.
|
||||
//
|
||||
// v2026.5.44 on extrait aussi actionId / amDoneById depuis values[13]
|
||||
// qui est un JSON stringifié contenant {NAME, ACTION_ID, AM_DONE_BY_ID, …}.
|
||||
// v2026.5.44 on extrait aussi actionId / amDoneById depuis la cellule JSON
|
||||
// stringifié qui contient {NAME, ACTION_ID, AM_DONE_BY_ID, …}.
|
||||
// Permet de filtrer la "bonne action" par ACTION_ID au lieu de matcher
|
||||
// par nom de tech (le matching par nom échouait par exemple sur les
|
||||
// actions où l'intervenant est noté "EZV_WS_REST_USER" ou un format
|
||||
// qui ne correspond pas exactement au tech.name de la config).
|
||||
let actionId = null;
|
||||
let amDoneById = null;
|
||||
if (values.length >= 14 && values[13]) {
|
||||
// v2026.5.50 — la cellule JSON est repérée par son contenu (la dernière qui
|
||||
// mentionne ACTION_ID) : EV a retiré la cellule vide qui la précédait
|
||||
// (29.09.2026), elle est passée de values[13] à values[12].
|
||||
const valeurJson = [...values].reverse().find(v => v && v.includes("ACTION_ID"));
|
||||
if (valeurJson) {
|
||||
// v2026.5.46 — BUG : dans le HTML, les guillemets du JSON imbriqué sont
|
||||
// encodés " (pas des " littéraux). Le regex cherchait des " littéraux
|
||||
// → ACTION_ID et surtout AM_DONE_BY_ID étaient TOUJOURS null → le matching
|
||||
// d'action par id (le plus fiable, indépendant du nom) ne marchait JAMAIS.
|
||||
// On DÉCODE values[13] d'abord pour que le regex matche.
|
||||
const raw13 = decodeUnicodeEscapes(values[13]);
|
||||
// On DÉCODE la cellule d'abord pour que le regex matche.
|
||||
const jsonDecode = decodeUnicodeEscapes(valeurJson);
|
||||
// Parsing tolérant via regex (on accepte avec ou sans guillemets autour
|
||||
// des valeurs numériques) — robuste aux formats JSON avec ou sans
|
||||
// échappements résiduels.
|
||||
const mAid = raw13.match(/"ACTION_ID"\s*:\s*"?(\d+)"?/);
|
||||
const mDid = raw13.match(/"AM_DONE_BY_ID"\s*:\s*"?(\d*)"?/);
|
||||
const mAid = jsonDecode.match(/"ACTION_ID"\s*:\s*"?(\d+)"?/);
|
||||
const mDid = jsonDecode.match(/"AM_DONE_BY_ID"\s*:\s*"?(\d*)"?/);
|
||||
if (mAid) actionId = mAid[1];
|
||||
if (mDid && mDid[1]) amDoneById = mDid[1];
|
||||
}
|
||||
@@ -10733,6 +10767,9 @@ async function fetchAndUpdateIntervention(iv, myToken, opts = {}) {
|
||||
if (!ficheResp.ok) {
|
||||
iv.ficheFetched = true;
|
||||
iv.ficheFetchError = ficheResp.error || "fetch_failed";
|
||||
if (ficheResp.error !== "session_expired") {
|
||||
LOG.warn("seq", `iv ${iv.actionId} (ref=${iv.ref}) fiche non chargée : ${iv.ficheFetchError}`);
|
||||
}
|
||||
if (ficheResp.error === "session_expired") {
|
||||
state.session = null;
|
||||
// session expirée → + bannière + abort de tout le refresh.
|
||||
@@ -10838,6 +10875,8 @@ async function fetchAndUpdateIntervention(iv, myToken, opts = {}) {
|
||||
} else if (tlResp && tlResp.error === "session_expired") {
|
||||
state.session = null;
|
||||
showSessionExpiredBanner();
|
||||
} else {
|
||||
LOG.warn("timeline", `iv ${iv.actionId} : timeline non chargée (${(tlResp && tlResp.error) || "échec"})`);
|
||||
}
|
||||
} catch (err) {
|
||||
LOG.warn("timeline", "erreur iv", iv.actionId, err);
|
||||
@@ -11169,12 +11208,16 @@ function parseFicheHtml(html) {
|
||||
* { rows: [
|
||||
* {value:"..."}, // [0..10] statut, groupe, dates, etc.
|
||||
* {value:"Date : ... Heure : ... Lieu : ..."}, // [11] DESCRIPTION
|
||||
* {value:""},
|
||||
* {value:"{\"ACTION_ID\":\"57700033\",...}"} // [13] JSON stringifié
|
||||
* {value:"{\"ACTION_ID\":\"57700033\",...}"} // [12] JSON stringifié
|
||||
* ] }
|
||||
* ] }}}
|
||||
*
|
||||
* On cherche l'action dont rows[13].ACTION_ID === actionId ; si trouvée, on
|
||||
* v2026.5.50 — jusqu'au 28.09.2026, EV intercalait une cellule vide en [12] et
|
||||
* le JSON était en [13] (14 cellules) ; depuis, 13 cellules. La cellule JSON est
|
||||
* repérée par son contenu (la dernière qui mentionne "ACTION_ID"), valable pour
|
||||
* les deux formats.
|
||||
*
|
||||
* On cherche l'action dont le JSON porte ACTION_ID === actionId ; si trouvée, on
|
||||
* retourne rows[11] nettoyé (br→\n, entités décodées) ; sinon null.
|
||||
*/
|
||||
function parseTimelineJsonForAction(jsonText, actionId) {
|
||||
@@ -11190,15 +11233,23 @@ function parseTimelineJsonForAction(jsonText, actionId) {
|
||||
const values = data?.data?.data?.values;
|
||||
if (!Array.isArray(values)) return null;
|
||||
|
||||
// Colonne de la description repérée par son nom (« Commentaire ou
|
||||
// Description ») : une cellule ajoutée ou retirée avant elle ne la décale plus.
|
||||
const colonnes = data?.data?.data?.columns;
|
||||
const idxColonneDescription = _indexColonneTimeline(colonnes, /description/i);
|
||||
const targetId = String(actionId);
|
||||
let actionsLisibles = 0;
|
||||
|
||||
for (const entry of values) {
|
||||
const rows = entry?.rows;
|
||||
if (!Array.isArray(rows) || rows.length < 14) continue;
|
||||
if (!Array.isArray(rows)) continue;
|
||||
|
||||
// rows[13] = JSON stringifié qui contient ACTION_ID
|
||||
const extraRaw = rows[13]?.value;
|
||||
if (!extraRaw || typeof extraRaw !== "string") continue;
|
||||
// JSON stringifié qui contient ACTION_ID : la dernière cellule qui le mentionne.
|
||||
const celluleJson = [...rows].reverse().find(
|
||||
c => typeof c?.value === "string" && c.value.includes('"ACTION_ID"'));
|
||||
const extraRaw = celluleJson?.value;
|
||||
if (!extraRaw) continue;
|
||||
actionsLisibles++;
|
||||
|
||||
let extra;
|
||||
try {
|
||||
@@ -11213,15 +11264,54 @@ function parseTimelineJsonForAction(jsonText, actionId) {
|
||||
|
||||
if (String(extra.ACTION_ID) !== targetId) continue;
|
||||
|
||||
// Trouvé : extraire la description (rows[11]) et la nettoyer.
|
||||
const rawDesc = rows[11]?.value || extra["AM_ACTION.DESCRIPTION"] || "";
|
||||
// Trouvé. Description : la colonne nommée, sinon la position historique 11,
|
||||
// jamais la cellule JSON elle-même ; en dernier recours, le champ du JSON.
|
||||
const celluleDesc = rows[idxColonneDescription >= 0 ? idxColonneDescription : 11];
|
||||
const texteCellule = celluleDesc && celluleDesc !== celluleJson &&
|
||||
typeof celluleDesc.value === "string" ? celluleDesc.value : "";
|
||||
const rawDesc = texteCellule || extra["AM_ACTION.DESCRIPTION"] || "";
|
||||
const cleaned = cleanHtmlBlock(rawDesc);
|
||||
return cleaned || null;
|
||||
}
|
||||
|
||||
// Des actions, mais aucune lisible : EV a changé le format de la timeline.
|
||||
if (values.length > 0 && actionsLisibles === 0) {
|
||||
_signalerFormatEvInconnu("timeline", {
|
||||
nbActions: values.length,
|
||||
nbCellules: values.map(e => (Array.isArray(e?.rows) ? e.rows.length : null)),
|
||||
colonnes: Array.isArray(colonnes) ? colonnes.map(_nomColonneTimeline) : "absentes"
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const _CHAMPS_NOM_COLONNE_TIMELINE = ["dbFieldName", "name", "label", "title"];
|
||||
|
||||
/** Nom lisible d'une colonne de la timeline EV (le premier champ de nom renseigné). */
|
||||
function _nomColonneTimeline(colonne) {
|
||||
const champ = _CHAMPS_NOM_COLONNE_TIMELINE.find(k => typeof colonne?.[k] === "string" && colonne[k]);
|
||||
return champ ? colonne[champ] : "?";
|
||||
}
|
||||
|
||||
/** Index de la première colonne de la timeline EV dont un nom répond au motif ; -1 sinon. */
|
||||
function _indexColonneTimeline(colonnes, motif) {
|
||||
if (!Array.isArray(colonnes)) return -1;
|
||||
return colonnes.findIndex(c => _CHAMPS_NOM_COLONNE_TIMELINE.some(
|
||||
k => typeof c?.[k] === "string" && motif.test(c[k])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Signale UNE fois par session et par source une réponse EV dont le format n'est
|
||||
* plus reconnu (mise à jour d'EasyVista) : structure seulement (nombres de
|
||||
* cellules, noms de colonnes), jamais les valeurs.
|
||||
*/
|
||||
const _formatsEvInconnusSignales = new Set();
|
||||
function _signalerFormatEvInconnu(source, structure) {
|
||||
if (_formatsEvInconnusSignales.has(source)) return;
|
||||
_formatsEvInconnusSignales.add(source);
|
||||
LOG.warn(source, "format EasyVista non reconnu : texte des actions indisponible", structure);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Nettoie un bloc HTML pour obtenir du texte brut lisible.
|
||||
|
||||
Reference in New Issue
Block a user