Une couleur par membre #10

Open
opened 2026-08-28 22:05:51 +01:00 by maxime · 4 comments
Owner

Description

Chaque membre reçoit une couleur ; les événements et tâches qui lui sont affectés à lui seul la portent. Un coup d'œil suffit alors pour savoir de qui relève une case du planning, sans lire le nom écrit.

Portée
Uniquement les affectations uniques portent la couleur d'un membre : mélanger deux couleurs ne veut rien dire. Un événement familial est attribué à tout le monde par défaut — il prend la couleur de la famille, qui se reconnaît ainsi au premier coup d'œil ; un événement attribué à personne garde son aspect d'aujourd'hui.

Où la couleur se lit

  • La grille de l'emploi du temps — jour, jour par membre, semaine et mois. Les membres attribués sont dans CATEGORIES (D14).
  • Les éléments d'une liste de tâches, qui portent un assignee.
  • Le bloc « tâches du jour » de l'accueil.

Ce qu'il faut trancher

  • Où vit la couleur. Un membre est un compte Nextcloud dans une équipe Circles, pas une ligne de cette application : il n'existe aujourd'hui aucune table où écrire une préférence par membre et par famille. La famille, elle, a déjà une couleur, utilisée pour son agenda.
  • Qui choisit. Attribution automatique à l'arrivée dans la famille, ou choix explicite ? L'automatique évite un écran mais donne des couleurs qu'on ne peut pas corriger.
  • Les collisions. Deux membres de la même couleur annulent tout l'intérêt. Il faut une palette bornée et une attribution qui évite ce qui est déjà pris.
  • Le contraste. La couleur porte du texte, en thème clair comme en thème sombre. Une palette choisie librement produit des cases illisibles ; les couleurs de Nextcloud sont un point de départ.
  • La couleur reste redondante avec le nom, elle ne le remplace pas — daltonisme, et cases trop petites pour un aplat lisible.

Plan d'action

  • Conception — les quatre questions ouvertes de la Description sont tranchées dans le commentaire de conception ci-dessous (où vit la couleur, qui choisit, collisions, contraste).
  • Migration 10 : table member_colors, attribution automatique à la lecture, route de correction PUT
  • Palette bornée partagée (contraste clair/sombre) ; pastille et correction dans le panneau Membres des paramètres de la famille
  • Emploi du temps : résolution unique de la couleur de membre sur les quatre vues
  • Listes de tâches et bloc « À faire aujourd'hui » de l'accueil
  • Suite e2e et catalogues l10n

Recette

  • Portes mécaniques : make lint typecheck test, puis make build-frontend (bundle IIFE) et cd e2e && npm test
  • Revue technique personnelle
  • Mise à jour du wiki (décision, schéma, interface, périmètre)
  • Revue fonctionnelle
  • Corriger les retours fonctionnels quand un commentaire porte des retours

