--- name: dyolink-lab-tasks description: Lab Tasks tab — list, sort, filters, case grouping, prosthesis colors, step-completed filter. Use when changing TasksPage, tasks API, or lab task list UX. --- # Lab Tasks Main UI: [`frontend/src/components/ui/lab/TasksPage.tsx`](frontend/src/components/ui/lab/TasksPage.tsx) Backend: [`backend/src/modules/tasks/`](backend/src/modules/tasks/) ## Default sort (backend) `sortBy=date` + `sortDir=desc`: 1. `labCase.sentAt` desc (newest case first) 2. `labCaseId`, `treatmentDetailId`, `prosthesisTypeCode` asc (stable grouping) 3. `stepOrder` asc (steps 1→N within prosthesis group) 4. `id` asc Other sorts use flat list on the frontend; `stepOrder asc` is still a tiebreaker. ## Case grouping (frontend) - **`sortBy === 'date'`** → grouped view via [`taskListGrouping.ts`](frontend/src/components/lab/taskListGrouping.ts): case header → prosthesis sub-header → task rows. - **Other sorts** → flat list; show muted hint (`groupingOff*` i18n keys). Each row keeps clinic/patient/teeth context. Components: `TaskCaseGroupHeader`, `TaskProsthesisGroupHeader`, `TaskRow`. ## Prosthesis colors - Map: [`catalog-type-colors.ts`](frontend/src/components/shared/catalog-type-colors.ts) → `PROSTHESIS_TYPE_COLORS` (one hex per catalog code). - Resolve with [`prosthesisTypeDisplay.ts`](frontend/src/components/treatment/prosthesisTypeDisplay.ts) — use `prosthesisTypeBadgeStyleFromCatalog(code, catalog)`, **not** list row index. - Load catalog via `prosthesisCatalogApi.list()` on Tasks/Cases/Today dashboard. ## Filters | Param | API | UI | |-------|-----|-----| | `q`, `clinicOrganizationId`, `status` | `GET /tasks` | Search, clinic, status | | `stepCompleted` | `GET /tasks` | Workflow step dropdown | | `pinImportant` | `GET /tasks` | Important first (sort pin) | | `assignedToMe` | `GET /tasks` | Only tasks assigned to current user | | `prosthesisTypeCode` | `GET /tasks` | From Today prosthesis chart deep link | | `unassignedOnly` | `GET /tasks` | Tasks with no assignee | | `overdue` | `GET /tasks` | Cases with due date before today and at least one in-progress task | | `sortBy=dueDate` | `GET /tasks` | Sort by `LabCase.dueDate` (flat list; grouping off) | | Clinics + steps options | `GET /tasks/filter-options` | Populates dropdowns (not from current page) | **Task assignment:** Managed in **Cases** (`TAB_CASES_EDIT`), not on Tasks tab. `PATCH /cases/:caseId/tasks/:taskId/assign`; assignable staff via `GET /cases/assignable-staff` (members with `TAB_TASKS_EDIT`, including participating owner). Case detail task row: step label, status badge, assign dropdown, and last-updated line on one compact row. **Tasks visibility & status edit:** All tasks remain visible to every user with task access (no hiding assigned tasks). **Unassigned** tasks or tasks **assigned to you** → status dropdown when `TAB_TASKS_EDIT`. **Assigned to someone else** → read-only “Assigned to {name}” badge instead of the dropdown (backend rejects status PATCH). Managers assign/monitor in Cases. **Step completed filter:** Restricts to prosthesis groups `(labCaseId, treatmentDetailId, prosthesisTypeCode)` where that `workflowStepCode` task is `COMPLETED`. Combined with `status=IN_PROGRESS`, returns only in-progress tasks in those groups (completed step row hidden). - **Important first:** `pinImportant=true` prepends important cases in sort order. - **Assigned to me:** `assignedToMe=true` filters to current user's assigned tasks only. - **Overdue cases:** `overdue=true` — `LabCase.dueDate` before start of UTC day **and** at least one task still `IN_PROGRESS`. Shown with error badge on Cases list/detail and task case headers. - **Sort by due date:** `sortBy=dueDate` — flat list (grouping off); tiebreakers match other non-date sorts. - **Reset view:** `resetView` restores `DEFAULT_TASKS_VIEW` from `tasksViewDefaults.ts`. - **URL state:** `parseTasksSearchParams` applies Today deep-link query params on mount (`importantOnly`, `overdueOnly`, `unassignedOnly`, `prosthesisTypeCode`, `status`, sort). - **Show in case:** flat-sort rows only; resets filters/sort, calls `GET /tasks/locate-page` to find the correct page in the full default-sorted list, then highlights + scrolls to the task. - **Complete animation:** when marking done under in-progress filter, row plays exit animation + success toast before refetch. ## APIs - `GET /tasks` — paginated flat task list (grouping is client-side when `sortBy=date`) - `PATCH /tasks/:taskId` — update status (only assignee or unassigned task) - `GET /tasks/filter-options` — clinics + workflow steps (localized) - `GET /tasks/locate-page` — page number for a task in the sorted filtered list - `GET /cases/assignable-staff` — staff eligible for task assignment - `PATCH /cases/:caseId/tasks/:taskId/assign` — assign or unassign (`assigneeUserId` nullable) List items include `caseSentAt`, `caseDueDate`, `isCaseOverdue`, `assignee`, `assignedAt` for case headers / flat rows. ## Case due dates (clinic → lab) - **Schema:** `LabCase.dueDate` (optional `DateTime`). - **Clinic set:** Treatment lab dispatch panel — date input on unsent shipment (saved with draft/send); on sent cases, blur saves via `PATCH /treatments/lab-cases/:labCaseId/due-date`. - **Edit lock:** Clinic cannot change due date after **all** tasks are completed (`taskProgress.completed === taskProgress.total`). - **Lab display:** Cases list + detail; Tasks case group header when grouped by date. - **Utils:** `backend/src/common/lab-case-due-date.ts`, `frontend/src/components/lab/labCaseDueDateDisplay.ts`, `LabCaseDueDateBadge`. ## Mobile UX (Tasks + Treatment dispatch) Keep changes minimal — match existing `sm:` breakpoint patterns elsewhere in the app. - **Task status control:** `LAB_TASK_STATUS_SELECT_CLASS` in [`formSelectStyles.ts`](frontend/src/components/shared/formSelectStyles.ts) — full-width, `min-h-[44px]`, `text-base` on mobile; compact on `sm+`. Read-only status / assignee badges match height on mobile. - **Sticky case header:** `TaskCaseGroupHeader` uses `sticky top-0 z-10` + translucent background when `sortBy=date` (grouped view). Sticks within dashboard `
` scroll. - **Tasks filters:** filter `