tickets: file T-343 — ticket-panel type filter chips

Bug / Ticket / Epic / Initiative toggle chips at the top of the tickets
panel, all on by default. Single-click toggles a type; double-click solos
it (chart-legend pattern), double-click again restores all; last-off
resets to all-on. Filed under the UI epic T-276 with a Frame0 wireframe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 17:54:40 +02:00
co-authored by Claude Opus 4.8
parent 0a6d5a5ef8
commit 91b7dbef4d
4 changed files with 287 additions and 0 deletions
@@ -3435,3 +3435,4 @@ SCOPE CLARIFICATION (2026-06-10, from user):
- Net behaviour: a fan-out of N agents -> N distinct collapsed cards, each containing its own complete run; surrounding non-agent tool calls still group into their normal Activity card.
Test additions: (a) two concurrent agents whose sidechain items interleave -> each agent''s run items land under its own card, none cross-attributed; (b) an unattributable sidechain item (no parent_tool_use_id, broken chain) is NOT swept into the nearest agent''s card; (c) regression: consecutive Bash/Read calls still form one Activity cluster.', NULL, '2026-06-10 15:28:52', '2026-06-10 15:28:52', '2026-06-10 15:28:52', NULL, '0352b24d88a4bf24ede628880fe9b502', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4FDREHRYRR7B9ER72KCQKC', 'status', 'backlog', 'in_progress', NULL, '2026-06-10 15:54:23', '2026-06-10 15:54:23', '2026-06-10 15:54:23', NULL, '8061a1d449acdb1e467239de0dce5d1d', 2) ON CONFLICT(hash) DO NOTHING;
+56
View File
@@ -2903,3 +2903,59 @@ SCOPE CLARIFICATION (2026-06-10, from user):
- Net behaviour: a fan-out of N agents -> N distinct collapsed cards, each containing its own complete run; surrounding non-agent tool calls still group into their normal Activity card.
Test additions: (a) two concurrent agents whose sidechain items interleave -> each agent''s run items land under its own card, none cross-attributed; (b) an unattributable sidechain item (no parent_tool_use_id, broken chain) is NOT swept into the nearest agent''s card; (c) regression: consecutive Bash/Read calls still form one Activity cluster.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-10 15:26:33', '2026-06-10 15:28:52', NULL, '05bc2daa013de8cf163cdd54c6014f07', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4FDREHRYRR7B9ER72KCQKC', 'story', '06FB0TNQM5TWC00GW0P3X02HZW', 'Ticket panel: per-type filter chips (Bug / Ticket / Epic / Initiative)', 'Add a row of type-filter toggle chips at the top of the tickets panel, directly below the "Filter tickets…" box (no section header — the chips read on their own). One chip per ticket type the user thinks in: Bug, Ticket, Epic, Initiative. All four ON by default.
## Behaviour
- Single-click a chip toggle that type in/out of the list.
- Double-click a chip isolate (solo) that type: turns it ON and all others OFF. Double-click the same chip again restore all to ON. This is the chart-legend solo pattern (Plotly/Tableau/Grafana) learnable and fully reversible.
- Last-off resets to all-on: disabling the final remaining type snaps all chips back ON. An empty type filter means "no filter", so the list is never mysteriously blank.
- Tooltip per chip: "Click to toggle · double-click to isolate".
## Type mapping (pql chip)
pql ticket types are initiative, epic, story, task, bug (see lib/builtin/tickets/src/ticket_colors.dart). The four chips map as:
- Bug bug
- Ticket story + task (leaf work items)
- Epic epic
- Initiative initiative
Each chip carries its type-colored dot + border using TicketTypeColors (bug #E87D7D, story/task green/grey, epic #78A0F8, initiative #C792EA).
## Filtering
- Type filter is ANDed with the existing text filter in _TicketsViewState (lib/builtin/tickets/src/tickets_view.dart): a ticket shows only if its type is enabled AND it matches the text filter.
- When the type filter hides all items in a status section, that section collapses out (same as text-filter behaviour today).
## Implementation notes
- Active-chip visual: filled tint + type-colored border (active) vs muted/no border (inactive) reuse the _Toggle pattern from lib/builtin/search/src/search_panel_view.dart and ClideTappable.
- Persist nothing across sessions for v1 (always all-on on load); revisit if requested.
- a11y: Semantics(button, toggled) per chip, mirroring the search-panel toggle.
## Wireframe
docs/design/wireframes/tickets/ticket-type-filters.json (+ .png export)', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-10 15:54:09', '2026-06-10 15:54:09', NULL, '3e7c03bbb536a4c2d45f770abfee8b13', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4FDREHRYRR7B9ER72KCQKC', 'story', '06FB0TNQM5TWC00GW0P3X02HZW', 'Ticket panel: per-type filter chips (Bug / Ticket / Epic / Initiative)', 'Add a row of type-filter toggle chips at the top of the tickets panel, directly below the "Filter tickets…" box (no section header — the chips read on their own). One chip per ticket type the user thinks in: Bug, Ticket, Epic, Initiative. All four ON by default.
## Behaviour
- Single-click a chip toggle that type in/out of the list.
- Double-click a chip isolate (solo) that type: turns it ON and all others OFF. Double-click the same chip again restore all to ON. This is the chart-legend solo pattern (Plotly/Tableau/Grafana) learnable and fully reversible.
- Last-off resets to all-on: disabling the final remaining type snaps all chips back ON. An empty type filter means "no filter", so the list is never mysteriously blank.
- Tooltip per chip: "Click to toggle · double-click to isolate".
## Type mapping (pql chip)
pql ticket types are initiative, epic, story, task, bug (see lib/builtin/tickets/src/ticket_colors.dart). The four chips map as:
- Bug bug
- Ticket story + task (leaf work items)
- Epic epic
- Initiative initiative
Each chip carries its type-colored dot + border using TicketTypeColors (bug #E87D7D, story/task green/grey, epic #78A0F8, initiative #C792EA).
## Filtering
- Type filter is ANDed with the existing text filter in _TicketsViewState (lib/builtin/tickets/src/tickets_view.dart): a ticket shows only if its type is enabled AND it matches the text filter.
- When the type filter hides all items in a status section, that section collapses out (same as text-filter behaviour today).
## Implementation notes
- Active-chip visual: filled tint + type-colored border (active) vs muted/no border (inactive) reuse the _Toggle pattern from lib/builtin/search/src/search_panel_view.dart and ClideTappable.
- Persist nothing across sessions for v1 (always all-on on load); revisit if requested.
- a11y: Semantics(button, toggled) per chip, mirroring the search-panel toggle.
## Wireframe
docs/design/wireframes/tickets/ticket-type-filters.json (+ .png export)', 'in_progress', 'medium', NULL, NULL, NULL, '2026-06-10 15:54:09', '2026-06-10 15:54:23', NULL, 'f5614424c00196aabab868db75483bf2', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
@@ -0,0 +1,230 @@
{
"name": "Ticket Panel — Type Filters",
"shapes": {
"panel": {
"type": "Rectangle",
"left": 80, "top": 60, "width": 400, "height": 520,
"fillColor": "#0d0d11",
"strokeColor": "#23232b",
"corners": [6, 6, 6, 6]
},
"filter-box": {
"type": "Rectangle",
"parent": "panel",
"left": 96, "top": 76, "width": 368, "height": 30,
"fillColor": "#16161c",
"strokeColor": "#2c2c36",
"corners": [5, 5, 5, 5]
},
"filter-icon": {
"type": "Text",
"parent": "filter-box",
"left": 108, "top": 84,
"text": "⌕",
"fontColor": "#5b5b66",
"fontSize": 13
},
"filter-text": {
"type": "Text",
"parent": "filter-box",
"left": 126, "top": 85,
"text": "Filter tickets…",
"fontColor": "#5b5b66",
"fontSize": 12
},
"refresh-icon": {
"type": "Text",
"parent": "filter-box",
"left": 442, "top": 84,
"text": "↻",
"fontColor": "#5b5b66",
"fontSize": 13
},
"chip-bug": {
"type": "Rectangle",
"parent": "panel",
"left": 96, "top": 118, "width": 66, "height": 24,
"fillColor": "#211519",
"strokeColor": "#E87D7D",
"corners": [4, 4, 4, 4]
},
"chip-bug-dot": {
"type": "Ellipse",
"parent": "chip-bug",
"left": 104, "top": 126, "width": 8, "height": 8,
"fillColor": "#E87D7D",
"strokeColor": "#E87D7D"
},
"chip-bug-label": {
"type": "Text",
"parent": "chip-bug",
"left": 117, "top": 124,
"text": "Bug",
"fontColor": "#e8e8ee",
"fontSize": 11
},
"chip-ticket": {
"type": "Rectangle",
"parent": "panel",
"left": 168, "top": 118, "width": 78, "height": 24,
"fillColor": "#15191d",
"strokeColor": "#7DD3A8",
"corners": [4, 4, 4, 4]
},
"chip-ticket-dot": {
"type": "Ellipse",
"parent": "chip-ticket",
"left": 176, "top": 126, "width": 8, "height": 8,
"fillColor": "#7DD3A8",
"strokeColor": "#7DD3A8"
},
"chip-ticket-label": {
"type": "Text",
"parent": "chip-ticket",
"left": 189, "top": 124,
"text": "Ticket",
"fontColor": "#e8e8ee",
"fontSize": 11
},
"chip-epic": {
"type": "Rectangle",
"parent": "panel",
"left": 252, "top": 118, "width": 68, "height": 24,
"fillColor": "#14171f",
"strokeColor": "#78A0F8",
"corners": [4, 4, 4, 4]
},
"chip-epic-dot": {
"type": "Ellipse",
"parent": "chip-epic",
"left": 260, "top": 126, "width": 8, "height": 8,
"fillColor": "#78A0F8",
"strokeColor": "#78A0F8"
},
"chip-epic-label": {
"type": "Text",
"parent": "chip-epic",
"left": 273, "top": 124,
"text": "Epic",
"fontColor": "#e8e8ee",
"fontSize": 11
},
"chip-init": {
"type": "Rectangle",
"parent": "panel",
"left": 326, "top": 118, "width": 86, "height": 24,
"fillColor": "#1a151f",
"strokeColor": "#C792EA",
"corners": [4, 4, 4, 4]
},
"chip-init-dot": {
"type": "Ellipse",
"parent": "chip-init",
"left": 334, "top": 126, "width": 8, "height": 8,
"fillColor": "#C792EA",
"strokeColor": "#C792EA"
},
"chip-init-label": {
"type": "Text",
"parent": "chip-init",
"left": 347, "top": 124,
"text": "Initiative",
"fontColor": "#e8e8ee",
"fontSize": 11
},
"divider": {
"type": "Line",
"parent": "panel",
"left": 96, "top": 158, "width": 368, "height": 1,
"strokeColor": "#23232b"
},
"section-header": {
"type": "Text",
"parent": "panel",
"left": 98, "top": 172,
"text": "▾ BACKLOG · 61",
"fontColor": "#6b6b76",
"fontSize": 10
},
"card-1": {
"type": "Rectangle",
"parent": "panel",
"left": 96, "top": 192, "width": 368, "height": 56,
"fillColor": "#0d0d11",
"strokeColor": "#23232b",
"corners": [4, 4, 4, 4]
},
"card-1-dot": {
"type": "Ellipse",
"parent": "card-1",
"left": 108, "top": 204, "width": 8, "height": 8,
"fillColor": "#C792EA",
"strokeColor": "#C792EA"
},
"card-1-id": {
"type": "Text",
"parent": "card-1",
"left": 122, "top": 200,
"text": "T-8",
"fontColor": "#e8e8ee",
"fontSize": 11
},
"card-1-title": {
"type": "Text",
"parent": "card-1",
"left": 108, "top": 220,
"text": "Tier 6 — extension API, settings, theming, builds",
"fontColor": "#b8b8c2",
"fontSize": 11
},
"card-2": {
"type": "Rectangle",
"parent": "panel",
"left": 96, "top": 254, "width": 368, "height": 56,
"fillColor": "#0d0d11",
"strokeColor": "#23232b",
"corners": [4, 4, 4, 4]
},
"card-2-dot": {
"type": "Ellipse",
"parent": "card-2",
"left": 108, "top": 266, "width": 8, "height": 8,
"fillColor": "#E87D7D",
"strokeColor": "#E87D7D"
},
"card-2-id": {
"type": "Text",
"parent": "card-2",
"left": 122, "top": 262,
"text": "T-19",
"fontColor": "#e8e8ee",
"fontSize": 11
},
"card-2-title": {
"type": "Text",
"parent": "card-2",
"left": 108, "top": 282,
"text": "Filter box loses focus on refresh tick",
"fontColor": "#b8b8c2",
"fontSize": 11
},
"caption": {
"type": "Text",
"parent": "panel",
"left": 96, "top": 540,
"text": "All four type filters ON by default — tap a chip to toggle that type out.",
"fontColor": "#56565f",
"fontSize": 10
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB