75 lines
3.8 KiB
Markdown
75 lines
3.8 KiB
Markdown
|
|
---
|
||
|
|
name: dyolink-lab-case-share-link
|
||
|
|
description: Lab case QR share link — access token, focus page, auth redirect, Cases QR UI. Use when changing share links, /lab-case/[token], lab-case-access API, or post-login redirect from share URLs.
|
||
|
|
---
|
||
|
|
|
||
|
|
# Lab case share link
|
||
|
|
|
||
|
|
Shipped cases get a stable **access token** and share URL. QR + link open a focused tasks page with comments — not a public page; JWT + org context required.
|
||
|
|
|
||
|
|
## Data & token lifecycle
|
||
|
|
|
||
|
|
- **Schema:** `LabCase.accessToken` (`String?`, `@unique`).
|
||
|
|
- **On first ship:** `treatments.service` sets `accessToken` + `sentAt` in the same update.
|
||
|
|
- **Backfill:** `LabCaseAccessService.ensureAccessToken()` for older sent cases when building case detail `shareUrl`.
|
||
|
|
- **URL:** `buildLabCaseShareUrl(token, locale)` → `{FRONTEND_URL}/{locale}/lab-case/{token}` (`backend/src/common/lab-case-access-token.ts`).
|
||
|
|
|
||
|
|
## Backend API (`LabCaseAccessController`)
|
||
|
|
|
||
|
|
Base path: `/lab-cases/access/:token` (JWT + selected org required).
|
||
|
|
|
||
|
|
| Route | Purpose |
|
||
|
|
|-------|---------|
|
||
|
|
| `GET :token` | Session metadata (access mode, permissions, patient, prosthesis groups) |
|
||
|
|
| `GET :token/tasks` | All case tasks (includes assignee for status rules) |
|
||
|
|
| `GET/POST :token/comments` | List / add comments |
|
||
|
|
| `PATCH :token/comments/:id/visibility` | Lab only — clinic visibility toggle |
|
||
|
|
|
||
|
|
**Access resolution** (`lab-case-access.service.ts`):
|
||
|
|
|
||
|
|
| Actor | View | Edit task status | Comments |
|
||
|
|
|-------|------|------------------|----------|
|
||
|
|
| Lab + `TAB_TASKS_READ`/`EDIT` | ✅ | ✅ if `TAB_TASKS_EDIT` + assignee rules | Post/toggle if `TAB_TASKS_EDIT` |
|
||
|
|
| Clinic + `TAB_TREATMENT_EDIT` + **treatment provider** | ✅ | ❌ read-only | Post only (no visibility toggle) |
|
||
|
|
| Everyone else | ❌ `LAB_CASE_ACCESS_DENIED` | | |
|
||
|
|
|
||
|
|
Task status updates use **`PATCH /tasks/:id`** (not token routes) — same assignee rule as Tasks tab: unassigned or assigned-to-you only.
|
||
|
|
|
||
|
|
## Frontend
|
||
|
|
|
||
|
|
| Piece | Path |
|
||
|
|
|-------|------|
|
||
|
|
| Focus page | `app/[locale]/(dashboard)/lab-case/[token]/page.tsx` → `CaseTasksFocusView` |
|
||
|
|
| API client | `lib/api/lab-case-access.ts` |
|
||
|
|
| QR UI | `LabCaseShareQrCode`, `LabCaseShareQrDialog`, thumb in `CaseDetailPanel` |
|
||
|
|
| QR package | `react-qr-code` (frontend only — no backend QR generation) |
|
||
|
|
|
||
|
|
**Cases detail header:** attachment preview **left**, QR thumb **right**, same row (`w-24 sm:w-32`). QR opens dialog (large QR + URL + copy); no inline copy on panel. Only when `shareUrl` present (sent case).
|
||
|
|
|
||
|
|
**Share focus page:** grouped tasks (reuse `TaskRow`, `TaskCaseGroupHeader`); comments section via `LabCaseCommentsPanel` + token API adapters. Access denied → inline message (`asApiError` for `LAB_CASE_ACCESS_DENIED`).
|
||
|
|
|
||
|
|
## Auth redirect (logged out → login → back)
|
||
|
|
|
||
|
|
Helpers: `lib/auth/postAuthRedirect.ts` (`sessionStorage` key `authRedirect`).
|
||
|
|
|
||
|
|
1. Logged-out user hits `/lab-case/{token}` → dashboard layout stores path + `router.replace('/login?from=…')`.
|
||
|
|
2. Login page `useSearchParams` (inside **Suspense**) calls `storeAuthRedirectFromPath(from)`.
|
||
|
|
3. After login + org ready: **one** `consumeAuthRedirect()` on login page (wait for `!isLoading` and org selected).
|
||
|
|
4. **Do not** `consumeAuthRedirect()` inside `useAuth.login()` — double consume sends user to `/today`.
|
||
|
|
5. Multi-org: redirect stays in storage until `selectOrganization()` consumes it.
|
||
|
|
|
||
|
|
## Tasks tab interaction
|
||
|
|
|
||
|
|
Grouped sort (`sortBy=date`): **one comments control on case header** (`expandedCommentsCaseId`), not per task row. Flat sort unchanged (`showCommentsButton={flatMode}`).
|
||
|
|
|
||
|
|
## i18n
|
||
|
|
|
||
|
|
- `cases.*` — QR dialog strings (`shareQrDialogTitle`, `copyShareLink`, …)
|
||
|
|
- `labCaseAccess.*` — focus page strings
|
||
|
|
- `errors.LAB_CASE_ACCESS_DENIED` — all three locales
|
||
|
|
|
||
|
|
## Verify
|
||
|
|
|
||
|
|
- Backend: `npm run build`; apply migration for `accessToken`.
|
||
|
|
- Frontend: `npx tsc --noEmit`; `next build` (login page Suspense for `useSearchParams`).
|