Second Brain documentation

ADR 0087 – Moteur de dashboards : layout focus, barre de charge découpée, enchaînement, widgets digest et links

Accepté (2026-10-17, dans le cadre de l'ADR 0086 §1 validé par Aurélien)

← Toutes les décisions

ADR 0087 – Moteur de dashboards : layout focus, barre de charge découpée, enchaînement, widgets digest et links

Statut : Accepté (2026-10-17, dans le cadre de l’ADR 0086 §1 validé par Aurélien)

Complète l’ADR 0038 (dashboards déclaratifs) et l’ADR 0070 (charge du jour). Côté backend seulement : définitions, méta-schéma, contrôles au chargement et DTO. L’affichage est le travail du front (lignes A3 à A6 du backlog).

Contexte

L’accueil « Maintenant » (ADR 0086) a besoin de ce que la grille de deux colonnes ne sait pas dire : une colonne principale et une colonne latérale, un champ de capture dans l’en-tête, une barre de charge qui sépare les retards du prévu, un enchaînement des tâches courtes, une liste « dans un coin de la tête » qui puise dans plusieurs vues, et une ligne de puces vers l’Inbox, une revue et une vue. Tout doit rester déclaratif et vérifié au chargement : une faute de frappe dans le YAML ne doit jamais casser l’accueil.

Décision

Clés du dashboard

layout: focus      # facultatif ; absent : grille de deux colonnes (ADR 0038, inchangée)
capture: true      # facultatif ; champ de capture dans l'en-tête du dashboard
  • layout n’admet que focus : colonne principale et colonne latérale (une seule colonne sur mobile, dans l’ordre de la liste).
  • capture vaut faux par défaut. Le dashboard ne dit pas quelle syntaxe de capture utiliser : c’est la capture par défaut (config/captures/, ADR 0042).

Clés communes des widgets

  • column: main | side : colonne du layout focus. Refusé si le dashboard n’est pas en layout: focus. Sans column, un widget d’un dashboard focus va dans la colonne principale (règle du front).
  • width reste réservé à la grille ; il est ignoré en focus.

Widget view : split et chain

- kind: view
  view: aujourdhui
  sum: tache.estimation
  capacity: 360
  split: "tache.echeance < today"
  chain: true
  • split : condition du langage where des vues (ADR 0028), la même grammaire, le même contrôle de types. Le front l’évalue sur les lignes reçues pour découper la barre de charge en « en retard » (lignes qui vérifient la condition), « prévu » (les autres) et « libre » (capacité moins le total). Il exige sum et capacity (méta-schéma) et un widget view.
  • Parce que le front évalue la condition sur les lignes, chaque champ cité doit être une colonne de la vue du widget (display.columns), comme sum. Une condition illisible, mal typée ou citant un champ hors colonnes invalide le fichier. node, réservé aux vues contextuelles, est refusé.
  • chain: true : propose « Enchaîner » (ouvrir les lignes une à une dans un mode focus). Réservé aux widgets view. Comportement entièrement côté navigateur ; « Fait » passe par l’action rapide existante (ADR 0025).

Widget digest

- kind: digest
  label: Dans un coin de la tête
  column: side
  sources:
    - { view: echeance-semaine-hors-aujourdhui, tag: Cette semaine, limit: 2 }
    - { view: en-attente-a-relancer, tag: En attente, limit: 2 }
  • sources : de 1 à 6 entrées { view, tag, limit? }. tag est obligatoire (l’étiquette montrée devant chaque ligne), limit de 1 à 50 (défaut 5). Les vues doivent exister.
  • Dédoublonnage « première source gagnante » : une ligne n’apparaît qu’une fois, dans la première source qui la porte, dans l’ordre de la liste. Il est fait par le front, après lecture de chaque source avec sa limite plus la marge nécessaire : le backend ne lit pas les vues. Une source qui a perdu des lignes au dédoublonnage n’est pas complétée au-delà de sa limit.
  • Pas de couleur libre : la teinte de l’étiquette vient du type du nœud (charte C3).
- kind: links
  column: side
  items:
    - { inbox: true, label: Inbox }
    - { review: hebdomadaire, label: Revue hebdo }
    - { view: terminees-semaine, label: Terminées cette semaine, count: true }
  • items : de 1 à 8 puces, chacune avec label et exactement une cible : inbox: true (Inbox, qui affiche toujours son compteur), review: <nom> (revue guidée, ADR 0037) ou view: <nom> (page de la vue ; count: true ajoute le nombre de lignes, plafonné comme le widget counter).
  • count est réservé aux puces view. Revue et vue doivent exister.
  • Le widget inbox de l’ADR 0038 reste disponible.

Contrôles et santé de la configuration

Le méta-schéma (dashboard.schema.json) porte la forme : valeurs de layout et column, 1 à 6 sources, étiquette obligatoire, une seule cible par puce, split exige sum et capacity, digest exige sources, links exige items. DefinitionLoader ajoute les contrôles qui croisent les définitions, avec les messages préfixés par la position (widgets[2].sources[0].view : vue inconnue : …) : vues et revues connues, split analysé et typé avec le langage de where et limité aux colonnes de la vue, column exige layout: focus, chain, split, sources et items réservés à leur genre de widget. Un fichier invalide est ignoré et signalé par GET /api/definitions/status, la version précédente reste en service (comme pour les autres définitions). Le rechargement vérifie aussi les revues, absentes jusqu’ici du contrôle des dashboards conservés.

API

GET /api/definitions/dashboards porte les nouvelles clés. DashboardDefinition gagne layout (absent hors focus) et capture (booléen) ; DashboardWidget gagne column, split, chain (absents si non déclarés), sources (DashboardDigestSource : view, tag, limit toujours présent) et items (DashboardLink : inbox, review, view, label, count, absents si non déclarés), listes vides par défaut. Le client Angular est régénéré ; les dashboards existants sont inchangés.

Configuration livrée

  • config/dashboards/maintenant.yaml (default: true) exactement comme l’ADR 0086 §1 ; aujourdhui.yaml perd default: true et reste accessible sur /dashboards/aujourdhui.
  • config/views/terminees-semaine.yaml : tâches faites ces 7 derniers jours, pour la puce « Terminées cette semaine ». La vue terminees (30 jours) reste inchangée.

Alternatives écartées

  • Évaluer split en SQL côté serveur (requête dédiée renvoyant les deux totaux) : un endpoint de plus pour un découpage que le front fait sur des lignes déjà lues ; la condition resterait de toute façon limitée aux colonnes affichées si le total doit correspondre à ce que voit l’utilisateur.
  • Dédoublonner côté serveur : le backend ne lit pas les vues d’un dashboard (le front les demande une à une, comme pour les widgets view) ; un endpoint d’agrégation serait nouveau et sans autre usage.
  • Couleur d’étiquette libre pour le digest : contraire à la charte (couleur = type de nœud).
  • Grille libre (col, row) : déjà écartée par l’ADR 0038 ; deux colonnes nommées suffisent.

Conséquences

  • Aucune migration. Les dashboards écrits avant cet ADR se chargent sans changement.
  • Le front doit gérer deux genres de widget de plus (digest, links) ; un front plus ancien ignorerait un widget de genre inconnu, la liste des genres du DTO étant fermée par allowableValues.