From 62d5d941212c5dc66dc665eb0feff2acd7bac578 Mon Sep 17 00:00:00 2001 From: rameen Date: Mon, 7 Sep 2026 23:18:00 +0330 Subject: [PATCH] review notification logic and fix minor issue. --- .cursor/rules/dyolink-overview.mdc | 2 +- .cursor/rules/lab-tasks.mdc | 1 + .cursor/skills/lab-tasks/SKILL.md | 2 +- AGENTS.md | 4 ++-- .../src/components/ui/notifications/NotificationCard.tsx | 9 +++++++-- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.cursor/rules/dyolink-overview.mdc b/.cursor/rules/dyolink-overview.mdc index a573b36..77e9b9b 100644 --- a/.cursor/rules/dyolink-overview.mdc +++ b/.cursor/rules/dyolink-overview.mdc @@ -46,4 +46,4 @@ Logical failures: `AppException(ErrorCode.X)` → `errors.X` in en/fa/nl. UI: `g ## Notifications (inbox + live tabs) -Header bell: `UserNotification` + Socket.IO. Same `notification.created` also drives sidebar tab badges and soft list refresh on **currently open** Cases/Tasks/Treatment/Orgs pages. Skills: `.cursor/skills/notifications-inbox/SKILL.md`, `.cursor/skills/tab-badges/SKILL.md`. +Header bell: `UserNotification` + Socket.IO. Same `notification.created` also drives sidebar tab badges and soft list refresh on **currently open** Cases/Tasks/Treatment/Orgs pages. Inbox + Tasks/Treatment badges use **`CASE_COMPLETED`** (every task in the case done); per-step `TASK_COMPLETED` is timeline-only. Skills: `.cursor/skills/notifications-inbox/SKILL.md`, `.cursor/skills/tab-badges/SKILL.md`. diff --git a/.cursor/rules/lab-tasks.mdc b/.cursor/rules/lab-tasks.mdc index a28eeb5..ef61ffe 100644 --- a/.cursor/rules/lab-tasks.mdc +++ b/.cursor/rules/lab-tasks.mdc @@ -17,5 +17,6 @@ alwaysApply: false - **Mobile UX:** `LAB_TASK_STATUS_SELECT_CLASS` (44px tap target on small screens); `TaskCaseGroupHeader` sticky while scrolling grouped tasks; filter selects use same touch sizing on Tasks. - **Show in case:** `GET /tasks/locate-page` finds page in full list; highlight + scroll. - **Today deep links:** `parseTasksSearchParams` + `prosthesisTypeCode` / `unassignedOnly` / `overdueOnly` query params on Tasks. +- **Inbox / tab badges:** completing a step writes timeline `TASK_COMPLETED` only; inbox + Tasks/Treatment badges fire on `CASE_COMPLETED` when no in-progress tasks remain. Full map: `.cursor/skills/lab-tasks/SKILL.md` diff --git a/.cursor/skills/lab-tasks/SKILL.md b/.cursor/skills/lab-tasks/SKILL.md index 1298848..c5064bf 100644 --- a/.cursor/skills/lab-tasks/SKILL.md +++ b/.cursor/skills/lab-tasks/SKILL.md @@ -95,7 +95,7 @@ Keep changes minimal — match existing `sm:` breakpoint patterns elsewhere in t ## Tab badges + live soft refresh -See `.cursor/skills/tab-badges/SKILL.md` — split lab Cases/Tasks counts, clinic Treatment; `useTabBadgeCounts` + `notifyTabBadgesChanged`. +See `.cursor/skills/tab-badges/SKILL.md` — split lab Cases/Tasks counts, clinic Treatment; `useTabBadgeCounts` + `notifyTabBadgesChanged`. Inbox and Tasks/Treatment badges fire on `CASE_COMPLETED` (all tasks done), not each workflow step. The case timeline still records `TASK_COMPLETED` per step. Inbox Socket.IO `notification.created` → `notifyTabBadgesChanged()` → silent `loadTasks({ silent: true })` on an open Tasks page (filters preserved; no remount). Details: `.cursor/skills/notifications-inbox/SKILL.md`. diff --git a/AGENTS.md b/AGENTS.md index 61e54cb..7aac795 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,7 +58,7 @@ frontend/src/ - Detail treatment type need **not** match appointment purpose — purpose only seeds the **first** line of an empty **appointment** draft (first open, and **Add detail** when the plan is `[]`). Further **Add detail** starts with an empty type. Unscheduled / New treatment still seeds a blank first line. - **History filters** are client-side only (`treatmentHistoryFilters.ts`): “Not shipped to lab” + single date on already-fetched patient history; includes live current draft when filtering. - **Lab shipments rail**: unified list with scope toggle **This patient** vs **All updates** (unread across org for **this clinician's cases only**, includes patient name). Opening a case from the rail **opens the lab dispatch view** (same slot as the chart). -- **Unread semantics**: Treatment tab badge = count of unread cases **for the user's own treatment plans** (per-case read cursor) and clears when a case is opened/marked read (not on tab visit). +- **Unread semantics**: Treatment tab badge = count of unread cases **for the user's own treatment plans** (per-case read cursor) and clears when a case is opened/marked read (not on tab visit). Completions count only when **every task in the case is done** (`CASE_COMPLETED`) — not each workflow step. - **Live lab rail**: `notification.created` → `notifyTabBadgesChanged()` silently refreshes patient lab cases + unread rail (does **not** clear draft/form state). - **Lab shipment progress + comments**: shown in **Lab dispatch panel** for the active shipment; expanding activity / opening comments marks that case read. Shared UI: `LabCaseCommentsPanel` — newest first; sent = start / received = end (`text-start`/`justify-start`, RTL-safe); pass `viewerSide`. @@ -68,7 +68,7 @@ frontend/src/ **Staff (quick ref):** Owner / `TAB_STAFF_EDIT` can **remove** a password (`POST /staff/members/:id/clear-password`) and copy a setup link — never set one for someone else. Login page unchanged (`passwordHash: null` cannot sign in). `/accept-invite` `password_setup` is password-only. See `.cursor/rules/staff.mdc`. -**Lab Tasks tab:** Newest case first; steps ordered 1→N; case grouping when sorted by date; `stepCompleted` filter; filter by case source (`origin`: received vs generated); prosthesis colors from catalog; job titles show the picker path to the leaf (`prosthesisJobPath.ts`); task assignment in **Cases** (compact row: status + assignee + last update); on **Tasks**, all staff see every task but only assignee (or unassigned pool) can change status — others see “Assigned to {name}” instead of the status dropdown; **case due dates** set/edited in clinic Treatment lab dispatch, shown on lab Cases/Tasks with overdue filter + sort; completing **`intraoral_scan`** completes every scan task in that case (case-scoped; catalog first step for all prosthesis types); **mobile:** larger task status controls, sticky case header when grouped; **tab badges:** `LabCaseActivity` + `GET /notifications/tab-counts` (lab Cases/Tasks split, clinic Treatment) — live via inbox Socket.IO → `notifyTabBadgesChanged()` + soft list refresh — see `.cursor/skills/lab-tasks/SKILL.md`, `.cursor/skills/tab-badges/SKILL.md`, `.cursor/skills/notifications-inbox/SKILL.md`. +**Lab Tasks tab:** Newest case first; steps ordered 1→N; case grouping when sorted by date; `stepCompleted` filter; filter by case source (`origin`: received vs generated); prosthesis colors from catalog; job titles show the picker path to the leaf (`prosthesisJobPath.ts`); task assignment in **Cases** (compact row: status + assignee + last update); on **Tasks**, all staff see every task but only assignee (or unassigned pool) can change status — others see “Assigned to {name}” instead of the status dropdown; **case due dates** set/edited in clinic Treatment lab dispatch, shown on lab Cases/Tasks with overdue filter + sort; completing **`intraoral_scan`** completes every scan task in that case (case-scoped; catalog first step for all prosthesis types); **mobile:** larger task status controls, sticky case header when grouped; **tab badges / inbox:** Tasks + Treatment badges and the header bell increment on **`CASE_COMPLETED`** (all tasks in the case done), not each step — the case timeline still lists `TASK_COMPLETED` per step; live via inbox Socket.IO → `notifyTabBadgesChanged()` + soft list refresh — see `.cursor/skills/lab-tasks/SKILL.md`, `.cursor/skills/tab-badges/SKILL.md`, `.cursor/skills/notifications-inbox/SKILL.md`. **Lab Cases tab:** Filter by **prosthesis type** (not treatment type); auto-select newest case on open; list **10 per page**; left rail list fills column height (`flex-1 overflow-y-auto`); list cards use `LabCaseProsthesisGroupsList` (colored type + teeth, shared with Treatment rail) plus **Received** / **Generated** origin badges. Job titles show the picker path to the **leaf** (`prosthesisJobPath.ts` — e.g. Crowns · PFM Crown, not “Crowns”; indirect is Inlay · Layered ceramic, not the long Veneer/Inlay/Onlay/Overlay parent). Deep link: `?caseId=`, `?clinicOrganizationId=`. **Share link:** QR + URL on sent cases (attachment left, QR right); opens `/lab-case/[token]` focus page. **Case Sheet PDF:** client A4 (`jspdf`/`html2canvas`); hex-only print layout; optional `externalCode` replaces order number. Lab-origin Start has no clinic send; comments and mark-read still work (no clinic-visibility toggle). Unstarted generated drafts can be deleted (`DELETE /cases/:id`). **Live:** inbox Socket.IO → `notifyTabBadgesChanged()` soft-refreshes list + selected detail (no remount). See `.cursor/skills/lab-cases/SKILL.md` and `.cursor/skills/lab-case-share-link/SKILL.md`. diff --git a/frontend/src/components/ui/notifications/NotificationCard.tsx b/frontend/src/components/ui/notifications/NotificationCard.tsx index c046ab6..8790665 100644 --- a/frontend/src/components/ui/notifications/NotificationCard.tsx +++ b/frontend/src/components/ui/notifications/NotificationCard.tsx @@ -77,8 +77,13 @@ export function notificationContextLine( break; case 'CASE_COMPLETED': if (patientName) parts.push(patientName); - if (clinicName) parts.push(clinicName); - else if (labName) parts.push(labName); + if (item.href.startsWith('/treatment')) { + if (labName) parts.push(labName); + } else if (clinicName) { + parts.push(clinicName); + } else if (labName) { + parts.push(labName); + } if (prosthesisLabel) parts.push(prosthesisLabel); break; case 'LAB_COMMENT_CLINIC':