81 lines
3.9 KiB
JSON
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."
|
|
}
|
|
]
|
|
}
|