Skip to content

feat(console): the Notes page lists the notes of a workspace and where they went (FRU-245) - #313

Merged
sakuga-agent[bot] merged 5 commits into
mainfrom
mathieu/fru-245-console-notes-toutes-les-notes-du-workspace-et-ou-elles-sont
Oct 14, 2026
Merged

sakuga-agent[bot] merged 5 commits into
mainfrom
mathieu/fru-245-console-notes-toutes-les-notes-du-workspace-et-ou-elles-sont

Conversation

@sakuga-agent

@sakuga-agent sakuga-agent Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Not critical (one screen of the console, and two short additions to the kit). Base main: #306 (FRU-244), the route it reads, is merged. The shape is read from the code of #306 (apps/worker/src/console-notes.ts), not only from the message that announced it: ref and url sit at the top of a note, a guest gets no place, the id is opaque, and pages go by a cursor.

Screen 10 of the mockup. Mathieu's decision on Q6, 2026-10-13: Fruitback keeps an index of the notes and the first 64 characters of each one, never its text. The page shows those 64 characters, and the whole note is read in the tracker or on the page.

What it is

  • The rail gets its « Notes » entry, between « Sites » and « Connectors » (SECTIONS, built: true). The Sites screen gets the end of its sentence: « Connect a tracker or see where every note went ».

  • Chips (role="group", « Where the notes went », aria-pressed): « All », then each place that has notes, in the order of the sources of the workspace, then « In Fruitback ». They use the kit FilterChip in its three states.

  • Filters:

    • a search (hidden label « Search notes »), which reads 300 ms after the last key;
    • « Site » and « Status » (kit Choice, compact, hidden label);
    • « Only my notes » (a new kit Checkbox).

    They live in the address (place, site, stage, mine, q, cursor, from): a reload keeps the view, and a filter goes back to the first page.

  • The table (960 px at the least), with one row per note:

    • Note: the 64 characters, on two lines at most. An excerpt of 64 characters was cut, and ends with « … ». Under it, the page and « Show on the page ».
    • Status: a drop of 10 px and the word.
    • Left by: « You », the role, « Former member », « Visitor · Verified by {site} », or « Visitor · Anonymous ».
    • Went to: the tracker at 600 and its reference as a link at 400 with an arrow, « Delivered to {host} » for an address, or « Kept in Fruitback ».
    • When: « 40 min ago », « Yesterday », « 3 days ago ».
  • « Send again » (owner and admin) for a note that an address did not take. It is disabled while the request is on its way. When it works, the list is read again and the row loses its « Not delivered yet ». When it is refused, the row says « Fruitback did not answer. Try again. ».

  • Pages (nav, « Pages of notes »): « 1 to 8 of 44 », « Previous », « Next ». The route pages by cursor, so the screen keeps the cursors of the pages before for « Previous ». A reload starts that list again: « Previous » then goes back to the first page.

  • States the mockup does not draw:

    • « Loading… », on the first read only;
    • a workspace with no note: « No note yet. Notes show here as soon as someone leaves one on a site of {workspace}. »;
    • a filter that keeps nothing: « No note matches. » and « Clear the filters »;
    • a read that failed: the alert of the Sites screen, « The notes did not load » / « Fruitback did not answer. Your notes are safe. » / « Try again ». It is never « No note yet ».

