Spunto CMS
Concepts

Vues enregistrées

Une vue est une donnée — arbre de filtres, tris et groupement — compilée en SQL à un seul endroit. Ce qu'elle sélectionne, et ce qu'elle change à l'affichage.

Une vue est une façon enregistrée de regarder une liste. Elle se compose de deux moitiés délibérément tenues à l'écart l'une de l'autre :

  • definition — ce que la vue sélectionne : un arbre de filtres, des tris, un groupement facultatif, et le choix de suivre ou non les rattachements à des listes secondaires ;
  • display — comment on la regarde : l'ordre des colonnes d'un kanban, et les champs que la vue montre (columns) — ordre, largeurs et colonnes masquées d'un tableau, et le même ordre et les mêmes champs masqués pour les cartes d'un kanban ou d'une pile. Un seul réglage par vue, quel que soit son kind.

Déplacer, élargir ou masquer une colonne modifie display. Cela ne peut faire entrer ni sortir une seule carte du résultat, parce que cela n'atteint jamais la partie qui sélectionne.

`display` est remplacé en bloc

Enregistrer une moitié oblige à renvoyer l'autre. Un client qui enregistre les largeurs de colonnes sans renvoyer groupOrder efface l'ordre des colonnes du kanban.

Une définition est une donnée, pas du SQL

{
  "filter": {
    "and": [
      { "op": "eq", "field": { "fieldId": "sys:status" }, "value": "<statusId>" },
      { "op": "contains", "field": { "fieldId": "<fieldId>" }, "value": "draft" }
    ]
  },
  "sort": [{ "field": { "fieldId": "sys:order" }, "direction": "asc" }],
  "groupBy": { "fieldId": "sys:status" },
  "includeSecondaryLists": true
}

Aucun fragment SQL ne peut entrer dans une vue — ni extrait, ni expression, ni nom de colonne. Une définition est un arbre sérialisable, et c'est ce qui permet de la stocker, de l'envoyer, d'en calculer le diff et de l'exécuter ailleurs que là où elle a été écrite.

Elle devient du SQL à un seul endroit du code. C'est une contrainte voulue : c'est elle qui permettra un jour d'aiguiller une vue vers un autre backend de lecture sans toucher à un seul écran.

Les types de vue

Une vue a un kind : table, board, list ou calendar.

Le type ne change pas ce que la vue sélectionne. Il change la mise en page et, pour les types groupés, la route à appeler.

L'interface affiche les quatre. Une vue calendar a trois mises en page — mois, semaine et chronologie — choisies dans son display, pas comme des types de plus.

Calendrier et chronologie

Un calendrier pose les cartes sur une propriété date (ou sur sys:created_at, en lecture seule). Quelle date, et quelle mise en page, vivent dans display.calendar :

{ "calendar": { "layout": "month", "dateField": "<fieldId date>", "endField": "<fieldId date>" } }

Comme les réglages de colonnes, c'est une façon de regarder, pas de sélectionner : la définition n'est pas touchée, et la même vue en tableau sélectionne les mêmes cartes. Le mois regardé n'est enregistré nulle part — il voyage avec chaque lecture, en range :

{
  "definition": { "...": "la définition de la vue, plus groupBy sur la date" },
  "groups": ["2026-10-26", "2026-10-27", "…", "2026-12-06"],
  "range": { "field": { "fieldId": "<fieldId date>" }, "from": "2026-10-26", "to": "2026-12-06" }
}

Un mois est donc une lecture groupée par jour : chaque case est un groupe, vides comprises, chacune avec son curseur — « +3 de plus » charge la suite de ce jour. La plage est compilée par l'API et borne les lignes comme les compteurs.

Avec un endField, une carte est une durée, et elle est rendue par toute fenêtre qu'elle chevauche : une rédaction commencée le 28 octobre et due le 4 novembre est dans les deux mois. Une fin absente, ou antérieure au début, vaut une durée d'un jour.

Dans l'interface, glisser une carte sur un autre jour écrit sa date, la déposer sur le panneau Sans date l'efface, et cliquer dans le vide d'une case crée une carte qui naît avec cette date. Sur la chronologie, glisser une barre décale ses deux bornes, tirer un bord n'écrit que ce bord, et cliquer sur la piste d'une carte sans date l'y pose.

Pourquoi la chronologie n'est pas un `kind`

kind fait partie de chaque vue dans chaque réponse. L'app iOS décode les réponses strictement : une valeur qu'elle ne connaît pas l'empêcherait d'ouvrir la liste entière. calendar est dans l'énumération depuis le début, donc la chronologie en est une mise en page — et les apps plus anciennes l'affichent simplement en tableau.

Le groupement, et pourquoi les colonnes viennent du domaine

Une colonne de kanban, c'est la vue, plus une valeur de groupe. Même SQL compilé, un prédicat de plus — c'est ce qui fait que la somme des colonnes vaut exactement la vue.

Deux conséquences que vous rencontrerez tout de suite :

Chaque groupe se pagine séparément. POST …/grouped-query renvoie des groupes, chacun avec son propre curseur. Découper une page plate en colonnes côté client remplirait trois colonnes au hasard de l'ordre de tri, et rendrait inexprimable un « charger la suite » sur une seule colonne.

L'appelant nomme les groupes qu'il veut. Un GROUP BY ne renvoie que les valeurs qui ont des lignes : une colonne « Terminé » vide n'existerait donc pas — et on ne peut pas déposer une carte dans une colonne qui n'est pas affichée. L'appelant fournit les groupes, parce que lui seul connaît les statuts de l'espace de travail, les options déclarées d'un select ou la liste des membres.

Un axe sur lequel on ne peut pas écrire n'accepte donc aucun dépôt : déposer une carte est une écriture, pas un geste de rangement.

Une clé de groupe doit faire l'aller-retour

Une clé de groupe sort d'une requête en ::text et rentre dans une autre pour retrouver les lignes de ce groupe. Si elle ne se relit pas dans le type de l'expression, la colonne affiche « 12 » en en-tête et zéro ligne dessous — sans aucune erreur nulle part. C'est pourquoi le groupement par sys:created_at groupe par jour de création, en UTC : lue telle quelle, la colonne est un horodatage dont la clé ne se relirait pas en date, et chaque carte aurait son groupe. Le jour fait l'aller-retour, et c'est ce dont un calendrier des créations a besoin.

L'ordre manuel est global à la liste

sys:order est un ordre par liste, pas un par colonne.

Déposer une carte en tête d'une colonne de kanban la place donc aussi en première ligne de la vue tableau. Notion garde un ordre distinct par groupe ; ici, il n'y en a qu'un, et ça se voit. Si cela vous surprend, c'est le comportement documenté, pas un bug.

Une conséquence appliquée à dessein : déposer dans une colonne vide n'écrit aucun ordre. La carte y sera première de toute façon, et en écrire un l'aurait propulsée en tête de la liste entière.

La vue par défaut

Une liste sans vue enregistrée n'est pas illisible. L'API publie CollectionDetail.defaultView : ordre manuel, groupement par statut, listes secondaires incluses.

Elle est publiée plutôt que recopiée dans chaque client, et pour une raison — voir listes et cartes.

Tri et pagination

La pagination est keyset uniquement. Il n'y a d'OFFSET nulle part dans l'API, et aucun moyen d'en demander un : le curseur est opaque, vous renvoyez ce qu'on vous a donné.

C'est mesuré, pas esthétique — vers la page 12 000, la forme avec offset prenait 348 ms là où la forme keyset en prenait 3.

Un curseur est lié au tri pour lequel il a été émis. Changez le tri, repartez du début ; l'API vous le dira plutôt que de renvoyer en silence une mauvaise page.

Détails dans interroger le contenu.

Sur cette page