From 3a28e1bd0aa79b84f2e655a88b0621b4a11cc854 Mon Sep 17 00:00:00 2001 From: Quentin Rouiller Date: Thu, 23 Apr 2026 16:21:48 +0200 Subject: [PATCH] =?UTF-8?q?v2026.5.26=20=E2=80=94=20Badge=20user-badge=20c?= =?UTF-8?q?liquable=20+=20auto-d=C3=A9tection=20EV=20=C3=A0=20l'ouverture?= =?UTF-8?q?=20admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.json | 2 +- viewer.css | 70 ++++++++++++++++++++------ viewer.js | 132 +++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 161 insertions(+), 43 deletions(-) diff --git a/manifest.json b/manifest.json index 9563de0..5a26957 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Planification", - "version": "2026.5.25", + "version": "2026.5.26", "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"], "host_permissions": [ diff --git a/viewer.css b/viewer.css index a3dd8dd..ac13374 100644 --- a/viewer.css +++ b/viewer.css @@ -2745,15 +2745,9 @@ header.topbar::before { filter: brightness(1.1); } /* v2026.5.18 : couleurs par catégorie (fond = couleur, texte blanc) */ -.pinned-popup-dock-pill.color-livraison { background: var(--c-livraison); color: white; border-color: transparent; } -.pinned-popup-dock-pill.color-installation { background: var(--c-installation); color: white; border-color: transparent; } -.pinned-popup-dock-pill.color-recup { background: var(--c-recup); color: white; border-color: transparent; } -.pinned-popup-dock-pill.color-remplacement { background: var(--c-remplacement); color: white; border-color: transparent; } -.pinned-popup-dock-pill.color-incident { background: var(--c-incident); color: white; border-color: transparent; } -.pinned-popup-dock-pill.color-rollout { background: var(--c-rollout); color: white; border-color: transparent; } -.pinned-popup-dock-pill.color-reservation { background: var(--c-reservation); color: white; border-color: transparent; } -.pinned-popup-dock-pill.color-absence { background: #2a2f36; color: white; border-color: transparent; } -.pinned-popup-dock-pill.color-autre { background: var(--c-autre); color: white; border-color: transparent; } +/* v2026.5.26 : les anciennes règles color-XXX qui mettaient un fond coloré vif + sont remplacées par une simple barre verticale à gauche de la pastille. + Les styles pour ::before sont plus bas dans le fichier. */ /* v2026.5.18 : bouton "Fermer tous" à droite du dock */ .pinned-popups-close-all { @@ -3007,19 +3001,49 @@ body.popup-dragging .pinned-popup { v2026.5.25 : pastille dock enrichie (lieu + service + date) + bouton Paramètres dans popup user-badge + ref dans mini-menu pill + v2026.5.26 : couleurs sobres (fond sombre + barre colorée à gauche) + + contenu centré ========================================================================== */ -/* Pastille dock : 3 lignes */ +/* Pastille dock : 3 lignes centrées, fond sombre, barre colorée à gauche */ .pinned-popup-dock-pill { flex-direction: column !important; - align-items: flex-start !important; - padding: 6px 14px !important; + align-items: center !important; + justify-content: center !important; + padding: 6px 14px 6px 18px !important; gap: 2px !important; line-height: 1.2 !important; - text-align: left; + text-align: center; min-width: 200px; max-width: 300px; + /* Fond sobre et texte bien lisible, peu importe la catégorie */ + background: var(--bg-muted) !important; + color: var(--text) !important; + border: 1px solid var(--border) !important; + position: relative; + overflow: hidden; } + +/* Barre verticale colorée à gauche = indicateur de catégorie */ +.pinned-popup-dock-pill::before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + background: var(--c-autre); +} +.pinned-popup-dock-pill.color-livraison::before { background: var(--c-livraison); } +.pinned-popup-dock-pill.color-installation::before { background: var(--c-installation); } +.pinned-popup-dock-pill.color-recup::before { background: var(--c-recup); } +.pinned-popup-dock-pill.color-remplacement::before { background: var(--c-remplacement); } +.pinned-popup-dock-pill.color-incident::before { background: var(--c-incident); } +.pinned-popup-dock-pill.color-rollout::before { background: var(--c-rollout); } +.pinned-popup-dock-pill.color-reservation::before { background: var(--c-reservation); } +.pinned-popup-dock-pill.color-absence::before { background: #666; } +.pinned-popup-dock-pill.color-autre::before { background: var(--c-autre); } + .pinned-popup-dock-pill-lieu { display: block; font-size: 13px; @@ -3028,23 +3052,27 @@ body.popup-dragging .pinned-popup { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + color: var(--text); + text-align: center; } .pinned-popup-dock-pill-service { display: block; font-size: 11px; font-weight: 500; - opacity: 0.85; + color: var(--text-muted); max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + text-align: center; } .pinned-popup-dock-pill-date { display: block; font-size: 10px; font-weight: 500; - opacity: 0.75; + color: var(--text-faint); font-family: var(--mono, monospace); + text-align: center; } /* Ref dans le mini-menu hover de la pastille */ @@ -3089,3 +3117,15 @@ body.popup-dragging .pinned-popup { .user-name-popup-settings .settings-ico { font-size: 15px; } + +/* ========================================================================== + v2026.5.26 : rond gris avec "?" quand user inconnu + ========================================================================== */ +.user-badge.user-badge-unknown { + --user-badge-color: #6b7280 !important; + opacity: 0.75; + font-weight: 500; +} +.user-badge.user-badge-unknown:hover { + opacity: 1; +} diff --git a/viewer.js b/viewer.js index a3fc558..616eb12 100644 --- a/viewer.js +++ b/viewer.js @@ -327,24 +327,60 @@ function markSessionActivity() { // v4.2 : fetche l'utilisateur EasyVista connecté (via background.js) et // l'affiche dans la topbar. En cas d'échec ou si aucun nom n'est trouvé, // le badge reste caché. +// v2026.5.26 : en cas d'échec, afficher quand même un rond vide avec "?" +// pour que l'user puisse ouvrir le popup (bouton Paramètres). Et retry +// automatique toutes les 60s (max 10 essais = 10 min) pour récupérer le user +// dès qu'il devient disponible (ex: après reconnexion SSO). +let _currentUserRetryCount = 0; +const _CURRENT_USER_MAX_RETRIES = 10; +const _CURRENT_USER_RETRY_DELAY_MS = 60 * 1000; + async function fetchAndShowCurrentUser() { + let success = false; try { const resp = await sendMessage({ type: "fetchCurrentUser" }); - if (!resp || !resp.ok || !resp.user) return; - const badge = document.getElementById("user-badge"); - if (!badge) return; - const fullName = resp.user.name || resp.user.login || null; - if (!fullName) return; - const initials = computeUserInitials(fullName); - badge.textContent = initials; - badge.title = fullName; - // v4.2.3 : couleur unique dérivée du nom, dans la palette neutre du thème - badge.style.setProperty("--user-badge-color", colorFromName(fullName)); - badge.classList.remove("hidden"); - state.currentUser = resp.user; + if (resp && resp.ok && resp.user) { + const badge = document.getElementById("user-badge"); + if (badge) { + const fullName = resp.user.name || resp.user.login || null; + if (fullName) { + const initials = computeUserInitials(fullName); + badge.textContent = initials; + badge.title = fullName; + badge.style.setProperty("--user-badge-color", colorFromName(fullName)); + badge.classList.remove("hidden"); + badge.classList.remove("user-badge-unknown"); + state.currentUser = resp.user; + success = true; + _currentUserRetryCount = 0; // reset au succès + } + } + } } catch (err) { console.warn("[currentUser] fetch failed:", err); } + + // v2026.5.26 : échec → afficher rond vide avec "?" + scheduler retry + if (!success) { + const badge = document.getElementById("user-badge"); + if (badge) { + badge.textContent = "?"; + badge.title = "Utilisateur inconnu — cliquer pour accéder aux paramètres"; + badge.style.setProperty("--user-badge-color", "#6b7280"); + badge.classList.remove("hidden"); + badge.classList.add("user-badge-unknown"); + } + // Schedule retry si pas trop d'essais + if (_currentUserRetryCount < _CURRENT_USER_MAX_RETRIES) { + _currentUserRetryCount++; + console.log(`[currentUser] retry ${_currentUserRetryCount}/${_CURRENT_USER_MAX_RETRIES} dans ${_CURRENT_USER_RETRY_DELAY_MS / 1000}s`); + setTimeout(() => { + fetchAndShowCurrentUser(); + }, _CURRENT_USER_RETRY_DELAY_MS); + } else { + console.warn("[currentUser] max retries atteint, arrêt"); + } + } } // v4.2.3 : calcule les initiales depuis un nom au format "Nom, Prénom" ou @@ -400,14 +436,20 @@ function toggleUserNamePopup() { hideUserNamePopup(); return; } - if (!state.currentUser || !state.currentUser.name) return; // v2026.5.17 : afficher aussi le temps restant de la session (MM:SS) avec // une couleur qui dépend du seuil (vert/jaune/rouge). + // v2026.5.26 : si user inconnu, afficher "Utilisateur inconnu" + retry + réglages popup.innerHTML = ""; const nameEl = document.createElement("div"); nameEl.className = "user-name-popup-name"; - nameEl.textContent = state.currentUser.name; + if (state.currentUser && state.currentUser.name) { + nameEl.textContent = state.currentUser.name; + } else { + nameEl.textContent = "Utilisateur inconnu"; + nameEl.style.fontStyle = "italic"; + nameEl.style.color = "var(--text-muted)"; + } popup.appendChild(nameEl); const sessEl = document.createElement("div"); @@ -1714,17 +1756,9 @@ function renderAdminSectionTeam(container, cfg, saveFn) { tableWrap.appendChild(table); - // Bouton Ajouter manuellement - const addBtn = document.createElement("button"); - addBtn.type = "button"; - addBtn.className = "btn btn-secondary"; - addBtn.textContent = "+ Ajouter manuellement"; - addBtn.style.marginTop = "10px"; - addBtn.addEventListener("click", () => { - rows.push({ id: "", name: "", included: true, days: [] }); - render(); - }); - tableWrap.appendChild(addBtn); + // v2026.5.26 : bouton "+ Ajouter manuellement" retiré. La liste complète + // des techniciens du groupe est chargée automatiquement depuis EasyVista + // à l'ouverture du panel admin (voir auto-détection plus bas). // Bouton Enregistrer const saveBtn = document.createElement("button"); @@ -1750,6 +1784,36 @@ function renderAdminSectionTeam(container, cfg, saveFn) { } render(); + + // v2026.5.26 : auto-détection EasyVista à l'ouverture du panel admin. + // On lance en arrière-plan (non-bloquant), et on merge avec cfg.team actuel + // quand le résultat arrive. Les techs actuellement en cfg.team restent + // cochés par défaut ; les nouveaux détectés du groupe apparaissent non-cochés. + (async () => { + try { + const resp = await sendMessage({ type: "detectTeam" }); + if (resp && resp.ok && resp.members && resp.members.length) { + for (const m of resp.members) { + const existing = rows.find(r => r.id === m.id); + if (existing) { + if (m.name && !m.name.startsWith("?") && existing.name.startsWith("?")) { + existing.name = m.name; + } + } else { + rows.push({ + id: m.id, + name: m.name || "? (" + m.id + ")", + included: !!m.alreadyInTeam, + days: [] + }); + } + } + render(); + } + } catch (err) { + console.warn("[admin auto-detect]", err); + } + })(); } // v5.0.0 : sections suivantes (placeholders, à enrichir v5.0.1+) @@ -6642,11 +6706,25 @@ function pinTooltip() { // Service : prendre les 2 dernières parties séparées par "/" // Ex: "AdmCV/DJES/SSCM/SSCM - Admin/TEO - OS" → "SSCM - Admin/TEO - OS" // "AdmCV/OJV/JPX/JPX Lavaux-Oron" → "JPX/JPX Lavaux-Oron" + // v2026.5.26 : dans le DERNIER segment seulement, couper au " - " (espace + // tiret espace) pour retirer les descriptions longues. Les + // noms propres comme "Lavaux-Oron" (sans espaces) sont préservés. + // Ex: "AdmCV/DJES/SSCM/SPOP - Centre administratif" → "SSCM/SPOP" + // "AdmCV/OJV/JPX/JPX Lavaux-Oron" → "JPX/JPX Lavaux-Oron" (inchangé) const serviceRaw = (info.service) || iv.categoryLine || ""; if (serviceRaw) { const parts = serviceRaw.split("/").map(s => s.trim()).filter(Boolean); - const last2 = parts.slice(-2).join("/"); - popup.dataset.service = last2; + let last2 = parts.slice(-2); + // Couper au " - " dans le DERNIER élément de last2 + if (last2.length > 0) { + const lastIdx = last2.length - 1; + const lastSeg = last2[lastIdx]; + const dashIdx = lastSeg.indexOf(" - "); + if (dashIdx > 0) { + last2[lastIdx] = lastSeg.substring(0, dashIdx).trim(); + } + } + popup.dataset.service = last2.join("/"); } else { popup.dataset.service = ""; }