Nothing that a pointer can reach moves (FRU-271, #294–#296)

A read made again keeps the rows on the screen until its answer replaces them. Only the first read says « Loading… ». The test « while a filter is read, the rows stay and nothing a pointer can reach moves » holds the answer to a filter. It then checks that the eight rows are still there, that there is no « Loading… », and that the chips, the search, « Send again » and « Next » have not moved. Then it releases the answer.

A defect found on the way, and its proof

A list chosen while the search waits its 300 ms was undone when the search landed (and the other way round). The setter of React Router builds from the address of the render that made it. The screen now keeps the last address it wrote, and takes the address of React Router only when it changes. Test: « a list chosen while the search waits its 300 ms is kept when the search lands ». With the functional setter of React Router, it fails (?q=mug, the status lost). It passes 4 times in a row now.

Gaps with the mockup, and why

Of the mockup Here Why
The chip « Not delivered 2 » not drawn the route counts those notes (counts.undelivered) and does not filter them: a chip that chooses nothing would be a false button. The rows still say « Not delivered yet ». Asked of fru-console: place=undelivered.
« Search the text, the page or the author » « Search the text, the page or the reference » the route searches excerpt, page, site, stage, reference (SEARCHED), not the author. Asked of fru-console; the sentence of the mockup comes back if it does.
GitHub Issues no it is not a connector (FRU-188)
« In Fruitback » added the notes kept with no tracker (named as on screen 20)
« Show on the page » opens the note by the id the widget gave it (seed, not sent yet by #306), then by its issue, else the page alone the id of the route is opaque. Asked of fru-console: a seed field.
A guest no chip, no column « Went to », no « Send again » the route sends a guest none of them
The text of a note its first 64 characters, « … » when cut Q6

Boxes at 1440 (macOS):

Element Mockup Built Gap
Chips 297, 126, h 36 296.5, 126.2, h 36 0.5
Search 297, 176, 610.4 × 40 296.5, 176.2, 610.4 × 40 0.5
« Site » 923, w 170.4 923, w 170.4 0
« Status » 1109, w 150.4 1109.3, w 150.4 0.3
« Only my notes » 1276, 116.3 × 40, box 16 1275.7, 116.3 × 40, box 16 0.3
Table 298, 233 297.5, 233.2 0.5
Columns 731, 858, 1024, 1278 723, 855, 1027, … the table sizes its columns by their text, and the text is not the mockup's
Pagination 40 px, padding 14, radius 6 the same 0

Captures, 1440 and 390, in agent-coord/scratch/fru-sf-workspace/fru-245/ (they cannot be uploaded): notes-list, notes-list-fr, notes-loading, notes-none, notes-no-match, notes-failed, notes-guest, notes-member, notes-status-open, notes-hover-chip, notes-hover-row, notes-focus2, each -1440.png and -390.png.

Kit (owner fru-sf-entree, asked on the chat, added here for want of an answer)

  • Checkbox {label, checked, onChange} in ui.tsx (the HeroUI Checkbox) and .kit-checkbox in app.css: a box of 16 px with the border of a control, ink when ticked, its words at 14 px 8 px after it, on a line of 40 px. A native <input> is refused in a screen.
  • Field takes type="search".

French added

English French
Every note left on the sites of {workspace}, and where it went. Chaque note laissée sur les sites de {workspace}, et où elle est partie.
Where the notes went Où sont parties les notes
All Toutes
In Fruitback Dans Fruitback
A source that was removed Une source retirée
Search notes Chercher dans les notes
Search the text, the page or the reference Chercher le texte, la page ou la référence
Search the text or the page (a guest: the route searches no reference for them) Chercher le texte ou la page
Status Statut
Any status Tous les statuts
Only my notes Seulement mes notes
Note / Left by / Went to / When Note / Laissée par / Partie vers / Quand
Show on the page Voir sur la page
You / Former member / Verified by {site} / Anonymous / Visitor Vous / Ancien membre / Vérifié par {site} / Anonyme / Visiteur
Somebody of the workspace Quelqu’un du workspace
Not delivered yet Pas encore livrée
Delivered to {host} Livrée à {host}
Pages of notes Pages de notes
{from} to {to} of {total} {from} à {to} sur {total}
Previous / Next Précédentes / Suivantes
The notes did not load Les notes ne se sont pas chargées
Fruitback did not answer. Your notes are safe. Fruitback n’a pas répondu. Vos notes sont en sécurité.
No note yet. Notes show here as soon as someone leaves one on a site of {workspace}. Pas encore de note. Les notes s’affichent ici dès que quelqu’un en laisse une sur un site de {workspace}.
No note matches. Aucune note ne correspond.
Clear the filters Effacer les filtres
see where every note went voir où est partie chaque note

Proof

  • e2e/console-notes.spec.ts, 15 tests, on stubs at the shape of feat(worker): the list of the notes of a workspace, its filters, its search, and Send again (FRU-244) #306. They cover:
    • the values: chips in their three states, filters, table, cells, the reference link, « Send again », pages, axe;
    • every filter writing the address and reading again, and a reload keeping the view;
    • the race of the search and a list;
    • the pages, by cursor and back;
    • « Send again » refused, then sent, and disabled on its way;
    • none against failed (the alert never reads « No note yet »), and no match with « Clear the filters »;
    • the held read, and the first read;
    • a guest and a member;
    • the link from Sites;
    • 390 px in English and French.
  • console-frame (the rail has five sections, and the room of the Notes page is 1240 px, capped at what the rail leaves), console-narrow (Notes at 390 and 320), console-sites (the new link). Every other console spec passes locally.
  • console-sites and console-notes now set the clock of the browser to noon of a fixed day for the test that reads « 2 h ago ». The specs merged with feat(console): the Sites table has the six columns of the mockup and the legend of the statuses (FRU-270) #302 would have read « Yesterday » between midnight and two in the morning.

Not verified

🤖 Generated with Claude Code


Sakuga Review · Score de confiance : 4/5

Mergeable après un regard sur le constat mineur : aucun défaut bloquant n'apparaît dans le diff lu, mais le libellé d'une livraison abandonnée reste à vérifier.

Constats

  1. P2 Une livraison abandonnée s'affiche comme « Pas encore livrée » apps/console/app/routes/notes.tsx:466
    Le type IndexedNote distingue delivery.state « waiting » et « gave-up », mais NoteRow n'en lit jamais l'état : une note dont le worker a cessé d'essayer affiche le même libellé « Not delivered yet » qu'une note encore en attente. Le constat ne touche que l'affichage : le bouton « Send again » reste disponible dans les deux cas. Correction : Lire delivery.state dans NoteRow et afficher pour « gave-up » le libellé qui dit que Fruitback a arrêté d'essayer, en gardant « Send again ».
Résumé

La PR ajoute la page Notes de la console (route w/:workspace/notes), ses filtres dans l'adresse, la pagination par curseur, le renvoi d'une note non livrée et une case à cocher du kit, puis relie la page Sites à la nouvelle page. J'ai relu la route, la page, le kit et les tests E2E modifiés ; la logique de lecture et de filtrage ne montre pas de défaut certain. Je n'ai pas vérifié le worker (la route /notes et ses compteurs) ni les fichiers de traduction au-delà des clés ajoutées.

Non relu
  • apps/console/app/ui.tsx: seul le composant Checkbox est lu, pas son usage HeroUI ailleurs
  • apps/console/app/messages.fr.ts: seules les clés ajoutées sont lues, pas la couverture des écrans
  • apps/console/app/routes/notes.tsx: le comportement réel de la route /notes du worker (compteurs byPlace, undelivered, curseur) n'est pas dans le message
  • e2e/console-notes.spec.ts: lu, mais non exécuté ; le test de largeur de e2e/console-frame.spec.ts est affaibli par Math.min et n'a pas été reconstruit

Revues (1) · Dernier commit relu : fix(console): a guest's search on Notes says wh... · Niveau lite · anthropic/claude-haiku-5.5 · 0,011 $

@sakuga-review sakuga-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Score de confiance : 3/5 · ⚠️ Rien de bloquant · La PR ne peut pas être fusionnée en l'état : le filtre « Only my notes » n'est pas appliqué de façon fiable et la gestion du renvoi de notes s'appuie sur des hypothèses non vérifiées ; le reste ressemble à une page nouvelle sans régression évidente.

🔍 Une revue plus poussée est conseillée : Vérifier que la route GET /notes du worker applique bien les filtres mine, place et stage et renvoie la forme de counts attendue, car la page en dépend entièrement.
Commentez @sakuga-review review balanced (environ 5 centimes) ou @sakuga-review review heavy (10 centimes à 1 dollar).

Niveau lite · anthropic/claude-haiku-5.5 · 0,009 $ · Le résumé, le diagramme et la liste des constats sont à la fin de la description de la PR.

Comment thread apps/console/app/routes/notes.tsx
@sakuga-review sakuga-review Bot added the review: 3/5 Score de confiance de Sakuga Review (5 : prête à merger) label Oct 13, 2026
@sakuga-agent

sakuga-agent Bot commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Reply to the lite review of ddb2121 (3/5). All three findings are answered with the code; nothing changed.

1. « Only my notes » sends a value the worker may ignore (P1): the route of #306 filters on it. In apps/worker/src/console-notes.ts (#306), the route reads mine, refuses another value than 1 or true (400 invalid-mine), and turns it into author: account in the query of the index: only the notes whose author is the account come back. This PR is stacked on #306 and merges after it. Filtering in the console would filter one page out of a list paged by the worker, and would get total and the cursor wrong.

2. « Previous » keeps a wrong offset after a reload (P2): the range shown is never wrong.

  • from is written in the address with the cursor at each « Next ». After a reload, the range of the page on screen is read from it, so it is right.
  • The stack of the cursors before is not in the address. So after a reload, « Previous » goes to the first page: it deletes cursor and from (page('previous'): back === '' deletes both), and the range then reads « 1 to 8 ».
  • What is lost is the pages in between, and that is said in docs/decisions/console.md (« a reload starts that list again »). Keeping every cursor in the address would make it unbounded.

3. « Clear the filters » can run a phantom search (P2): the pending search cannot bring a filter back.

  • « Clear the filters » sets typed to ''. The effect of the search then clears the timer of the old text before it sets its own (the cleanup of useEffect([typed])).
  • The timer it sets writes q = '' from the latest address (latest.current, the fix of the race in this PR), which filter already left with no filter.
  • So at most one more write lands, and it changes nothing.
  • Held by « a filter that keeps nothing says so, and clears in one press » (the search field and the address are empty, eight rows) and by « a list chosen while the search waits its 300 ms is kept when the search lands ».

@sakuga-agent

sakuga-agent Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

@sakuga-review review balanced

@sakuga-review sakuga-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Score de confiance : 3/5 · ⚠️ Rien de bloquant · Fusion possible avec réserves : la pagination « Previous » a un défaut d'état sur rechargement ou lien partagé, le reste de la page Notes est cohérent avec ses spécifications.

Niveau balanced · anthropic/claude-sonnet-5.5 · 0,180 $ · Le résumé, le diagramme et la liste des constats sont à la fin de la description de la PR.

Comment thread apps/console/app/routes/notes.tsx
@Mheaus
Mheaus added this pull request to stack #320 October 14, 2026 08:07
Base automatically changed from mathieu/fru-244-worker-lister-les-notes-dun-workspace-filtres-recherche to main October 14, 2026 08:11
Mheaus and others added 5 commits October 14, 2026 10:34
…e they went (FRU-245)

Screen 10 of the mockup, read from the index of the notes (FRU-244): the
chips of where the notes went, a search, the lists of the site and the
status, « Only my notes », a table of the first 64 characters of each
note with its page and « Show on the page », its status, its author,
where it went with its reference, and « Send again » for an address
that did not take it. The pages follow the cursor of the route. The
filters live in the address. A read again keeps the rows on the screen,
so nothing that a pointer can reach moves while it is on its way.

The rail gets its « Notes » entry, and the Sites screen its link « see
where every note went ». The kit gets a Checkbox and a search field.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…nswers it (FRU-245)

The reference and the address of the issue are at the top of a note, a
guest gets no place at all, and the name of a REST connector is its
address. A guest reads no column « Went to ». « Show on the page » opens
the note by the id the widget gave it, or by its issue, or opens the page
alone: the id of the route is opaque.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… it (FRU-245)

Asked by the balanced review: after a reload or from a shared link, the
cursors before the page are not known. The test opens the second page by
its address, reads its range, presses « Previous », and finds the first
page with no cursor and no offset left in the address.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The route of #306, as merged, searches no reference for a guest
(SEARCHED_WITHOUT_TRACKER). The field said « or the reference » to them;
it now says « Search the text or the page ».

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@sakuga-agent
sakuga-agent Bot force-pushed the mathieu/fru-245-console-notes-toutes-les-notes-du-workspace-et-ou-elles-sont branch from 9c8553e to 0f8f118 Compare October 14, 2026 08:38
@sakuga-agent

sakuga-agent Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Rebased by hand on main, now that #306 (FRU-244) and #299 are merged. git range-diff: 95e12d6 → 9d7d3f7, 9a9416b → f010cb6, ddb2121 → ed00e73, 9c8553e → b85c761. One conflict, in e2e/console-sites.spec.ts: the comment and since() of the fixed clock, which #314 put on main in a later form. main's version is kept.

One commit on top, 0f8f118: the route as merged searches no reference for a guest (SEARCHED_WITHOUT_TRACKER). So a guest's search field now says « Search the text or the page » (French: « Chercher le texte ou la page »), and the guest test checks it. The rest of the shape of #306 is the one this page read.

Locally on 0f8f118: console unit tests 119; console-notes 16, console-sites 18, console-frame 17, console-narrow 5, console-kit 20.

The two review threads are answered in place (r4265449598, r4268279734): the range is never wrong, and a link to a later page is now held by a test. I resolve them.

@sakuga-review sakuga-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Score de confiance : 4/5 · ⚠️ Rien de bloquant · Mergeable après un regard sur le constat mineur : aucun défaut bloquant n'apparaît dans le diff lu, mais le libellé d'une livraison abandonnée reste à vérifier.

Niveau lite · anthropic/claude-haiku-5.5 · 0,011 $ · Le résumé, le diagramme et la liste des constats sont à la fin de la description de la PR.

@sakuga-review sakuga-review Bot added review: 4/5 Score de confiance de Sakuga Review (5 : prête à merger) and removed review: 3/5 Score de confiance de Sakuga Review (5 : prête à merger) labels Oct 14, 2026
@sakuga-agent

sakuga-agent Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Reply to the lite review of 0f8f118 (4/5, approved).

« Une livraison abandonnée s'affiche comme « Pas encore livrée » » (P2): true, and kept on purpose for this PR. Nothing pushed here. The two states lead to the same action: « Send again » sends both, through POST …/notes/<id>/retry, which takes waiting and gave-up alike (#306). The mockup has only one sentence for the cell, « Not delivered yet ». A sentence that tells the person that Fruitback stopped trying is new text, and it belongs with the panel of a REST connector, which already says when the worker gave up (FRU-122). Once this PR is merged, I will propose it as a small follow-up on main, not on a green PR.

@sakuga-agent
sakuga-agent Bot merged commit c933596 into main Oct 14, 2026
16 checks passed
@sakuga-agent
sakuga-agent Bot deleted the mathieu/fru-245-console-notes-toutes-les-notes-du-workspace-et-ou-elles-sont branch October 14, 2026 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: 4/5 Score de confiance de Sakuga Review (5 : prête à merger)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant