multica/_features/issue-board-polish.json
Naiyuan Qing 9127e543d5 feat: add event bus, WS workspace isolation, and global store migration
- Add internal event bus (server/internal/events/) with synchronous
  pub/sub and panic isolation per listener
- Upgrade WebSocket Hub to workspace-scoped rooms with JWT auth
  and membership verification on connect
- Add 10 new WS event types (comment CRUD, inbox read/archive,
  agent create/delete, workspace/member events)
- Refactor all handlers and TaskService to publish events via Bus
  instead of direct Hub.Broadcast calls
- Add WS broadcast listener that routes events to correct workspace
- Frontend: WSClient sends token + workspace_id on connect with
  auto-reconnect refetch
- Frontend: centralized useRealtimeSync hook dispatches all WS
  events to global Zustand stores
- Migrate issues and inbox pages from local useState to global
  useIssueStore/useInboxStore
- Make store addIssue/addItem idempotent to prevent duplicates
- Remove dead packages/hooks/src/use-realtime.ts
- Add feature tracking files for 4 planned features

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 10:08:27 +08:00

102 lines
5.3 KiB
JSON

{
"id": "issue-board-polish",
"name": "Issue Board & Detail Polish",
"status": "designing",
"createdAt": "2026-03-25",
"completedAt": null,
"description": "Fix drag-drop data consistency bugs, complete issue detail page interactions, and polish board/list views to Linear MVP quality with consistent shadcn UI.",
"currentState": "Board view has 5 columns (missing blocked), drag-drop has 3 data consistency bugs, issue detail title/description not editable, create dialog missing assignee/due date fields, comments lack optimistic updates and author-only edit/delete.",
"decisions": [
"Board shows 6 columns: backlog, todo, in_progress, in_review, done, blocked. Cancelled issues visible via filter only.",
"Drag-drop revert respects current filter params",
"WS issue events check against current filter before adding to view",
"AbortController used for filter change requests to prevent race conditions",
"Issue title: click-to-edit inline input, blur/Enter saves, Escape cancels",
"Issue description: click-to-edit textarea, blur saves",
"Comment creation uses optimistic update (show immediately, confirm on API response)",
"Comment edit/delete only by author or workspace admin (backend enforced)",
"All loading/empty/error states use shadcn Skeleton and consistent patterns"
],
"tasks": [
{
"task": "Fix: Board drag-drop revert respects active filters",
"done": false,
"scope": "When drag-drop API call fails, revert calls listIssues with current filterStatus and filterPriority params instead of bare { limit: 200 }."
},
{
"task": "Fix: WS issue events respect current filter",
"done": false,
"scope": "issue:created handler checks if new issue matches filterStatus/filterPriority before adding. issue:updated handler removes issue from view if it no longer matches filter."
},
{
"task": "Fix: Filter change race condition with AbortController",
"done": false,
"scope": "useEffect for filter changes creates AbortController, passes signal to fetch, aborts previous request on new filter change. Stale responses ignored."
},
{
"task": "Fix: Board add blocked column, fix empty column drop target",
"done": false,
"scope": "visibleStatuses includes 'blocked'. All columns have min-h-[200px] for reliable drop target. Cancelled issues not shown as column but accessible via filter."
},
{
"task": "Fix: Board card click vs drag conflict",
"done": false,
"scope": "When isDragging is true, Link inside card has pointer-events-none to prevent navigation during drag."
},
{
"task": "Fix: List view status group order matches STATUS_ORDER",
"done": false,
"scope": "List view groups issues using STATUS_ORDER constant instead of hardcoded different order."
},
{
"task": "Create Issue dialog: add Assignee and Due Date fields",
"done": false,
"scope": "Dialog includes AssigneePicker and date picker (Calendar popover). Fields passed to api.createIssue(). Existing StatusPicker and PriorityPicker remain."
},
{
"task": "Issue detail: inline editable title",
"done": false,
"scope": "Title renders as h1. Click transforms to Input. Blur or Enter calls api.updateIssue with optimistic update. Escape reverts. Empty title rejected."
},
{
"task": "Issue detail: inline editable description",
"done": false,
"scope": "Description renders as paragraph. Click transforms to Textarea. Blur saves with optimistic update. Empty description allowed (clears field)."
},
{
"task": "Issue detail: listen to issue:updated WS event",
"done": false,
"scope": "Detail page subscribes to issue:updated. When event.issue.id matches current issue, merges update into local state (unless user is actively editing that field)."
},
{
"task": "Issue detail: acceptance criteria and context refs always addable",
"done": false,
"scope": "Show 'Add acceptance criteria' and 'Add context reference' buttons even when arrays are empty. Click reveals input field."
},
{
"task": "Comment: optimistic create",
"done": false,
"scope": "On submit, immediately append comment with temp ID and muted styling. On API success, replace temp with real. On API error, remove temp and show toast."
},
{
"task": "Comment: backend author-only edit/delete",
"done": false,
"scope": "UpdateComment and DeleteComment in comment.go load comment first, verify author_id matches request user OR user is workspace admin. Return 403 otherwise."
},
{
"task": "Comment: hover timestamp shows full date tooltip",
"done": false,
"scope": "Relative time ('2h ago') wrapped in shadcn Tooltip showing full ISO-formatted date on hover."
},
{
"task": "Issue delete: cancel running tasks first",
"done": false,
"scope": "DeleteIssue handler calls TaskService.CancelTasksForIssue before deleting issue to prevent FK constraint errors."
},
{
"task": "UI: consistent loading/empty/error states across issues pages",
"done": false,
"scope": "Board empty columns show 'No issues' muted text. List empty groups hidden. Initial load uses Skeleton. Error shows toast + retry. Filter with no results shows 'No matching issues' with clear filter link."
}
]
}