Files
dyolink/.cursor/skills/tab-badges/SKILL.md

4.6 KiB
Raw Permalink Blame History

name, description
name description
dyolink-tab-badges Sidebar tab badge counts (Cases/Tasks/Treatment) from LabCaseActivity + read cursors. Use when changing tab-counts API, activity emit, read state, or Sidebar badges — distinct from the header inbox bell.

Tab badges (Cases / Tasks / Treatment)

Sidebar unread pills for lab Cases, lab Tasks, and clinic Treatment. Activity is stored as LabCaseActivity (shared clinic↔lab case events); badges are org-type-specific.

Backend: backend/src/modules/notifications/
Activity types: backend/src/common/lab-case-activity.ts
Frontend hook: frontend/src/lib/hooks/useTabBadgeCounts.ts

Models

  • LabCaseActivity — append-only events: CASE_SENT, CLINIC_COMMENT, LAB_COMMENT, CASE_IMPORTANT, CASE_AMENDED (stub for Step 7), TASK_COMPLETED, TASK_ASSIGNED
  • LabCaseUserTabReadState — per user/org/tab cursor (TASKS) for sidebar badge clearing on tab visit.
  • LabCaseUserReadState — per user/org/labCase cursor; drives Cases tab count and hasUnread on case list cards

Tab badge buckets (Option B)

Org Tab Activity types
LAB Cases CASE_SENT, CLINIC_COMMENT, CASE_IMPORTANT
LAB Tasks TASK_COMPLETED, LAB_COMMENT, TASK_ASSIGNED (assignee only)
CLINIC Treatment LAB_COMMENT (only visibleToClinic), TASK_COMPLETEDonly lab cases for treatments the user provided

Counts exclude events where actorUserId === current user. Clinic LAB_COMMENT counts only when payload.visibleToClinic === true.

APIs

  • GET /notifications/tab-counts{ cases?, tasks?, treatment? }Cases count = number of cases with unread Cases-bucket activity (per-case read cursor)
  • GET /notifications/lab-cases/:labCaseId/activities — activity feed for a case (clinic-safe lab comments)
  • POST /notifications/mark-tab-read { tab } — Tasks only (Cases/Treatment skip tab-level clear)
  • POST /notifications/mark-case-read { labCaseId } — opening a case clears that cases unread dot and updates Cases tab count
  • GET /treatments/patients/:patientId/lab-cases — patient shipment summaries for Treatment rail + tracker cards
  • GET /treatments/lab-cases/unread — org-wide unread shipment summaries for Treatment “All updates” scope

Emit activity from

Event Service
First send treatments.service sendLabCaseCASE_SENT
Comment lab-case-comments.serviceCLINIC_COMMENT / LAB_COMMENT
Mark important cases.service updateImportant (only when set true) → CASE_IMPORTANT
Task completed tasks.service updateStatusTASK_COMPLETED
Task assigned cases.service assignTaskTASK_ASSIGNED (inbox + Tasks badge for assignee, including self-assign)

After mutations, frontend calls notifyTabBadgesChanged() (window event).

Live path: inbox Socket.IO notification.created also dispatches that event so sidebar counts refresh without navigation. Only currently mounted feature pages soft-reload list data on the same event (silent; Treatment form/draft untouched). Unmounted tabs do not fetch list data until the user opens them. See .cursor/skills/notifications-inbox/SKILL.md.

Frontend pattern (same as org connections)

  • useTabBadgeCounts() — always mounted in dashboard Sidebar; fetch on pathname change + tab-badges-changed
  • useMarkTabReadOnVisit() — Tasks page only (Cases/Treatment badges clear when opening unread cases)
  • NavBadgePill in Sidebar.tsx
  • Organizations pending connections still use usePendingConnectionsCount (separate pending-state API)
  • Live soft refresh (mounted page only):
    • Cases → silent list + selected detail
    • Tasks → silent task list
    • Treatment (TreatmentWorkspace) → silent patient lab cases + unread rail
    • Orgs → silent list on pending-connections-changed only
  • TASK_ASSIGNED Tasks badge is assignee-scoped (payload.assigneeUserId); other Tasks-bucket events stay org-wide for users with tab access.

Out of scope (later steps)

  • Push / email
  • Prefetching unmounted tab list data on every socket event
  • Pushing full page remounts / wiping Treatment draft state on live events
  • CASE_AMENDED emit (Step 7)

Permission-free bell + UserNotification fan-out + Socket.IO — see .cursor/skills/notifications-inbox/SKILL.md. Independent of tab badge cursors.