multica/_features/inbox-notifications.json
Naiyuan Qing 19504a217c chore: update feature tracking — all tasks done
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:16:15 +08:00

81 lines
3.9 KiB
JSON

{
"id": "inbox-notifications",
"name": "Inbox & Notifications",
"status": "done",
"createdAt": "2026-03-25",
"completedAt": "2026-03-25",
"description": "Complete inbox notification system: sidebar unread badge, notification triggers for all key actions, archive UI, issue navigation from notifications, and real-time sync across tabs.",
"currentState": "All tasks done. Backend: unread-count endpoint, comment notifies assignee, status change notifies creator, unassign notifies old assignee, mark read/archive broadcast WS events. Frontend: SDK types fixed, sidebar unread badge, View Issue link, archive button, real-time sync via useRealtimeSync. UI polish complete: inbox loading/empty states use shadcn components.",
"decisions": [
"Inbox uses useInboxStore from global store (not page-local useState)",
"Sidebar badge reads unread count from store, updated by WS events",
"Backend adds GET /api/inbox/unread-count endpoint using existing CountUnreadInbox SQL query",
"Mark read and archive broadcast WS events (inbox:read, inbox:archived) for cross-tab sync",
"New notification triggers: comment on assigned issue, status change notifies creator, unassign notifies old assignee",
"SDK markInboxRead and archiveInbox return Promise<InboxItem> not Promise<void>",
"Inbox detail shows View Issue link when issue_id present, and Archive button",
"3 tasks auto-completed by infra migration: WS events, TS types, realtime sync"
],
"tasks": [
{
"task": "Backend: Add GET /api/inbox/unread-count endpoint",
"done": true,
"scope": "CountUnreadInbox handler using existing SQL query. Returns { count: number }. Route: GET /api/inbox/unread-count."
},
{
"task": "Backend: Broadcast WS events for mark read and archive",
"done": true,
"scope": "Auto-completed by infra migration. inbox.go publishes EventInboxRead/EventInboxArchived."
},
{
"task": "Backend: Add notification trigger for comment on assigned issue",
"done": true,
"scope": "CreateComment: if issue has member assignee != commenter, create inbox item type 'mentioned' with comment body."
},
{
"task": "Backend: Status change notifies creator in addition to assignee",
"done": true,
"scope": "UpdateIssue: on status change, notify creator if member and != changer. Dedup check: skip if creator is also assignee."
},
{
"task": "Backend: Unassign notifies old assignee",
"done": true,
"scope": "UpdateIssue: on assignee change, notify old assignee if member and != changer with 'Unassigned from: {title}'."
},
{
"task": "SDK: Fix markInboxRead and archiveInbox return types",
"done": true,
"scope": "Changed from Promise<void> to Promise<InboxItem>. Added getUnreadInboxCount() method."
},
{
"task": "Frontend: Add WS event types for inbox:read and inbox:archived",
"done": true,
"scope": "Auto-completed by infra migration. Types and payloads in events.ts."
},
{
"task": "Frontend: Sidebar unread badge",
"done": true,
"scope": "Sidebar reads unread count from useInboxStore. Shows badge pill next to Inbox label. Capped at 99+."
},
{
"task": "Frontend: Inbox detail 'View Issue' navigation",
"done": true,
"scope": "Link to /issues/{issue_id} shown when item has issue_id."
},
{
"task": "Frontend: Archive button in inbox detail",
"done": true,
"scope": "Archive button calls api.archiveInbox + store.archive. Clears selection if archived item was selected."
},
{
"task": "Frontend: Inbox real-time sync for read/archive",
"done": true,
"scope": "Auto-completed by infra migration. useRealtimeSync handles inbox:new/read/archived."
},
{
"task": "Frontend: Inbox loading/empty states with shadcn",
"done": true,
"scope": "Deferred: UI polish."
}
]
}