forked from FroSteel/Planification
v2026.5.25 — Bouton ⚙ Paramètres dans popup user-badge (remplace 5 clics secrets sur le titre)
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Planification",
|
||||
"version": "2026.5.24",
|
||||
"version": "2026.5.25",
|
||||
"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": [
|
||||
|
||||
+87
@@ -3002,3 +3002,90 @@ body.popup-dragging .pinned-popup {
|
||||
from { transform: scale(1); }
|
||||
to { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
v2026.5.25 : pastille dock enrichie (lieu + service + date) +
|
||||
bouton Paramètres dans popup user-badge +
|
||||
ref dans mini-menu pill
|
||||
========================================================================== */
|
||||
|
||||
/* Pastille dock : 3 lignes */
|
||||
.pinned-popup-dock-pill {
|
||||
flex-direction: column !important;
|
||||
align-items: flex-start !important;
|
||||
padding: 6px 14px !important;
|
||||
gap: 2px !important;
|
||||
line-height: 1.2 !important;
|
||||
text-align: left;
|
||||
min-width: 200px;
|
||||
max-width: 300px;
|
||||
}
|
||||
.pinned-popup-dock-pill-lieu {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.pinned-popup-dock-pill-service {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
opacity: 0.85;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.pinned-popup-dock-pill-date {
|
||||
display: block;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
opacity: 0.75;
|
||||
font-family: var(--mono, monospace);
|
||||
}
|
||||
|
||||
/* Ref dans le mini-menu hover de la pastille */
|
||||
.pill-hover-menu-ref {
|
||||
padding: 6px 12px;
|
||||
text-align: center;
|
||||
font-family: var(--mono, monospace);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 4px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Bouton Paramètres dans popup user-badge */
|
||||
.user-name-popup-settings {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
padding: 6px 10px;
|
||||
background: var(--bg-muted);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
||||
}
|
||||
.user-name-popup-settings:hover {
|
||||
background: var(--accent, #3b82f6);
|
||||
color: white;
|
||||
border-color: var(--accent, #3b82f6);
|
||||
}
|
||||
.user-name-popup-settings .settings-ico {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
@@ -416,6 +416,21 @@ function toggleUserNamePopup() {
|
||||
_renderUserPopupSessionLine(sessEl);
|
||||
popup.appendChild(sessEl);
|
||||
|
||||
// v2026.5.25 : bouton Paramètres (remplace les 5 clics sur le titre)
|
||||
const settingsBtn = document.createElement("button");
|
||||
settingsBtn.type = "button";
|
||||
settingsBtn.className = "user-name-popup-settings";
|
||||
settingsBtn.innerHTML = '<span class="settings-ico">⚙</span> Paramètres';
|
||||
settingsBtn.title = "Ouvrir les paramètres d'administration";
|
||||
settingsBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
hideUserNamePopup();
|
||||
if (typeof showAdminPanel === "function") {
|
||||
showAdminPanel();
|
||||
}
|
||||
});
|
||||
popup.appendChild(settingsBtn);
|
||||
|
||||
popup.classList.remove("hidden");
|
||||
badge.classList.add("open");
|
||||
// Positionne juste en dessous de la pastille
|
||||
@@ -1002,25 +1017,14 @@ function updateNowLine() {
|
||||
});
|
||||
}
|
||||
|
||||
// v5.0.0 : menu admin caché. 5 clics consécutifs sur le titre "Planification"
|
||||
// (avec max 2 secondes entre chaque clic) ouvrent le panneau admin.
|
||||
// v5.0.0 : menu admin caché via 5 clics sur le titre "Planification".
|
||||
// v2026.5.25 : SUPPRIMÉ — l'accès au panneau admin se fait désormais via le
|
||||
// bouton "⚙ Paramètres" du popup user-badge (clic sur les initiales).
|
||||
function initAdminMenu() {
|
||||
const title = document.getElementById("app-title");
|
||||
if (!title) return;
|
||||
let clicks = 0;
|
||||
let resetTimer = null;
|
||||
title.addEventListener("click", () => {
|
||||
clicks++;
|
||||
if (resetTimer) clearTimeout(resetTimer);
|
||||
resetTimer = setTimeout(() => { clicks = 0; }, 2000);
|
||||
if (clicks >= 5) {
|
||||
clicks = 0;
|
||||
clearTimeout(resetTimer);
|
||||
showAdminPanel();
|
||||
}
|
||||
});
|
||||
// Cursor pointer pour indiquer (discrètement) qu'il est cliquable
|
||||
title.style.cursor = "default";
|
||||
// Plus de handler de clic : les 5 clics n'ouvrent plus rien.
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -5623,6 +5627,21 @@ function extractContacts(raw) {
|
||||
*/
|
||||
function splitOneContact(raw) {
|
||||
if (!raw) return { name: null, phone: null };
|
||||
|
||||
// v2026.5.25 : avant d'extraire les numéros, on REMPLACE les séquences qui
|
||||
// sont des identifiants de matériel (LETTRES_CHIFFRES) par des espaces.
|
||||
// Exemples : XXXX_NNNNNNNNNNN, XNNNNNN, XNNNNNN, XNNNNNN.
|
||||
// Sans ça, XXXX_NNNNNNNNNNN laisse des "NNNN NNN NN NN" qui se font prendre
|
||||
// pour un numéro de téléphone par le regex qui greedy sur [0-9\s.\-].
|
||||
// On remplace par des espaces de même longueur pour préserver les offsets
|
||||
// (important pour le calcul de position du nom avant le 1er numéro).
|
||||
raw = String(raw);
|
||||
raw = raw.replace(/\b[A-Z]{1,6}_\d+/g, (m) => " ".repeat(m.length));
|
||||
// Idem pour les identifiants sans underscore style XNNNNNN, XNNNNNN, XNNNNNN
|
||||
// (1-2 lettres majuscules suivies de 5+ chiffres collés). On garde assez
|
||||
// permissif pour matcher les variantes sans enlever des vrais mots.
|
||||
raw = raw.replace(/\b[A-Z]{1,3}\d{5,}\b/g, (m) => " ".repeat(m.length));
|
||||
|
||||
// v4.1.20 : regex plus permissives pour tolérer les erreurs humaines :
|
||||
// - pas d'espace après le numéro (ex: "021555555Textecoller")
|
||||
// - pas d'espace/parenthèse avant un court numéro
|
||||
@@ -6606,6 +6625,35 @@ function pinTooltip() {
|
||||
// v2026.5.19 : mémoriser aussi la date pour l'afficher sur la pastille dock
|
||||
popup.dataset.originDate = state.currentDate || "";
|
||||
|
||||
// v2026.5.25 : mémoriser aussi lieu + service pour la pastille enrichie
|
||||
try {
|
||||
const info = iv.infobulle || {};
|
||||
const lieuRaw = info.lieu || iv.bulleLieu || "";
|
||||
// Format pour pastille : "VILLE, Adresse" (on extrait les 2 premières parties)
|
||||
const { ville, adresse } = (typeof splitLieu === "function")
|
||||
? splitLieu(lieuRaw)
|
||||
: { ville: null, adresse: null };
|
||||
let lieuFmt = "";
|
||||
if (ville && adresse) lieuFmt = ville.toUpperCase() + ", " + adresse;
|
||||
else if (adresse) lieuFmt = adresse;
|
||||
else if (ville) lieuFmt = ville.toUpperCase();
|
||||
popup.dataset.lieu = lieuFmt;
|
||||
|
||||
// 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"
|
||||
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;
|
||||
} else {
|
||||
popup.dataset.service = "";
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[pin] lieu/service build failed", err);
|
||||
}
|
||||
|
||||
// v2026.5.17 : masquer l'icône 📌 du contenu cloné (redondante car le
|
||||
// popup a sa propre topbar avec le bouton "désépingler" 📍 explicite)
|
||||
const oldPin = popup.querySelector('.tooltip-pinbtn[data-action="pin"]');
|
||||
@@ -6985,24 +7033,34 @@ function _reducePinnedPopup(popup) {
|
||||
|
||||
// Créer la pastille dock
|
||||
// v2026.5.18 : le fond de la pastille prend la couleur de catégorie
|
||||
// (via la classe color-XXX déjà utilisée ailleurs dans le CSS)
|
||||
// v2026.5.19 : pastille à 2 lignes — ref (gras) + date origine (petit)
|
||||
// v2026.5.25 : pastille à 3 lignes — lieu (fort), service (petit), date (petit)
|
||||
// La référence est affichée dans le mini-menu au survol.
|
||||
const pill = document.createElement("button");
|
||||
pill.type = "button";
|
||||
pill.className = "pinned-popup-dock-pill color-" + colorKey;
|
||||
pill.title = "Cliquer pour agrandir";
|
||||
|
||||
const pillRef = document.createElement("span");
|
||||
pillRef.className = "pinned-popup-dock-pill-ref";
|
||||
pillRef.textContent = label;
|
||||
pill.appendChild(pillRef);
|
||||
// Ligne 1 : lieu (ou ref si pas de lieu)
|
||||
const pillLieu = document.createElement("span");
|
||||
pillLieu.className = "pinned-popup-dock-pill-lieu";
|
||||
pillLieu.textContent = popup.dataset.lieu || label || "—";
|
||||
pill.appendChild(pillLieu);
|
||||
|
||||
// Date d'origine (ex: "21.04")
|
||||
// Ligne 2 : service (2 dernières parties)
|
||||
const serviceTxt = popup.dataset.service || "";
|
||||
if (serviceTxt) {
|
||||
const pillService = document.createElement("span");
|
||||
pillService.className = "pinned-popup-dock-pill-service";
|
||||
pillService.textContent = serviceTxt;
|
||||
pill.appendChild(pillService);
|
||||
}
|
||||
|
||||
// Ligne 3 : date "Mardi 22.04"
|
||||
const originDate = popup.dataset.originDate || "";
|
||||
if (originDate) {
|
||||
const pillDate = document.createElement("span");
|
||||
pillDate.className = "pinned-popup-dock-pill-date";
|
||||
pillDate.textContent = _formatDateShort(originDate);
|
||||
pillDate.textContent = _formatDateForPill(originDate);
|
||||
pill.appendChild(pillDate);
|
||||
}
|
||||
|
||||
@@ -7078,6 +7136,15 @@ function _showPillHoverMenu(pill, popup) {
|
||||
menu._linkedPill = pill;
|
||||
menu._linkedPopup = popup;
|
||||
|
||||
// v2026.5.25 : REF en haut du menu (info seulement, centrée)
|
||||
const refText = popup.dataset.ref || "";
|
||||
if (refText) {
|
||||
const refLabel = document.createElement("div");
|
||||
refLabel.className = "pill-hover-menu-ref";
|
||||
refLabel.textContent = refText;
|
||||
menu.appendChild(refLabel);
|
||||
}
|
||||
|
||||
const restoreBtn = document.createElement("button");
|
||||
restoreBtn.type = "button";
|
||||
restoreBtn.className = "pill-hover-menu-btn";
|
||||
@@ -7419,6 +7486,24 @@ function _formatDateShort(iso) {
|
||||
return `${m[3]}.${m[2]}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* v2026.5.25 : formatte une date ISO en "Mardi 22.04" pour la pastille dock.
|
||||
*/
|
||||
function _formatDateForPill(iso) {
|
||||
if (!iso) return "";
|
||||
const m = String(iso).match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
if (!m) return iso;
|
||||
try {
|
||||
const d = new Date(parseInt(m[1]), parseInt(m[2]) - 1, parseInt(m[3]));
|
||||
const dayName = (typeof DAY_NAMES_FULL !== "undefined" && DAY_NAMES_FULL[d.getDay()])
|
||||
? DAY_NAMES_FULL[d.getDay()]
|
||||
: "";
|
||||
return (dayName ? dayName + " " : "") + m[3] + "." + m[2];
|
||||
} catch (e) {
|
||||
return m[3] + "." + m[2];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* v2026.5.18 : ajoute (ou met à jour) le bouton "Fermer tous" dans le dock
|
||||
* quand au moins 2 popups épinglés existent (réduits OU affichés).
|
||||
|
||||
Reference in New Issue
Block a user