Repository navigation
feat(webapp): filter the board and keep its state in the url - #536
Merged
Merged
Conversation
Congrats! CodSpeed is installed 🎉
You will start to see performance impacts in the reports once the benchmarks are run from your default branch.
|
NathaelB
force-pushed
the
feature/planification-ws6
branch
from
September 18, 2026 12:40
ff1b29d to
ffd55ba
Compare
Contributor
Author
|
🚀 Preview deployed: https://pr-536.mestier.fr Synced revision |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #467. Refs #461. The last of seven. A member narrows the board to one project — or one assignee, one label, one search — and the narrowed board is a URL they can bookmark and share.
Written on top of WS5's branch (#517, since merged), which is where the board itself lives. Now replayed onto
mainwithgit rebase --onto, so it carries this workstream's single commit and nothing of WS5's — the conflicts GitHub reported came entirely from the stale base, and a plaingit rebase mainwould have tried to replay WS5 over its own squash-merge. Re-verified after the rebase: 180 files, 1864 tests, all green, and the filtered drop/rollback guard re-run on its own againstmain's copy of the board.The trap this workstream had to avoid
useMoveBoardTaskwrites its optimistic update straight intouseBoardTasks's cache entry — that is why the board has its own query instead of reusinguseRootTasks, and WS5 said so in a doc comment. Add filters without putting them in the query key and the optimistic write lands on the wrong entry: cards jump or vanish on drop, and the existing rollback test does not catch it, because it runs unfiltered.Both hooks now build their request from one
boardTasksRequest(organizationId, filters), so the write names the entry the screen reads.This is verified by mutation rather than by inspection: pointing the mutation's key at empty filters makes exactly one test fail —
moves optimistically and rolls back, filters and all— and leaves the other 85 in the module green. That is the proof the new test is load-bearing and that the old one would not have covered it. Reproduced independently before merge.Every filter is server-side
No client-side filtering of a fetched page: a filter that only hides loaded rows lies about the column counts, which is the one number a board is read for. The only
.filter()over the fetched tasks groups them into their five columns by status — presentation, not filtering.The bar: project picker first and visually dominant, then assignee, then label, then search, plus an "unscheduled only" toggle.
customer_idis accepted by the endpoint but has no control, because #467 did not ask for one; it would drop straight into the schema.The URL is the single source of truth
boardSearchSchemafollowsplanningSearchSchema's register — every field carries.catch(...), plus an outer.catchso "never throws" holds even for a non-object search. A malformed value falls back on its own field only:?project_id=project-1&unscheduled=peut-etre&q=validates to{ project_id: 'project-1' }and the board renders.Two of the folds are behavioural rather than cosmetic, and both come from a WS4 rule that is invisible in the client's types:
q=present-but-empty would match every title and widen the listing;unscheduled=falsewould widen it for nothing.Both fold to
undefined, sounscheduledis aliteral(true): the toggle is either on and in the address, or off and absent.A cleared board leaves a clean address.
EMPTY_BOARD_FILTERScarries all five keys explicitlyundefinedand "Effacer les filtres" navigates with that object rather than{}— a key present-and-undefinedis dropped from the URL, a key merely omitted keeps its old value. Asserted assearchStr() === ''.Search commits on submit, not per keystroke:
qlives in the address, and one history entry per character would defeat the back button. The input is uncontrolled and keyed on the appliedq, so back and forward still reset it — the half-typed word is DOM state, the applied filter is the URL.The widening, stated rather than hidden
WS4's
GET /taskslists roots when no filter is present and every matching task at any depth as soon as one is. Deliberate — a project's board must show its subtasks — but it means applying a filter moves the column counts, which would read as a bug to anyone watching.So the board says it: while any filter is on, the toolbar states that matching subtasks are shown as cards alongside root tasks. The test harness implements the widening rule independently of the app, so the behaviour is exercised rather than assumed — unfiltered, En cours holds one card; filtered on a project, two, the second a subtask of a Backlog card.
Verification
Baseline on WS5's branch was 1744 across 177 files: +3 files, +31 tests, nothing broken.
Note for whoever runs this on a loaded machine:
maincurrently fails about five tests under load —planning-calendar-feature,task-sheet-feature,absences-overview-feature,team-list-feature,workflow-canvas-feature— each passing in isolation. That flakiness predates this chantier and is not this branch's.Deviations
ui/board-ui.tsxgains two optional props,toolbarandemptyReason. Not in this workstream's declared file list, though not forbidden either: the bar has to live inside the page shell WS5's UI owns, and an empty filtered board needs to say why it is empty. No behaviour change when both are omitted.<select>for the three pickers, not the shadcn combobox and notplanning/ui/assignee-picker.tsx— that one is a multi-select popover built for the task form, and the board wants one value at a time.projects/ui/project-form-dialog.tsxis the precedent for a plain select, and the reason is in the component's doc comment.Open
useProjects. Past that the picker truncates — the same cap the projects list already documents as out of scope. A deep link to an unlisted project still filters correctly, but the picker reads "Projet introuvable".