bugfix: appointment hours now use the client timezone on UTC servers.
Logical API errors throw stable codes so users see translated messages instead of a generic bad request. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -24,5 +24,6 @@ Frontend maps `details[].code` through the `errors` namespace.
|
||||
|
||||
## Do not
|
||||
|
||||
- Throw Nest `BadRequestException('English sentence')` for user-facing errors.
|
||||
- Show raw `error.message` or stack traces to users.
|
||||
- Add English-only strings inline in components.
|
||||
|
||||
@@ -10,5 +10,6 @@ alwaysApply: false
|
||||
- **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).
|
||||
- **Working hours / create:** interpret wall-clock in the **client IANA time zone** (`timeZone` on create/update, e.g. `Asia/Tehran`). Do not use Node `Date#getHours()` / `getDay()` on the server (UTC Docker vs clinic local). Helper: `zoned-civil-time.ts`.
|
||||
- **Disabled feedback:** use `title` + click `toast.showInfo` with `common.readOnlyAccess` (same key as Staff/Patients) or the domain reason (`infoPastViewOnly`, `infoEditBlockedHasTreatment`).
|
||||
- Prisma: `Treatment.appointmentId` is `onDelete: SetNull` — deleting an appointment does **not** cascade-delete treatments/cases/tasks; do not rely on cascade for cleanup.
|
||||
|
||||
@@ -38,6 +38,10 @@ CLINIC + LAB KPIs/charts in `modules/today/today.service.ts`. Deep links: `compo
|
||||
|
||||
QR + URL for **sent** cases; focus page `/lab-case/[token]`. Auth redirect via `postAuthRedirect.ts` + `useEnterAppWhenAuthenticated` (not inside `login()`). Skill: `.cursor/skills/lab-case-share-link/SKILL.md`.
|
||||
|
||||
## API errors
|
||||
|
||||
Logical failures: `AppException(ErrorCode.X)` → `errors.X` in en/fa/nl. UI: `getUserFacingError`. Skill: `.cursor/skills/api-errors/SKILL.md`. Appointments/working hours: client IANA `timeZone` (never Node local `getHours()`).
|
||||
|
||||
## Notifications (inbox + live tabs)
|
||||
|
||||
Header bell: `UserNotification` + Socket.IO. Same `notification.created` also drives sidebar tab badges and soft list refresh on **currently open** Cases/Tasks/Treatment/Orgs pages. Skills: `.cursor/skills/notifications-inbox/SKILL.md`, `.cursor/skills/tab-badges/SKILL.md`.
|
||||
|
||||
@@ -7,16 +7,16 @@ description: Adds or migrates Dyolink API error codes with frontend translations
|
||||
|
||||
## Backend
|
||||
|
||||
1. Add to `ErrorCode` in `backend/src/common/errors/error-codes.ts`.
|
||||
1. Add code to `ErrorCode` in `backend/src/common/errors/error-codes.ts`.
|
||||
2. Throw with `AppException`:
|
||||
|
||||
```typescript
|
||||
throw new AppException(ErrorCode.MY_CODE, HttpStatus.BAD_REQUEST, [
|
||||
{ field: 'email', code: ErrorCode.VALIDATION_EMAIL_INVALID },
|
||||
]);
|
||||
throw new AppException(ErrorCode.MY_CODE, HttpStatus.BAD_REQUEST);
|
||||
```
|
||||
|
||||
3. DTOs: `@IsEmail({}, { message: ErrorCode.VALIDATION_EMAIL_INVALID })`
|
||||
3. DTOs: always `{ message: ErrorCode.X }` on class-validator decorators (do not rely on constraint-key fallbacks — e.g. `@Matches` is not always a mobile number).
|
||||
4. Do **not** throw Nest `BadRequestException('English…')` — unmapped Nest exceptions fall back to HTTP status only (`BAD_REQUEST`, `AUTH_UNAUTHORIZED`, …).
|
||||
5. Wall-clock rules (working hours, weekday): pass the client **IANA** `timeZone` and use `zoned-civil-time.ts`. Never `Date#getHours()` / `getDay()` on the UTC server.
|
||||
|
||||
## Frontend
|
||||
|
||||
|
||||
Reference in New Issue
Block a user