improvement: Frontend Dockerfile updated so that aseests in public folder loads on the website.
This commit is contained in:
@@ -7,6 +7,7 @@ alwaysApply: false
|
||||
# Appointments
|
||||
|
||||
- List includes `hasTreatment` when a `Treatment` row is linked (`appointmentId`).
|
||||
- **Patient:** create/update must use a **named** patient of this org (`createdByOrganizationId`, not walk-in). Helper `ensurePatientInOrg`. Do not book another clinic’s patient UUID.
|
||||
- **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.
|
||||
- **Past days:** new bookings stay blocked. Existing appointments **without** treatment can be edited/deleted; with treatment → toast `infoEditBlockedHasTreatment` (no modal). Banner clicks are not gated by `canBook` (slots still are).
|
||||
|
||||
@@ -11,6 +11,7 @@ Monorepo: `backend/` (NestJS + Prisma), `frontend/` (Next.js + next-intl), `infr
|
||||
|
||||
- **CLINIC** orgs: patients, appointments, treatment, staff.
|
||||
- **LAB** orgs: cases, tasks, lab workflows.
|
||||
- Named **patients** are scoped to `createdByOrganizationId`. `mobile` stays globally unique — other-org / walk-in hit `PATIENT_MOBILE_UNAVAILABLE` (no shared row).
|
||||
- Tab access: `TAB_*_READ` / `TAB_*_EDIT` in `backend/src/common/permissions.ts`. EDIT implies READ.
|
||||
|
||||
## Agent behavior
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
description: Brand and FDI SVG sources live under frontend/src/assets, not public/
|
||||
globs: frontend/src/assets/**,frontend/src/components/ui/shared/Brand*.tsx,frontend/public/**,frontend/scripts/**
|
||||
globs: frontend/src/assets/**,frontend/src/components/ui/shared/Brand*.tsx,frontend/public/**,frontend/scripts/**,frontend/Dockerfile
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
@@ -11,3 +11,4 @@ alwaysApply: false
|
||||
- **FDI tooth sources:** `frontend/src/assets/fdi/`. The chart uses inlined paths in `realisticToothAssets.ts`; regenerate with `frontend/scripts/extract-tooth-svgs.mjs`.
|
||||
- **Prosthesis catalog illustrations:** source `frontend/src/assets/prosthesis-catalog/*.svg` (painted navy, not `currentColor`). Serve the same filenames from `frontend/public/prosthesis-catalog/` as `<img>` — Next cannot import these as URLs under Turbopack 16.1, and denture/veneer are too large to inline. Map codes in `prosthesisCatalogIcons.ts`. Copy into `public/` when adding a file.
|
||||
- **`public/`** is only for files that must be fetched by URL (e.g. og images, prosthesis catalog icons). Do not put themeable brand/FDI SVGs there — `<img src>` cannot inherit `currentColor`.
|
||||
- **Docker:** Next `output: 'standalone'` does **not** include `public/`. `frontend/Dockerfile` must `COPY` builder `/app/public` to `./public` next to `server.js` (after the standalone copy). Missing this 404s `/prosthesis-catalog/*.svg` in staging/prod.
|
||||
|
||||
@@ -13,7 +13,7 @@ alwaysApply: false
|
||||
- **Route:** `/lab-case/[token]` → `CaseTasksFocusView` (dashboard layout, auth required).
|
||||
- **Access:** lab (`TAB_TASKS_*`) or clinic treatment **provider** (`TAB_TREATMENT_EDIT` + `isActorTreatmentProvider`); else `LAB_CASE_ACCESS_DENIED`.
|
||||
- **Task status on link page:** same assignee rule as Tasks — `canEditLabTaskStatus`; backend `PATCH /tasks/:id` enforces assignee.
|
||||
- **Auth redirect:** `postAuthRedirect.ts`; dashboard stores path on logout redirect; login stores `?from=` **then** `useEnterAppWhenAuthenticated` consumes **once** after org ready — ❌ do not consume in `useAuth.login()` / `registerTrial`. Invites: `login()` then `navigateIntoAppIfOrgSelected` (no enter-app hook on invite pages).
|
||||
- **Auth redirect:** `postAuthRedirect.ts`; dashboard stores path on logout redirect; login stores `?from=` **then** `useEnterAppWhenAuthenticated` consumes **once** after org ready — ❌ do not consume in `useAuth.login()` / `registerTrial`. Invites (join + `password_setup`): `login()` then `navigateIntoAppIfOrgSelected` (no enter-app hook on invite pages).
|
||||
- **Login page:** wrap `useSearchParams` in `<Suspense>` for `next build`.
|
||||
|
||||
Skill: `.cursor/skills/lab-case-share-link/SKILL.md`
|
||||
|
||||
12
.cursor/rules/patients.mdc
Normal file
12
.cursor/rules/patients.mdc
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
description: Clinic patients — org-scoped named records, globally unique mobile
|
||||
globs: backend/src/modules/patients/**,backend/src/modules/appointments/appointments.service.ts,backend/src/modules/treatments/treatments.service.ts,frontend/src/components/ui/patient/**,frontend/src/lib/api/patients.ts
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Patients
|
||||
|
||||
- List / get / update / create: **this org** + `isWalkIn: false` (`createdByOrganizationId`).
|
||||
- `Patient.mobile` stays **globally unique**. Same-org named create returns `{ existing: true }`. Other org, walk-in, or null creator → `PATIENT_MOBILE_UNAVAILABLE` (409). Do **not** return or mention the other clinic’s row.
|
||||
- Appointment create/update and `POST /treatments` named `patientId`: `ensurePatientInOrg` (named + this org). Walk-in sentinel is per clinic (`walk-in-patient.ts`), hidden from Patients/search/booking.
|
||||
- History / lab-case lists still query treatments for **this** `organizationId` even if the patient UUID is guessed.
|
||||
@@ -10,7 +10,7 @@ alwaysApply: false
|
||||
|
||||
- **Login + register:** `useEnterAppWhenAuthenticated` after org ready → `appPathAfterAuth()` (`consumeAuthRedirect()` once, else `/today`).
|
||||
- **Login `?from=`:** `storeAuthRedirectFromPath` **before** that hook (effect order).
|
||||
- **Staff / org invite:** accept → `login(email, password)` → `navigateIntoAppIfOrgSelected`. ❌ Do not put the hook on invite pages (logged-in visitors must finish accept).
|
||||
- **Staff / org invite:** accept → `login(email, password)` → `navigateIntoAppIfOrgSelected`. Same for `/accept-invite` `mode: password_setup` (password fields only). ❌ Do not put the hook on invite pages (logged-in visitors must finish accept). Login does not special-case `passwordHash: null` — those users cannot sign in until they set a password via the setup link.
|
||||
- **Forgot password:** navigates itself to `/settings/account?reset=1`. ❌ Do not add the enter-app hook there.
|
||||
- **Multi-org:** redirect stays in sessionStorage until `selectOrganization()` → `appPathAfterAuth()`.
|
||||
- ❌ Never `consumeAuthRedirect()` inside `useAuth.login()` or `registerTrial`.
|
||||
|
||||
13
.cursor/rules/staff.mdc
Normal file
13
.cursor/rules/staff.mdc
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
description: Staff passwords — setup link only; never set another user’s password
|
||||
globs: backend/src/modules/staff/**,frontend/src/components/ui/staff/**,frontend/src/app/**/accept-invite/**,frontend/src/lib/api/staff.ts
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Staff passwords
|
||||
|
||||
- Owner / `TAB_STAFF_EDIT` may **clear** a password, never set one for someone else.
|
||||
- `POST /staff/members/:membershipId/clear-password`: `passwordHash: null`, delete sessions, revoke unused invites on that membership, mint a 7-day `/accept-invite` URL. Refuse owner, self, pending, disabled (`STAFF_CANNOT_CLEAR_OWN_PASSWORD`, `STAFF_PASSWORD_CLEAR_ACTIVE_ONLY`).
|
||||
- List DTO: `hasPassword` boolean only (never the hash). `previewInvite` `mode`: `join` | `password_setup` from `membership.isActive`.
|
||||
- Login page unchanged — null hash is invalid credentials until they set a password on the setup link.
|
||||
- `/accept-invite` `password_setup`: password fields only; then `login()` + `navigateIntoAppIfOrgSelected` (no enter-app hook).
|
||||
Reference in New Issue
Block a user