feat: replace patient messages tab with pinnable notes

Port the tauri AddNoteModal notes feature into the admin patient case-file,
replacing the mislabeled «پیام‌ها» (SMS log) tab with «یادداشت‌ها».

Backend (src/Patient):
- PatientNote entity + repository (record-scoped, pinned-first ordering)
- CRUD endpoints on PatientController: GET /notes, POST /note,
  PATCH /note/{uuid} (edit body + toggle pin), DELETE /note/{uuid}
- author display name captured server-side from the current user
- migration for patient_notes; docs/api/patient.md updated

Frontend (assets/admin):
- NotesTab: compose box, newest/oldest sort, pinned-first list with
  accent rail + pin/edit/delete, edit modal, confirm-delete, empty state
- tab key/label/icon messages -> notes; onAddNote deep-links the notes tab

Tests: PatientNoteTest (create/list-order/edit/pin/delete/validation/ownership),
PatientDetailPage notes cases (render/empty/pin/create).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-16 12:51:33 +03:30
co-authored by Claude Opus 4.8
parent 65083854cb
commit 15c92903e1
8 changed files with 636 additions and 39 deletions
+34
View File
@@ -577,6 +577,40 @@ When an appointment's status changes to `confirmed` via `PATCH /api/v1/appointme
| 422 | `ERR_VALIDATION_001` | متن خالی (`field: body`) |
| 404 | `ERR_PATIENT_001` / `ERR_NOT_FOUND_001` | رکورد/پیام یافت نشد یا tenant دیگر |
> توجه: پنل ادمین دیگر تب «پیام‌ها» را نشان نمی‌دهد؛ جای آن «یادداشت‌ها» آمده است. این اندپوینت‌ها باقی می‌مانند ولی توسط پنل مصرف نمی‌شوند.
---
## یادداشت‌های بیمار (Notes)
یادداشت‌های شخصیِ پرسنل روی پرونده، **پین‌شدنی**. مشترک بین همه‌ی کارکنانِ صاحبِ پرونده؛ نام سازنده هنگام ثبت ذخیره می‌شود (پس از حذف کاربر هم باقی می‌ماند). scope به رکورد و tenant.
**Permission:** `IS_AUTHENTICATED_FULLY` (مالک رکورد)
### GET `/api/v1/patient/{uuid}/notes`
لیست، **پین‌شده‌ها اول، سپس جدیدترین**. Response: `{ success, data: [{ uuid, body, pinned, author, created_at, updated_at }] }`
### POST `/api/v1/patient/{uuid}/note`
```json
{ "body": "متن یادداشت", "pinned": false }
```
`body` الزامی (trim)؛ `pinned` اختیاری (پیش‌فرض `false`). `author`/سازنده سمت سرور از کاربر جاری (`real_name` یا موبایل) پر می‌شود. Response `201`.
### PATCH `/api/v1/patient/note/{uuid}`
```json
{ "body": "متن جدید", "pinned": true }
```
هر دو فیلد اختیاری (partial). با ارسال `pinned` تنها → toggle پین بدون تغییر متن. `body` خالی → `422`. `updated_at` ست می‌شود. فقط مالک.
### DELETE `/api/v1/patient/note/{uuid}`
حذف. فقط مالک؛ در غیر این صورت `404`.
### Errors
| HTTP | Code | Description |
|------|------|-------------|
| 422 | `ERR_VALIDATION_001` | متن خالی (`field: body`) |
| 404 | `ERR_PATIENT_001` / `ERR_NOT_FOUND_001` | رکورد/یادداشت یافت نشد یا tenant دیگر |
---
## مالی بیمار (Financials: پرداخت / تراکنش / کیف‌پول)