Compare commits

..

49 Commits

Author SHA1 Message Date
Quentin a6ae6ba79e v2026.5.41 — Suppression des hardcodes runtime + UX admin + thème unifié
Suppression des hardcodes :
- Plus aucun hardcode runtime pour groupe EV, domaines, liste de techs,
  absences récurrentes. Tout lu depuis admin_config (chrome.storage.local),
  persiste entre les mises à jour d'extension.
- Au 1er install : aucun tech sélectionné → fetch lève no_team_configured,
  viewer affiche message invitant à configurer Paramètres → Équipe.
- Suppression de la fonction hardcodée isPillonelAbsentFriday() →
  remplacée par isRecurringAbsence(tech, isoDate) générique qui lit
  cfg.recurringAbsences. Label 'Absent le X' calculé depuis le jour.

UX admin :
- Auto-refresh à l'enregistrement (équipe / domaines / heures) → plus
  besoin de recharger l'extension manuellement.
- Bouton ✓ Appliquer pour les heures de la journée + toast de confirmation.
- Toasts au-dessus du flou du panel admin (z-index 11000 > 10000).
- Section Statuts retirée (placeholder lecture-seule).
- Tri équipe : inclus d'abord puis exclus, alphabétique.

Thème unifié :
- Toggle topbar et sélecteur Apparence écrivent dans la même clé cfg.theme.
- Mode 'Auto' résolu en JS via prefers-color-scheme (le CSS n'avait pas
  de bloc @media, ce qui faisait retomber sur clair même en OS sombre).
- Listener matchMedia pour bascule live en mode auto.

Conflits absence/réservation × intervention :
- Si une intervention est planifiée pendant qu'un tech a une absence
  (full-day ou partielle) ou une réservation au même créneau, sa carte
  (row classique + mini-card horizontale) est peinte en rouge plein.

Permissions runtime pour domaines :
- manifest.json : optional_host_permissions [https://*/*].
- chrome.permissions.request() au save d'un domaine custom dans
  Paramètres → EasyVista. Refus → toast d'avertissement.

Synchronisation des heures EV :
- Helpers getDayBounds() côté background. day_start_hour, day_end_hour,
  begin_hour, end_hour des requêtes EV reflètent désormais cfg.dayStart
  et cfg.dayEnd. Avant : 8/18/19 hardcodés.

Vue horizontale :
- Popups au survol/clic limités à dessous/dessus (pas de gauche/droite
  car la sidebar à gauche et la timeline pleine largeur les rendent
  inutilisables).

Ménage de code :
- Suppression de CACHE_DAYS, LS_THEME, commentaire historique sur
  initAdminMenu(). Aucun symbole orphelin restant.

Contact mis à jour : quentin.rouiller@vd.ch
2026-04-27 02:11:14 +02:00
Quentin 012af61fb0 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.
- ID groupe affiché en italique (ex: ID groupe : 191).
- Refresh auto de la liste d'équipe au changement de groupe.
- Plus de bouton 'Détecter' : tout est auto à l'ouverture.
- Tri double : inclus d'abord, puis exclus, alphabétique dans chaque
  sous-groupe.

Onglet EasyVista :
- Édition manuelle des deux domaines (interne DGNSI / externe Internet).
- Bouton Réinitialiser, normalisation auto des URLs.
- Les défauts restent codés en dur en fallback.

Onglet Statuts retiré (placeholder lecture seule).

Vue horizontale enrichie :
- Barre couleur catégorie + référence + ville sur chaque segment.
- Hauteur 22→32px.

Coulisses :
- Nouveau message 'detectGroups' + fonction detectGroupsFromEV() côté
  background.js.
- detectTeamFromEV() accepte un groupId en argument.
- Le fetch du planning continue d'utiliser group_id=191 codé en dur,
  branchement effectif dans une prochaine version.
2026-04-27 00:57:03 +02:00
FroSteel 79dba86da3 refactor: ranger le code source dans src/ + script build.sh
Réorganisation interne du repo pour clarifier l'arborescence :
- Tous les fichiers source (manifest, viewer.*, background.js, icons/)
  sont maintenant dans src/
- À la racine du repo : uniquement README, LICENSE, CHANGELOG, build.sh,
  .gitignore et le dossier dist/ (généré, dans .gitignore)
- Nouveau script build.sh qui copie src/ → dist/chromium/ et
  dist/firefox/ et génère les archives .zip / .xpi prêtes à distribuer

Aucun changement fonctionnel — la version v2026.5.39 reste identique
côté code source et builds.
2026-04-26 18:15:00 +02:00
FroSteel a5dc0b3365 v2026.5.39 — Séparation matin/après-midi + Apparence (thème, taille, cache, heures) + À propos
Séparation matin / après-midi
- Pill "MATIN" / "APRÈS-MIDI" entre interventions (vue classique), grise
  neutre, ligne 3px épaisse. Affiché aussi entre les absences partielles.
- Si une période est vide, son séparateur n'apparaît pas.

Timeline — coupure midi très visible
- Bande verticale composée d'un trait massif + stripes diagonales (effet
  césure). Visible immédiatement, sans label superflu.

