Files
gestion_projets/README.md
Bertrand Benjamin a0b8ef9dcf Rendre les notes d'une phase consultables depuis la vue par mois
Elles n'étaient accessibles que dans l'infobulle de la ligne, où l'on ne pense
pas à aller les chercher et d'où l'on ne peut rien copier. Or une note est
précisément ce qu'on va lire quand on fait le point — « pourquoi cette phase
est-elle bloquée ».

Un triangle en tête de ligne les déplie en dessous, sur les phases qui en
portent. C'est celui qui plie un projet sur la frise, au même endroit et avec
la même rotation : ce qui s'apprend d'un côté vaut de l'autre. La gouttière se
lit ainsi d'un coup d'œil comme la liste de ce qui a quelque chose à dire.

Toute la ligne déplie, pas seulement le triangle. Une première version faisait
de l'intitulé lui-même le bouton, avec un chevron à sa suite : la cible était
trop petite pour un geste aussi banal, et rien ne justifiait cette parcimonie
dans une vue où le clic n'a aucun autre effet. Le triangle reste comme
indicateur plutôt que comme cible, le curseur et le fond au survol disent que
la ligne réagit. Les lignes sans notes, elles, ne réagissent pas : promettre un
geste qui ne fait rien est pire que ne rien promettre.

Il n'en reste pas moins un vrai button : la ligne n'est pas focusable, c'est
donc le seul chemin clavier vers les notes.

Les notes restent repliées par défaut pour deux raisons. La densité d'abord :
trois lignes sous chaque intitulé étalent un mois sur deux écrans et ruinent la
lecture en diagonale. Et parce qu'une phase sur trois seulement en porte, les
afficher d'office donnerait des lignes de hauteurs inégales, ce qui casse
l'alignement des colonnes sur lequel repose tout le balayage du regard.

L'état déplié vit dans le DOM et nulle part ailleurs : il ne survit pas à un
changement de filtre, qui reconstruit la liste. Le remonter dans l'état de
l'application achèterait cher une continuité que personne ne réclame — quand la
liste change, on la relit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 05:40:08 +02:00

264 lines
13 KiB
Markdown

# Frise multi-projets
Un outil de **planification macro** : voir sur une seule frise chronologique où en est chaque
projet, quelles sont ses grandes phases, et comment les projets se situent les uns par rapport aux
autres.
Ce n'est **pas** un outil de suivi quotidien : pas de sous-tâches, pas de tickets, pas de
checklists. Une phase représente plusieurs semaines ou plusieurs mois de travail.
## Démarrer
Récupérer le binaire de sa plateforme, le poser dans un dossier avec son planning, et le
lancer — par un double-clic ou en ligne de commande :
```sh
./frise
```
Le navigateur s'ouvre tout seul. **Rien à installer** : l'interface est embarquée dans
l'exécutable, qui ne dépend d'aucune bibliothèque système — pas même de la glibc. Il tourne tel
quel sur n'importe quelle distribution, et sur une machine sans Python ni Node.
Par défaut, le planning est le fichier `projets.json` **posé à côté de l'exécutable**. C'est ce
qui permet de déposer les deux dans un dossier synchronisé (Dropbox, Nextcloud, Syncthing) et de
retrouver son outil et ses données sur n'importe quelle machine.
```
📁 Mon dossier synchronisé
├── frise ← l'exécutable
└── projets.json ← le planning
```
Options :
```sh
./frise --port 9000 # imposer un port (sinon : le premier libre à partir de 8000)
./frise --data ~/plannings/2027.json # ouvrir un autre fichier
./frise --no-browser # ne pas ouvrir le navigateur
```
Au premier lancement, le planning est vide et aucun fichier n'est créé tant qu'on n'a rien
saisi. Pour découvrir l'outil sur des données réalistes, copier `data/exemple.json` en
`projets.json` à côté de l'exécutable.
### Compiler soi-même
```sh
./build.sh # les cinq plateformes, dans dist/
./build.sh local # seulement la machine courante
```
Il faut [Go](https://go.dev) 1.22 ou plus récent, et rien d'autre : aucune dépendance externe,
aucun `vendor/`. Les binaires des trois systèmes se produisent depuis une seule machine, sans
intégration continue ni SDK tiers.
### Développer
```sh
go run . --dev
```
Le mode `--dev` sert `index.html`, `css/` et `js/` **depuis le disque** plutôt que depuis le
binaire : un simple rafraîchissement suffit à voir ses modifications, sans recompiler. Il lit
alors `data/projets.json`, comme avant.
```sh
go test ./... # le serveur
node --test tests/ # le modèle métier
```
## Prise en main
**Nouveau projet** crée le projet aussitôt, sous un nom provisoire, et ouvre ses paramètres avec
ce nom présélectionné : on tape simplement par-dessus. Il n'y a aucun formulaire à valider — ni
ici, ni pour une phase. Tout s'applique au fil de la saisie, et un projet créé par erreur se
supprime depuis son propre panneau.
Dans la colonne de gauche, le **triangle** plie et déplie le couloir d'un projet, et un clic sur
son **nom** ouvre ses paramètres : nom, couleur, tags. Deux commandes apparaissent au survol de
la ligne, sur son bord droit :
- **`+`** ajoute une phase à la suite de la dernière, et ouvre son panneau de détail ;
- **`⋯`** ouvre le menu du projet : ajouter une phase, plier, masquer de la frise, renommer,
figer la référence, supprimer. Un clic droit sur le libellé ouvre le même menu.
Il n'y a pas de commande séparée pour créer un **jalon** : c'est une phase dont on coche la case
« Jalon » dans le panneau, ce qui ramène sa fin sur son début. La bascule joue dans les deux sens
à tout moment.
La colonne se **redimensionne** en glissant la ligne qui la sépare de la frise, en haut à
gauche : un double-clic revient à la largeur d'origine. Cette largeur est propre au navigateur,
elle n'est pas enregistrée dans le planning.
Une fois un projet en place :
| Geste | Effet |
|---|---|
| Glisser le corps d'une barre | Décale la phase, durée conservée |
| Glisser le bord d'une barre | Change la date de début ou de fin |
| Clic sur une barre ou son nom | Ouvre le panneau de détail |
| Clic sur une autre barre, panneau ouvert | Bascule le panneau sur cette phase |
| Clic sur un nom de phase hors écran | Ramène la frise sur elle |
| `←` `→` | Décale d'un jour la phase sélectionnée |
| `Maj` + `←` `→` | Allonge ou raccourcit d'un jour |
| `Échap` | Annule le glisser en cours, ou ferme le panneau |
| Clic dans une zone vide | Ferme le panneau ouvert |
| `Maj` + molette | Fait défiler la frise horizontalement |
| `↑` `↓` dans un menu | Parcourt les entrées |
Faire défiler jusqu'au bord droit élargit la frise vers le futur, et jusqu'au bord gauche
vers le passé. Le bouton **Aujourd'hui** ramène la vue sur la date du jour.
### La vue par mois
Les deux boutons **Frise** et **Par mois**, en haut à gauche, changent de regard sur les mêmes
données. La vue par mois déroule toutes les tâches et tous les jalons du planning en une seule
liste, projets mêlés, groupés par mois — celui où chaque phase **commence**. C'est la réponse à
« qu'est-ce qui arrive le mois prochain », que la frise donne mal parce qu'elle sépare les projets
en couloirs.
Chaque ligne se lit dans cet ordre : quand — la période, `31 → 13 nov.` quand la phase déborde du
mois —, chez qui — le projet et ses tags —, puis quoi — l'intitulé de la tâche ou du jalon — et
enfin son statut. Une phase longue n'est **pas** répétée dans les mois qu'elle traverse : elle
n'apparaît qu'à son mois de départ, avec sa date de fin écrite en clair.
Les phases qui portent des **notes** le signalent par un triangle en tête de ligne, le même que
celui qui plie un projet sur la frise. Un clic **n'importe où sur la ligne** déplie la note en
dessous, un second la referme. Rien n'est affiché d'office — trois lignes de notes sous chaque
intitulé étaleraient un mois sur deux écrans.
Cette liste se **lit** — elle ne s'édite pas. Déplacer une phase demande de voir ce qu'elle
chevauche, donc la frise ; c'est là que les dates et les notes se modifient. Seuls les tags et les
notes y sont cliquables, et ni filtrer ni déplier ne touche aux données. Le bouton **Aujourd'hui**
ramène ici sur le mois en cours, et la vue affichée n'est pas mémorisée : l'outil s'ouvre toujours
sur la frise.
### Filtrer par tag
Un projet peut porter des **tags**`client`, `interne`, `R&D` —, saisis séparés par des virgules
dans le dialogue « Renommer, changer la couleur… ». Il n'y a pas de liste à tenir : les tags
existants sont proposés en un clic, et un tag disparaît dès que plus aucun projet ne le porte.
Ils s'affichent en petites pastilles colorées dans la colonne de gauche, et une barre de filtres
apparaît sous la barre d'outils dès qu'un tag existe. Cliquer un tag — dans la barre de filtres ou
directement sur la pastille d'un projet — restreint la frise à ceux qui le portent ; recliquer le
retire. Le filtre vaut pour les **deux vues** : il ne change pas quand on passe de la frise à la
liste par mois.
Cocher plusieurs tags **cumule les conditions** : `client` + `urgent` ne montre que les projets
portant les deux. Le filtre n'est pas enregistré, il ne vaut que pour la session en cours. Le bouton
**Tout afficher** le vide d'un coup.
Les barres s'accrochent au lundi pendant le glisser. Pour une date au jour près, passer
par les champs du panneau de détail.
## Ce que fait l'outil
- **Plusieurs projets**, chacun décomposé en **phases** ayant un nom, des dates de début et de fin,
un statut, des notes libres. Une phase peut être un **jalon** (une date unique, rendue en losange).
- **Une frise commune**, les projets empilés en couloirs, pour les comparer d'un coup d'œil.
- **Une barre cumulative par projet**, segmentée en teintes selon le statut de chaque phase : la
forme d'ensemble, toujours visible sur la ligne du projet et toujours rendue pareil. Elle ne se
glisse pas.
- **Couloirs pliables** : déplié, chaque phase occupe sa ligne sous la barre cumulative ; plié, il
ne reste que celle-ci.
- **Manipulation directe** : glisser une barre pour la décaler, tirer ses bords pour changer sa
durée. Les barres s'accrochent au lundi ; pour une date exacte, on passe par le panneau de détail.
- **Planning de référence** : une fois le planning validé, on le fige. La frise affiche alors une
barre fantôme sous la barre réelle et la dérive en semaines — de quoi répondre à « ce projet a-t-il
glissé, et de combien ? ».
- **Un en-tête à trois niveaux** — année, mois, numéro de semaine ISO — qui donne le
repère temporel sans jamais descendre au jour : à cette échelle, la semaine suffit.
Il n'y a qu'une seule échelle, donc aucun réglage de zoom à comprendre.
- **Une frise sans fin** : elle s'élargit d'elle-même quand le défilement approche d'un
bord, ce qui permet d'aller planifier dans un futur qu'aucune phase n'occupe encore.
- **Une vue par mois** : toutes les tâches et tous les jalons en une liste, projets mêlés, groupés
par mois de début. Elle se lit, elle ne s'édite pas.
- **Des tags sur les projets**, et un filtre qui restreint les deux vues à ceux qui les portent
tous.
- **Repère « aujourd'hui »** et masquage d'un projet.
## Ce que l'outil ne fait pas
Volontairement : pas de dépendances entre phases, pas de sous-tâches, pas de collaboration temps
réel, pas d'authentification, pas de gestion de charge par personne, pas de notifications.
Les durées se comptent en **jours calendaires** : ni week-ends ni jours fériés ne sont gérés. À
l'échelle de phases de plusieurs semaines, la distinction n'apporte rien.
Les raisons derrière ces choix sont détaillées dans [docs/decisions.md](docs/decisions.md).
## Où vivent les données
Tout tient dans un fichier JSON, `data/projets.json`, décrit dans
[docs/modele-donnees.md](docs/modele-donnees.md). Il est indenté et utilise des identifiants
parlants (`site-web`, `cadrage`) plutôt que des UUID : il reste éditable à la main et lisible dans un
diff git.
**À l'ouverture**, le serveur copie le planning dans un dossier `backups/` voisin, horodaté : c'est
l'état d'avant la séance, celui qu'on veut retrouver si elle tourne mal. Si le serveur reste allumé
plusieurs jours, une copie est reprise au plus une fois par jour. Rouvrir l'outil sans avoir rien
modifié ne laisse pas de trace.
Cinquante sauvegardes sont conservées, les plus anciennes étant supprimées au fil de l'eau. Comme
elles valent une par séance et non une par modification, cela représente des mois d'historique
plutôt que la dernière demi-heure.
Pour restaurer, **arrêter `frise` et fermer l'onglet d'abord** : l'application garde son planning
en mémoire et sa prochaine sauvegarde écraserait ce qu'on vient de remettre en place.
```sh
ls -lt backups/ # repérer la version voulue
cp backups/projets-20260801-170352.json projets.json
```
Le planning n'est **pas versionné** : `.gitignore` exclut `projets.json`, ce dépôt ne contenant
que le code. Seul `data/exemple.json` y figure, comme jeu de démonstration.
**Pour synchroniser plusieurs machines**, déposer l'exécutable et son `projets.json` dans un
espace synchronisé. L'option `--data` accepte n'importe quel chemin, le planning peut donc aussi
vivre ailleurs que le binaire.
Deux frictions propres à ce mode de distribution, qui touchent tout exécutable et pas seulement
celui-ci : le bit d'exécution n'est pas toujours préservé par la synchronisation (`chmod +x frise`
une fois par machine), et un binaire non signé déclenche un avertissement au premier lancement —
SmartScreen sous Windows, Gatekeeper sous macOS.
L'outil ne gère pas les conflits — si deux machines modifient le planning en même temps, la dernière
écriture l'emporte. Les sauvegardes horodatées de `data/backups/` sont le filet de sécurité.
## Tests
```sh
node --test # la logique métier
go test ./... # le serveur
```
Côté navigateur : validation des dates, bornes d'un projet, calcul de dérive, génération des
identifiants, normalisation et filtrage des tags, regroupement des phases par mois. Côté serveur : lecture et écriture du planning,
refus des corps invalides, rotation des sauvegardes, écriture atomique, résolution du chemin de
données. Aucune dépendance de part ni d'autre, les lanceurs intégrés à Node et à Go suffisent.
## Organisation du code
| Fichier | Rôle |
|---|---|
| `main.go` | Sert l'interface embarquée, expose `GET`/`PUT` sur `/api/data`, écrit les sauvegardes |
| `build.sh` | Compile les binaires des cinq plateformes dans `dist/` |
| `js/model.js` | Données et règles métier : CRUD, validation, bornes, référence, dérive, tags, regroupement par mois |
| `js/storage.js` | Dialogue avec le serveur, sauvegarde debouncée, indicateur d'état |
| `js/timeline.js` | Rendu de la frise : échelle, couloirs, barres, jalons, pli/dépli |
| `js/mois.js` | Rendu de la vue par mois : groupes, lignes, ancrage sur le mois courant |
| `js/tags.js` | Pastilles de tags, partagées par les deux vues |
| `js/drag.js` | Glisser et redimensionner les barres |
| `js/detail.js` | Panneau de détail d'une phase |
| `js/projet.js` | Panneau des paramètres d'un projet : nom, couleur, tags |
| `js/menu.js` | Menu contextuel et dialogue de confirmation |
| `js/app.js` | Amorçage, état en mémoire, câblage des événements |
Le rendu utilise des éléments DOM positionnés en absolu, pas de SVG ni de bibliothèque Gantt.
Les identifiants de code et les clés JSON sont en anglais ; l'interface, les commentaires et la
documentation sont en français.