MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-440

6.15 Board filtering (wire the disabled board `[Filter]` seam)

Done
Description

Wire the board filter that the /boards toolbar reserves as a permanently disabled [Filter] button today (page.tsx: "Filter is a disabled seam here — Epic 6 wires board filtering"). Stories 3.7 / 3.8 / 4.2 / 4.5 / 4.7 all defer board filtering to "Epic 6" in prose, and the 3.8.4 over-cap banner ("refine the board filter") points at this seam — but no Epic-6 story ever owned it: Story 6.1 scoped its filter builder to the /issues navigator only, naming the board "the documented extension when a use case lands." This is that use case. (Not a bug — the disabled state is a deliberate seam; the gap is the missing story, the same shape as the board-column-admin planning gap.)

The verified shape (rung 1 — Atlassian docs). A Jira board is backed by a filter and the user narrows it with board quick-filters / its saved filter — filtering is a first-class board affordance, not navigator-only. Today the board read (boardsService.getBoard / loadColumnCards) takes no predicate and projects every card (bounded only by the 3.8 cap), so a user cannot shrink an over-cap board even though the banner tells them to.

The fix — REUSE, not rebuild. The structured filter AST + compiler (lib/filters/*, 6.1) and saved filters (lib/savedFilters/*, 6.2) are done, and the issue list already threads a compiled predicate into its read (findByProjectFiltered via resolveFilterAst). This story threads that SAME predicate into the board projection (each column shows only matching cards), mounts the SAME filter builder + saved-filter picker on the board toolbar (board-scoped, URL-addressable, reload-safe), and points the over-cap "refine filter" CTA at it. The 3.8 bounded/virtualized load + over-cap truncated flag now apply to the filtered set, and the 4.5 Scrum sprint-scope filter composes with it (narrow WITHIN the active sprint).

Plus — the missing Work type facet (6.15.5). The shipped quick filter (IssueFilterBar) is a curated four-facet subset (text · kind · status · assignee); the Work type field (WorkItemTypecode/design/test/… + the nullable Untyped bucket) was reachable only via [Advanced], so a user could not filter the board (or the /issues list) by Work type from the quick popover. The FilterAST + 6.1 registry already DEFINE and COMPILE type (enumField('type', 'type-select', { nullable, WORK_ITEM_TYPES })), so this is a missing quick-filter facet, not a grammar change — 6.15.5 adds the Work type facet group to the SHARED IssueFilterBar (so /issues gains it too), and the board reuses it.

Out of scope. The filter grammar/compiler/registry (6.1 owns type — already complete, no change) and the board read (6.15.2 threads whatever AST — type already compiles); cross-project boards (still the 3.7 Epic-6 extension); altering the Scrum scope filter (4.5). No new migration.

Verification

  • Pull the Story branch, pnpm install, pnpm prisma migrate dev (reports "No difference detected" — this story adds no schema; it reuses the 6.1 filter substrate), pnpm db:seed, pnpm dev.
  • pnpm test:coverage — Vitest (real Postgres) over the filtered board read (predicate applied per column, the cap/truncated computed over the FILTERED set, workspace + 6.4-permission scope) ≥ 90% per-file branch/fn/line.
  • The repro, now passing: sign in as zhuyue@motir.co / !QAZ1qazmoooon / motir/boards. The toolbar Filter is now enabled; open it, build a quick filter (e.g. type = Bug) or pick a saved filter → the board re-projects so every column shows only matching cards; the active filter shows as a chip/summary; Clear restores the full board. The selection survives a reload (it is in the URL).
  • Work type facet (6.15.5): open the filter — the popover now shows a Work type group (Code / Design / Test / … + Untyped) between Kind and Status; selecting e.g. Design narrows the board to design work items. The SAME facet appears on /issues (shared IssueFilterBar).
  • Over-cap CTA: on a board that exceeds the 3.8 cap, the "refine the board filter" banner’s CTA opens the filter (it no longer points at a dead seam); applying a filter that brings the set under the cap dismisses the banner.
  • Scrum compose: on a Scrum board with an active sprint, a board filter narrows WITHIN the sprint scope (4.5) — it does not show cards outside the sprint.
  • pnpm test:e2e --grep board-filter — apply a filter on a board end-to-end → only matching cards across columns → clear restores.
  • a11y: the board filter affordance (closed / open builder / saved-filter picker / active chips / no-results board) passes the strict axe sweep; fully keyboard-operable; colour via --el-*, shape via element-semantic tokens; next-intl (en + zh).