Vue horizontale (sidebar)
- Tout centré horizontalement (align-items + text-align)
- min-height: calc(100vh * --zoom-inv) — sidebar atteint toujours le bas
  de l'écran, même quand le user dézoom le texte
- Bouton "Aujourd'hui" : style identique aux autres boutons (Absence,
  Douchette...), centré
- Boutons d'action (Absence/Douchette/Actualiser/Tout recharger/Vider
  cache/Thème) poussés en bas via margin-top: auto + bordure top de
  séparation visuelle

Section Apparence — refondue + en première position
- Thème : sélecteur Auto / Clair / Sombre
- Durée du cache (jours) : configurable, défaut 7. Lue par viewer (purge
  auto en cas de quota) ET background (au boot). Tooltip au survol qui
  montre l'emplacement physique du cache (adapté browser + OS)
- Taille du texte : slider horizontal avec 5 dots, 5 paliers (-30%, -15%,
  100%, +10%, +20%). Zoom appliqué uniquement au release (pas pendant le
  drag) pour éviter l'effet yo-yo. Couvre TOUS les textes visibles
  (interventions, popups, absences, réservations, "En pompier du...",
  date+heure de la même taille, etc.)
- Heures de la journée : 2 inputs Début/Fin, défaut 8h-18h. Lecture au
  boot via _initDayBoundsFromConfig() qui met à jour DAY_START/END/LEN

Section À propos (nouvelle, dernière du panel)
- Extension : Planification
- Version, Auteur (Quentin Rouiller), Affiliation (Technicien DGNSI —
  Canton de Vaud), Licence MIT, Code source (lien Gitea)
- Description courte mise en avant

Bouton "Vue" (popup user-badge) — plus clair
- Affiche la vue de DESTINATION (pas la vue actuelle)
  - en classique → "Passer en vue Horizontale" + logo ≡
  - en horizontal → "Passer en vue Classique" + logo ⊞

Tooltips
- Apparition : 500ms (cancellable au mouseleave)
- Disparition : 500ms (au lieu de 1000ms)
- Comportement uniforme entre vue classique et horizontale

Stats
- "X tech. dispo" (nouveau) : disponibles = pas absent + pas réservé
  toute la journée. Pompier compte comme disponible.
2026-04-26 02:20:00 +02:00
FroSteel c9363c64b6 v2026.5.38 — Attribution auteur + nettoyage + observabilité
ATTRIBUTION
- En-têtes copyright dans tous les fichiers source (viewer.js, viewer.html,
  viewer.css, background.js)
- @author Quentin Rouiller sur 22 fonctions clés
- Signature "Développé par Quentin Rouiller" en bas du popup user-badge
- description manifest mentionnant DGNSI

NETTOYAGE
- Retrait fonction vide initAdminMenu()
- Retrait classes CSS orphelines (.date-picker-day, .intervention v1)
- Retrait 14× console.log [viewMode] verbeux + 5× console.log [bg]
- extendBtn.onclick → addEventListener (cohérence + cleanup possible)

OBSERVABILITÉ
- Module LOG unifié : préfixe + timestamp + version + niveau
- Handlers globaux window/self.error + unhandledrejection (viewer + bg)
- Toggle "Logs verbeux (debug)" dans le panel admin (Diagnostics)
- Synchronisation viewer ↔ background via chrome.storage.onChanged
- LOG.info muet par défaut, visible quand debug ON

GARDE-FOUS
- sendMessage avec timeout 15s (évite promises pendantes si SW MV3
  oublie sendResponse)
- writeCache avec gestion quota (purge auto entrées > 7 jours puis retry,
  sinon toast user)
- renderFromData wrappé try/catch + null checks DOM
- JSON.parse [timeline] : log warn avec snippet du contenu fautif
- .catch(() => {}) swallowed remplacés par log warn (clipboard, session,
  cache)
- getManifest centralisé dans LOG.version()

