The assignee panel is the card's people, and a search for anybody else #225
No reviewers
Labels
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Grandiras/Ponente!225
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "assignee-menu"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The card dialog's assignee panel used to show every member as a chip, with the card's people ticked. It grew with the deployment rather than with the card. On a 40-member test deployment it was more than half of the dialog's elements (117 of 228), and finding one name meant reading all of them. This ships the redesign the canvas has carried since the 2026-08-29 UX pass (Card Modal · assignee redesign (not built)).
What changes
BoardService.SharedWithAsync, which returns null for a board open to everybody (no query in that case). Banned members are not offered either. Only the menu narrows: a name already on a card keeps its chip, the same way a banned member's does.js/assignee-menu.js). On a desktop it scrolls just far enough to show the menu's foot. On a phone it brings the menu to the top, where the keyboard can't cover it. The search field only gets focus automatically with a fine pointer, so a phone keyboard doesn't pop up uninvited.BoardPagenow asks for everything before the dialog exists. An await in a Blazor event handler is a render, so the dialog used to appear without its move panel and grow one a query later, during its entrance. This connects to #224.Canvas
The two not built frames were compared against the app and replaced with traced Card Modal · Assign menu frames (light and dark), showing the menu searched down to two people. Card Modal (light and dark) and Phone — Card modal are retraced.
capture.mjsandframes.shknow the new state. The saveddesign/Ponente.freeis in the second commit. The archive drops two images, and they were only the deleted frames' comment avatars.Tests
permissions.spec.jsnow covers:Runs:
tools/verify.sh e2e:permissions,board,calendar,calendar-all,mobile,changelogandaccessspecs, 62 passed.permissions.spec.jsagain with--repeat-each=2: passed.dotnet test: 560 passed.Docs:
docs/ui.md(the picker's reasoning, and the read-only dialog's Assign me),docs/boards-and-permissions.md(moves, the read-only dialog, whatpermissions.specpins) anddocs/frontend.md(the canvas status and the new JS module). Changelog entry added in both locales.Merge note: this and #224 both touch
app.css,docs/ui.mdand the Unreleased changelog block, so whichever merges second needs its conflicts resolved. Each changelog block needs both bullets.🤖 Generated with Claude Code
🤖 Reviewing this PR — the review will link the full session transcript.
✅ Verdict: approve — no blocking issues found.
A careful, well-tested redesign that matches its own documentation. Verified in depth:
SharedWithAsyncis semantically right. For a restricted board it returns exactly theBoardMembersDIDs, which is the same setBoardAccess.VisibleTolets through (no admin bypass — board admins are on grant rows by construction);nullforEveryoneand an empty set for a missing board are both the honest answers. Only the menu narrows; the server-sideCanSetAssigneesrule is untouched, so this is a UI narrowing, as documented, not a new gate — and no new data exposure, sinceAvailableMembersalready reached the dialog.OpenCard/StartNewCardnow computecanEdit, boards and the shared-with set before_modalCardis set, andCloseModalresets the new state. Calendar asks per-card-board (_modalBoardId), which is the right board on the general calendar. (Nit, no action needed: Calendar's call always pays the visibility query even for anEveryoneboard, whereBoardPage's helper short-circuits — one cheap query on dialog open only.)CanAssignSelfcorrectly disappears once the reader is on the card (the chip's × takes over) and degrades safely for the empty-OnlyDidcase; static-vs-pressable chip rendering matches the old rule..modal-bodyis the scroll container and the absolutely positioned menu extends its scrollable overflow, so thescrollByapproach is sound; the 599px phone breakpoint matches the app's own media queries; the fixed catcher has no transformed ancestor by the time the menu opens (the modal'sslideUphas finished), soinset: 0covers the viewport; catcher z 20 under menu z 30 with the add button deliberately under the catcher reproduces the column filter's toggle shape. The ≤6px measurement error from the entrance animation is inside the 12px gap.DisposeAsynccalls the idempotentbase.Dispose(), so the locale subscription is dropped whether Blazor calls one or both; the module import follows the codebase's convention;modal-escape.js's<body>-focused guard doesn't collide with the search field's focus.assignees.more/.onepair; the e2e premises hold (new boards are createdRestrictedwith only the creator, so the own-board "not offered" assertion is testing what it claims).One non-blocking correctness bug in the menu's keyboard path is filed as a line comment (Enter on a focused row double-toggles); the type-then-Enter flow the PR describes is unaffected.
Known follow-ups the PR already discloses, for the record: the
Ponente.freecanvas commit trails this one, and the merge note about #224 touchingapp.css,docs/ui.mdand the Unreleased changelog block stands — whichever merges second needs both changelog bullets.⏱ 11m 11s · 20 steps · $0.1010 · tokens: 376.7k in / 35.6k out (+804.5k cache-read) · Go: 5h 10% · wk 78% · mo 72%
📜 Full session transcript
@ -41,0 +67,4 @@role="dialog"aria-label="@L.T("modal.assignees")"@ref="_menu"@onkeydown="OnMenuKeyDown"The Enter handler sits on the
.assignee-menudiv, so it also fires for a keydown that bubbles up from a focused row button (reached by Tab from the search field). With text in the field, one Enter press then toggles twice: the handler togglesMatches().FirstOrDefault(), and the browser's native button activation fires a click that toggles the focused row. If the focused row is the first match the two cancel out — Enter visibly does nothing; if it isn't, two members change with one keypress. Escape is fine where it is (it must catch the whole menu), but Enter belongs on the search input itself (@onkeydownon the<input>), where there is no native click to double-fire. Narrow keyboard-only path — the type-then-Enter, click and Escape flows all behave and are pinned by the spec — so not blocking, but worth fixing while the component is fresh.🤖 Reviewing this PR — the review will link the full session transcript.
💬 Verdict: comment — the reviewer did not submit a structured review; the raw output follows.
(empty)
⏱ 1m 59s · 0 steps · $0.0000 · tokens: 0 in / 0 out (+0 cache-read) · Go: 5h 10% · wk 78% · mo 72%
🤖 Reviewing this PR — the review will link the full session transcript.
✅ Verdict: approve — no blocking issues found.
Re-review after new commits: the one issue from my previous pass is resolved. The Enter-in-menu double-toggle is fixed by moving the handler onto the search input and leaving only Escape on the menu container, with a new spec section pinning the Tab-to-row → Enter path (one toggle per keypress). The docs and inline remarks were updated to match. Everything else from the first pass stands as verified:
SharedWithAsyncsemantics, the render-once dialog setup inBoardPage/Calendar, the read-only dialog's Assign me path, the JS/CSS scroll and focus behaviour, the disposal chain, locale keys in both languages, and the canvas/test coverage. Remaining notes are all non-blocking and already disclosed by the PR itself: thePonente.freecanvas commit trails the code commit, and the merge conflict with #224 overapp.css,docs/ui.mdand the Unreleased changelog block needs resolving in whichever PR lands second. Good to merge.⏱ 1m 0s · 0 steps · $0.0000 · tokens: 0 in / 0 out (+0 cache-read) · Go: 5h 11% · wk 78% · mo 72%
@ -41,0 +77,4 @@@ref="_search"@bind="_query"@bind:event="oninput"@onkeydown="OnSearchKeyDown" />Fixed since the last pass: Enter is handled on the field itself, and the menu-level keydown handler now only catches Escape, so a focused row's Enter is just its native click — one keypress toggles one person. The new spec assertions ("takes them off, once" / "and the next puts them back") pin exactly the path that used to double-fire. Thanks for the clean fix and the test.
🔧 Let opencode-bot fix this — tick the box and opencode-bot picks up the review above, on this PR branch. Nothing runs until you do.