diff --git a/README.md b/README.md index 9ed8368..ba360e2 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ Chaque projet porte trois commandes dans la colonne de gauche : - **le triangle** plie et déplie son couloir ; - **l'œil** le masque de la frise sans le supprimer ; -- **`⋯`** ouvre son menu : ajouter une phase ou un jalon, renommer, changer la couleur, - figer la référence, supprimer le projet. Un clic droit sur le libellé ouvre le même menu. +- **`⋯`** ouvre son menu : ajouter une phase ou un jalon, renommer, changer la couleur et les + tags, figer la référence, supprimer le projet. Un clic droit sur le libellé ouvre le même menu. Une fois un projet en place : @@ -56,6 +56,21 @@ Une fois un projet en place : 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. +### 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. + +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. @@ -76,6 +91,7 @@ par les champs du panneau de détail. 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. +- **Des tags sur les projets**, et un filtre qui restreint la frise à ceux qui les portent tous. - **Repère « aujourd'hui »** et masquage d'un projet. ## Ce que l'outil ne fait pas @@ -114,14 +130,15 @@ node --test ``` Couvre la logique métier : validation des dates, bornes d'un projet, calcul de dérive, génération des -identifiants. Aucune dépendance, le lanceur de tests intégré à Node suffit. +identifiants, normalisation et filtrage des tags. Aucune dépendance, le lanceur de tests intégré à +Node suffit. ## Organisation du code | Fichier | Rôle | |---|---| | `serve.py` | Sert les fichiers statiques, expose `GET`/`PUT` sur `/api/data`, écrit les sauvegardes | -| `js/model.js` | Données et règles métier : CRUD, validation, bornes, référence, dérive | +| `js/model.js` | Données et règles métier : CRUD, validation, bornes, référence, dérive, tags | | `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/drag.js` | Glisser et redimensionner les barres | diff --git a/docs/decisions.md b/docs/decisions.md index 309e4bb..3434c57 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -218,3 +218,46 @@ Attention en modifiant `centrerSur` : `scrollLeft` porte sur la grille entière, première colonne est celle des libellés. Comme elle est `sticky`, elle masque en permanence les premiers pixels de la vue, et le centre utile des couloirs est décalé d'une demi-largeur de colonne par rapport au centre de la frise. + +## 16. Tags libres et filtre conjonctif + +Passé une dizaine de projets, la frise devient un mur : on veut n'y voir que les projets +clients, ou que ceux d'un pôle. D'où des **tags** sur le projet, et un filtre. + +**Étiquettes libres, pas de référentiel.** Aucune liste de tags autorisés n'est tenue +quelque part : les tags disponibles sont ceux que portent les projets, et un tag disparaît +de la barre de filtres dès qu'il n'orne plus rien. Un référentiel séparé aurait exigé une +interface pour le gérer, et se serait désynchronisé. La contrepartie — la dérive +orthographique, « client », « Clients », « client final » — est traitée par deux gardes : +la comparaison ignore la casse, et le dialogue d'un projet propose en un clic les tags déjà +employés ailleurs. + +**Le filtre exige *tous* les tags cochés**, pas au moins un. Cocher un tag de plus resserre +donc toujours la sélection : on part du tout et on élague, sans jamais voir la frise se +repeupler en cochant. La disjonction se comporte à l'inverse, et il faut alors relire la +liste des tags actifs pour savoir dans quel sens la prochaine case va jouer. + +**Le filtre n'est pas enregistré.** C'est un état de vue, comme la fenêtre temporelle +(décision 15) : un filtre qui survivrait au rechargement rouvrirait le planning amputé sans +qu'on se souvienne pourquoi. Le masquage par l'œil (`hidden`), lui, est bien enregistré — +et les deux ne font pas la même chose : + +| | Filtre par tags | Œil (`hidden`) | +|---|---|---| +| Portée | La vue entière, libellé compris | Les barres seules, le libellé reste grisé | +| Durée de vie | La session | Enregistré dans le fichier | +| Question posée | « Je ne regarde que les projets clients » | « Ce projet existe, mais il encombre ma frise » | + +**La couleur d'un tag est calculée, pas choisie** : une teinte HSL dérivée du nom par +hachage. Rien à stocker, rien à saisir, et le même tag garde sa couleur partout. Des +collisions de teinte sont possibles et sans gravité — le nom est toujours écrit à côté. + +**Un tag trop long est tronqué, pas refusé.** Contrairement à une date invalide, qui bloque +le chargement en nommant le fautif, la longueur d'un tag est cosmétique : refuser d'ouvrir +un planning entier pour un libellé bavard serait disproportionné. La limite est donc +appliquée en normalisant, ce qui la rend vraie par construction — aussi bien pour un fichier +édité à la main que pour une saisie dans le dialogue. + +Créer un projet alors qu'un filtre est actif **préremplit ses tags** avec ceux du filtre : +sans cela, le nouveau venu naîtrait invisible, hors de la vue depuis laquelle on vient de le +demander.