Compare commits

..
50 Commits
Author SHA1 Message Date
Quentin Rouiller 54b8f826df docs: clarification audience — coordinateurs DGNSI (pas techniciens)
L'extension est utilisée par les coordinateurs DGNSI qui pilotent dans
EasyVista le planning de l'équipe technicienne. Les techniciens
eux-mêmes consultent leur planning sur leur douchette terrain et
n'utilisent pas cet outil.

Mises à jour cohérentes : 'public cible' dans README et CHANGELOG.
Les mentions 'technicien' qui désignent le sujet du planning (cartes
des techniciens, liste des techniciens cochés, etc.) sont conservées
puisque ce sont les personnes DONT on regarde le planning.
2026-04-27 06:02:12 +02:00
FroSteel 8ecf2d3df4 Actualiser README.md 2026-04-27 05:52:42 +02:00
Quentin Rouiller 9d8d8102d7 chore: retire CLAUDE.md et toutes mentions externes du repo
- Suppression du fichier CLAUDE.md (workflow de développement interne)
- Retrait des références correspondantes dans README.md et CHANGELOG.md
- .gitignore : retire la section dédiée (les règles secrets génériques
  .env / *.token / secrets.json couvrent l'essentiel)

Le repo ne contient plus que les sources, la doc utilisateur et les
métadonnées du projet.
2026-04-27 05:39:53 +02:00
Quentin Rouiller 48b00a8585 docs(README): refonte pour clarté + audience + installation directe
- Section Installation rapide ajoutée en tête (Firefox + Chromium avec
  liens directs vers la release courante).
- Audience clarifiée : 'coordinateurs et techniciens DGNSI' (pas juste
  techniciens — les deux rôles utilisent l'extension).
- Doublon de phrase v2026.5.40 retiré.
- Versions notables limitées aux 5 dernières + lien vers CHANGELOG / wiki
  Versions pour l'historique complet.
- Lien direct cliquable vers chaque version notable.
- Bandeau de liens vers toutes les pages wiki en haut du README.
- Lignes de code mises à jour : ~10 700 (viewer.js) + ~1 600 (background.js)
  + ~4 800 (viewer.css).
- Structure du repo réécrite pour refléter le layout flat sur Gitea
  (build.sh à la racine, pas dans Autres/).
- Section Développement simplifiée + référence à CLAUDE.md pour le
  workflow détaillé.
2026-04-27 05:31:29 +02:00
Quentin Rouiller 6bb97addd6 docs(CLAUDE.md): clarifie Phase 3 signature (addon AMO déjà enregistré)
L'addon [email protected] est désormais enregistré sur AMO.
Mise à jour du workflow Phase 3 : pour les versions futures, utiliser
'Téléverser une nouvelle version' (pas 'Submit a New Add-on'). Précision
aussi sur le fait que Chrome/Edge ne sont pas concernés par AMO et n'ont
pas d'auto-update natif.
2026-04-27 05:18:30 +02:00
Quentin Rouiller 05275a3be5 firefox: update_hash v2026.5.43 → sha256 du .xpi signé AMO 2026-04-27 05:02:27 +02:00
Quentin Rouiller d7b680fb3f v2026.5.43 — Fix Firefox menu dock position + stabilité popup pin/unpin
Bug Firefox uniquement : positionnement du menu hover des pastilles du
dock (popup réduit) corrigé. La cause était que getBoundingClientRect()
était appelé immédiatement après appendChild sans que Firefox n'ait fini
de calculer la mise en page, combiné à un transform: translateY dans
l'animation d'apparition du menu. Fix : positionnement hors écran initial,
force-layout via offsetHeight, puis pose finale. Animation CSS simplifiée
en opacité-only.

Stabilité popup au pin/unpin (tous navigateurs) : la popup épinglée
bougeait de 16px et changeait légèrement de taille quand on la
dé-épinglait via le bouton 📌. Cause : .pinned-popup avait padding-top
28px + border 2px alors que .soft-unpinned avait padding-top 12px + border
1px. Fix : .soft-unpinned conserve désormais les mêmes dimensions, juste
la couleur de bordure change (--border-strong gris au lieu de --accent
bleu) pour signaler le mode détaché.
2026-04-27 04:57:03 +02:00
Quentin Rouiller 3c7e7c0c25 firefox: update_hash v2026.5.42 → sha256 du .xpi signé par Mozilla AMO
Le .xpi distribué sur la release v2026.5.42 a été remplacé par sa version
signée AMO (signature META-INF/mozilla.rsa + COSE). Le sha256 dans
firefox-updates.json reflète maintenant le .xpi signé, ce qui permet
l'auto-update Firefox vers la version signée et installable.
2026-04-27 03:44:11 +02:00
Quentin Rouiller b3677d661a chore: change addon ID en [email protected]
L'ID précédent ([email protected]) était déjà enregistré sur AMO.
Nouvel ID : [email protected] — nom plus explicite (mention
DGNSI), domaine inchangé.

build.sh + firefox-updates.json mis à jour avec le nouvel ID. Sha256 du
.xpi v2026.5.42 régénéré.
2026-04-27 03:32:44 +02:00
Quentin Rouiller 8390db9937 v2026.5.42 — Nettoyage de commentaires + exemples génériques
Passage en revue des commentaires de viewer.js : les exemples qui
illustraient le parsing des contacts/lieux/références/codes-barres ont
été uniformisés en placeholders abstraits (Nom1 Prénom1 +41XXXXXXXXX,
SYYMMDD_NNNNN, XXXX_NNNNNNNN, etc.) plutôt que des chaînes spécifiques.

Comportement runtime strictement inchangé — uniquement de la documentation
et des commentaires. README, CHANGELOG et pages wiki Versions/Utilisation
mis à jour de manière cohérente.
2026-04-27 03:28:13 +02:00
Quentin Rouiller 2cc9552fbf security: anonymisation de toutes les données nominatives résiduelles
Suite à un audit de sécurité, retrait de TOUTES les données réelles dans
le code et la documentation :

- src/viewer.js : commentaires-exemples qui contenaient de vrais noms +
  numéros de téléphone (Seda Kaya, Hélène Dongiovanni, Krkic Admir et leurs
  numéros) → remplacés par 'Nom1 Prénom1 +41XXXXXXXXX', etc.
- src/viewer.js : refs tickets EV avec dates concrètes (SYYMMDD_NNNNN avec
  vraies dates) → remplacées par 'SYYMMDD_NNNNN' génériques.
- src/viewer.js : codes-barres / numéros de série (TPCQ_NNN, MNNN, DNNN,
  TNNN avec vrais chiffres) → remplacés par 'XXXX_NNNNNNNN', 'XNNNNNN'.
- README.md, CHANGELOG.md, wiki Utilisation/Versions : exemples de référence
  ticket S260424_00042 → SYYMMDD_NNNNN.

Aucune donnée nominative ni identifiant réel ne subsiste dans le code,
les commentaires, ni la documentation publique. Sha256 du .xpi mis à jour
dans firefox-updates.json.
2026-04-27 03:23:20 +02:00
Quentin Rouiller 0327a55c74 fix(firefox): ajoute background.scripts fallback (compat MV3 Firefox/AMO)
Mozilla AMO rejetait le .xpi avec :
  Unsupported "/background/service_worker" manifest property used without
  "/background/scripts" property as Firefox-compatible fallback.

build.sh ajoute maintenant 'scripts: [background.js]' à background.* dans
le manifest Firefox uniquement (Chrome ignore 'scripts' quand
'service_worker' est présent ; Firefox ignore 'service_worker' et utilise
'scripts'). Les deux navigateurs chargent le même background.js.

Sha256 du .xpi v2026.5.41 mis à jour dans firefox-updates.json.
2026-04-27 03:02:38 +02:00
Quentin Rouiller 67708d1ad3 chore: simplifie firefox-updates.json (repo public, URL raw fixe)
- update_url remis sur .../raw/branch/main/firefox-updates.json maintenant
  que le repo est public (raw URL accessible sans auth).
- firefox-updates.json toujours à la racine, contient toutes les versions ;
  Firefox lit la liste et choisit la plus haute compatible.
- Sha256 du .xpi v2026.5.41 mis à jour suite au rebuild.
- CLAUDE.md : note sur le channel d'update simplifiée.
2026-04-27 03:00:02 +02:00
Quentin Rouiller 1730758cb4 Distribution: firefox-updates.json + CLAUDE.md (workflow Claude) + nettoyage secrets
- firefox-updates.json à la racine : manifest auto-update Firefox avec entrées
  v2026.5.40 et v2026.5.41 (sha256 NON SIGNÉ pour le moment, à remplacer par
  celui des .xpi signés AMO).
- build.sh : maintient firefox-updates.json automatiquement à chaque build
  (ajoute ou met à jour l'entrée de la version courante avec son sha256
  calculé sur le .xpi produit).
- CLAUDE.md : workflow complet pour Claude Code (build → test → push → wiki →
  signature AMO). Token Gitea jamais dans le fichier (stocké hors repo en
  mémoire Claude .claude/projects/.../memory/gitea_token.md).
- .gitignore : ajout _archives/, .claude/, .env, *.token, secrets.json.
- README.md / CHANGELOG.md : retrait email auteur en clair (renvoi vers
  page wiki Contact, email obfusqué en entités HTML).
2026-04-27 02:55:18 +02:00
Quentin Rouiller 7c0742594c v2026.5.41 — Suppression des hardcodes runtime + UX admin + thème unifié 2026-04-27 03:00:00 +02:00
Quentin Rouiller af85473837 v2026.5.40 — Sélection groupe EV + édition domaines + tri équipe + vue horizontale enrichie 2026-04-27 00:43:00 +02:00
Quentin Rouiller 47a0bca998 refactor: ranger le code source dans src/ + script build.sh 2026-04-27 00:00:00 +02:00
Quentin Rouiller e92b0c4444 v2026.5.39 — Séparation Matin / Après-midi + Apparence (thème, taille du texte, durée du cache, heures de la journée) 2026-04-26 18:10:00 +02:00
Quentin Rouiller 957b754bdc v2026.5.38 — Attribution auteur + nettoyage + observabilité (LOG unifié, handlers globaux d'erreur, toggle logs verbeux dans admin) 2026-04-25 22:55:00 +02:00
Quentin Rouiller aabda3ba7e v2026.5.37 — Refonte vue horizontale (sidebar complète) : topbar supprimée, user-badge + titre + bouton Aujourd'hui + date/heure + stats en sidebar 2026-04-24 13:45:50 +02:00
Quentin Rouiller 6a0324b252 v2026.5.36 — Sidebar verticale en vue horizontale (#horizontal-wrapper [sidebar 200px] + [main]) [code interpolé entre v2026.5.35 et v2026.5.37] 2026-04-24 13:22:08 +02:00
Quentin Rouiller fd466504c2 v2026.5.35 — Fix popup épinglé position vue horizontale + stats gauche 2026-04-24 13:11:16 +02:00
Quentin Rouiller 02524e78b2 v2026.5.34 — Bouton 📌 restauré + badge user cliquable + positionTooltipAnchored unifiée [code interpolé] 2026-04-24 12:56:34 +02:00
Quentin Rouiller 193b3252d4 v2026.5.33 — Vue horizontale : interactions différenciées (hover/clic) [code interpolé] 2026-04-24 12:12:32 +02:00
Quentin Rouiller 3a28e1bd0a v2026.5.26 — Badge user-badge cliquable + auto-détection EV à l'ouverture admin 2026-04-23 16:21:48 +02:00
Quentin Rouiller 10a1aef4c7 v2026.5.25 — Bouton ⚙ Paramètres dans popup user-badge (remplace 5 clics secrets sur le titre) 2026-04-23 16:00:38 +02:00
Quentin Rouiller b77f0a9caa v2026.5.24 — Améliorations diverses 2026-04-23 15:40:00 +02:00
Quentin Rouiller f7f81f7d9d v2026.5.23 — Polish UX 2026-04-23 15:31:44 +02:00
Quentin Rouiller ddb075d563 v2026.5.22 — Stabilité popups 2026-04-23 15:13:04 +02:00
Quentin Rouiller f6dc9eaf7b v2026.5.21 — Polish positionnement popups 2026-04-23 15:03:54 +02:00
Quentin Rouiller 3d5bdbab3d v2026.5.20 — Safe area : popups jamais cachés sous topbar/dock 2026-04-23 14:48:16 +02:00
Quentin Rouiller ad952ebc55 v2026.5.19 — Drag-and-drop des popups épinglés 2026-04-23 14:34:08 +02:00
Quentin Rouiller 1a7393c297 v2026.5.18 — Polish date custom 2026-04-23 14:20:52 +02:00
Quentin Rouiller d589447533 v2026.5.17 — Date custom : label localisé (jour de la semaine en français) 2026-04-23 14:00:10 +02:00
Quentin Rouiller ea5a42c5e1 v2026.5.16 — Passage au schéma de versionning ANNÉE.MAJEURE.PATCH + faux input date custom (Mardi 24.04.2026) 2026-04-23 13:03:58 +02:00
Quentin Rouiller 763e63d9c6 v5.0.15 — Absences partielles affichées comme rows (gris foncé) 2026-04-21 16:24:24 +02:00
Quentin Rouiller bea236ca88 v5.0.14 — Affichage timeline pour absences partielles seules 2026-04-21 16:13:04 +02:00
Quentin Rouiller d6ab8d59e0 v5.0.13 — Cache + retry 2026-04-21 16:04:00 +02:00
Quentin Rouiller 909ddb8301 v5.0.12 — Stabilité 2026-04-21 15:49:08 +02:00
Quentin Rouiller 6794360887 v5.0.11 — Détection contexte réseau (interne/externe via SSO) 2026-04-21 15:44:14 +02:00
Quentin Rouiller 7ba28d3bac v5.0.10 — Stabilité session EV 2026-04-21 15:32:44 +02:00
Quentin Rouiller e17f604d9e v5.0.9 — Surveillance timeout session EasyVista (compteur tick 1s, alertes 5min/2min) 2026-04-21 15:19:06 +02:00
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
Quentin Rouiller 86f52029f5 v5.0.4 — Améliorations admin/UX 2026-04-21 12:40:08 +02:00
Quentin Rouiller 984f326b39 v5.0.3 — Ajustements admin et stabilité 2026-04-20 14:03:34 +02:00
Quentin Rouiller 6d3058028f v5.0.1 — Refonte topbar : horloge HH:MM + compteur session EV + admin caché (5 clics titre) 2026-04-20 13:21:16 +02:00
Quentin Rouiller c59abbed23 v4.3.3 — Soft unpin popup + nettoyage tooltip persistance 2026-04-20 09:13:20 +02:00
11 changed files with 194 additions and 204 deletions
-5
View File
@@ -44,11 +44,6 @@ _archives/
Old.zip Old.zip
Old/ Old/
# Mémoire / config Claude Code (ne jamais commit, contient potentiellement
# des tokens, des notes user, etc.)
.claude/
CLAUDE.local.md
# Variables d'environnement / secrets # Variables d'environnement / secrets
.env .env
.env.* .env.*
+44 -16
View File
@@ -1,14 +1,41 @@
# CHANGELOG — Extension Planification EasyVista Canton de Vaud # CHANGELOG — Extension Planification EasyVista Canton de Vaud
> Ce changelog documente l'évolution de l'extension Chrome/Firefox "Planification" > Ce changelog documente l'évolution de l'extension Chrome/Firefox "Planification"
> développée par FroSteel pour les techniciens DGNSI (Canton de Vaud). > développée par Quentin Rouiller pour les coordinateurs DGNSI (Canton de Vaud).
> >
> Les versions documentées ci-dessous sont celles dont les détails sont connus. > Les versions documentées ci-dessous sont celles dont les détails sont connus.
> Pour les versions plus anciennes, Claude Code se basera sur l'analyse du code > Pour les versions plus anciennes, l'analyse du code source permet de
> source pour déterminer un message de commit pertinent. > reconstituer un message de version pertinent.
--- ---
## v2026.5.43 — Fix Firefox : positionnement menu dock + stabilité popup pin/unpin
### Menu hover sur pastille du dock (popup réduit)
- Bug Firefox uniquement : quand un popup épinglé était réduit dans la
taskbar du bas, le menu qui apparaît au survol de la pastille
(Agrandir / Fermer) se positionnait trop haut, pas juste au-dessus de
la pastille.
- Cause : `getBoundingClientRect()` était appelé immédiatement après
`appendChild`, avant que Firefox n'ait calculé la mise en page.
Combiné avec un `transform: translateY(4px)` dans l'animation
`pill-hover-menu-appear`, Firefox lisait des dimensions décalées.
- Fix : positionnement hors écran initial, force-layout via
`void offsetHeight`, mesure des dimensions, puis pose finale. CSS de
l'animation simplifiée en opacité-only (plus de transform).
### Stabilité popup au pin/unpin
- Bug : la popup épinglée bougeait visuellement et changeait légèrement
de taille quand on la dé-épinglait avec le bouton 📌 (puis l'inverse).
- Cause : `.pinned-popup` avait `padding-top: 28px` (place pour la
dragbar) et `border: 2px`, alors que `.soft-unpinned` avait
`padding-top: 12px` et `border: 1px`. Le contenu se décalait de 16px
vers le haut et la popup devenait 1px plus fine de chaque côté.
- Fix : `.soft-unpinned` conserve désormais `padding-top: 28px` et
`border: 2px` comme `.pinned-popup`. Bordure passe juste en
`--border-strong` (gris discret) plutôt que `--accent` (bleu) pour
signaler visuellement le mode "détaché". Position et taille stables.
## v2026.5.42 — Nettoyage de commentaires + exemples génériques ## v2026.5.42 — Nettoyage de commentaires + exemples génériques
- Passage en revue des commentaires de `src/viewer.js` : les exemples qui - Passage en revue des commentaires de `src/viewer.js` : les exemples qui
@@ -66,7 +93,7 @@
au viewer via `err.kind`. au viewer via `err.kind`.
- Toutes les anciennes constantes hardcodées (`EV_ORIGINS`, - Toutes les anciennes constantes hardcodées (`EV_ORIGINS`,
`DEFAULT_SUPPORT_IDS` interne à `detectTeamFromEV`, `DEFAULT_SUPPORT_IDS` interne à `detectTeamFromEV`,
`isPillonelAbsentFriday`) ont été remplacées ou retirées. `isXXXAbsentFriday`) ont été remplacées ou retirées.
### Conflits absence/réservation × intervention ### Conflits absence/réservation × intervention
- Nouveau code visuel : si une intervention est planifiée pendant - Nouveau code visuel : si une intervention est planifiée pendant
@@ -163,7 +190,7 @@
### Attribution auteur ### Attribution auteur
- Ajout en-têtes copyright dans tous les fichiers source - Ajout en-têtes copyright dans tous les fichiers source
(viewer.js, viewer.html, viewer.css, background.js) (viewer.js, viewer.html, viewer.css, background.js)
- Ajout `@author FroSteel` sur les fonctions principales - Ajout `@author Quentin Rouiller` sur les fonctions principales
(loadForDate, buildCard, buildTooltipHTML, pinTooltip, _softUnpinPopup, (loadForDate, buildCard, buildTooltipHTML, pinTooltip, _softUnpinPopup,
positionTooltipAnchored, _applyViewMode, _moveElementsToSidebar, positionTooltipAnchored, _applyViewMode, _moveElementsToSidebar,
_restoreElementsToTopbar, fetchAndShowCurrentUser, _maybeRetryFetchUser, _restoreElementsToTopbar, fetchAndShowCurrentUser, _maybeRetryFetchUser,
@@ -171,7 +198,7 @@
openPersistentTimelinePopup, showTooltip, _findFreePopupPosition, openPersistentTimelinePopup, showTooltip, _findFreePopupPosition,
_clampPopupInSafeArea, findEasyVistaSession, fetchPlanningXml, _clampPopupInSafeArea, findEasyVistaSession, fetchPlanningXml,
fetchCurrentUser, detectNetworkContext) fetchCurrentUser, detectNetworkContext)
- Ajout signature "Développé par FroSteel" en bas du popup - Ajout signature "Développé par Quentin Rouiller" en bas du popup
user-badge (style cohérent avec footer version : 11px, italique, user-badge (style cohérent avec footer version : 11px, italique,
gris atténué, séparateur fin) gris atténué, séparateur fin)
- Mise à jour `description` du manifest pour mentionner DGNSI - Mise à jour `description` du manifest pour mentionner DGNSI
@@ -259,17 +286,17 @@
- Stats rapides .tech-row-stats ajoutés au header (nb interv, Xm · Ya) - Stats rapides .tech-row-stats ajoutés au header (nb interv, Xm · Ya)
## v2026.5.31 — Sarcelle pour absence récurrente (REJETÉ par utilisateur) ## v2026.5.31 — Sarcelle pour absence récurrente (REJETÉ par utilisateur)
- Couleur Pillonel vendredi : sarcelle foncée #0f766e / soft #ccfbf1 - Couleur absence récurrente (jour fixe) : sarcelle foncée #0f766e / soft #ccfbf1
- Variables --c-recurring, --c-recurring-soft - Variables --c-recurring, --c-recurring-soft
- Layout 4 colonnes forcées + scroll interne cartes (REJETÉ : "scroll en continu") - Layout 4 colonnes forcées + scroll interne cartes (REJETÉ : "scroll en continu")
## v2026.5.30 — Absence récurrente cyan + mode compact 24" ## v2026.5.30 — Absence récurrente cyan + mode compact 24"
- Absence récurrente Pillonel vendredi en cyan - Absences récurrentes (configurées par tech) en cyan
- Mode compact @media (max-width: 1920px) avec grid-template-columns: repeat(4, 1fr) - Mode compact @media (max-width: 1920px) avec grid-template-columns: repeat(4, 1fr)
## v2026.5.29 — Contraste++ + footer ## v2026.5.29 — Contraste++ + footer
- Contrastes encore plus forts (text-muted #d0d5de dark, #2e3642 light) - Contrastes encore plus forts (text-muted #d0d5de dark, #2e3642 light)
- Footer FRS/version : 13px badge avec fond bg-muted + bordure - Footer QRO/version : 13px badge avec fond bg-muted + bordure
- Fix highlight row : selector .intervention-v2[data-iv-idx] - Fix highlight row : selector .intervention-v2[data-iv-idx]
- Scroll-into-view automatique au hover segment timeline - Scroll-into-view automatique au hover segment timeline
@@ -313,8 +340,9 @@
## Versions antérieures (v5.x et v4.x) ## Versions antérieures (v5.x et v4.x)
> Ces versions sont à analyser par Claude Code à partir des fichiers source. > Ces versions ne sont pas documentées en détail. Pour les analyser à partir
> Indices clés à chercher dans le viewer.js : > des fichiers source historiques (consultables via les tags git), voici les
> indices clés à chercher dans `viewer.js` :
> >
> - Présence de `pinTooltip` → version >= v4.x > - Présence de `pinTooltip` → version >= v4.x
> - Présence de `_softUnpinPopup` → version >= v4.3.3 > - Présence de `_softUnpinPopup` → version >= v4.3.3
@@ -336,20 +364,20 @@
- v4.2.3 : grande popup timeline persistante (clic), suit-souris (hover) - v4.2.3 : grande popup timeline persistante (clic), suit-souris (hover)
- v4.2.3 : bindTimelinePopover, showTimelinePopover, moveTimelineTooltip - v4.2.3 : bindTimelinePopover, showTimelinePopover, moveTimelineTooltip
- v4.2.4 : setTooltipViewportPosition (détection auto fixed/abs) - v4.2.4 : setTooltipViewportPosition (détection auto fixed/abs)
- v4.2.9 : pied de page discret FRS/version - v4.2.9 : pied de page discret QRO/version
- v4.2.9 : initModalScrollLock (bloquer scroll arrière modal) - v4.2.9 : initModalScrollLock (bloquer scroll arrière modal)
- v4.3.0 : tooltip live libéré après épinglage (réutilisable autres survols) - v4.3.0 : tooltip live libéré après épinglage (réutilisable autres survols)
- v4.3.3 : _softUnpinPopup (désépinglage mou) - v4.3.3 : _softUnpinPopup (désépinglage mou)
### v3.x et antérieures — Versions de base ### v3.x et antérieures — Versions de base
- À analyser par Claude Code - Code historique consultable via les tags git correspondants.
--- ---
## Notes techniques persistantes (toutes versions) ## Notes techniques persistantes (toutes versions)
- 8 techs hardcodés : "76272,83725,66635,92235,90070,40944,72485,86874" - 8 techs hardcodés à l'origine (depuis v2026.5.41 : retirés, alimentés par admin_config)
- Pillonel Olivier (ID 40944) absent tous les vendredis (hardcodé) - Absences récurrentes (un tech absent un jour fixe par semaine) hardcodées à l'origine, depuis v2026.5.41 configurables via Paramètres → Équipe
- Group ID EasyVista : 191 - Group ID EasyVista : 191
- Domaines cibles : itsma.etat-de-vaud.ch (interne), itsma.vd.ch (externe) - Domaines cibles : itsma.etat-de-vaud.ch (interne), itsma.vd.ch (externe)
- SSO : Canton ForgeRock OpenAM - SSO : Canton ForgeRock OpenAM
@@ -362,6 +390,6 @@
## Auteur ## Auteur
**FroSteel** (FRS) **Quentin Rouiller** (QRO)
Technicien DGNSI — Canton de Vaud Technicien DGNSI — Canton de Vaud
Contact : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact) Contact : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact)
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2026 FroSteel Copyright (c) 2026 Quentin Rouiller
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+63 -118
View File
@@ -1,16 +1,39 @@
# Planification — Extension EasyVista Canton de Vaud # Planification — Extension EasyVista Canton de Vaud
Extension Chrome / Firefox pour visualiser de manière claire et rapide le planning des techniciens DGNSI (Canton de Vaud) dans EasyVista. Extension Chrome / Firefox pour visualiser de manière claire et rapide le planning EasyVista de l'équipe technicienne DGNSI (Canton de Vaud).
> 📖 **Documentation utilisateur complète** : [wiki](https://gitea.netaplaid.ch/FroSteel/Planification/wiki) ([Home](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Home) · [Utilisation](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Utilisation) · [Versions](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Versions) · [Architecture](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Architecture) · [Dépannage](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/D%C3%A9pannage) · [Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact))
## Aperçu rapide ## Aperçu rapide
- **Auteur** : FroSteel (FRS) - **Auteur** : Quentin Rouiller (QRO), Technicien DGNSI — Canton de Vaud
- **Cible** : techniciens DGNSI (Canton de Vaud), EasyVista (`itsma.etat-de-vaud.ch` / `itsma.vd.ch`) - **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 - **Démarrage projet** : jeudi 16 avril 2026
- **Version actuelle** : `v2026.5.42` - **Version actuelle** : [`v2026.5.43`](https://gitea.netaplaid.ch/FroSteel/Planification/releases/tag/v2026.5.43) (latest)
- **Contact** : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact) - **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) - **Manifest** : V3 (Chrome/Edge/Firefox 140+)
- **Format** : `.zip` (Chromium) + `.xpi` signé (Firefox) - **Format** : `.zip` (Chromium) + `.xpi` signé Mozilla (Firefox)
- **Distribution** : auto-update natif Firefox via `firefox-updates.json`
## Installation rapide
### Firefox 🦊 (recommandé — auto-update)
1. Télécharger le `.xpi` signé depuis la **[release courante](https://gitea.netaplaid.ch/FroSteel/Planification/releases/latest)**.
2. Drag-and-drop dans `about:addons` de Firefox.
3. Cliquer "Ajouter".
À partir de là, l'extension se met à jour **automatiquement** à chaque nouvelle version (vérification toutes les ~24 h via `firefox-updates.json`).
### Chrome / Edge / Brave 🌐 (manuel)
1. Télécharger le `.zip` depuis la **[release courante](https://gitea.netaplaid.ch/FroSteel/Planification/releases/latest)**.
2. Décompresser dans un dossier permanent.
3. `chrome://extensions/` (ou `edge://extensions/`) → activer **Mode développeur** → "Charger l'extension non empaquetée" → sélectionner le dossier décompressé.
Les mises à jour sont **manuelles** : à chaque nouvelle release, retélécharger le `.zip`, écraser le dossier, puis cliquer ⟳ (Recharger) sur la carte de l'extension.
➡ Pour le détail complet (stockage, désinstallation, comparatif), voir [wiki Utilisation → Installation et navigateurs](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Utilisation#installation-et-navigateurs).
## Fonctionnalités principales ## Fonctionnalités principales
@@ -40,7 +63,7 @@ Extension Chrome / Firefox pour visualiser de manière claire et rapide le plann
- **Congé / Congés** : cyan `#06b6d4` (suffixe `s` adaptatif) - **Congé / Congés** : cyan `#06b6d4` (suffixe `s` adaptatif)
- **Pompier** : rouge `#b03030` - **Pompier** : rouge `#b03030`
- Badge + barre gauche colorée + dégradé fond - Badge + barre gauche colorée + dégradé fond
- Absence récurrente Pillonel vendredi : cyan (depuis v2026.5.30) - Absences récurrentes (configurées par tech) : cyan (depuis v2026.5.30)
### User et session ### User et session
- Badge user avec photo/initiales en topbar - Badge user avec photo/initiales en topbar
@@ -87,7 +110,13 @@ Le numéro de **majeure** n'est **pas** un mois et **pas** un chiffre lié au ca
## Versions notables ## Versions notables
### `v2026.5.42` (latest, 27 avril 2026) — Nettoyage de commentaires + exemples génériques ### `v2026.5.43` (latest, 27 avril 2026) — Fix Firefox : menu dock + stabilité popup pin/unpin
- Firefox : le menu hover sur les pastilles du dock (popup réduit) se
positionne désormais correctement au-dessus de la pastille.
- Pin/unpin : la popup épinglée ne bouge plus et garde la même taille
quand on la dé-épingle / re-épingle.
### `v2026.5.42` — Nettoyage de commentaires + exemples génériques
- Uniformisation des exemples utilisés dans les commentaires de `viewer.js` - Uniformisation des exemples utilisés dans les commentaires de `viewer.js`
(parsing contacts/lieux/références/codes-barres) en placeholders abstraits. (parsing contacts/lieux/références/codes-barres) en placeholders abstraits.
Comportement runtime strictement inchangé. Comportement runtime strictement inchangé.
@@ -99,7 +128,7 @@ Le numéro de **majeure** n'est **pas** un mois et **pas** un chiffre lié au ca
- **Heures de la journée** : bouton ✓ Appliquer explicite (au lieu de save direct), toast de confirmation, refetch automatique du planning. Synchronisation effective avec les requêtes EV (`day_start_hour` / `day_end_hour` / `begin_hour` / `end_hour`) — avant, l'affichage changeait mais les requêtes restaient sur 8h-19h hardcodés. - **Heures de la journée** : bouton ✓ Appliquer explicite (au lieu de save direct), toast de confirmation, refetch automatique du planning. Synchronisation effective avec les requêtes EV (`day_start_hour` / `day_end_hour` / `begin_hour` / `end_hour`) — avant, l'affichage changeait mais les requêtes restaient sur 8h-19h hardcodés.
- **Thème unifié** : le toggle 🌙 de la topbar et le sélecteur Apparence du panel admin écrivent dans la même clé (`cfg.theme`). Le mode "Automatique" est résolu en JS via `prefers-color-scheme` (le CSS n'avait pas de bloc `@media`, ce qui faisait retomber sur le clair même quand l'OS était en sombre). Listener `matchMedia` pour bascule live en mode auto. - **Thème unifié** : le toggle 🌙 de la topbar et le sélecteur Apparence du panel admin écrivent dans la même clé (`cfg.theme`). Le mode "Automatique" est résolu en JS via `prefers-color-scheme` (le CSS n'avait pas de bloc `@media`, ce qui faisait retomber sur le clair même quand l'OS était en sombre). Listener `matchMedia` pour bascule live en mode auto.
- **Conflit absence/réservation × intervention** : si une intervention est planifiée pendant qu'un tech a une absence (toute la journée ou demi-journée) ou une réservation au même créneau, sa carte est peinte en **rouge plein** (intervention conflictuelle). Logique : full-day → toutes en rouge ; partiel → seules celles en chevauchement. - **Conflit absence/réservation × intervention** : si une intervention est planifiée pendant qu'un tech a une absence (toute la journée ou demi-journée) ou une réservation au même créneau, sa carte est peinte en **rouge plein** (intervention conflictuelle). Logique : full-day → toutes en rouge ; partiel → seules celles en chevauchement.
- **Pillonel & Cie** : suppression de la fonction hardcodée `isPillonelAbsentFriday()`. L'absence récurrente est désormais générique : `RECURRING_ABSENCES[tech.id]` lit `cfg.recurringAbsences` et le label "Absent le X" est calculé dynamiquement depuis le jour de la semaine. - **Absences récurrentes génériques** : suppression de la fonction hardcodée `isXXXAbsentFriday()`. L'absence récurrente est désormais générique : `RECURRING_ABSENCES[tech.id]` lit `cfg.recurringAbsences` et le label "Absent le X" est calculé dynamiquement depuis le jour de la semaine.
- **Notifications au-dessus du flou** : z-index `.toast-stack` relevé à 11000 (le panel admin est à 10000) pour que les toasts de feedback restent visibles quand l'admin est ouvert. - **Notifications au-dessus du flou** : z-index `.toast-stack` relevé à 11000 (le panel admin est à 10000) pour que les toasts de feedback restent visibles quand l'admin est ouvert.
- **Vue horizontale** : popups au survol/clic limités aux candidats `dessous`/`dessus` (la sidebar à gauche et la timeline pleine largeur rendent gauche/droite peu praticables). - **Vue horizontale** : popups au survol/clic limités aux candidats `dessous`/`dessus` (la sidebar à gauche et la timeline pleine largeur rendent gauche/droite peu praticables).
- **Tri équipe** : inclus d'abord, puis exclus, alphabétique dans chaque sous-groupe (ne saute plus quand on coche/décoche). - **Tri équipe** : inclus d'abord, puis exclus, alphabétique dans chaque sous-groupe (ne saute plus quand on coche/décoche).
@@ -109,7 +138,6 @@ Le numéro de **majeure** n'est **pas** un mois et **pas** un chiffre lié au ca
### `v2026.5.40` — Sélection groupe EV + édition domaines + tri équipe + vue horizontale enrichie ### `v2026.5.40` — Sélection groupe EV + édition domaines + tri équipe + vue horizontale enrichie
- **Onglet Équipe** : sélecteur de groupe EasyVista (SI-CSS, SI-EXT, …) en tête de section, détecté automatiquement à l'ouverture du panel via le `<select id="plan_group_id">` de la page Planning EV. Robuste aux ajouts/renommages côté EV. - **Onglet Équipe** : sélecteur de groupe EasyVista (SI-CSS, SI-EXT, …) en tête de section, détecté automatiquement à l'ouverture du panel via le `<select id="plan_group_id">` de la page Planning EV. Robuste aux ajouts/renommages côté EV.
- **Onglet Équipe** : sélecteur de groupe EasyVista (SI-CSS, SI-EXT, …) en tête de section, détecté automatiquement à l'ouverture du panel via le `<select id="plan_group_id">` de la page Planning EV. Robuste aux ajouts/renommages côté EV.
- ID groupe affiché en italique (ex: `ID groupe : 191`). - ID groupe affiché en italique (ex: `ID groupe : 191`).
- Quand on change de groupe, la liste d'équipe se rafraîchit automatiquement avec les membres du nouveau groupe. - Quand on change de groupe, la liste d'équipe se rafraîchit automatiquement avec les membres du nouveau groupe.
- Plus de bouton "Détecter" : tout est auto à l'ouverture. - Plus de bouton "Détecter" : tout est auto à l'ouverture.
@@ -122,110 +150,29 @@ Le numéro de **majeure** n'est **pas** un mois et **pas** un chiffre lié au ca
### `v2026.5.39` — Séparation Matin / Après-midi + Apparence ### `v2026.5.39` — Séparation Matin / Après-midi + Apparence
- Pills "MATIN" / "APRÈS-MIDI" entre les interventions de chaque tech - Pills "MATIN" / "APRÈS-MIDI" entre les interventions de chaque tech
- Section **Apparence** dans les paramètres : thème, taille du texte, durée du cache, heures de la journée - Section **Apparence** dans les paramètres : thème, taille du texte, durée du cache, heures de la journée
- Section **À propos** (version, auteur, licence)
### `v2026.5.38` — Attribution auteur + nettoyage + observabilité ➡ Pour l'historique complet (40+ versions depuis le 16 avril 2026), voir le **[CHANGELOG.md](CHANGELOG.md)** ou la **[page wiki Versions](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Versions)**.
- Module `LOG` unifié + handlers globaux d'erreur
- Toggle "Logs verbeux (debug)" dans le panel admin
- En-têtes copyright dans tous les fichiers source
### `v2026.5.37` — Refonte vue horizontale (sidebar complète)
- Topbar entièrement déplacée en sidebar verticale
### `v2026.5.36` — Sidebar verticale en vue horizontale
- Wrapper flex-row `#horizontal-wrapper` [sidebar 200px] + [main]
### `v2026.5.32` — Vue horizontale togglable
- Bouton ⊞ "Vue" dans popup user-badge
### `v2026.5.27` — Classification absences
- Maladie indigo, Congé cyan, Pompier rouge
### `v4.2.3` — Grande popup timeline persistante
- Clic segment timeline = popup persistante
### `v4.1.3` — Tooltips épinglables
- Introduction de `pinTooltip`
### `v1.0.0` (16 avril 2026) — Initiale
- Premier viewer EasyVista pour le canton
Voir [CHANGELOG.md](CHANGELOG.md) pour l'historique complet (40+ versions taggées).
## Architecture technique ## Architecture technique
``` ```
Planning/ Planification/ # Layout du repo Gitea (public)
├── src/ # Sources de l'extension (chargées par le navigateur) ├── src/ # Sources de l'extension (chargées par le navigateur)
│ ├── manifest.json # Manifest V3 (Chrome) + gecko_settings (Firefox) │ ├── manifest.json # Manifest V3 (Chrome) + browser_specific_settings (Firefox)
│ ├── background.js # Service worker : fetch planning XML, gestion session, fetch fiches │ ├── background.js # Service worker (~1 600 lignes)
│ ├── viewer.html # Interface principale │ ├── viewer.html # Interface principale
│ ├── viewer.js # Logique (~9 500 lignes) │ ├── viewer.js # Logique UI (~10 700 lignes)
│ ├── viewer.css # Styles + thèmes clair/sombre │ ├── viewer.css # Styles + thèmes clair/sombre (~4 800 lignes)
│ └── icons/ # icon16, icon48, icon128 │ └── icons/ # icon16, icon48, icon128
├── Autres/ # Méta : build script + docs (depuis v2026.5.40) ├── build.sh # Génère dist/chromium/, dist/firefox/, .zip, .xpi, met à jour firefox-updates.json
│ ├── build.sh # Génère dist/chromium/, dist/firefox/, .zip, .xpi ├── firefox-updates.json # Manifest auto-update Firefox (servi via update_url)
│ ├── CHANGELOG.md ├── README.md
│ ├── LICENSE ├── CHANGELOG.md
│ └── README.md ├── LICENSE # MIT
├── Builds/ # Artefacts distribués aux techniciens └── .gitignore
│ ├── Chromium/
│ ├── Firefox/
│ ├── planification-vYYYY.M.PATCH-chromium.zip
│ └── planification-vYYYY.M.PATCH-firefox.xpi
└── dist/ # Sortie de build (gitignoré)
``` ```
### `viewer.js` — fonctions clés ➡ Pour le détail des composants, fonctions clés et flux de données, voir la **[page wiki Architecture](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Architecture)**.
| Fonction | Introduite | Rôle |
|---|---|---|
| `loadForDate` | v1.0.0 | Fetch + parse planning pour une date donnée |
| `buildTooltipHTML` | v1.0.0 | Construction HTML du tooltip d'intervention |
| `pinTooltip` | v4.1.3 | Épingler un tooltip (le rendre permanent) |
| `bindTimelinePopover` | v4.2.3 | Lier popover timeline aux segments |
| `showTimelinePopover` | v4.2.3 | Afficher popover persistante |
| `openPersistentTimelinePopup` | v4.2.3 | Grande popup détaillée |
| `setTooltipViewportPosition` | v4.2.4 | Détection auto fixed/abs |
| `_softUnpinPopup` | v4.3.3 | Désépinglage mou (popup reste visible) |
| `initAppClock` | v5.0.0 | Horloge HH:MM topbar |
| `initSessionTimer` | v5.0.0 | Compteur session EV (tick 1s) |
| `_applyViewMode` | v2026.5.32 | Toggle vue classique/horizontale |
| `_maybeRetryFetchUser` | v2026.5.34 | Relance opportuniste fetch user |
| `positionTooltipAnchored` | v2026.5.34 | Positionnement unifié (4 candidats) |
| `renderAdminSectionTeam` | v5.0.0 | Onglet admin Équipe (sélecteur groupe EV depuis v2026.5.40) |
| `renderAdminSectionEV` | v5.0.0 | Onglet admin EasyVista (édition domaines depuis v2026.5.40) |
### `background.js` — fonctions clés
| Fonction | Rôle |
|---|---|
| `findEasyVistaSession` | Trouve l'onglet EV ouvert + extrait PHPSESSID |
| `fetchPlanningXml` | Fetch XML calendar_block du planning |
| `fetchFicheHtml` | Fetch HTML d'une fiche EV (avec retry SSO) |
| `fetchCurrentUser` | Identifie l'user EV connecté |
| `detectGroupsFromEV` (v2026.5.40) | Parse le `<select id="plan_group_id">` → liste des groupes EV |
| `detectTeamFromEV` | Liste les membres d'un groupe (paramétrable depuis v2026.5.40) |
| `evFetch` | Wrapper fetch avec headers EV (Referer, X-Requested-With) |
### Constantes / valeurs hardcodées (toutes versions)
- Group ID EV par défaut : `191` (SI-CSS) — surchargeable via le sélecteur depuis v2026.5.40
- Pillonel Olivier (ID 40944) : absent tous les vendredis (récurrent)
- GUIDs forms EV :
- Demande : `S={C99ECD05-3D48-4C62-ABF0-66292053AED6}`
- Incident : `I={07ED9C68-6172-48EA-8A58-90912B0A283E}`
- SSO : Canton ForgeRock OpenAM
- Storage keys : `admin_config`, `view_mode`
- Domaines : `itsma.etat-de-vaud.ch` (interne), `itsma.vd.ch` (externe)
## Installation
### Firefox
Télécharger le `.xpi` depuis `Builds/` ou le serveur de mises à jour interne, puis drag-and-drop dans `about:addons`.
### Chrome / Edge
Mode développeur : décompresser `Builds/planification-vYYYY.M.PATCH-chromium.zip` (ou utiliser directement `dist/chromium/`) et charger en tant qu'extension non empaquetée.
## Développement ## Développement
@@ -234,29 +181,27 @@ git clone https://gitea.netaplaid.ch/FroSteel/Planification.git
cd Planification cd Planification
# Modifier les sources dans src/ # Modifier les sources dans src/
# Bumper la version dans src/manifest.json + ajouter une entrée dans Autres/CHANGELOG.md # Bumper src/manifest.json + entrée CHANGELOG.md
# Builder : ./build.sh
./Autres/build.sh # → dist/chromium/, dist/firefox/, dist/*.zip, dist/*.xpi
# → produit dist/chromium/, dist/firefox/, dist/*.zip, dist/*.xpi # → firefox-updates.json mis à jour (sha256 .xpi NON SIGNÉ)
# Copier dans Builds/ pour distribution :
cp -r dist/chromium Builds/Chromium
cp -r dist/firefox Builds/Firefox
cp dist/*.zip dist/*.xpi Builds/
git add -A git add -A
git commit -m "vYYYY.M.PATCH — description" git commit -m "vYYYY.M.PATCH — description"
git tag vYYYY.M.PATCH git tag vYYYY.M.PATCH
git push origin main git push origin main vYYYY.M.PATCH
git push --tags
``` ```
Pour Firefox : signer le `.xpi` sur AMO en mode "On your own" (Unlisted),
remplacer l'asset `.xpi` de la release Gitea, puis mettre à jour le sha256
de cette version dans `firefox-updates.json`.
## Licence ## Licence
[MIT License](LICENSE) — Copyright (c) 2026 FroSteel [MIT License](LICENSE) — Copyright (c) 2026 Quentin Rouiller
## Auteur ## Auteur
**FroSteel** (FRS) **Quentin Rouiller** (QRO)
Technicien DGNSI — Canton de Vaud Technicien DGNSI — Canton de Vaud
Contact : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact) Contact : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact)
+2 -2
View File
@@ -27,7 +27,7 @@ import json
with open('src/manifest.json', 'r') as f: m = json.load(f) with open('src/manifest.json', 'r') as f: m = json.load(f)
m['browser_specific_settings'] = { m['browser_specific_settings'] = {
'gecko': { 'gecko': {
'id': 'personne-ficti[email protected]', 'id': 'planificati[email protected]',
'strict_min_version': '140.0', 'strict_min_version': '140.0',
'update_url': 'https://gitea.netaplaid.ch/FroSteel/Planification/raw/branch/main/firefox-updates.json', 'update_url': 'https://gitea.netaplaid.ch/FroSteel/Planification/raw/branch/main/firefox-updates.json',
'data_collection_permissions': {'required': ['none']} 'data_collection_permissions': {'required': ['none']}
@@ -64,7 +64,7 @@ xpi = f"dist/planification-v${VERSION}-firefox.xpi"
with open(xpi, 'rb') as f: sha = hashlib.sha256(f.read()).hexdigest() with open(xpi, 'rb') as f: sha = hashlib.sha256(f.read()).hexdigest()
JSON_PATH = "firefox-updates.json" JSON_PATH = "firefox-updates.json"
ADDON_ID = "personne-ficti[email protected]" ADDON_ID = "planificati[email protected]"
update_link = f"https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v${VERSION}/planification-v${VERSION}-firefox.xpi" update_link = f"https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v${VERSION}/planification-v${VERSION}-firefox.xpi"
if os.path.exists(JSON_PATH): if os.path.exists(JSON_PATH):
+7 -2
View File
@@ -1,11 +1,16 @@
{ {
"addons": { "addons": {
"personne-ficti[email protected]": { "planificati[email protected]": {
"updates": [ "updates": [
{
"version": "2026.5.43",
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.43/planification-v2026.5.43-firefox.xpi",
"update_hash": "sha256:2bdf1b0a781080f4a86600579eb8c2049e060b9e8a0439212f3f29d280d5b93e"
},
{ {
"version": "2026.5.42", "version": "2026.5.42",
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.42/planification-v2026.5.42-firefox.xpi", "update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.42/planification-v2026.5.42-firefox.xpi",
"update_hash": "sha256:3d8cf762bf0921f9da473a9a5e31368fee21f0b9fd71f9f9432d256127de8674" "update_hash": "sha256:a5291a1eab6768d430dfc1cf032f93aeb788083da620ae0568b9dee68f14734d"
}, },
{ {
"version": "2026.5.41", "version": "2026.5.41",
+8 -8
View File
@@ -4,10 +4,10 @@
* Service worker (Manifest V3) : récupération session EV, fetch planning XML, * Service worker (Manifest V3) : récupération session EV, fetch planning XML,
* fetch fiches détaillées, gestion cache. * fetch fiches détaillées, gestion cache.
* *
* Copyright (c) 2026 FroSteel * Copyright (c) 2026 Quentin Rouiller
* Licensed under the MIT License — see LICENSE file in the project root. * Licensed under the MIT License — see LICENSE file in the project root.
* *
* @author FroSteel * @author Quentin Rouiller
* @repository https://gitea.netaplaid.ch/FroSteel/Planification * @repository https://gitea.netaplaid.ch/FroSteel/Planification
*/ */
@@ -207,7 +207,7 @@ chrome.action.onClicked.addListener(async () => {
/** /**
* Trouve l'onglet EasyVista ouvert et récupère phpsessid + origin. * Trouve l'onglet EasyVista ouvert et récupère phpsessid + origin.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
async function findEasyVistaSession() { async function findEasyVistaSession() {
// v2026.5.41 : les origines EV viennent de admin_config (éditables dans // v2026.5.41 : les origines EV viennent de admin_config (éditables dans
@@ -237,7 +237,7 @@ async function findEasyVistaSession() {
* Ce n'est PAS le HTML de la page Planning — le serveur ne rend pas les données * Ce n'est PAS le HTML de la page Planning — le serveur ne rend pas les données
* dans le HTML, elles arrivent via cet endpoint AJAX. * dans le HTML, elles arrivent via cet endpoint AJAX.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
async function fetchPlanningXml(origin, phpsessid, unixDate) { async function fetchPlanningXml(origin, phpsessid, unixDate) {
// v2026.5.41 : groupId vient de admin_config (défaut SI-CSS). // v2026.5.41 : groupId vient de admin_config (défaut SI-CSS).
@@ -520,7 +520,7 @@ async function extendSessionKeepAlive(origin, phpsessid) {
* @param {boolean} force - si true, ignore le cache et refait le test * @param {boolean} force - si true, ignore le cache et refait le test
* @returns {Promise<"internal"|"external">} * @returns {Promise<"internal"|"external">}
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
async function detectNetworkContext(force = false) { async function detectNetworkContext(force = false) {
const CACHE_KEY = "network_context_v2"; // v5.0.12 : nouvelle clé pour invalider le cache fautif v5.0.11 const CACHE_KEY = "network_context_v2"; // v5.0.12 : nouvelle clé pour invalider le cache fautif v5.0.11
@@ -660,7 +660,7 @@ function watchReconnectTabForIamLogin(tabId) {
* Retourne { name: "Nom Prénom" | null, login: "..." | null } ou null si * Retourne { name: "Nom Prénom" | null, login: "..." | null } ou null si
* tout a échoué. * tout a échoué.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
async function fetchCurrentUser(origin, phpsessid) { async function fetchCurrentUser(origin, phpsessid) {
const url = `${origin}/index.php?PHPSESSID=${encodeURIComponent(phpsessid)}`; const url = `${origin}/index.php?PHPSESSID=${encodeURIComponent(phpsessid)}`;
@@ -1039,7 +1039,7 @@ async function deletePlanningItem(origin, phpsessid, actionId, kind) {
* Retourne { groups: [{id, name}, ...] }. Liste vide si le fetch échoue ou * Retourne { groups: [{id, name}, ...] }. Liste vide si le fetch échoue ou
* si EV renvoie une page de login / redirection JS. * si EV renvoie une page de login / redirection JS.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
async function detectGroupsFromEV(origin, phpsessid) { async function detectGroupsFromEV(origin, phpsessid) {
const url = `${origin}/index.php?eventName=HelpDesk_PlanningItem&PHPSESSID=${encodeURIComponent(phpsessid)}`; const url = `${origin}/index.php?eventName=HelpDesk_PlanningItem&PHPSESSID=${encodeURIComponent(phpsessid)}`;
@@ -1175,7 +1175,7 @@ async function detectTeamFromEV(origin, phpsessid, groupIdArg, supportIdsArg) {
} }
console.log("[bg] parsing pattern 1 (checkbox) :", results.length, "résultats"); console.log("[bg] parsing pattern 1 (checkbox) :", results.length, "résultats");
// Pattern 2 : fallback <option value="76272">Nom...</option> // Pattern 2 : fallback <option value="NNNNN">Nom...</option>
if (results.length === 0) { if (results.length === 0) {
const rxOption = /<option[^>]*value=["'](\d{4,7})["'][^>]*>([^<]+)<\/option>/gi; const rxOption = /<option[^>]*value=["'](\d{4,7})["'][^>]*>([^<]+)<\/option>/gi;
let mO; let mO;
+2 -2
View File
@@ -1,8 +1,8 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Planification", "name": "Planification",
"version": "2026.5.42", "version": "2026.5.43",
"description": "Vue claire et rapide du planning des techniciens EasyVista. Développé par FroSteel — DGNSI, Canton de Vaud.", "description": "Vue claire et rapide du planning des techniciens EasyVista. Développé par Quentin Rouiller — DGNSI, Canton de Vaud.",
"permissions": [ "permissions": [
"activeTab", "activeTab",
"scripting", "scripting",
+17 -11
View File
@@ -1,10 +1,10 @@
/** /**
* Planification — Extension navigateur EasyVista (Canton de Vaud / DGNSI) * Planification — Extension navigateur EasyVista (Canton de Vaud / DGNSI)
* *
* Copyright (c) 2026 FroSteel * Copyright (c) 2026 Quentin Rouiller
* Licensed under the MIT License — see LICENSE file in the project root. * Licensed under the MIT License — see LICENSE file in the project root.
* *
* @author FroSteel * @author Quentin Rouiller
* @repository https://gitea.netaplaid.ch/FroSteel/Planification * @repository https://gitea.netaplaid.ch/FroSteel/Planification
*/ */
@@ -1670,7 +1670,7 @@ html.view-horizontal .timeline-noon {
} }
/* ───────────────────────────────────────────────────────────────────────── /* ─────────────────────────────────────────────────────────────────────────
v4.1.20 : Message d'absence récurrente (Pillonel vendredi) v4.1.20 : Message d'absence récurrente (configurée par tech)
───────────────────────────────────────────────────────────────────────── */ ───────────────────────────────────────────────────────────────────────── */
.tech-absence-recurring { .tech-absence-recurring {
padding: 14px 12px; padding: 14px 12px;
@@ -2013,11 +2013,14 @@ body.modal-open {
z-index: 5 !important; z-index: 5 !important;
opacity: 1 !important; opacity: 1 !important;
pointer-events: auto !important; pointer-events: auto !important;
/* Pas de bordure bleue, pas de padding-top (plus de dragbar), juste les /* v2026.5.43 : on conserve les MÊMES dimensions que .pinned-popup
styles de base du tooltip (hérités de .tooltip). */ (padding-top 28px, border 2px) pour que la popup ne bouge ni ne change
border: 1px solid var(--border-strong) !important; de taille au softUnpin. La dragbar est juste retirée (l'espace
reste, c'est le tradeoff pour préserver position + taille).
Bordure plus discrète (variable --border-strong au lieu de --accent). */
border: 2px solid var(--border-strong) !important;
box-shadow: var(--shadow-hover) !important; box-shadow: var(--shadow-hover) !important;
padding-top: 12px !important; padding-top: 28px !important;
animation: none !important; animation: none !important;
} }
@@ -3045,9 +3048,12 @@ body.popup-dragging .pinned-popup {
min-width: 130px; min-width: 130px;
animation: pill-hover-menu-appear 0.12s ease-out; animation: pill-hover-menu-appear 0.12s ease-out;
} }
/* v2026.5.43 : pas de transform dans cette animation — Firefox inclut les
transforms dans getBoundingClientRect ce qui fausse le calcul de position
du menu juste après son insertion dans le DOM. Animation en opacité seule. */
@keyframes pill-hover-menu-appear { @keyframes pill-hover-menu-appear {
from { opacity: 0; transform: translateY(4px); } from { opacity: 0; }
to { opacity: 1; transform: translateY(0); } to { opacity: 1; }
} }
.pill-hover-menu-btn { .pill-hover-menu-btn {
display: flex; display: flex;
@@ -3419,7 +3425,7 @@ html.theme-dark .card.absence-cat-conge {
} }
/* ========================================================================== /* ==========================================================================
v2026.5.30 : absences récurrentes (Pillonel vendredi) en cyan v2026.5.30 : absences récurrentes (configurées par tech) en cyan
(même couleur que Congé mais texte distinct "Absent le vendredi") (même couleur que Congé mais texte distinct "Absent le vendredi")
========================================================================== */ ========================================================================== */
@@ -4221,7 +4227,7 @@ html.view-horizontal .card-status-note.pompier {
/* ========================================================================== /* ==========================================================================
v2026.5.38 : Signature auteur en bas du popup user-badge v2026.5.38 : Signature auteur en bas du popup user-badge
Style cohérent avec le footer "FRS/vX.Y.Z" en bas à droite : petit, gris Style cohérent avec le footer "QRO/vX.Y.Z" en bas à droite : petit, gris
atténué, séparé par une fine ligne supérieure. atténué, séparé par une fine ligne supérieure.
========================================================================== */ ========================================================================== */
.user-name-popup-author { .user-name-popup-author {
+2 -2
View File
@@ -2,10 +2,10 @@
<!-- <!--
Planification — Extension navigateur EasyVista (Canton de Vaud / DGNSI) Planification — Extension navigateur EasyVista (Canton de Vaud / DGNSI)
Copyright (c) 2026 FroSteel Copyright (c) 2026 Quentin Rouiller
Licensed under the MIT License — see LICENSE file in the project root. Licensed under the MIT License — see LICENSE file in the project root.
@author FroSteel @author Quentin Rouiller
@repository https://gitea.netaplaid.ch/FroSteel/Planification @repository https://gitea.netaplaid.ch/FroSteel/Planification
--> -->
<html lang="fr"> <html lang="fr">
+45 -34
View File
@@ -5,10 +5,10 @@
* réservations, absences, statut pompier, etc. Regroupé par tech avec * réservations, absences, statut pompier, etc. Regroupé par tech avec
* timelines visuelles et popups détaillés. * timelines visuelles et popups détaillés.
* *
* Copyright (c) 2026 FroSteel * Copyright (c) 2026 Quentin Rouiller
* Licensed under the MIT License — see LICENSE file in the project root. * Licensed under the MIT License — see LICENSE file in the project root.
* *
* @author FroSteel * @author Quentin Rouiller
* @repository https://gitea.netaplaid.ch/FroSteel/Planification * @repository https://gitea.netaplaid.ch/FroSteel/Planification
*/ */
@@ -494,7 +494,7 @@ const _CURRENT_USER_RETRY_DELAY_MS = 60 * 1000;
/** /**
* Récupère le nom de l'utilisateur EasyVista connecté et l'affiche dans le badge. * Récupère le nom de l'utilisateur EasyVista connecté et l'affiche dans le badge.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
async function fetchAndShowCurrentUser() { async function fetchAndShowCurrentUser() {
const attemptId = _currentUserRetryCount + 1; const attemptId = _currentUserRetryCount + 1;
@@ -581,7 +581,7 @@ async function fetchAndShowCurrentUser() {
* *
* @param {string} reason - contexte pour les logs (ex: "after_load_success") * @param {string} reason - contexte pour les logs (ex: "after_load_success")
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function _maybeRetryFetchUser(reason) { function _maybeRetryFetchUser(reason) {
if (state.currentUser && state.currentUser.name) { if (state.currentUser && state.currentUser.name) {
@@ -713,11 +713,11 @@ function toggleUserNamePopup() {
popup.appendChild(settingsBtn); popup.appendChild(settingsBtn);
// v2026.5.38 : signature auteur en bas du popup user-badge // v2026.5.38 : signature auteur en bas du popup user-badge
// Style : même look que le footer "FRS/vX.Y.Z" en bas à droite — petit, // Style : même look que le footer "QRO/vX.Y.Z" en bas à droite — petit,
// gris atténué, séparé par une fine ligne supérieure. // gris atténué, séparé par une fine ligne supérieure.
const authorLine = document.createElement("div"); const authorLine = document.createElement("div");
authorLine.className = "user-name-popup-author"; authorLine.className = "user-name-popup-author";
authorLine.textContent = "Développé par FroSteel"; authorLine.textContent = "Développé par Quentin Rouiller";
authorLine.title = "Auteur de l'extension"; authorLine.title = "Auteur de l'extension";
popup.appendChild(authorLine); popup.appendChild(authorLine);
@@ -1190,12 +1190,12 @@ function initModalScrollLock() {
updateLock(); // au cas où un modal serait déjà là au boot updateLock(); // au cas où un modal serait déjà là au boot
} }
// v4.2.9 : pied de page discret "FRS / vX.X.X" en bas à droite. // v4.2.9 : pied de page discret "QRO / vX.X.X" en bas à droite.
// La version est lue depuis le manifest (source unique de vérité). // La version est lue depuis le manifest (source unique de vérité).
/** /**
* Pied de page discret bas-droite avec FRS et numéro de version. * Pied de page discret bas-droite avec QRO et numéro de version.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function initAppFooter() { function initAppFooter() {
if (document.querySelector(".app-footer")) return; if (document.querySelector(".app-footer")) return;
@@ -1209,7 +1209,7 @@ function initAppFooter() {
} }
const el = document.createElement("div"); const el = document.createElement("div");
el.className = "app-footer"; el.className = "app-footer";
el.textContent = `FRS${version ? " / " + version : ""}`; el.textContent = `QRO${version ? " / " + version : ""}`;
document.body.appendChild(el); document.body.appendChild(el);
} }
@@ -1266,7 +1266,7 @@ function _toggleView() {
* logs verbeux étape par étape ont été retirés, ils polluaient la console * logs verbeux étape par étape ont été retirés, ils polluaient la console
* à chaque toggle. * à chaque toggle.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function _applyViewMode() { function _applyViewMode() {
const mode = _getCurrentView(); const mode = _getCurrentView();
@@ -1314,7 +1314,7 @@ function _applyViewMode() {
* v2026.5.36 : crée ou retrouve la sidebar, et y déplace les éléments listés. * v2026.5.36 : crée ou retrouve la sidebar, et y déplace les éléments listés.
* Les éléments sont déplacés dans l'ordre du tableau. * Les éléments sont déplacés dans l'ordre du tableau.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function _moveElementsToSidebar(ids) { function _moveElementsToSidebar(ids) {
// Créer (ou retrouver) le wrapper qui contiendra sidebar + main côte à côte // Créer (ou retrouver) le wrapper qui contiendra sidebar + main côte à côte
@@ -1392,7 +1392,7 @@ function _moveElementsToSidebar(ids) {
* v2026.5.36 : restaure chaque élément à son parent d'origine (mémorisé dans * v2026.5.36 : restaure chaque élément à son parent d'origine (mémorisé dans
* data-orig-parent). Utilisé quand on revient en vue classique. * data-orig-parent). Utilisé quand on revient en vue classique.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function _restoreElementsToTopbar(ids) { function _restoreElementsToTopbar(ids) {
// v2026.5.37 : d'abord remettre les flèches dans .date-nav (avant de // v2026.5.37 : d'abord remettre les flèches dans .date-nav (avant de
@@ -1499,7 +1499,7 @@ function _restoreToOriginalParent(el, label) {
/** /**
* Horloge HH:MM au milieu de la topbar, mise à jour chaque minute (depuis v5.0.0). * Horloge HH:MM au milieu de la topbar, mise à jour chaque minute (depuis v5.0.0).
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function initAppClock() { function initAppClock() {
const el = document.getElementById("app-clock"); const el = document.getElementById("app-clock");
@@ -2934,7 +2934,7 @@ function renderAdminSectionAbout(container, cfg, saveFn) {
</div> </div>
<div class="admin-about-row"> <div class="admin-about-row">
<div class="admin-about-label">Auteur</div> <div class="admin-about-label">Auteur</div>
<div class="admin-about-value"><strong>FroSteel</strong></div> <div class="admin-about-value"><strong>Quentin Rouiller</strong></div>
</div> </div>
<div class="admin-about-row"> <div class="admin-about-row">
<div class="admin-about-label">Affiliation</div> <div class="admin-about-label">Affiliation</div>
@@ -3695,7 +3695,7 @@ async function _purgeOldCacheEntries() {
/** /**
* Charge le planning pour la date donnée (fetch XML EasyVista + cache). * Charge le planning pour la date donnée (fetch XML EasyVista + cache).
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
async function loadForDate(isoDate, opts = {}) { async function loadForDate(isoDate, opts = {}) {
LOG.info("load", `début pour ${isoDate}`, { opts, hasSession: !!state.session }); LOG.info("load", `début pour ${isoDate}`, { opts, hasSession: !!state.session });
@@ -5876,7 +5876,7 @@ function _recurringAbsenceLabel(isoDate) {
/** /**
* Construit la card HTML d'un technicien (header, timeline, interventions, absences). * Construit la card HTML d'un technicien (header, timeline, interventions, absences).
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function buildCard(tech, isoDate) { function buildCard(tech, isoDate) {
const card = document.createElement("section"); const card = document.createElement("section");
@@ -6572,7 +6572,7 @@ function getStatusClass(iv) {
/** /**
* Lie les segments timeline à un popover persistant au clic (depuis v4.2.3). * Lie les segments timeline à un popover persistant au clic (depuis v4.2.3).
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function bindTimelinePopover(el) { function bindTimelinePopover(el) {
// v2026.5.40 r4 : comportement UNIFIÉ vue classique + vue horizontale. // v2026.5.40 r4 : comportement UNIFIÉ vue classique + vue horizontale.
@@ -6706,7 +6706,7 @@ function findIvByActionId(actionId) {
/** /**
* Ouvre une grande popup détaillée pour un segment timeline. * Ouvre une grande popup détaillée pour un segment timeline.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function openPersistentTimelinePopup(el) { function openPersistentTimelinePopup(el) {
if (!el) { if (!el) {
@@ -7330,10 +7330,10 @@ function showToast(label, ref) {
/** /**
* Formate un numéro de téléphone suisse / français. * Formate un numéro de téléphone suisse / français.
* 079 000 00 79 (mobile CH) * 079 123 45 67 (mobile CH)
* 021 000 00 63 (fixe CH) * 021 123 45 67 (fixe CH)
* +41 79 000 00 67 * +41 79 123 45 67
* +33 1 00 00 00 07 * +33 1 23 45 67 89
* Si le format n'est pas reconnu, renvoie le numéro tel quel (avec les chiffres seuls). * Si le format n'est pas reconnu, renvoie le numéro tel quel (avec les chiffres seuls).
*/ */
function formatPhone(raw) { function formatPhone(raw) {
@@ -7632,8 +7632,8 @@ function cleanContactName(raw) {
} }
/** /**
* Split un lieu du type "Lausanne/Rue de l'Exemple 9 bis" en * Split un lieu du type "Lausanne/Rue Caroline 9 bis" en
* { ville: "Lausanne", adresse: "Rue de l'Exemple 9 bis" } * { ville: "Lausanne", adresse: "Rue Caroline 9 bis" }
* Si format inconnu, retourne { ville: null, adresse: raw }. * Si format inconnu, retourne { ville: null, adresse: raw }.
*/ */
function splitLieu(raw) { function splitLieu(raw) {
@@ -7645,7 +7645,7 @@ function splitLieu(raw) {
// v2026.5.16 : le format EasyVista peut avoir jusqu'à 3 parties séparées // v2026.5.16 : le format EasyVista peut avoir jusqu'à 3 parties séparées
// par "/" : VILLE / ADRESSE / PRÉCISIONS (étage, bureau, indications). // par "/" : VILLE / ADRESSE / PRÉCISIONS (étage, bureau, indications).
// Exemple : "LAUSANNE / Av. de l'Exemple 19 / 4eme en face de l'ascenseur" // Exemple : "LAUSANNE / Av. de Beaulieu 19 / 4eme en face de l'ascenseur"
// On ne garde que VILLE + ADRESSE. Les précisions (3e partie et suivantes) // On ne garde que VILLE + ADRESSE. Les précisions (3e partie et suivantes)
// sont strippées — elles alourdissent la carte et sont disponibles dans // sont strippées — elles alourdissent la carte et sont disponibles dans
// le tooltip détaillé. // le tooltip détaillé.
@@ -7992,7 +7992,7 @@ function _cancelPendingShowTooltip() {
/** /**
* Affiche un tooltip au survol d'une intervention/réservation. * Affiche un tooltip au survol d'une intervention/réservation.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function showTooltip(e, iv, rowEl) { function showTooltip(e, iv, rowEl) {
// v2026.5.39 : on planifie l'affichage à +500ms. // v2026.5.39 : on planifie l'affichage à +500ms.
@@ -8252,7 +8252,7 @@ function reapplyTooltipPosition() {
* @param {HTMLElement} sourceEl - l'élément déclencheur (row, card, segment) * @param {HTMLElement} sourceEl - l'élément déclencheur (row, card, segment)
* @param {object} opts - options { anchorBelow: true pour préférer dessous } * @param {object} opts - options { anchorBelow: true pour préférer dessous }
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function positionTooltipAnchored(sourceEl, opts) { function positionTooltipAnchored(sourceEl, opts) {
opts = opts || {}; opts = opts || {};
@@ -8406,7 +8406,7 @@ function _rectsOverlap(a, b) {
* ligne source `rowEl`. Essaie dans l'ordre : droite, gauche, dessous, dessus. * ligne source `rowEl`. Essaie dans l'ordre : droite, gauche, dessous, dessus.
* Retourne {x, y} en coordonnées document, ou null si aucune position libre. * Retourne {x, y} en coordonnées document, ou null si aucune position libre.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function _findFreePopupPosition(rowEl, w, h) { function _findFreePopupPosition(rowEl, w, h) {
const pad = 14; const pad = 14;
@@ -8520,7 +8520,7 @@ function _findFreePopupPosition(rowEl, w, h) {
* v4.3.0 : épingle la bulle courante en la clonant dans un popup détaché * v4.3.0 : épingle la bulle courante en la clonant dans un popup détaché
* ancré au contenu. Le tooltip live redevient disponible. * ancré au contenu. Le tooltip live redevient disponible.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function pinTooltip() { function pinTooltip() {
if (!state.currentTooltipIv) return; if (!state.currentTooltipIv) return;
@@ -8919,7 +8919,7 @@ async function _refreshPinnedPopupIv(popup, iv) {
* *
* @param {HTMLElement} el - le popup à désépingler * @param {HTMLElement} el - le popup à désépingler
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function _softUnpinPopup(el) { function _softUnpinPopup(el) {
if (!el) { if (!el) {
@@ -9339,9 +9339,19 @@ function _showPillHoverMenu(pill, popup) {
}); });
menu.appendChild(closeBtn); menu.appendChild(closeBtn);
// v2026.5.43 (Firefox-fix) : positionner le menu HORS écran d'abord pour
// qu'il soit layouté sans flash, puis mesurer ses dimensions, puis poser
// la position finale. Sans ça, Firefox lit parfois des dimensions à 0
// (timing de l'animation `pill-hover-menu-appear` + transform initial),
// ce qui projette le menu n'importe où sur l'écran.
menu.style.left = "-9999px";
menu.style.top = "-9999px";
menu.style.visibility = "hidden";
document.body.appendChild(menu); document.body.appendChild(menu);
// Force le navigateur à calculer la mise en page maintenant (Firefox ne
// le fait pas toujours sur getBoundingClientRect immédiat après append).
void menu.offsetHeight;
// Positionner au-dessus de la pastille
const r = pill.getBoundingClientRect(); const r = pill.getBoundingClientRect();
const menuR = menu.getBoundingClientRect(); const menuR = menu.getBoundingClientRect();
let left = r.left + (r.width / 2) - (menuR.width / 2); let left = r.left + (r.width / 2) - (menuR.width / 2);
@@ -9349,6 +9359,7 @@ function _showPillHoverMenu(pill, popup) {
if (left + menuR.width > window.innerWidth - 4) left = window.innerWidth - menuR.width - 4; if (left + menuR.width > window.innerWidth - 4) left = window.innerWidth - menuR.width - 4;
menu.style.left = left + "px"; menu.style.left = left + "px";
menu.style.top = (r.top - menuR.height - 8) + "px"; menu.style.top = (r.top - menuR.height - 8) + "px";
menu.style.visibility = "";
// Garder ouvert si la souris entre dans le menu // Garder ouvert si la souris entre dans le menu
menu.addEventListener("mouseenter", () => { menu.addEventListener("mouseenter", () => {
@@ -9415,7 +9426,7 @@ function _getPopupSafeArea() {
* dans la safe area. Appelé à l'épinglage, pendant le drag, et quand le dock * dans la safe area. Appelé à l'épinglage, pendant le drag, et quand le dock
* apparaît/disparaît. * apparaît/disparaît.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function _clampPopupInSafeArea(popup) { function _clampPopupInSafeArea(popup) {
if (!popup) return; if (!popup) return;
@@ -10203,7 +10214,7 @@ function bindTooltipInteractions() {
/** /**
* Construit le HTML du tooltip détaillé pour une intervention/réservation. * Construit le HTML du tooltip détaillé pour une intervention/réservation.
* *
* @author FroSteel * @author Quentin Rouiller
*/ */
function buildTooltipHTML(iv) { function buildTooltipHTML(iv) {
if (!iv) return '<dl><dt>Info</dt><dd>—</dd></dl>'; if (!iv) return '<dl><dt>Info</dt><dd>—</dd></dl>';