Files
gestion_projets/docs/modele-donnees.md
Bertrand Benjamin 3be367488c Tags sur les projets, en version 3 du format
Un projet porte désormais une liste d'étiquettes libres, destinées à filtrer la
frise. Il n'y a pas de référentiel à tenir : les tags disponibles sont ceux que
portent les projets.

normaliserTags() nettoie la liste à l'enregistrement — espaces réduits, vides
écartés, doublons fusionnés à la casse près, tri alphabétique pour qu'une
ressaisie dans un autre ordre ne produise aucun diff. Les accents, eux, comptent :
« éditeur » et « editeur » restent deux tags distincts, contrairement aux
identifiants qui doivent tenir dans une URL.

Un tag trop long est tronqué plutôt que refusé : sa longueur est cosmétique, et
bloquer le chargement d'un planning entier pour un libellé bavard serait
disproportionné. La validation ne vérifie donc que le type — un tag mal typé
serait un tag qu'on croit poser et qui ne filtre rien.

teinteTag() dérive une teinte HSL du nom par hachage : la couleur d'un tag n'est
ni stockée ni saisie, et reste la même partout où il apparaît.

Le format passe en version 3. Un fichier en version 2 se charge sans rien
demander, ses projets recevant une liste vide, et il est réécrit en version 3 à
la première sauvegarde. Le bump empêche une version antérieure de l'outil
d'effacer silencieusement les tags en réécrivant le fichier.

Dix tests couvrent la normalisation, la comparaison, la teinte et le filtre.

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

7.3 KiB

Modèle de données

Tout le planning tient dans un seul fichier JSON, par défaut data/projets.json.

Le fichier est écrit indenté sur 2 espaces, avec les clés dans un ordre stable, pour rester lisible dans un diff git et éditable à la main.

Structure

{
  "version": 3,
  "projects": [
    {
      "id": "site-web",
      "name": "Refonte du site web",
      "color": "#3b82f6",
      "tags": ["client", "web"],
      "collapsed": false,
      "hidden": false,
      "baselineDate": "2026-06-12",
      "phases": [
        {
          "id": "cadrage",
          "name": "Cadrage",
          "start": "2026-08-01",
          "end": "2026-08-20",
          "status": "done",
          "milestone": false,
          "notes": "Ateliers avec les trois pôles.",
          "baseline": { "start": "2026-07-25", "end": "2026-08-10" }
        },
        {
          "id": "livraison",
          "name": "Mise en ligne",
          "start": "2026-11-02",
          "end": "2026-11-02",
          "status": "todo",
          "milestone": true,
          "notes": ""
        }
      ]
    }
  ]
}

Racine

Champ Type Description
version entier Version du format. Vaut 3. Sert à détecter un fichier trop ancien au chargement.
projects tableau Les projets, dans l'ordre d'affichage des couloirs.

Un fichier en version 2 — celle d'avant les tags — se charge sans rien demander : ses projets reçoivent une liste de tags vide, et il est réécrit en version 3 à la première sauvegarde.

Projet

Champ Type Description
id chaîne Identifiant parlant, dérivé du nom (Refonte du site website-web). Unique dans le fichier.
name chaîne Nom affiché. Non vide.
color chaîne Couleur du couloir, en hexadécimal #rrggbb.
tags tableau Étiquettes libres servant à filtrer la frise. Trié, sans doublon, éventuellement vide.
collapsed booléen true si le couloir est plié.
hidden booléen true si le projet est masqué de la frise. Ses données restent intactes.
baselineDate chaîne ou absent Date à laquelle la référence a été figée. Absent si elle ne l'a jamais été.
phases tableau Les phases, triées par date de début croissante.

Phase

Champ Type Description
id chaîne Identifiant parlant, unique au sein de son projet.
name chaîne Nom affiché. Non vide.
start chaîne Date de début, AAAA-MM-JJ.
end chaîne Date de fin incluse, AAAA-MM-JJ.
status chaîne todo, doing, done ou blocked.
milestone booléen true pour un jalon.
notes chaîne Texte libre, éventuellement vide.
baseline objet ou absent Dates de référence : { "start": …, "end": … }. Absent tant que la référence n'a pas été figée.

Règles de validation

