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-445

6.15.5 Quick-filter Work type facet — add the WorkItemType facet to the shared IssueFilterBar (facet state + facet→AST + i18n); the board + /issues both reuse it

Done
Description

Estimate: 30m · Depends on: 6.15.1

Add the Work type facet the quick filter is missing. The shipped quick-filter popover (IssueFilterBar) exposes text · kind · status · assignee; the WorkItemType field (type) is fully DEFINED + COMPILED in the 6.1 registry (enumField('type', 'type-select', { nullable: true, valueWhitelist: WORK_ITEM_TYPES })) and is reachable via the [Advanced] builder — but NOT as a quick facet, so a user cannot filter by Work type from the popover. Add the facet to the SHARED component (the board and the /issues navigator both gain it). No grammar/registry/compiler change and no board-read changetype already compiles; this is purely the quick-filter facet + its state + the facet→AST conversion.

Facet state (lib/issues/issueListFilter.ts): add types: WorkItemType[] (+ an includeUntyped flag for the registry’s nullable empty bucket) to the IssueFilter shape, mirroring kinds/statuses — the DEFAULT value, the toggleType helper, and the IssueFilterParams URL round-trip (encode/decode), so the facet is shareable + reload-safe.

Facet UI (app/(authed)/issues/_components/IssueFilterBar.tsx): add the Work type group between Kind and Status per the 6.15.1 design — a role="listbox" aria-multiselectable of the 10 WORK_ITEM_TYPES (leading glyph = WorkItemTypeIcon in the --el-type-* hue, label labels.workItemType.*, trailing accent Check) + the Untyped row (the IS-NULL bucket). The active-count badge + the applied summary chips include the Work type values.

Facet→AST (lib/filters/ast.ts facetFilterToAst): push a { field: 'type', operator: 'is_any_of', value } condition from the selected types, and map the Untyped selection to the registry’s empty-bucket token (the same is_empty pattern assignee’s Unassigned uses) so the basic→advanced upgrade stays lossless. i18n en + zh (the labels.workItemType.* keys exist; add the facet group label, e.g. filterWorkType).

Acceptance criteria

  • The quick-filter popover shows a Work type facet (the 10 types + Untyped); selecting types narrows the read to matching work types; the facet round-trips through the URL/params (reload-safe) and feeds the active-count badge + applied chips.
  • facetFilterToAst emits the type is_any_of condition (and maps Untyped → the empty-bucket operator); the basic→advanced upgrade drops no condition (lossless), proven by a unit test over the new facet.
  • NO change to the filter grammar/registry/compiler or the board read (they already support type); matches the 6.15.1 design (reuses WorkItemTypeIcon — no hand-rolled UI); colour via --el-*, shape via element-semantic tokens; axe-clean; en + zh; component + unit tests; pnpm test:coverage ≥ 90% on changed files.

Context refs

  • design/boards/board-filter.mock.html (6.15.1 — the Work type facet layout, panel 1) + design/boards/design-notes.md (the "NET-NEW in 6.15 — the Work type facet" note)
  • app/(authed)/issues/_components/IssueFilterBar.tsx (the popover to extend) + lib/issues/issueListFilter.ts (the IssueFilter facet state + the param round-trip)
  • lib/filters/ast.ts (facetFilterToAst) + lib/filters/registry.ts (the type field def — already complete) + lib/issues/executorDefaults.ts (WORK_ITEM_TYPES)
  • lib/issues/workItemTypeMeta.ts (WORK_ITEM_TYPE_META — glyph + hue) + components/issues/WorkItemTypeIcon.tsx
  • messages/en.json + messages/zh.json (labels.workItemType.* + the new filterWorkType group label); motir-core/CLAUDE.md (4-layer; colour/shape tokens)