Files

4.2 KiB

name, description
name description
dyolink-lab-case-share-link 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.tsxCaseTasksFocusView
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 via LabCaseCommentsPanel + token API (viewerSide from session.accessMode). Newest-first; sent/received use logical start/end. 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) before useEnterAppWhenAuthenticated.
  3. After login/register + org ready: one consume via appPathAfterAuth() in that hook. Staff/org invite: login() then navigateIntoAppIfOrgSelected ( no hook on invite pages).
  4. Do not consumeAuthRedirect() inside useAuth.login() or registerTrial — double consume sends user to /today.
  5. Multi-org: redirect stays in storage until selectOrganization()appPathAfterAuth().
  6. Forgot-password navigates to account reset itself — do not add the enter-app hook there.

Rule: .cursor/rules/post-auth-navigation.mdc.

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).