improvement: Some improvements done. some bugs fixed.
This commit is contained in:
@@ -12,9 +12,10 @@ description: Lab Cases tab — list, filters, detail panel, assignment, share QR
|
||||
## List behavior
|
||||
|
||||
- **Default sort:** `startedAt` / `sentAt` desc (newest first). Lab-origin drafts (`origin === LAB_INTERNAL && !startedAt`) show a **Draft** badge. All list/detail cards also show **Received** (`CLINIC_DISPATCH`) or **Generated** (`LAB_INTERNAL`) via `LabCaseOriginBadge`.
|
||||
- **Task group headers:** prosthesis type is **colored catalog text** (`prosthesisTypeColorFromCatalog`), not a Badge pill. Same on Tasks rows/headers.
|
||||
- **Page size:** `PAGE_SIZE = 10` in `CasesPage.tsx`.
|
||||
- **Auto-select:** On tab open / after filter reload, select first list item if none selected; keep selection when still in list; `?caseId=` URL wins.
|
||||
- **Add case:** `TAB_CASES_EDIT` — `POST /cases` then replace the **right pane** with `CaseCreatePanel` (inline wizard, not a modal). Prosthesis-only lines, `ProsthesisAssignChart` (same picker/stacking as Treatment). Summary rows: `LabCaseToothJobsList` (tooth · types). Header: referring clinic/dentist, patient name/mobile, optional ACTIVE partner clinic, due date (`AppDateInput`). **Start** (`POST /cases/:id/start`) generates tasks; no `LabCaseSend`, no clinic inbox/`CASE_SENT`. **Delete** (`DELETE /cases/:id`) only for unstarted lab-origin drafts (`LAB_INTERNAL` && `!startedAt`); confirm in the create-panel header next to Start. Received clinic cases and started generated cases cannot be deleted.
|
||||
- **Add case:** `TAB_CASES_EDIT` — `POST /cases` then replace the **right pane** with `CaseCreatePanel` (inline wizard, not a modal). Prosthesis-only lines, `ProsthesisAssignChart` (same picker/stacking as Treatment). Summary rows: `LabCaseToothJobsList` (connected same-type teeth share a row; otherwise tooth · types). Header: referring clinic/dentist, patient name/mobile, optional ACTIVE partner clinic, due date (`AppDateInput`). **Start** (`POST /cases/:id/start`) generates tasks; no `LabCaseSend`, no clinic inbox/`CASE_SENT`. **Delete** (`DELETE /cases/:id`) only for unstarted lab-origin drafts (`LAB_INTERNAL` && `!startedAt`); confirm in the create-panel header next to Start. Received clinic cases and started generated cases cannot be deleted.
|
||||
- **Right panel:** Draft lab-origin → wizard; started/received → `CaseDetailPanel`.
|
||||
- **Left rail layout:** section is `flex flex-col` + `lg:min-h-[420px]` / `h-full` with `lg:items-stretch` on the grid; case list wrapper is `flex-1 min-h-0 overflow-y-auto` (do **not** use a fixed `max-h-[55vh]` — that leaves empty space above pagination).
|
||||
|
||||
@@ -36,7 +37,7 @@ Filter options: `GET /cases/filter-options` → `clinics`, `prosthesisTypes` (di
|
||||
|
||||
Match Treatment shipment cards: patient name, clinic, **colored prosthesis groups + teeth** (`LabCaseProsthesisGroupsList`), sent date, progress bar, due-date badge, unread dot. **No patient mobile** on list cards. Prosthesis row order: **type · teeth**, then Connected badge when that group is a bridge.
|
||||
|
||||
List item shape: `prosthesisGroups: { prosthesisTypeCode, teeth[] }[]` from task teeth aggregation. After send, each tooth is its own group (stacked types on that tooth share one `prosthesisTypeCode` key like `crown+abutment`). **Connected** means that tooth’s `selectionGroupId` is shared by more than one tooth — not `teeth.length > 1` on the row.
|
||||
List item shape: `prosthesisGroups: { prosthesisTypeCode, teeth[] }[]` from task teeth aggregation. Unconnected teeth stay one group each (stacked types on that tooth share one `prosthesisTypeCode` key like `crown+abutment`). **Connected** teeth that share a type are one group whose `teeth` lists the bridge. **Partial denture** is one group whose `teeth` is every selected unit.
|
||||
|
||||
## Detail panel
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Backend: [`backend/src/modules/tasks/`](backend/src/modules/tasks/)
|
||||
`sortBy=date` + `sortDir=desc`:
|
||||
|
||||
1. `labCase.sentAt` desc (newest case first)
|
||||
2. `labCaseId`, `sourceKey`, `tooth`, `prosthesisTypeCode` asc (one pipeline per tooth)
|
||||
2. `labCaseId`, `sourceKey`, `tooth`, `prosthesisTypeCode` asc (one pipeline per unconnected tooth; connected same-type teeth share a pipeline; `partial_denture` is one pipeline for all selected teeth)
|
||||
3. `stepOrder` asc (steps 1→N within that tooth)
|
||||
4. `id` asc
|
||||
|
||||
@@ -21,7 +21,7 @@ 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 → **per-tooth** prosthesis sub-header → task rows (`treatmentDetailId:teeth:prosthesisTypeCode`).
|
||||
- **`sortBy === 'date'`** → grouped view via [`taskListGrouping.ts`](frontend/src/components/lab/taskListGrouping.ts): case header → prosthesis sub-header (one per pipeline: unconnected tooth, or connected same-type bridge) → task rows (`treatmentDetailId:teeth:prosthesisTypeCode`).
|
||||
- **Other sorts** → flat list; show muted hint (`groupingOff*` i18n keys). Each row keeps clinic/patient/teeth context.
|
||||
|
||||
Components: `TaskCaseGroupHeader`, `TaskProsthesisGroupHeader`, `TaskRow`.
|
||||
@@ -31,7 +31,7 @@ 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.
|
||||
- Resolve with [`prosthesisTypeDisplay.ts`](frontend/src/components/treatment/prosthesisTypeDisplay.ts) — use `prosthesisTypeColorFromCatalog(code, catalog)` as **text color** (no type pills), **not** list row index.
|
||||
- Load catalog via `prosthesisCatalogApi.list()` on Tasks/Cases/Today dashboard.
|
||||
|
||||
## Filters
|
||||
|
||||
@@ -43,7 +43,7 @@ Right-column entry is **not** a three-step wizard. Type dropdown + `TreatmentDet
|
||||
| **Treatment** | Type dropdown + `TreatmentDetailAttachmentsStrip`, `FdiToothChart` / `ProsthesisAssignChart`, full-width Notes | Always |
|
||||
| **Lab** | `LabCasesDispatchPanel` under the form | When active detail type is lab-dependent. Auto-ensures a shipment draft (teeth/arch not required to create the draft). **No default lab or prosthesis type** on a new detail (including siblings in the same plan). Last **3 sent** labs appear as chips under search — pick is explicit. Comments stay on the dispatch panel. |
|
||||
|
||||
- Prosthesis types are assigned on the chart (`ProsthesisAssignChart` + `ProsthesisJobPopover` in `prosthesisTree.ts`). One restoration slot per tooth (crown / indirect / screw-retained); implant abutments and post & core **stack**. Arch products (denture, night guard, digital) use **Upper arch / Lower arch** labels and store `UA`/`LA` rows — they do not select 16 FDI teeth. Lab dispatch is on the same screen (no Treatment → Lab stepper).
|
||||
- Prosthesis types are assigned on the chart (`ProsthesisAssignChart` + `ProsthesisJobPopover` in `prosthesisTree.ts`). One **restoration** slot per tooth (crown / veneer-inlay-onlay-overlay); **screw-retained** is an implant add-on (`stackGroup: implant`, paints the crown) and stacks with a restoration. Implant abutments and post & core also stack (post & core stays off when the implant is screw-retained). Complete denture / overdenture / appliances / digital use **Upper arch / Lower arch** (`UA`/`LA`). **Partial denture** is tooth-level (select FDI teeth, Removable in the tooth picker); after send it is **one lab job** for all those teeth. Picker leaves are filtered by `chartRegion` so Removable appears in both tooth and arch pickers. Lab dispatch is on the same screen (no Treatment → Lab stepper).
|
||||
- Detail chips show **type + teeth**, not “Detail N”. Lab-dependent chips use colored sent/unsent text (same size as the label); sent date stays on Lab dispatch.
|
||||
- Detail type may differ from appointment purpose. Purpose seeds the first line of an empty **appointment** draft (first open, and **Add detail** when the plan is `[]`). Later **Add detail** starts with an empty type. Unscheduled / New treatment still seeds a blank first line.
|
||||
- Lab shipments rail / “Go to dispatch” / load-with-focus **scrolls** to the dispatch panel (`pendingScrollToLabRef` + `labPanelRef`).
|
||||
@@ -55,14 +55,14 @@ Right-column entry is **not** a three-step wizard. Type dropdown + `TreatmentDet
|
||||
|
||||
|
||||
|
||||
Helpers: `frontend/src/components/treatment/toothSelectionGroups.ts`. Persisted as `toothSelectionGroups` on the detail. Lab **dispatch** lists **one row per tooth** (`LabCaseToothJobsList`, colored catalog types). After send, Cases/Tasks keep one item per tooth; stacked types on that tooth **union** their workflow steps (not merge teeth by type). `LabCaseTask` unique key includes `tooth`.
|
||||
Helpers: `frontend/src/components/treatment/toothSelectionGroups.ts`. Persisted as `toothSelectionGroups` on the detail. Lab **dispatch** lists connected teeth that share a type as **one row** (`LabCaseToothJobsList`); unconnected teeth stay one row per tooth (stacked codes stay together). After send, Cases/Tasks match that: a connected same-type bridge is one pipeline; stacked types on a **single** tooth **union** their workflow steps; `partial_denture` is one task set per `sourceKey`. `LabCaseTask` unique key includes `tooth` (first tooth of a merged bridge).
|
||||
|
||||
Chart hit-testing (`FdiToothChart.tsx`): the hit target is the **unrotated full cell** (`pointerdown` + keyboard). Do **not** bind both `pointerdown` and `click` (double-toggle looks like a miss). Visual glyph is `pointer-events-none`; tilt lives on the outer wrapper; **scale/hover is nested inside** so inline `transform` does not kill scale. Horizontal inset (~22%) leaves a dead zone between teeth; FDI numbers are also clickable (`ToothNumber`).
|
||||
|
||||
- **Plain click:** add/remove single; deselecting a tooth in a bridge removes it and splits/shrinks remaining sides (never a 1-tooth connected group).
|
||||
- **Neighbor circles:** when two arch-adjacent teeth are both selected, an empty circle appears **between** them (not per-tooth). Click empty → connect; click filled → disconnect (teeth stay selected). Transitive A–B + B–C = one bridge.
|
||||
- **Shift+click:** inclusive same-arch range → all selected as **singles** (empty circles); overlapping existing bridges are absorbed as singles too. Midline neighbors (11–21, 41–31) allowed.
|
||||
- **Prosthesis assign chart:** Shift = range then assign to the span; Ctrl/Cmd or drag copies the last type-set. Arch products open from the Upper/Lower arch labels and store `UA`/`LA` (not 16 FDI teeth). `pruneToothProsthesisForGroups` must keep those sentinels.
|
||||
- **Prosthesis assign chart:** Shift = range then assign to the span; Ctrl/Cmd or drag copies the last type-set. Click a selected tooth **opens the picker**; click drops a tooth only from an in-progress Shift range that still has no jobs. Arch products open from the Upper/Lower arch labels and store `UA`/`LA` (not 16 FDI teeth). Partial denture is assigned from selected teeth (not the arch labels). `pruneToothProsthesisForGroups` must keep those sentinels.
|
||||
- Prevent browser selection artifacts (`select-none`, Shift `preventDefault` on mousedown).
|
||||
- On group change, prune/remap `labCase.toothProsthesis` via `pruneToothProsthesisForGroups`.
|
||||
- Connected UI label: `ConnectedSelectionBadge` (shared `Badge` + primary tint) in dispatch + lab case lists.
|
||||
@@ -80,7 +80,7 @@ Chart hit-testing (`FdiToothChart.tsx`): the hit target is the **unrotated full
|
||||
|
||||
| **Preview** | `previewTreatment`; `selectedPreviewId !== null` = **browse mode** | History click updates preview only |
|
||||
|
||||
| **Form** | `details[]`, `labCaseDrafts[]` | Appointment change → draft API; **Load into workspace** → hydrate |
|
||||
| **Form** | `details[]`, `labCaseDrafts[]` | Appointment change → draft API; **Load into workspace** → hydrate. Autosave and strip-switch **flush both** `saveDraft` then `saveLabCases`. Dirty includes the lab-case snapshot (jobs, dest, due date), not only details. Non-prosthesis plans keep empty drafts and do not create lab rows. |
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user