Appliquées par js/model.js et couvertes par tests/model.test.js.

  • end est postérieure ou égale à start.
  • Un jalon (milestone: true) a nécessairement start === end. Cocher « jalon » sur une phase de plusieurs jours ramène end sur start.
  • Les dates suivent strictement AAAA-MM-JJ et doivent exister réellement dans le calendrier (2026-02-30 est rejetée).
  • id de projet unique dans le fichier ; id de phase unique dans son projet.
  • Un id est engendré à partir du nom : minuscules, accents retirés, tout ce qui n'est ni lettre ni chiffre remplacé par un tiret. En cas de collision, un suffixe numérique est ajouté (cadrage-2). Un id ne change jamais si le nom est modifié ensuite — il identifie, il ne décrit pas.
  • status fait partie des quatre valeurs autorisées ; toute autre valeur est ramenée à todo.
  • color est un hexadécimal #rrggbb valide.
  • tags est un tableau de chaînes. Le tableau lui-même et le type de ses éléments sont vérifiés — un tag mal typé serait un tag qu'on croit poser et qui ne filtre rien. Leur contenu, en revanche, est normalisé sans erreur (voir ci-dessous).

Tags

Une étiquette libre, posée sur un projet, sans liste fermée à tenir à jour : les tags disponibles sont simplement ceux que portent les projets.

À l'enregistrement, normaliserTags() nettoie la liste :

  • espaces de bord retirés, espaces internes réduits à un seul ;
  • tags vides écartés ;
  • doublons fusionnés à la casse prèsClient et client sont le même tag, et c'est la première graphie rencontrée qui est conservée ;
  • tag tronqué au-delà de 24 caractères (MAX_LONGUEUR_TAG) ;
  • tri alphabétique, pour que ressaisir les mêmes tags dans un autre ordre ne produise aucun diff git.

Les accents comptent : éditeur et editeur restent deux tags distincts. C'est la différence avec fabriquerId(), qui les retire parce qu'un identifiant doit tenir dans une URL — un tag, lui, n'est jamais qu'affiché.

La couleur d'un tag n'est pas stockée : elle se déduit du nom par un hachage (teinteTag() → une teinte HSL entre 0 et 359). Elle est donc stable d'une session à l'autre et identique partout où le tag apparaît, sans rien avoir à gérer. Deux tags peuvent tomber sur des teintes voisines : la couleur aide à repérer, elle ne porte pas d'information à elle seule — le nom est toujours écrit à côté.

Le filtre, lui, n'est pas dans le fichier : c'est un état de vue, au même titre que la fenêtre temporelle. Voir decisions.md, section 16.

Un fichier invalide n'est jamais réparé en silence : le chargement échoue avec un message qui pointe le projet et la phase fautifs, pour que le fichier puisse être corrigé à la main.

Dates : conventions

Les dates sont manipulées comme des chaînes AAAA-MM-JJ, pas comme des objets Date. Cela évite tous les pièges de fuseau horaire : new Date("2026-08-01") est interprétée en UTC alors que new Date(2026, 7, 1) l'est en heure locale, ce qui décale d'un jour selon le fuseau. Les conversions n'ont lieu que dans les helpers de calcul de model.js.

end est incluse : une phase du 2026-08-01 au 2026-08-01 dure un jour.

Tout se compte en jours calendaires, week-ends et jours fériés compris.

Les numéros de semaine affichés dans l'en-tête suivent l'ISO 8601 : la semaine appartient à l'année où tombe son jeudi. semaineISO() renvoie donc le numéro et l'année correspondante, qui peut différer de celle de la date.

Bornes et dérive

  • Les bornes d'un projet vont de la plus petite start à la plus grande end de ses phases. Un projet sans phase n'a pas de bornes et s'affiche comme un couloir vide.
  • Figer la référence copie, pour chaque phase du projet, ses start et end courantes dans son champ baseline, et inscrit la date du jour dans baselineDate. L'opération est rejouable : la refiger après un arbitrage assumé repart d'une base propre.
  • La dérive d'un projet est l'écart, en jours, entre la plus grande end actuelle et la plus grande end de référence. Elle est affichée arrondie en semaines. Une phase créée après le figeage n'a pas de baseline et ne compte pas dans la référence, mais compte dans les dates actuelles : ajouter une phase en fin de projet crée donc bien une dérive, ce qui est le comportement voulu.