Plan de MEP

  • Une seule livraison 1.0.x ; migration SQLite v10 appliquée au premier démarrage — aucun appel réseau dans la migration, aucun backfill (l'attribution paresseuse couvre les familles existantes, aucune intervention)
  • Rien à changer dans appinfo/info.xml : ^/api/.* relaie déjà GET, POST, PUT et DELETE
## Description Chaque membre reçoit une couleur ; les événements et tâches qui lui sont affectés à lui seul la portent. Un coup d'œil suffit alors pour savoir de qui relève une case du planning, sans lire le nom écrit. **Portée** Uniquement les affectations **uniques** portent la couleur d'un membre : mélanger deux couleurs ne veut rien dire. Un événement familial est attribué à tout le monde par défaut — il prend **la couleur de la famille**, qui se reconnaît ainsi au premier coup d'œil ; un événement attribué à personne garde son aspect d'aujourd'hui. **Où la couleur se lit** - La grille de l'emploi du temps — jour, jour par membre, semaine et mois. Les membres attribués sont dans `CATEGORIES` (D14). - Les éléments d'une liste de tâches, qui portent un `assignee`. - Le bloc « tâches du jour » de l'accueil. **Ce qu'il faut trancher** - **Où vit la couleur.** Un membre est un compte Nextcloud dans une équipe Circles, pas une ligne de cette application : il n'existe aujourd'hui aucune table où écrire une préférence par membre et par famille. La famille, elle, a déjà une couleur, utilisée pour son agenda. - **Qui choisit.** Attribution automatique à l'arrivée dans la famille, ou choix explicite ? L'automatique évite un écran mais donne des couleurs qu'on ne peut pas corriger. - **Les collisions.** Deux membres de la même couleur annulent tout l'intérêt. Il faut une palette bornée et une attribution qui évite ce qui est déjà pris. - **Le contraste.** La couleur porte du texte, en thème clair comme en thème sombre. Une palette choisie librement produit des cases illisibles ; les couleurs de Nextcloud sont un point de départ. - **La couleur reste redondante avec le nom**, elle ne le remplace pas — daltonisme, et cases trop petites pour un aplat lisible. ## Plan d'action - [x] Conception — les quatre questions ouvertes de la Description sont tranchées dans le commentaire de conception ci-dessous (où vit la couleur, qui choisit, collisions, contraste). - [x] Migration 10 : table `member_colors`, attribution automatique à la lecture, route de correction `PUT` - [x] Palette bornée partagée (contraste clair/sombre) ; pastille et correction dans le panneau Membres des paramètres de la famille - [x] Emploi du temps : résolution unique de la couleur de membre sur les quatre vues - [x] Listes de tâches et bloc « À faire aujourd'hui » de l'accueil - [x] Suite e2e et catalogues l10n ## Recette - [x] Portes mécaniques : `make lint typecheck test`, puis `make build-frontend` (bundle IIFE) et `cd e2e && npm test` - [x] Revue technique personnelle - [x] Mise à jour du wiki (décision, schéma, interface, périmètre) - [x] Revue fonctionnelle - [x] Corriger les retours fonctionnels quand un commentaire porte des retours ## Plan de MEP - Une seule livraison 1.0.x ; migration SQLite v10 appliquée au premier démarrage — aucun appel réseau dans la migration, aucun backfill (l'attribution paresseuse couvre les familles existantes, aucune intervention) - Rien à changer dans `appinfo/info.xml` : `^/api/.*` relaie déjà GET, POST, PUT et DELETE
maxime added this to the 1.0.x milestone 2026-08-28 22:05:51 +01:00
Author
Owner

Conception — fonctionnel et technique

Principes

Une couleur est un attribut du membre au sein d'une famille — pas du compte Nextcloud, pas de l'appareil. Toute la famille voit la même, sur tous ses appareils ; une même personne peut donc porter une couleur différente dans deux familles. Elle n'est jamais que redondante : le nom reste affiché partout où la couleur se lit (daltonisme, aplats trop petits pour être lus seuls).

Elle se lit par affectation :

  • un événement dont CATEGORIES nomme exactement un membre connu (D14) prend la couleur du membre ;
  • un événement qui en nomme plusieurs prend la couleur de la famille — c'est l'événement familial, le cas majoritaire, et il se reconnaît ainsi au premier coup d'œil ;
  • un événement qui n'en nomme aucun garde la teinte dérivée d'aujourd'hui (hueOf) — rien n'est affecté, rien ne s'impose ;
  • une tâche dont l'assignee est renseigné — une tâche est par construction affectée à une seule personne ;
  • jamais un repas (rien ni personne n'est affecté), jamais un abonnement externe (personne ici n'a affecté).

Les quatre tranches

1. Où vit la couleur — dans SQLite, table member_colors.

Le test de D27 est net : un choix que toute la famille doit voir sur tous ses appareils n'est ni une préférence de personne (API Preferences) ni un réglage d'écran par appareil (localStorage). C'est une donnée de famille, comme families.color. Circles n'offre pas de métadonnée par membre inscriptible sans un appel réseau de plus, et le compte Nextcloud n'est pas le membre — D14 prévoit des membres sans compte.

CREATE TABLE member_colors (
    family_id   TEXT NOT NULL REFERENCES families(id) ON DELETE CASCADE,
    member_key  TEXT NOT NULL,   -- la clé opaque de D14, pas l'id d'appartenance
    color_index INTEGER NOT NULL,
    PRIMARY KEY (family_id, member_key)
);

Pourquoi un index et pas un hex : la palette est une convention d'affichage. L'index voyage sur le fil et reste stable si la palette est retouchée pour le contraste ; un hex stocké serait faux le lendemain. La clé est la clé opaque (aujourd'hui le user id — le même choix que keyOf du filtre du planning, et non l'id d'appartenance Circles, qui change quand on quitte puis revient).

Une ligne n'affirme pas qu'un membre existe — le Circle est seul maître de l'appartenance (D4). Elle ne se lit qu'à côté d'une liste de membres réellement chargée ; une clé périmée est inerte, jamais autoritaire (D27).

2. Qui choisit — l'automatique à l'arrivée, corrigeable par n'importe quel membre.

L'attribution est matérialisée à la lecture, dans getMembers — l'unique entonnoir des membres, déjà derrière le cache de D19 : un membre sans ligne reçoit la première couleur de la palette libre dans sa famille. Aucun branchement dans le flux d'invitation (qui enchaîne déjà des appels Nextcloud), et un membre ajouté depuis Nextcloud directement reçoit aussi sa couleur. La migration ne back-fille rien : la première lecture s'en charge, au prix d'une écriture par membre et par famille. C'est une écriture dans une route de lecture, mais bornée — le cache de D19 en est la limite.

La correction vit dans le panneau Membres des paramètres de la famille : une pastille par ligne, qui ouvre la palette. N'importe quel membre peut corriger — même précédent que les abonnements (D34) : la famille est une unité de confiance, et un enfant sans compte ne peut pas choisir lui-même. L'automatique évite l'écran ; la correction évite ses couleurs immuables.

3. Les collisions — impossibles par construction.

La palette est bornée à 8. L'attribution prend la première couleur libre, dans l'ordre fixe de la palette. Le sélecteur désactive les couleurs déjà portées par un autre membre : deux membres de la même couleur ne peuvent pas exister tant que la famille tient dans la palette. Au-delà de 8 membres, le repli est un hachage stable de la clé modulo 8 — déterministe sur tous les appareils, collision inévitable mais jamais vacillante.

4. Le contraste — la palette est fixe et double.

Chaque entrée donne un fond et un texte, pour le thème clair et le thème sombre, dérivés des couleurs officielles Nextcloud, contraste ≥ 4.5 dans les deux thèmes. Pas de couleur libre : une palette choisie librement produit des cases illisibles. Les valeurs exactes se figent à la construction, contre la palette @nextcloud/vue.

Comment la couleur se lit

Une seule fonction de résolution, memberColorOf(key), partout — pas une branche par vue :

  • Emploi du temps — les quatre vues reçoivent le résolveur comme elles reçoivent déjà nameOf : jour, jour par membre, semaine, mois. Dans colorOf (stores/events.ts) : members à exactement une clé connue → couleur du membre ; plusieurs → couleur de la famille, celle que l'agenda d'événements porte déjà dans la réponse de période (D31) ; aucune → teinte dérivée inchangée. Les autres agendas gardent leur couleur propre (D31). L'échéance d'une tâche affectée suit la même règle — l'issue l'exige sur les quatre vues ; sa nature reste lisible par le bandeau du jour entier en semaine et au jour, et par la fiche projetée qui nomme « Listes » au mois. La vue « jour par membre » porte en plus la pastille dans l'en-tête de colonne. Repli silencieux : une clé sans ligne connue n'a pas de couleur — le rendu est inchangé, jamais faux.
  • Listes de tâches — la puce d'assignee de ListRow prend la couleur du membre (propriété CSS --of-member-color, classes distinctes par colonne pour l'e2e).
  • Accueil — la route summary marque chaque SummaryTask d'un assigneeColor : les membres sont lus via getMembers (cache de D19), et l'accueil ne gagne pas d'appel parallèle — l'instantané borné est le contrat du fichier. Une lecture refusée ne fait pas échouer le résumé : la couleur est null, la tâche reste affichée (un échec de lecture ne devient jamais un résultat vide).

Ce que la couleur ne traverse pas

  • Jamais écrite dans CalDAV. Ni propriété X, ni CATEGORIES : un téléphone ou Nextcloud Calendar l'ignorerait ou l'afficheraient mal, et changer la couleur d'un membre devrait alors réécrire chaque événement. Elle se dérive au rendu, toujours ; calendar-projection.ts est inchangé et aucune mutation de liste ne gagne de crochet de projection.
  • Pas dans l'export (D36). Une couleur est une préférence de famille sur des personnes ; l'import réassigne comme une arrivée.
  • Pas sur la page hors ligne (D35). Elle lit Cache Storage, qui ne contient pas les membres ; la page reste ce qu'elle est.
  • Pas dans le sélecteur de famille ni le filtre par membre. Les avatars y font déjà le travail.

Interface et tests

  • FamilyMember gagne color: number (index de palette) ; le sélecteur de correction est une pastille + palette dans FamilyMembersPanel, à NcColorPicker si le composant le permet, sinon huit pastilles en NcPopover.
  • La route : PUT /api/families/:id/members/:memberId/color, corps { color }, index validé par zod (message source, jamais de phrase construite à la main) ; 404 pour un memberId inconnu — jamais 403, qui révélerait l'existence ; 409 avec message traduit si un autre membre porte déjà la couleur ; la réponse est l'instantané complet des membres (D26). ^/api/.* relaie déjà PUT — rien à déclarer dans info.xml, routes.test.ts le vérifiera.
  • e2e : une famille à deux membres, une tâche affectée à l'un — la ligne porte --of-member-color avec la valeur attendue (le sélecteur échoue si la couleur disparaît) ; l'événement attribué à un seul membre porte sa couleur sur la grille ; un changement de couleur depuis les paramètres se voit sur la liste. Classes distinctes par colonne (.item*, .home-entry*).
  • l10n : les nouvelles sources (libellés du panneau, noms de couleurs pour l'aria) passent par t() — en getter si une constante les porte à l'import (le piège de constants/lists.ts) ; make l10n-build puis le l10n-check de la CI.
# Conception — fonctionnel et technique ## Principes Une couleur est un attribut du membre **au sein d'une famille** — pas du compte Nextcloud, pas de l'appareil. Toute la famille voit la même, sur tous ses appareils ; une même personne peut donc porter une couleur différente dans deux familles. Elle n'est jamais que redondante : le nom reste affiché partout où la couleur se lit (daltonisme, aplats trop petits pour être lus seuls). Elle se lit par **affectation** : - un événement dont `CATEGORIES` nomme exactement un membre connu (D14) prend la couleur du membre ; - un événement qui en nomme plusieurs prend **la couleur de la famille** — c'est l'événement familial, le cas majoritaire, et il se reconnaît ainsi au premier coup d'œil ; - un événement qui n'en nomme aucun garde la teinte dérivée d'aujourd'hui (`hueOf`) — rien n'est affecté, rien ne s'impose ; - une tâche dont l'`assignee` est renseigné — une tâche est par construction affectée à une seule personne ; - jamais un repas (rien ni personne n'est affecté), jamais un abonnement externe (personne ici n'a affecté). ## Les quatre tranches **1. Où vit la couleur — dans SQLite, table `member_colors`.** Le test de D27 est net : un choix que toute la famille doit voir sur tous ses appareils n'est ni une préférence de personne (API Preferences) ni un réglage d'écran par appareil (`localStorage`). C'est une donnée de famille, comme `families.color`. Circles n'offre pas de métadonnée par membre inscriptible sans un appel réseau de plus, et le compte Nextcloud n'est pas le membre — D14 prévoit des membres sans compte. ```sql CREATE TABLE member_colors ( family_id TEXT NOT NULL REFERENCES families(id) ON DELETE CASCADE, member_key TEXT NOT NULL, -- la clé opaque de D14, pas l'id d'appartenance color_index INTEGER NOT NULL, PRIMARY KEY (family_id, member_key) ); ``` Pourquoi un index et pas un hex : la palette est une convention d'affichage. L'index voyage sur le fil et reste stable si la palette est retouchée pour le contraste ; un hex stocké serait faux le lendemain. La clé est la clé opaque (aujourd'hui le user id — le même choix que `keyOf` du filtre du planning, et non l'id d'appartenance Circles, qui change quand on quitte puis revient). Une ligne n'affirme pas qu'un membre existe — le Circle est seul maître de l'appartenance (D4). Elle ne se lit qu'à côté d'une liste de membres réellement chargée ; une clé périmée est inerte, jamais autoritaire (D27). **2. Qui choisit — l'automatique à l'arrivée, corrigeable par n'importe quel membre.** L'attribution est **matérialisée à la lecture**, dans `getMembers` — l'unique entonnoir des membres, déjà derrière le cache de D19 : un membre sans ligne reçoit la première couleur de la palette libre dans sa famille. Aucun branchement dans le flux d'invitation (qui enchaîne déjà des appels Nextcloud), et un membre ajouté depuis Nextcloud directement reçoit aussi sa couleur. La migration ne back-fille rien : la première lecture s'en charge, au prix d'une écriture par membre et par famille. C'est une écriture dans une route de lecture, mais bornée — le cache de D19 en est la limite. La correction vit dans le panneau Membres des paramètres de la famille : une pastille par ligne, qui ouvre la palette. **N'importe quel membre peut corriger** — même précédent que les abonnements (D34) : la famille est une unité de confiance, et un enfant sans compte ne peut pas choisir lui-même. L'automatique évite l'écran ; la correction évite ses couleurs immuables. **3. Les collisions — impossibles par construction.** La palette est bornée à 8. L'attribution prend la première couleur libre, dans l'ordre fixe de la palette. Le sélecteur **désactive** les couleurs déjà portées par un autre membre : deux membres de la même couleur ne peuvent pas exister tant que la famille tient dans la palette. Au-delà de 8 membres, le repli est un hachage stable de la clé modulo 8 — déterministe sur tous les appareils, collision inévitable mais jamais vacillante. **4. Le contraste — la palette est fixe et double.** Chaque entrée donne un fond **et** un texte, pour le thème clair et le thème sombre, dérivés des couleurs officielles Nextcloud, contraste ≥ 4.5 dans les deux thèmes. Pas de couleur libre : une palette choisie librement produit des cases illisibles. Les valeurs exactes se figent à la construction, contre la palette `@nextcloud/vue`. ## Comment la couleur se lit Une seule fonction de résolution, `memberColorOf(key)`, partout — pas une branche par vue : - **Emploi du temps** — les quatre vues reçoivent le résolveur comme elles reçoivent déjà `nameOf` : jour, jour par membre, semaine, mois. Dans `colorOf` (`stores/events.ts`) : `members` à exactement une clé connue → couleur du membre ; plusieurs → **couleur de la famille**, celle que l'agenda d'événements porte déjà dans la réponse de période (D31) ; aucune → teinte dérivée inchangée. Les autres agendas gardent leur couleur propre (D31). L'échéance d'une tâche affectée suit la même règle — l'issue l'exige sur les quatre vues ; sa nature reste lisible par le bandeau du jour entier en semaine et au jour, et par la fiche projetée qui nomme « Listes » au mois. La vue « jour par membre » porte en plus la pastille dans l'en-tête de colonne. **Repli silencieux : une clé sans ligne connue n'a pas de couleur** — le rendu est inchangé, jamais faux. - **Listes de tâches** — la puce d'`assignee` de `ListRow` prend la couleur du membre (propriété CSS `--of-member-color`, classes distinctes par colonne pour l'e2e). - **Accueil** — la route `summary` marque chaque `SummaryTask` d'un `assigneeColor` : les membres sont lus via `getMembers` (cache de D19), et l'accueil ne gagne pas d'appel parallèle — l'instantané borné est le contrat du fichier. Une lecture refusée ne fait pas échouer le résumé : la couleur est `null`, la tâche reste affichée (un échec de lecture ne devient jamais un résultat vide). ## Ce que la couleur ne traverse pas - **Jamais écrite dans CalDAV.** Ni propriété X, ni `CATEGORIES` : un téléphone ou Nextcloud Calendar l'ignorerait ou l'afficheraient mal, et changer la couleur d'un membre devrait alors réécrire chaque événement. Elle se dérive au rendu, toujours ; `calendar-projection.ts` est inchangé et aucune mutation de liste ne gagne de crochet de projection. - **Pas dans l'export (D36).** Une couleur est une préférence de famille sur des personnes ; l'import réassigne comme une arrivée. - **Pas sur la page hors ligne (D35).** Elle lit Cache Storage, qui ne contient pas les membres ; la page reste ce qu'elle est. - **Pas dans le sélecteur de famille ni le filtre par membre.** Les avatars y font déjà le travail. ## Interface et tests - `FamilyMember` gagne `color: number` (index de palette) ; le sélecteur de correction est une pastille + palette dans `FamilyMembersPanel`, à `NcColorPicker` si le composant le permet, sinon huit pastilles en `NcPopover`. - La route : `PUT /api/families/:id/members/:memberId/color`, corps `{ color }`, index validé par zod (message source, jamais de phrase construite à la main) ; 404 pour un `memberId` inconnu — jamais 403, qui révélerait l'existence ; 409 avec message traduit si un autre membre porte déjà la couleur ; la réponse est **l'instantané complet des membres** (D26). `^/api/.*` relaie déjà PUT — rien à déclarer dans `info.xml`, `routes.test.ts` le vérifiera. - e2e : une famille à deux membres, une tâche affectée à l'un — la ligne porte `--of-member-color` avec la valeur attendue (le sélecteur échoue si la couleur disparaît) ; l'événement attribué à un seul membre porte sa couleur sur la grille ; un changement de couleur depuis les paramètres se voit sur la liste. Classes distinctes par colonne (`.item*`, `.home-entry*`). - l10n : les nouvelles sources (libellés du panneau, noms de couleurs pour l'aria) passent par `t()` — en getter si une constante les porte à l'import (le piège de `constants/lists.ts`) ; `make l10n-build` puis le `l10n-check` de la CI.
Author
Owner

Implémenté sur feat/issue-10-member-colors (8 commits, non rebasé, non poussé).

Ce qui a été construit

  • Migration 10 : member_colors(family_id, member_key, color_index), clé = la clé opaque de D14, index de palette et non couleur, aucun backfill.
  • getMembers matérialise : chaque membre sans ligne reçoit la première couleur libre de la palette ; au-delà de huit membres, repli par hachage stable de la clé.
  • PUT /api/families/:id/members/:memberId/color : 404 pour un membre inconnu, 409 si la couleur est portée, réponse = l'instantané complet des membres (D26).
  • Palette : les huit couleurs officielles @nextcloud/vue (Purple, Feldspar, Gold, Olivine, Acapulco, Boston Blue, Mariner, Blue Violet), chacune assombrie jusqu'à un contraste de 4,7 avec le texte blanc — ce que les puces de la semaine supposent déjà, donc une seule paire pour les deux thèmes. Dans styles/tokens.css comme propriétés personnalisées ; les indices ne sont jamais renumérotés.
  • Rendu : colorOf décide seul — exactement un membre connu → sa couleur (échéance affectée comprise, l'agenda n'y change rien) ; plusieurs membres → la couleur de la famille ; personne nommé → la teinte dérivée d'aujourd'hui. Pastille dans les en-têtes de colonnes de « jour par membre », puce d'assigné colorée dans les listes, « À faire aujourd'hui » coloré depuis l'instantané du résumé (un refus de lecture Circles laisse la tâche visible, sans couleur).
  • Jamais écrit dans CalDAV, l'export ou la page hors ligne ; les projections sont inchangées.

Portes mécaniques

Porte Résultat
make lint typecheck test 0 erreur (avertissements préexistants seuls), 478 tests backend + 388 frontend
make build-frontend + IIFE bundle ok
cd e2e && npm test 100 passed, dont member-colors.spec.ts (3 tests : puce de tâche, grille membre/famille, correction + couleur prise refusée)
make l10n-build 421 sources, toutes traduites

Revue technique (branch-review)

Bloquant : rien.

À signaler :

  • Un échéancier affecté prend la couleur de son membre aussi en vue mois, où le bandeau du jour entier ne le distingue plus d'un événement — le « quoi » n'y reste lisible que par la fiche projetée. C'est l'arbitrage prévu par la conception ; à confirmer en revue fonctionnelle.
  • Le sélecteur de correction désactive les couleurs prises ; la 409 de la route est le garde-fou côté serveur contre une liste périmée.
  • Pendant la mise à jour du wiki, une sonde d'API a supprimé la page Architecture (l'endpoint PATCH /wiki/page/… de Forgejo se comporte comme un renommage quand le champ title manque) — restaurée depuis l'historique git dans la foulée, puis D39, le schéma et le paragraphe UX publiés. Les trois commits wiki sont dans OrganisateurFamilial.wiki (main).

Wiki

  • Architecture : décision D39 + table member_colors dans le schéma.
  • UX : « a member wears one colour per family » dans les conséquences concrètes.
  • AGENTS.md : état du projet + l'invariant (couleur dérivée, jamais écrite là où elle se lit ; indices jamais renumérotés).

Intégration

git switch main && git merge --ff-only feat/issue-10-member-colors && git branch -d feat/issue-10-member-colors

Restent ouverts : revue fonctionnelle (à faire sur l'instance locale — créer une famille à deux membres, affecter une tâche, corriger une couleur depuis les paramètres) et le retrait des retours qu'elle donnera.

Implémenté sur `feat/issue-10-member-colors` (8 commits, non rebasé, non poussé). ## Ce qui a été construit - **Migration 10** : `member_colors(family_id, member_key, color_index)`, clé = la clé opaque de D14, index de palette et non couleur, aucun backfill. - **`getMembers` matérialise** : chaque membre sans ligne reçoit la première couleur libre de la palette ; au-delà de huit membres, repli par hachage stable de la clé. - **`PUT /api/families/:id/members/:memberId/color`** : 404 pour un membre inconnu, 409 si la couleur est portée, réponse = l'instantané complet des membres (D26). - **Palette** : les huit couleurs officielles `@nextcloud/vue` (Purple, Feldspar, Gold, Olivine, Acapulco, Boston Blue, Mariner, Blue Violet), chacune assombrie jusqu'à un contraste de 4,7 avec le texte blanc — ce que les puces de la semaine supposent déjà, donc une seule paire pour les deux thèmes. Dans `styles/tokens.css` comme propriétés personnalisées ; les indices ne sont jamais renumérotés. - **Rendu** : `colorOf` décide seul — exactement un membre connu → sa couleur (échéance affectée comprise, l'agenda n'y change rien) ; plusieurs membres → la couleur de la famille ; personne nommé → la teinte dérivée d'aujourd'hui. Pastille dans les en-têtes de colonnes de « jour par membre », puce d'assigné colorée dans les listes, « À faire aujourd'hui » coloré depuis l'instantané du résumé (un refus de lecture Circles laisse la tâche visible, sans couleur). - **Jamais écrit** dans CalDAV, l'export ou la page hors ligne ; les projections sont inchangées. ## Portes mécaniques | Porte | Résultat | |---|---| | `make lint typecheck test` | 0 erreur (avertissements préexistants seuls), 478 tests backend + 388 frontend | | `make build-frontend` + IIFE | bundle ok | | `cd e2e && npm test` | **100 passed**, dont `member-colors.spec.ts` (3 tests : puce de tâche, grille membre/famille, correction + couleur prise refusée) | | `make l10n-build` | 421 sources, toutes traduites | ## Revue technique (branch-review) **Bloquant : rien.** À signaler : - Un échéancier affecté prend la couleur de son membre aussi en vue mois, où le bandeau du jour entier ne le distingue plus d'un événement — le « quoi » n'y reste lisible que par la fiche projetée. C'est l'arbitrage prévu par la conception ; à confirmer en revue fonctionnelle. - Le sélecteur de correction désactive les couleurs prises ; la 409 de la route est le garde-fou côté serveur contre une liste périmée. - Pendant la mise à jour du wiki, une sonde d'API a supprimé la page `Architecture` (l'endpoint `PATCH /wiki/page/…` de Forgejo se comporte comme un renommage quand le champ `title` manque) — restaurée depuis l'historique git dans la foulée, puis D39, le schéma et le paragraphe UX publiés. Les trois commits wiki sont dans `OrganisateurFamilial.wiki` (`main`). ## Wiki - `Architecture` : décision **D39** + table `member_colors` dans le schéma. - `UX` : « a member wears one colour per family » dans les conséquences concrètes. - `AGENTS.md` : état du projet + l'invariant (couleur dérivée, jamais écrite là où elle se lit ; indices jamais renumérotés). ## Intégration ```bash git switch main && git merge --ff-only feat/issue-10-member-colors && git branch -d feat/issue-10-member-colors ``` Restent ouverts : **revue fonctionnelle** (à faire sur l'instance locale — créer une famille à deux membres, affecter une tâche, corriger une couleur depuis les paramètres) et le retrait des retours qu'elle donnera.
Author
Owner

Recette fonctionelle

  • Les bouton de couleur ne sont pas aligné verticalement
    Capture d’écran 2026-09-22 à 00.03.17
  • Le color picker ne se ferme pas quand on clique en dehors. Il faut re cliquer sur la couleur pour fermer le picker
    Capture d’écran 2026-09-22 à 00.03.26
  • [ ] Je n'ai pas la même couleur entre le planning et l'accueil. C'est l'accueil qui n'a pas la bonne couleur
### Recette fonctionelle - [ ] Les bouton de couleur ne sont pas aligné verticalement ![Capture d’écran 2026-09-22 à 00.03.17](/attachments/bf5e91f9-a4a8-4721-99d1-ad10a7241eb7) - [ ] Le color picker ne se ferme pas quand on clique en dehors. Il faut re cliquer sur la couleur pour fermer le picker ![Capture d’écran 2026-09-22 à 00.03.26](/attachments/b89379e9-b86f-4144-bf3f-449ef8005361) - [ ] Je n'ai pas la même couleur entre le planning et l'accueil. C'est l'accueil qui n'a pas la bonne couleur
Author
Owner

Les trois retours de la revue sont corrigés sur feat/issue-10-member-colors (4 commits de plus, non rebasé, non poussé).

1. Les pastilles n'étaient pas alignées verticalement

La pastille vivait après le nom du membre : sa position horizontale dépendait alors de la largeur de ce qui ferme la ligne — le badge « propriétaire » est plus étroit que le bouton « Retirer », et les pastilles des deux lignes se retrouvaient sur deux verticales différentes (12 px d'écart sur votre capture). La pastille vit désormais entre l'avatar et le nom : sa place ne dépend plus que de la largeur de l'avatar.

2. Le sélecteur ne se fermait pas au clic extérieur

Le sélecteur était un popover fait main, qui ne se fermait que sur son propre déclencheur. C'est un NcPopover désormais — le composant que le sélecteur de famille utilise déjà — qui possède le clic extérieur et la touche Échap.

3. L'accueil n'avait pas la bonne couleur

La carte « À venir » de l'accueil colore un événement par son agenda (D31), et la règle D39 n'y était jamais arrivée : un événement attribué à un seul membre portait sa couleur sur la grille mais le bleu familial sur l'accueil. L'instantané du résumé transporte désormais la carte clé → couleur à côté des événements, et « À venir » résout exactement comme la grille : un membre seul → sa couleur ; plusieurs → la couleur de la famille ; personne → celle de l'agenda. Les deux écrans ne peuvent plus se contredire.

Vérification

Porte Résultat
make lint typecheck test 0 erreur, 478 tests backend + 388 frontend
make l10n-check OK (le .pot a suivi les numéros de ligne déplacés, aucune source nouvelle)
make build-frontend + IIFE bundle ok
cd e2e && npm test 101 passed, dont le nouveau test : les pastilles au même alignement sur chaque ligne, la palette qui se ferme au clic extérieur, et « À venir » comparé à la grille sur le même événement

Le wiki est à jour (D39 porte maintenant l'accord accueil/grille dans son tableau et son paragraphe, le paragraphe UX aussi). Un incident d'instance à noter : un premier passage de la spec sur une instance encrassée par mes exécutions répétées a fait expirer le teardown (limites de taux accumulées) — le reset suivant a purgé les compteurs et tout est repassé en 27 s ; rien à voir avec le code.

Reste : votre re-lecture des trois points sur l'instance locale, puis l'intégration — git switch main && git merge --ff-only feat/issue-10-member-colors.

Les trois retours de la revue sont corrigés sur `feat/issue-10-member-colors` (4 commits de plus, non rebasé, non poussé). ## 1. Les pastilles n'étaient pas alignées verticalement La pastille vivait après le nom du membre : sa position horizontale dépendait alors de la largeur de ce qui ferme la ligne — le badge « propriétaire » est plus étroit que le bouton « Retirer », et les pastilles des deux lignes se retrouvaient sur deux verticales différentes (12 px d'écart sur votre capture). La pastille vit désormais **entre l'avatar et le nom** : sa place ne dépend plus que de la largeur de l'avatar. ## 2. Le sélecteur ne se fermait pas au clic extérieur Le sélecteur était un popover fait main, qui ne se fermait que sur son propre déclencheur. C'est un `NcPopover` désormais — le composant que le sélecteur de famille utilise déjà — qui possède le clic extérieur et la touche Échap. ## 3. L'accueil n'avait pas la bonne couleur La carte « À venir » de l'accueil colore un événement par **son agenda** (D31), et la règle D39 n'y était jamais arrivée : un événement attribué à un seul membre portait sa couleur sur la grille mais le bleu familial sur l'accueil. L'instantané du résumé transporte désormais la carte clé → couleur à côté des événements, et « À venir » résout exactement comme la grille : un membre seul → sa couleur ; plusieurs → la couleur de la famille ; personne → celle de l'agenda. Les deux écrans ne peuvent plus se contredire. ## Vérification | Porte | Résultat | |---|---| | `make lint typecheck test` | 0 erreur, 478 tests backend + 388 frontend | | `make l10n-check` | OK (le `.pot` a suivi les numéros de ligne déplacés, aucune source nouvelle) | | `make build-frontend` + IIFE | bundle ok | | `cd e2e && npm test` | **101 passed**, dont le nouveau test : les pastilles au même alignement sur chaque ligne, la palette qui se ferme au clic extérieur, et « À venir » comparé à la grille sur le même événement | Le wiki est à jour (D39 porte maintenant l'accord accueil/grille dans son tableau et son paragraphe, le paragraphe UX aussi). Un incident d'instance à noter : un premier passage de la spec sur une instance encrassée par mes exécutions répétées a fait expirer le teardown (limites de taux accumulées) — le reset suivant a purgé les compteurs et tout est repassé en 27 s ; rien à voir avec le code. Reste : votre re-lecture des trois points sur l'instance locale, puis l'intégration — `git switch main && git merge --ff-only feat/issue-10-member-colors`.
Sign in to join this conversation.
No description provided.