# Resident Documents — Luồng frontend (Upload & Duyệt)

Mô tả tài liệu này giới thiệu luồng frontend cho chức năng upload tài liệu của resident và quy trình duyệt (approval) bởi admin.

**Endpoints chính**

- `POST /api/v1/resident-documents` — Tạo 1..n document cho resident được lấy từ `resident_id` trong JWT (token).
- `PUT /api/v1/resident-documents/{document_id}` — Admin cập nhật document (thay đổi `status` thành `approved`/`rejected`, hoặc sửa các trường khác).

**Yêu cầu trước khi gọi API**

- Người dùng phải đăng nhập và gửi `Authorization: Bearer <token>`.
- Token phải chứa claim `resident_id` (hệ thống trả về claim này khi user có resident liên kết). Nếu thiếu sẽ nhận lỗi validation `MISSING_RESIDENT`.
- File phải được upload trước (qua endpoint upload file of backend) và bạn sẽ nhận được một `file_id` — dùng `file_id` trong payload.

**Luồng UI (kịch bản phổ biến)**

1. Upload file
   - Người dùng chọn ảnh/file ở form và upload lên API upload file (server trả `file_id`).
2. Tạo document (POST)
   - FE gom dữ liệu document (mảng) gồm `file_id`, `document_type`, optional `document_name`, `description`, `is_primary`.
   - Gọi `POST /api/v1/resident-documents` với body là mảng document. Header có `Authorization`.
3. Hiển thị kết quả
   - Sau khi tạo thành công, FE hiển thị document trong danh sách (status: pending/approved/rejected).
   - Nên gọi lại `GET /api/v1/residents/{id}/documents` để lấy dữ liệu chuẩn từ server.
4. Quy trình admin duyệt
   - Admin vào trang duyệt, mở document và thực hiện hành động Approve / Reject.
   - FE gọi `PUT /api/v1/resident-documents/{document_id}` với trường `status` = `approved` hoặc `rejected`.
   - Khi approved backend sẽ set `verified_at` và `verified_by`.

**Request / Response mẫu**

POST (tạo theo token):

```http
POST /api/v1/resident-documents
Authorization: Bearer <TOKEN>
Content-Type: application/json

[
  {
    "file_id": "a3f5b0b4-1111-2222-3333-e9c1",
    "document_type": "portrait",
    "document_name": "Ảnh chân dung",
    "description": "Ảnh mới",
    "is_primary": false
  }
]
```

Response (tóm tắt):

```json
{
  "success": true,
  "data": [
    {
      "id": "d4f2...",
      "resident_id": "7a1c...",
      "file_id": "a3f5b0b4-1111-2222-3333-e9c1",
      "document_type": "portrait",
      "status": "pending",
      "created_by": "user-uuid",
      "created_at": "2026-05-20T10:00:00Z"
    }
  ]
}
```

PUT (admin approve):

```http
PUT /api/v1/resident-documents/d4f2...
Authorization: Bearer <ADMIN_TOKEN>
Content-Type: application/json

{
  "status": "approved",
  "document_name": "Ảnh chính thức"
}
```

Response (tóm tắt):

```json
{
  "success": true,
  "data": {
    "id": "d4f2...",
    "resident_id": "7a1c...",
    "status": "approved",
    "verified_by": "admin-uuid",
    "verified_at": "2026-05-20T11:00:00Z"
  }
}
```

**Hiển thị trạng thái trong UI**

- `pending` — Hiển thị badge "Đang chờ duyệt" (màu vàng). Có thể hiện nút để huỷ hoặc edit thông tin (tuỳ quyền).
- `approved` — Hiển thị badge "Đã duyệt" (màu xanh) và hiển thị `verified_by` + `verified_at`.
- `rejected` — Hiển thị badge "Bị từ chối" (màu đỏ); hiển thị lý do nếu server trả.

**Xử lý lỗi & edge cases**

- `MISSING_RESIDENT` (validation): token không chứa `resident_id` — hiển thị thông báo và yêu cầu user liên hệ admin hoặc đăng nhập tài khoản có resident.
- `401` — token hết hạn/không hợp lệ — redirect login.
- `403` — không có quyền admin khi gọi API duyệt — ẩn các action admin trên UI nếu user không có quyền.
- `400` — payload không hợp lệ — hiển thị lỗi trường cụ thể.

**Gợi ý triển khai FE (components & UX)**

- `FileUpload` component: upload file, nhận `file_id` trả về từ backend.
- `DocumentForm` (modal): chọn `document_type`, nhập `document_name`, `description`, và flag `is_primary`.
- `DocumentsList` component: hiển thị danh sách document với badge trạng thái, action menu (edit/delete nếu có quyền), preview file.
- `AdminReviewPanel`: approve/reject buttons, optional comment field; confirm modal trước khi gọi PUT.
- Sau create/approve: re-fetch danh sách hoặc update local state; tránh tin tưởng hoàn toàn optimistic update cho trạng thái duyệt (nên re-fetch để đồng bộ `verified_by`/`verified_at`).

**Tham chiếu backend (code để FE tham khảo)**

- Controller create bằng token: [src/controllers/api/v1/resident-documents/index.ts](src/controllers/api/v1/resident-documents/index.ts)
- Controller admin update: [src/controllers/api/v1/resident-documents/{document_id}.ts](src/controllers/api/v1/resident-documents/{document_id}.ts)
- OpenAPI path: [src/contracts/resident/paths.ts](src/contracts/resident/paths.ts)

---

Nếu bạn muốn, mình có thể:

- thêm ví dụ cURL chi tiết cho FE, hoặc
- tạo snippet React hook (`useResidentDocuments`) để tái sử dụng.
