improvement: treatment plan turned into a wizard. shift+click control added to FDI tooth chart for cunnected prosthesises.

This commit is contained in:
2026-07-17 00:39:32 +03:30
parent cf07b4d8a8
commit 68fc9d5d6d
28 changed files with 1461 additions and 477 deletions

View File

@@ -0,0 +1,12 @@
---
description: Clinic appointments — delete/patient locks when treatment linked
globs: frontend/src/components/ui/appointments/**,frontend/src/components/appointments/**,backend/src/modules/appointments/**
alwaysApply: false
---
# Appointments
- List includes `hasTreatment` when a `Treatment` row is linked (`appointmentId`).
- **Patient change** blocked while linked → `APPOINTMENT_PATIENT_LOCKED` (UI: `patientLockedHint`).
- **Delete** blocked while linked → `APPOINTMENT_HAS_TREATMENT` (hide delete + `deleteBlockedHint`). Empty appointments (no treatment yet) remain deletable.
- Prisma: `Treatment.appointmentId` is `onDelete: SetNull` — deleting an appointment does **not** cascade-delete treatments/cases/tasks; do not rely on cascade for cleanup.

View File

@@ -1,40 +1,22 @@
---
description: Treatment workspace — preview vs form, history, load flow, lab dispatch
globs: frontend/src/components/ui/treatment/**,frontend/src/components/treatment/**,frontend/src/components/shared/treatmentSelection.ts,frontend/src/components/shared/scrollWithinMain.ts,backend/src/modules/treatments/**
alwaysApply: false
---
# Treatment workspace
- **Browse mode** (`selectedPreviewId` set): preview only; banner + **Load into workspace**; form unchanged until load.
- **Current draft** preview: heading “Current draft”; no load button while editing live.
- **Entry wizard:** Teeth → Content → Lab via `WizardStepper` (not detail chips). Lab step only for lab-dependent (prosthesis) active detail. Appointment purpose does not gate type or Next.
- **Tooth groups:** Shift+same-arch range = connected; click in connected span → collapse to that single only. `toothSelectionGroups.ts`; prune lab `toothProsthesis` on change.
- **Detail chrome:** chips + Add at top; **Remove** = trash on chip (unsent, >1 detail; disabled when day-locked / no edit / uploading). No delete in Content step.
- **Lab dispatch attention:** `LabDispatchAttentionPanel` — unsent lab-dependent details; quick jump to dispatch.
- **History API:** patient-scoped; non-owners filtered by provider on treatment or appointment; org owners see all.
- **History filters (client-side):** `PastTreatmentsPanel` — “Not shipped to lab” + single date; helpers in `treatmentHistoryFilters.ts`. No new fetch params.
- **Lab shipment block:** prosthesis/lab-dependent detail with no teeth saves but cannot ship — `LabShipmentBlockedNotice`, inline banner, toast on add shipment.
- **Lab case comments:** `DetailLabCaseCommentsSection` under detail when sent and lab tasks not all complete (`taskProgress` from API).
- **Today checkbox:** unchecked when viewing a non-today day; checking Today unlocks selection and auto-picks nearest appointment.
- **Lab search:** `LinkedOrganizationSearchCombobox` — select from results; invite lab via `/organizations?action=invite-lab` when permitted.
- **Scroll:** use `scrollWithinMainScrollContainer`; shared `Checkbox` only.
- **History filters (client-side):** `PastTreatmentsPanel` — “Not shipped to lab” + single date; helpers in `treatmentHistoryFilters.ts`.
- **Lab shipment block:** lab-dependent detail with no teeth saves but cannot ship — `LabShipmentBlockedNotice`, inline banner, toast on add shipment.
- **Edit gating:** `canEditTreatmentForDay` = permission + appointment + not past day + `live` mode. Sent detail locks that line; Add still OK same day. Upload rejects sent (`TREATMENT_DETAIL_SENT`).
- **Lab search:** `LinkedOrganizationSearchCombobox`; invite lab via `/organizations?action=invite-lab` when permitted.
- **Scroll:** `scrollWithinMainScrollContainer`; shared `Checkbox` only.
Full map: `.cursor/skills/treatment-workspace/SKILL.md`

View File

@@ -2,7 +2,7 @@
name: dyolink-treatment-workspace
description: Treatment tab workspace — appointments strip, preview vs form, history, load flow, draft autosave, lab dispatch. Use when changing treatment UX, preview/history, or lab dispatch in TreatmentWorkspace.
description: Treatment tab workspace — appointments strip, preview vs form, history, load flow, draft autosave, entry wizard, connected teeth, lab dispatch. Use when changing treatment UX, preview/history, or lab dispatch in TreatmentWorkspace.
---
@@ -28,7 +28,37 @@ Thin route: `app/[locale]/(dashboard)/treatment/page.tsx` (supports `?appointmen
3. **Treatment history**`PastTreatmentsPanel.tsx` (past saved plans for patient; **client-side** filters in `treatmentHistoryFilters.ts`)
4. **Editor**`TreatmentDetailsEditor.tsx`, `FdiToothChart.tsx`, `LabCasesDispatchPanel.tsx`
4. **Editor**detail chips + Add (`TreatmentDetailsEditor` chrome); entry wizard (`WizardStepper`); step panels: `FdiToothChart`, Content fields, `LabCasesDispatchPanel`
## Entry wizard (Teeth / Content / Lab)
Right-column entry uses `WizardStepper` (`components/ui/shared/WizardStepper.tsx`) — numbered nodes + connector rail. **Do not** reuse detail-chip tab styling for steps.
| Step | UI | Notes |
|------|-----|--------|
| **Teeth** | `FdiToothChart` | Shift+click range; connected dots |
| **Content** | `TreatmentDetailsEditor` fields only (`showChrome={false}`) | Type / notes / attachments — **no delete button** |
| **Lab** | `LabCasesDispatchPanel` | Shown in stepper **only** when active detail type is lab-dependent (`labDependentCodes`) |
- Detail type may differ from appointment purpose (purpose only defaults new details).
- Next/Back navigate visible steps; leaving prosthesis while on Lab returns to Content.
## Tooth selection groups
Helpers: `frontend/src/components/treatment/toothSelectionGroups.ts`. Persisted as `toothSelectionGroups` on the detail; lab tasks group by `selectionGroupId`.
- **Plain click:** add/remove single; if tooth is in a **connected** group → drop the whole span and keep **only** that tooth as a single (no peel / no connected+single pair for the same span).
- **Shift+click:** inclusive same-arch range between prior anchor and target; replaces overlapping groups. 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.
@@ -161,17 +191,27 @@ Draft writes require provider match (`ensureAppointmentProvider`) unless org own
## Edit gating
```typescript
canEditTreatmentForDay = canEdit && selectedAppointment && !isViewingPastDay && workspaceMode === 'live'
```
Past day or `historical` workspace mode freezes the treatment form + most lab-dispatch fields.
### Per-detail sent lock
`isDetailLocked` = linked lab case has `sentAt`. Locked details: type, comment, teeth, attachments are read-only (UI + draft save skips updates/deletes; attachment upload returns `TREATMENT_DETAIL_SENT`).
### Add / remove details
- **Add detail** stays enabled whenever `canEditTreatmentForDay` — even if sibling details are already sent.
- **Remove detail:** trash icon on each **detail chip** in chrome (`TreatmentDetailsEditor` `showChrome`). Same gates as before: only when `details.length > 1`; disabled when `!canEdit`, day/workspace `disabled`, detail sent (`isDetailLocked`), or `uploadBusy`. Confirm via `confirmRemoveDetail`. Drop linked unsent lab drafts with the detail. **Do not** put a delete control in the Content wizard step.
### Sent lab shipment fields (vs detail)
After send, destination / prosthesis / shipment attachments are frozen. **Due date** stays editable until all lab tasks complete (UI still respects day/mode `disabled`). **Comments** stay editable until tasks complete and intentionally ignore the day/mode gate.
Full map helpers: `treatmentDetailRules.ts`, `LabCasesDispatchPanel.tsx`.
## Optional fields