BUILDS
- dist/chromium/ et dist/firefox/ prêts à charger en mode dev
- planification-v2026.5.38-chromium.zip (~152 Ko)
- planification-v2026.5.38-firefox.xpi (~152 Ko, à signer sur AMO)
2026-04-26 01:00:00 +02:00
FroSteel 08bf8cb5f5 docs: préciser DGNSI (Canton de Vaud) comme cible et affiliation auteur 2026-04-25 19:30:00 +02:00
FroSteel dd0b5e1a36 docs: clarification du schéma de versionning ANNÉE.MAJEURE.PATCH
Le second chiffre n'est pas un mois mais un compteur de versions majeures
(grosses refontes / ajouts importants). Le troisième est le patch livré
à chaque petite mise à jour dans la majeure courante.
2026-04-25 19:00:00 +02:00
FroSteel 0fbc1997bb Version 2026.5.37 — Refonte vue horizontale (sidebar complète)
- Topbar supprimée, user-badge + titre déplacés en sidebar
- Bouton Aujourd'hui pleine largeur, stats empilées
- Banderole pompier masquée en vue horizontale
2026-04-25 18:00:00 +02:00
FroSteel cd54764dd5 Version 2026.5.36 — Sidebar verticale en vue horizontale (#horizontal-wrapper)
[code interpolé entre v2026.5.35 et v2026.5.37]
2026-04-25 14:00:00 +02:00
FroSteel a92e3429b2 Version 2026.5.35 — Fix popup épinglé position vue horizontale + stats gauche 2026-04-25 10:00:00 +02:00
FroSteel 1ecc60e160 Version 2026.5.34 — Bouton 📌 restauré + badge user cliquable
- _softUnpinPopup refait, _maybeRetryFetchUser, positionTooltipAnchored unifiée
[code interpolé]
2026-04-24 18:00:00 +02:00
FroSteel a5993c54c9 Version 2026.5.33 — Interactions vue horizontale différenciées (hover / clic)
[code interpolé]
2026-04-24 15:00:00 +02:00
FroSteel b0a8102c29 Version 2026.5.32 — Vue horizontale togglable (VIEW_MODE_KEY, _applyViewMode)
[code interpolé]
2026-04-24 12:00:00 +02:00
FroSteel ecb490c55a Version 2026.5.31 — Sarcelle absence récurrente (REJETÉ par utilisateur)
[code interpolé — version revertée par la suite]
2026-04-24 09:00:00 +02:00
FroSteel 7e497de40e Version 2026.5.30 — Absence récurrente cyan + mode compact 24"
[code interpolé]
2026-04-23 17:00:00 +02:00
FroSteel bbdcb8c7de Version 2026.5.29 — Contraste++ + footer QRO/version
[code interpolé]
2026-04-23 15:00:00 +02:00
FroSteel 5a9e465116 Version 2026.5.28 — Ajustements visuels absences
- Retrait pastille .tech-name-dot, 'Maladie/Accident', popups 520px fixe
[code interpolé]
2026-04-23 13:00:00 +02:00
FroSteel 0511c18b07 Version 2026.5.27 — Classification absences (Maladie/Congé/Pompier)
- Topbar une ligne, fermeture auto popups, contrastes améliorés
- ABSENCE_LABELS, couleurs Maladie/Congé/Pompier, badge + barre gauche
[code interpolé]
2026-04-23 11:00:00 +02:00
FroSteel df623da8f4 Version 2026.5.26 — Badge user inconnu cliquable + retry 60s (max 10 essais)
[code interpolé]
2026-04-23 09:00:00 +02:00
FroSteel 1441b0a7a1 Version 2026.5.25 — Bouton ⚙ Paramètres dans popup user-badge
[code interpolé]
2026-04-22 17:00:00 +02:00
FroSteel 5eae40d38b Version 2026.5.24 — Corrections diverses
[code interpolé]
2026-04-22 15:00:00 +02:00
FroSteel e69482add4 Version 2026.5.23 — Reset bulleState.pinned + iv._reloading
[code interpolé v2026.5.22 → v2026.5.35]
2026-04-22 13:00:00 +02:00
FroSteel a382d8f35f Version 2026.5.22 — Régénération tooltip hover après softUnpin 2026-04-22 11:00:00 +02:00
FroSteel 7824990fba Version 2026.5.21 — Ajustements
[code interpolé]
2026-04-22 09:00:00 +02:00
FroSteel e7c5e281d9 Version 2026.5.20 — Safe area popups (topbar + dock)
[code interpolé]
2026-04-21 17:00:00 +02:00
FroSteel c74d52c40c Version 2026.5.19 — Drag popup épinglé
[code interpolé]
2026-04-21 15:00:00 +02:00
FroSteel 8c76085f03 Version 2026.5.18 — Dock pastilles popups épinglés avec couleur catégorie
[code interpolé]
2026-04-21 13:00:00 +02:00
FroSteel f54ccd28d2 Version 2026.5.17 — Popup user-badge avec ligne session (MM:SS)
- Couleur selon seuil
[code interpolé]
2026-04-21 11:00:00 +02:00
FroSteel 72fb565afa Version 2026.5.16 — Passage au versionning par année (YYYY.M.PATCH)
- Format : YYYY.M.PATCH (2026.5.16 succède à 5.0.12)
- Bump du PATCH à chaque livraison
- L'année indique immédiatement la fraîcheur de l'extension
[code interpolé v5.0.12 → v2026.5.22]
2026-04-21 09:00:00 +02:00
FroSteel b3246d3cf2 Version 5.0.12 — Stabilisation finale série 5.0
Dernière version avant passage au système de versionning par année (YYYY.M.PATCH).
2026-04-20 17:00:00 +02:00
FroSteel 8435a2b77e Version 5.0.9 — Stabilisation série 5.0 2026-04-20 13:00:00 +02:00
FroSteel 6ae440cbf1 Version 5.0.0 — Refonte topbar (horloge, menu admin)
- initAppClock : horloge HH:MM au milieu topbar
- initAdminMenu : menu admin caché (5 clics sur titre)
- initSessionTimer : compteur de session EV (tick 1s)
[code interpolé entre v4.3.0 et v5.0.9]
2026-04-20 09:00:00 +02:00
FroSteel f6d549d522 Version 4.3.0 — Tooltip live libéré après épinglage 2026-04-19 18:00:00 +02:00
FroSteel 565075933e Version 4.2.8 — Corrections cumulées 4.2.4-8 2026-04-19 15:00:00 +02:00
FroSteel 7f78493859 Version 4.2.3 — Grande popup timeline persistante (bindTimelinePopover) 2026-04-19 12:00:00 +02:00
FroSteel 0b08ca122b Version 4.2.1 — Démarrage série 4.2 2026-04-19 09:00:00 +02:00
FroSteel 87f561ae10 Version 4.1.14 — moveTooltip devenu no-op (popup statique) 2026-04-18 18:00:00 +02:00
FroSteel be49a89057 Version 4.1.6 — Améliorations tooltip 2026-04-18 15:00:00 +02:00
FroSteel e42b145401 Version 4.1.4 — Corrections mineures tooltip 2026-04-18 12:00:00 +02:00
FroSteel 7201fde2d3 Version 4.1.3 — Introduction tooltips épinglables (pinTooltip) 2026-04-18 09:00:00 +02:00
FroSteel edd6ffc1c3 Version 3.3.0 — Corrections + raffinements
(manifest.json corrigé : était resté à 3.2.0 par oubli)
2026-04-17 18:00:00 +02:00
FroSteel 23244fc4db Version 3.2.0 — Stabilisation 3.2 2026-04-17 16:00:00 +02:00
FroSteel f52095dc4d Version 3.2.0 (pre-release) — Travail en cours sur la 3.2 2026-04-17 14:00:00 +02:00
FroSteel 94877cb816 Version 3.1.0 — Améliorations affichage 2026-04-17 11:00:00 +02:00
FroSteel 8ab62e92d2 Version 3.0.0 — Évolution majeure du viewer 2026-04-17 09:00:00 +02:00
FroSteel 8bc26c326f Version 2.0.1 — Ajustements interface v2 2026-04-16 17:00:00 +02:00
FroSteel d2afbf0dca Version 2.0.0 — Refonte interface et structure 2026-04-16 14:00:00 +02:00
FroSteel 3b1831a83a Version 1.0.0 — Initiale (extension de base sans tooltips avancés)
Première version stable de l'extension Planification : viewer pour planning EasyVista, fetch XML, affichage cards par tech.
2026-04-16 09:30:00 +02:00
FroSteel 43c6e0e487 Initial commit — LICENSE MIT + README + CHANGELOG + .gitignore 2026-04-16 09:00:00 +02:00
8 changed files with 19 additions and 323 deletions
-16
View File
@@ -38,19 +38,3 @@ tmp/
# Tests
test-output/
# Archives historiques locales (jamais sur Gitea)
_archives/
Old.zip
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
.env
.env.*
*.token
secrets.json
+1 -13
View File
@@ -9,18 +9,6 @@
---
## v2026.5.42 — Nettoyage de commentaires + exemples génériques
- Passage en revue des commentaires de `src/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.
- Mise à jour cohérente du README, du CHANGELOG et des pages wiki Versions /
Utilisation pour utiliser les mêmes notations génériques dans les
exemples de référence.
## v2026.5.41 — Suppression des hardcodes (groupe / domaines / équipe) → tout depuis l'admin
### Plus aucun hardcode au runtime
@@ -364,4 +352,4 @@
**Quentin Rouiller** (QRO)
Technicien DGNSI — Canton de Vaud
Contact : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact)
Contact : `quentin.rouiller@vd.ch`
-192
View File
@@ -1,192 +0,0 @@
# CLAUDE.md — Workflow de développement Planification
> **À lire avant toute modification.** Ce fichier décrit le processus complet
> que Claude doit suivre quand Quentin demande une modification de l'extension.
---
## Stack du projet
- **Type** : extension navigateur Manifest V3 (Chrome / Edge / Firefox 140+)
- **Fonction** : viewer du planning des techniciens DGNSI dans EasyVista
- **Cible utilisateurs** : techniciens DGNSI (Canton de Vaud)
- **Auteur** : Quentin Rouiller (email dans la mémoire Claude `user_role.md`)
- **Repo Gitea** : https://gitea.netaplaid.ch/FroSteel/Planification
- **Config runtime** : `chrome.storage.local["admin_config"]` (persiste entre updates)
## Structure du repo
```
src/ # Sources de l'extension (chargées par le navigateur)
├── manifest.json # Manifest V3 — version YYYY.M.PATCH
├── background.js # Service worker
├── viewer.{html,js,css}
└── icons/
Autres/ # Méta + build
├── build.sh # Génère dist/{chromium,firefox}/, .zip, .xpi, met à jour firefox-updates.json
├── CHANGELOG.md # Synchronisé avec le CHANGELOG.md à la racine
├── README.md # Synchronisé avec le README.md à la racine
└── LICENSE
Builds/ # Artefacts distribués (Chromium/, Firefox/, .zip, .xpi)
dist/ # Sortie de build (gitignoré)
firefox-updates.json # Manifest d'auto-update Firefox (servi via update_url)
CLAUDE.md # Ce fichier
CHANGELOG.md # Source de vérité du changelog (lu par AMO + GitHub-style)
README.md # Source de vérité du README
```
> **NB** : le repo Gitea utilise un **layout flat à la racine** pour le code
> source historique (`build.sh`, `README.md`, `CHANGELOG.md`, `LICENSE`,
> `firefox-updates.json` à la racine, et `src/` pour le code). En local,
> le dossier `Autres/` contient une copie de ces fichiers — tu peux éditer
> l'un ou l'autre, mais quand tu pousses sur Gitea c'est la racine qui doit
> être mise à jour.
---
## Workflow standard d'une demande de modification
Quand Quentin demande une nouvelle fonctionnalité ou un bugfix :
### Phase 1 — Code + build local (toi seul, pas encore de push)
1. **Comprendre la demande**, poser des questions si nécessaire avant d'écrire du code.
2. **Coder les modifications** dans `src/` (jamais directement dans `dist/` ou `Builds/`).
3. **Bumper la version** : incrémenter le 3e chiffre dans `src/manifest.json`
(ex: `2026.5.41``2026.5.42`). Bump majeur (2e chiffre) seulement pour
les refontes ; année (1er chiffre) au passage à 2027.
4. **Mettre à jour le CHANGELOG** (`Autres/CHANGELOG.md` ET la copie racine
`CHANGELOG.md`) en ajoutant une nouvelle entrée en haut.
5. **Mettre à jour le README** (`Autres/README.md` ET racine `README.md`)
si la nouvelle version touche aux fonctionnalités principales.
6. **Builder** : `./Autres/build.sh` — ça produit `dist/chromium/`,
`dist/firefox/`, le `.zip` et le `.xpi` avec la nouvelle version.
7. **Annoncer à Quentin** : "v2026.5.X buildée, recharge l'extension dans
ton navigateur et teste". Décrire brièvement ce qui a changé visuellement.
8. **Attendre son retour**. Tant qu'il n'a pas dit "OK", ne pas pousser sur
Gitea. Si correction demandée, retourner à l'étape 2.
### Phase 2 — Push sur Gitea (uniquement après validation explicite)
Quand Quentin dit "OK push" / "valide" / équivalent :
1. **Préparer un clone Gitea à jour** dans `/tmp/planif-push/` (clone si pas
présent, sinon `git fetch origin && git reset --hard origin/main`).
2. **Synchroniser** :
- `rsync -a --delete /Users/quentin/Documents/Planning/src/ /tmp/planif-push/src/`
- Copier les versions racine de `CHANGELOG.md`, `README.md`, `LICENSE`,
`build.sh` (les versions racine sur Gitea, pas celles de `Autres/`)
3. **Régénérer `firefox-updates.json`** à la racine du repo : ajouter
l'entrée de la nouvelle version en haut de la liste `updates` (les
anciennes entrées restent — Firefox prend la version la plus haute
parmi celles listées). Le `update_link` pointe vers la release Gitea :
`https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/vYYYY.M.PATCH/planification-vYYYY.M.PATCH-firefox.xpi`.
Le `update_hash` est calculé après signature AMO (cf. Phase 3).
Le repo Gitea est **public**, donc l'URL fixe `update_url` =
`https://gitea.netaplaid.ch/FroSteel/Planification/raw/branch/main/firefox-updates.json`
est accessible sans auth → Firefox peut le fetcher directement.
`build.sh` maintient automatiquement ce JSON à chaque build (ajoute /
met à jour l'entrée de la version courante).
4. **Commit + push** :
```bash
cd /tmp/planif-push
git add -A
git commit -m "vYYYY.M.PATCH — <description courte>"
git push origin main
git tag -a vYYYY.M.PATCH -m "vYYYY.M.PATCH"
git push origin vYYYY.M.PATCH
```
5. **Créer la release Gitea** via l'API (POST
`/repos/FroSteel/Planification/releases`) avec :
- `tag_name`: `vYYYY.M.PATCH`
- `name`: `vYYYY.M.PATCH`
- `body`: extrait du CHANGELOG (la section de cette version)
6. **Uploader les binaires** comme assets de la release :
- `dist/planification-vYYYY.M.PATCH-chromium.zip`
- `dist/planification-vYYYY.M.PATCH-firefox.xpi` (NON signé pour le moment)
7. **Mettre à jour le wiki Gitea** :
- Page **Versions** : ajouter une entrée détaillée en haut (dérivée du CHANGELOG)
- Page **Utilisation** : si le changement modifie l'UX (ajout d'un bouton,
d'une section admin, d'un comportement) → documenter
- Page **Architecture** : si nouvelles fonctions clés / nouvelle config
persistée → documenter
### Phase 3 — Signature Firefox (manuel, fait par Quentin)
C'est la seule étape que Claude ne peut pas automatiser :
1. Quentin va sur https://addons.mozilla.org/developers/
2. Submit New Version → uploade le `.xpi` non signé de la release Gitea
3. Choisit **"On your own"** (Unlisted, self-distributed)
4. Mozilla signe → Quentin télécharge le `.xpi` signé
Quentin revient ensuite avec le `.xpi` signé et demande "remplace par le signé".
À ce moment Claude fait :
1. Remplacer l'asset `.xpi` de la release Gitea (delete + upload)
2. Calculer le `sha256` du `.xpi` signé
3. Mettre à jour `firefox-updates.json` : ajouter `"update_hash": "sha256:<hash>"`
4. Commit + push le JSON mis à jour
À partir de ce moment, l'auto-update Firefox fonctionne pour cette version.
---
## Token Gitea
⚠️ **Le token API Gitea ne doit JAMAIS apparaître dans ce fichier ni dans le
repo Gitea**. Il est stocké uniquement dans la mémoire Claude locale
(`~/.claude/projects/-Users-quentin-Documents-Planning/memory/gitea_token.md`,
hors repo). Si Claude perd la mémoire (nouvelle session non héritée),
demander à Quentin de redonner le token.
Header API à utiliser : `Authorization: token <TOKEN>` + `User-Agent: curl/8.4.0`
(le User-Agent évite que Cloudflare bloque les requêtes Python urllib).
---
## Règles importantes
- **Ne jamais hardcoder** dans `src/` : groupe EV, équipe, domaines, absences
récurrentes. Tout passe par `admin_config`. Les seuls hardcodes acceptables
sont les **filets de sécurité** (DEFAULT_GROUP_ID, DEFAULT_EV_ORIGINS pour
le 1er install). Cf. v2026.5.41 pour la migration complète.
- **Ne jamais pousser sur Gitea sans validation explicite** de Quentin.
- **Toujours bumper la version** avant un push qui modifie le code.
- **Toujours mettre à jour le CHANGELOG** avant un push.
- **Tags non touchés** sur Gitea : `v1.0.0`-`v3.3.0`, `v4.1.x`-`v4.3.0`,
`v5.0.0`, `v2026.5.27`-`v2026.5.32` (ceux-là pointent vers du code
reconstitué historique, ne jamais les bouger).
- **Force-push uniquement si Quentin le demande explicitement.**
- **L'email de l'auteur** ne doit apparaître nulle part dans `src/` ni dans
les fichiers Markdown du repo (CLAUDE.md, README.md inclus). Il est stocké
uniquement en mémoire Claude (`user_role.md` / `gitea_token.md`) et exposé
obfusqué (entités HTML) sur la page wiki Contact.
---
## Pages wiki Gitea
| Page | Contenu | Quand mettre à jour |
|---|---|---|
| **Home** | Pitch, contexte, démarrage rapide | Rarement |
| **Utilisation** | Guide complet pour l'utilisateur | À chaque changement UX |
| **Versions** | Historique détaillé des versions | À chaque release |
| **Architecture** | Doc technique (fonctions, config, structure) | À chaque ajout d'helper / changement structurel |
| **Contact** | Email obfusqué + lien Issue Gitea | Rarement |
URL de base wiki : `https://gitea.netaplaid.ch/FroSteel/Planification/wiki/<NOM>`
Endpoint API : `/api/v1/repos/FroSteel/Planification/wiki/page/<NOM>` (PATCH avec
`content_base64`).
---
## Pour résumer ton rôle, Claude
Quentin demande une modif → tu codes → tu builds → il teste → il valide →
tu push tout (Gitea + wiki + firefox-updates.json). Plus tard il revient avec
le `.xpi` signé d'AMO → tu mets à jour la release et le `update_hash` du JSON.
Si tu hésites sur quoi faire à un moment, **demande**. Ne suppose pas.
+4 -9
View File
@@ -7,8 +7,8 @@ Extension Chrome / Firefox pour visualiser de manière claire et rapide le plann
- **Auteur** : Quentin Rouiller (QRO)
- **Cible** : techniciens DGNSI (Canton de Vaud), EasyVista (`itsma.etat-de-vaud.ch` / `itsma.vd.ch`)
- **Démarrage projet** : jeudi 16 avril 2026
- **Version actuelle** : `v2026.5.42`
- **Contact** : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact)
- **Version actuelle** : `v2026.5.41`
- **Contact** : `quentin.rouiller@vd.ch`
- **Manifest** : V3 (Chrome/Edge/Firefox)
- **Format** : `.zip` (Chromium) + `.xpi` signé (Firefox)
@@ -87,12 +87,7 @@ Le numéro de **majeure** n'est **pas** un mois et **pas** un chiffre lié au ca
## Versions notables
### `v2026.5.42` (latest, 27 avril 2026) — Nettoyage de commentaires + exemples génériques
- Uniformisation des exemples utilisés dans les commentaires de `viewer.js`
(parsing contacts/lieux/références/codes-barres) en placeholders abstraits.
Comportement runtime strictement inchangé.
### `v2026.5.41` — Suppression des hardcodes + UX admin + thème unifié
### `v2026.5.41` (latest, 27 avril 2026) — Suppression des hardcodes + UX admin + thème unifié
- **Plus aucun hardcode runtime** pour le groupe EV, les domaines, la liste de techniciens ou les absences récurrentes. Tout est piloté par `admin_config` (chrome.storage.local), persisté entre les mises à jour.
- **Au 1er install** : aucun tech sélectionné, aucune absence récurrente. Le viewer affiche un message *"Aucun technicien sélectionné"* tant que l'utilisateur n'a rien configuré dans Paramètres → Équipe.
- **Édition des domaines** : `chrome.permissions.request()` au save quand l'utilisateur saisit un domaine custom (au-delà des 2 défauts). Manifest `optional_host_permissions: ["https://*/*"]` pour accepter n'importe quel domaine HTTPS après accord du navigateur.
@@ -259,4 +254,4 @@ git push --tags
**Quentin Rouiller** (QRO)
Technicien DGNSI — Canton de Vaud
Contact : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact)
Contact : `quentin.rouiller@vd.ch`
-49
View File
@@ -6,8 +6,6 @@
# Usage : ./build.sh
###############################################################################
set -e
# Le script est dans Autres/ — on remonte d'un cran pour se placer à la
# racine du projet, où se trouvent src/ et dist/.
cd "$(dirname "$0")"
VERSION=$(python3 -c "import json; print(json.load(open('src/manifest.json'))['version'])")
@@ -33,15 +31,6 @@ m['browser_specific_settings'] = {
'data_collection_permissions': {'required': ['none']}
}
}
# Firefox MV3 ne supporte pas (encore) 'service_worker' → AMO rejette.
# On ajoute 'scripts' (event page) comme fallback compatible Firefox.
# Chrome ignore 'scripts' quand 'service_worker' est présent ; Firefox
# ignore 'service_worker' et utilise 'scripts'. Les deux navigateurs
# chargent ainsi le même background.js.
bg = m.get('background', {})
if 'scripts' not in bg:
bg['scripts'] = ['background.js']
m['background'] = bg
with open('dist/firefox/manifest.json', 'w') as f:
json.dump(m, f, indent=2, ensure_ascii=False)
f.write('\n')
@@ -55,44 +44,6 @@ cd dist/firefox && zip -rq "../planification-v${VERSION}-firefox.xpi" . && cd .
echo ""
echo "==> Builds prêts dans dist/"
ls -la dist/*.zip dist/*.xpi 2>/dev/null
# ---- firefox-updates.json : ajout/mise à jour de l'entrée pour cette version
# (sha256 du .xpi NON SIGNÉ — sera remplacé par celui du .xpi signé après AMO).
python3 - <<EOF
import json, hashlib, os
xpi = f"dist/planification-v${VERSION}-firefox.xpi"
with open(xpi, 'rb') as f: sha = hashlib.sha256(f.read()).hexdigest()
JSON_PATH = "firefox-updates.json"
ADDON_ID = "planification@netaplaid.ch"
update_link = f"https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v${VERSION}/planification-v${VERSION}-firefox.xpi"
if os.path.exists(JSON_PATH):
with open(JSON_PATH) as f: data = json.load(f)
else:
data = {"addons": {ADDON_ID: {"updates": []}}}
addon = data.setdefault("addons", {}).setdefault(ADDON_ID, {"updates": []})
updates = addon.setdefault("updates", [])
# Retirer toute entrée existante pour cette version (idempotent)
updates = [u for u in updates if u.get("version") != "${VERSION}"]
# Ajouter en tête
updates.insert(0, {
"version": "${VERSION}",
"update_link": update_link,
"update_hash": "sha256:" + sha,
})
addon["updates"] = updates
with open(JSON_PATH, 'w') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.write('\n')
print(f" ✓ firefox-updates.json mis à jour (sha256 NON SIGNÉ : {sha[:16]}…)")
EOF
echo ""
echo "Pour Chrome : charger dist/chromium/ en mode développeur"
echo "Pour Firefox : signer dist/planification-v${VERSION}-firefox.xpi sur AMO"
echo " Après signature, remplacer le sha256 dans firefox-updates.json par celui du .xpi signé."
-23
View File
@@ -1,23 +0,0 @@
{
"addons": {
"planification@netaplaid.ch": {
"updates": [
{
"version": "2026.5.42",
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.42/planification-v2026.5.42-firefox.xpi",
"update_hash": "sha256:3d8cf762bf0921f9da473a9a5e31368fee21f0b9fd71f9f9432d256127de8674"
},
{
"version": "2026.5.41",
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.41/planification-v2026.5.41-firefox.xpi",
"update_hash": "sha256:fbf7d8a57ad060306cb43f3db3a5d5b599bb07c75c4ef0dbd0346406bdb6c65b"
},
{
"version": "2026.5.40",
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.40/planification-v2026.5.40-firefox.xpi",
"update_hash": "sha256:2ba0758960b931f4211c613c75bbf21b3a250572dddc70d854ff1ecca3220421"
}
]
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Planification",
"version": "2026.5.42",
"version": "2026.5.41",
"description": "Vue claire et rapide du planning des techniciens EasyVista. Développé par Quentin Rouiller — DGNSI, Canton de Vaud.",
"permissions": [
"activeTab",
+13 -20
View File
@@ -6406,17 +6406,10 @@ function _buildMiniCardsRow(realInterventions, cardEl) {
openInterventionFromTimeline(card, { background: true });
return;
}
// v2026.5.41 : en vue horizontale, le clic simple ouvre la fiche EV
// dans un nouvel onglet (au lieu d'épingler la popup, comportement
// peu utile en horizontal car la popup est déjà visible au survol).
const isHorizontal = document.documentElement.classList.contains("view-horizontal");
if (isHorizontal) {
e.preventDefault();
e.stopPropagation();
openInterventionFromTimeline(card, { background: false });
return;
}
// Vue classique : clic = épingle la grande popup ancrée.
// v2026.5.40 r10 : forcer l'affichage IMMÉDIAT de la grande popup
// ancrée AVANT pinTooltip(). Sans ça, si le délai showTooltip 500ms
// n'est pas encore écoulé OU si la petite popup timeline était
// affichée juste avant, c'est elle qui se faisait épingler.
e.stopPropagation();
_cancelPendingShowTooltip();
_cancelPendingTimelinePopover();
@@ -7448,32 +7441,32 @@ function splitOneContact(raw) {
// 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 (1-2 lettres + 5+ chiffres).
// 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 (1-2 lettres
// majuscules suivies de 5+ chiffres collés). On garde assez permissif
// pour matcher les variantes sans enlever des vrais mots.
// 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: "0XXXXXXXXTextecoller")
// - pas d'espace après le numéro (ex: "021555555Textecoller")
// - pas d'espace/parenthèse avant un court numéro
// LONG : +41 / +33 / 0X suivis de chiffres/espaces/points/tirets
// On ne limite plus par séparateur après — on laisse le moteur
// consommer le numéro le plus long possible (greedy) puis on
// s'arrête dès qu'on tombe sur un caractère non numérique.
// v4.2 : on accepte aussi le format "41XXXXXXXXX" sans + devant (fréquent
// quand EasyVista concatène "prefixe+tel" sans espace : "Nom,
// Prénom 41XXXXXXXXX" → extraire 41XXXXXXXXX puis reformater en
// quand EasyVista concatène "prefixe+tel" sans espace : Nom,
// Prénom 41XXXXXXXXX → extraire 41XXXXXXXXX puis reformater en
// +41 XX XXX XX XX). On exige exactement 11 chiffres collés pour
// éviter de matcher des codes postaux ou autres nombres.
// v2026.5.16 : ne PAS matcher si le numéro est précédé d'une lettre ou
// d'un underscore (identifiants style XXXX_NNNNNNNN, XXX0123456,
// d'un underscore (identifiants style XXXX_NNNNNNNN, ABC123456,
// SERIAL_0123456789). On ajoute un lookbehind négatif (?<![A-Za-z_]).
const rxLong = /(?<![A-Za-z_])(\+41\s?\d(?:[\d\s.\-]*\d)?|\+33\s?\d(?:[\d\s.\-]*\d)?|0\d(?:[\d\s.\-]*\d)?|(?<!\d)41\d{9}(?!\d)|(?<!\d)33\d{9}(?!\d))/g;
// SHORT : numéro interne court (5 chiffres).
@@ -7535,7 +7528,7 @@ function splitOneContact(raw) {
// Critères d'un vrai nom : contient au moins un mot qui commence par une
// majuscule ET n'est pas juste un identifiant technique.
if (name) {
const looksLikeIdentifier = /^[A-Z]{2,}[_\-]\d+$/.test(name); // ex: XXXX_NNNNNNNN
const looksLikeIdentifier = /^[A-Z]{2,}[_\-]\d+$/.test(name); // XXXX_NNNNNNNN
const startsWithQuantity = /^\d+x(\s|$)/i.test(name); // "1x" ou "1x pc"
const noCapitalWord = !/\b[A-ZÉÈÀÂÎÔÛÇ][a-zéèàâîôûç]+/.test(name); // aucun mot "Xxxxx"
const hasOnlyTechTokens = /^(\d+x|pc|mac|t[ée]l[ée]phone|ecran|docking|rollout)(\s+(\d+x|pc|mac|t[ée]l[ée]phone|ecran|docking|rollout|[A-Z]\d+))*\s*$/i.test(name);