# Modal "Cập nhật quyền tài liệu" — màn Kho tài liệu

> Tài liệu con của [`KHO-TAI-LIEU.md`](./KHO-TAI-LIEU.md). Hàm dùng chung nằm trong `KHO-TAI-LIEU.functions.ts`.
> **Khảo sát trực tiếp bằng Playwright MCP ngày 2026-07-29** trên sitdev (`https://sunecm-dev.sharepoint.vn/#`),
> tài khoản `ecm01` (Thủ thư), trên tài liệu `HSTM-349-htrt/TL001`.
> Mọi locator dưới đây đều đã tự tay xác minh trên DOM thật.

Đây là màn dùng cho các case **UC25 1.4.6 — 608 / 609 / 610** (cập nhật & lưu phân quyền của **Tài liệu**).

> 📌 **Phạm vi tài liệu — chỉ mô tả màn hình** (field, nút, testId, cấu trúc modal, thông báo, luồng
> thao tác). **Không** ghi logic test / kỳ vọng của case / quy tắc nghiệp vụ vào đây — chỗ của chúng
> là spec `tests/<mục>/`, `<mục>.steps.ts` và `<mục>.md`. Xem `KHO-TAI-LIEU.md` mục đầu file.

---

## 1. Cách mở

Từ màn chi tiết 1 **Tài liệu** → hover `btn-more` → click `btn-kiem-tra-phan-quyen`
("Kiểm tra phân quyền"). Chờ ~5 s.

```ts
// Mở màn chi tiết TL: từ BHS → tab Cấu trúc hồ sơ → click tên tài liệu
await openBoHoSo(page, recordUrl);
await openTabCauTrucHoSo(page);
await getActiveTabPane(page)
  .locator('[data-testid^="lnk-ten-ho-so-tai-lieu-"]')
  .filter({ hasText: tenTaiLieu })
  .click();
```

> ✅ Dùng hàm **`openChiTietTaiLieu(page, recordUrl, tenTaiLieu)`** rồi
> **`openCapNhatQuyenTaiLieu(page)`**.

**Xếp lớp modal** (đã đo): modal BHS (lớp 1) → modal chi tiết TL (lớp 2, **chồng lên**, không thay thế)
→ modal Cập nhật quyền tài liệu (lớp 3).

| Modal              | `lbl-modal-title`                              | Số `btn-more` | Có tab "Phân quyền" |
| ------------------ | ---------------------------------------------- | :-----------: | :-----------------: |
| BHS                | `"HSTM-349-htrt\nKhai báo"`                    | **2** (ở tab Cấu trúc hồ sơ) |        ❌         |
| Chi tiết Tài liệu  | `"HSTM-349-htrt/TL001\nKhai báo"`              |       1       |        ❌         |
| Cập nhật quyền TL  | `"Cập nhật quyền tài liệu <mã tài liệu>"`      |       0       |        ✔          |

> ⚠️ `page.url()` đổi sang `itemId` của **tài liệu** khi mở màn chi tiết TL → giữ `recordUrl` của BHS.
> 🚨 Modal chi tiết TL chỉ có **1** `btn-more` → `getRecordModal(page).getByTestId("btn-more").first()` an toàn.

---

## 2. Cấu trúc modal

- Chỉ **1 tab**: `Phân quyền` + badge trạng thái `Kế thừa` / `Độc lập`
  (giống hệt tab Phân quyền của modal Thêm/Cập nhật thư mục).
- Alert đầu modal + nút hành động trong `.ant-alert-action`.
- 5 trường quyền: **Quyền Owner / Quyền Tạo mới / Quyền Cập nhật / Quyền Tải file / Quyền Xem**.
- Footer: `Xác nhận` / `Hủy`.
- ❌ **Không có `data-testid`** cho tab, alert, 5 trường quyền và nút footer — chỉ có
  `lbl-modal-title`, `btn-close-modal` và các `avatar-container` / `avatar-image`.
  → đã ghi vào `KHO-TAI-LIEU.TODO-DEV.md`.
- ❌ Modal này **không có nút org-chart** (`*-orgchart-btn`) như form BHS — chỉ nhập bằng cách gõ tên.

Locator modal: `.ant-modal-content:visible` **có tab "Phân quyền"** →
✅ dùng `getThuMucModal(page)` (dùng chung với modal thư mục, xem `KHO-TAI-LIEU.functions.ts`).

---

## 3. Hai trạng thái của modal

### 3a. Đang KẾ THỪA

| Phần            | Giá trị đo được                                                                 |
| --------------- | --------------------------------------------------------------------------------- |
| Badge tab       | `Kế thừa`                                                                         |
| Alert           | `"Thư mục/Tài liệu này đang kế thừa quyền từ thư mục cha"` + nút `"Đặt quyền độc lập"` |
| Chú thích       | `"Quyền đang được kế thừa từ thư mục cha. Thay đổi quyền ở thư mục cha sẽ ảnh hưởng đến thư mục này."` |
| 5 trường quyền  | Chỉ là nhãn + **avatar chỉ đọc** — số ô nhập `.ant-select-selection-search-input` = **0** |
| `.people-picker` | Vẫn có **5** phần tử trong DOM (nhưng không chứa ô nhập)                          |

> ⚠️ Vì `.people-picker` tồn tại ở cả 2 trạng thái, **đừng** dùng số lượng picker để phân biệt —
> phân biệt bằng **số ô nhập** (`expectPickerPhanQuyenChiDoc`).

### 3b. Đã ĐỘC LẬP (quyền riêng)

| Phần           | Giá trị đo được                                                                        |
| -------------- | ----------------------------------------------------------------------------------------- |
| Badge tab      | `Độc lập`                                                                                 |
| Alert          | `"Thư mục/Tài liệu này có quyền độc lập — không kế thừa từ thư mục cha"` + nút `"Khôi phục kế thừa"` |
| Chú thích      | `"Quyền được cấu hình riêng cho thư mục này. Thay đổi quyền ở thư mục cha sẽ không ảnh hưởng."` |
| 5 trường quyền | Có đủ **5 ô nhập** → chỉnh sửa được                                                       |

> ⚠️ Ô nhập luôn có thuộc tính `readonly` khi select **đang đóng** (cơ chế AntD), `disabled = false`,
> wrapper **không** có class `ant-select-disabled`. Click vào ô thì `readonly` mất
> (đã đo: `readOnly` false khi dropdown mở). Đừng assert `toBeEditable()` trước khi click —
> xem `expectPickerNhapDuoc`.

---

## 4. Chuyển trạng thái

### 4a. "Đặt quyền độc lập" (Kế thừa → Độc lập)

Click nút trong `.ant-alert-action` → confirm dialog `.ant-modal-confirm`:

| Phần     | Giá trị                                                                                                                          |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Title    | `"Đặt quyền độc lập?"`                                                                                                            |
| Nội dung | `"Toàn bộ quyền của thư mục/tài liệu này sẽ được đặt lại và không còn kế thừa từ thư mục cha. Hành động này không thể hoàn tác."` |
| Nút      | `Hủy` / `Xác nhận`                                                                                                                |

Sau khi Xác nhận: badge → `Độc lập`, alert đổi, **5 ô nhập xuất hiện**, chip kế thừa **giữ nguyên**.
❗ **Không có toast** ở bước này (thay đổi mới chỉ ở phía UI, phải bấm `Xác nhận` ở footer mới lưu).

> ✅ Dùng hàm **`datQuyenDocLap(page, modal?)`** (dùng chung với modal thư mục).

### 4b. "Khôi phục kế thừa" (Độc lập → Kế thừa)

| Phần     | Giá trị                                                                                                                       |
| -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Title    | `"Khôi phục kế thừa quyền?"`                                                                                                   |
| Nội dung | `"Toàn bộ quyền độc lập của thư mục/tài liệu này sẽ bị xóa và thay thế bằng quyền từ thư mục cha. Hành động này không thể hoàn tác."` |
| Nút      | `Hủy` / `Xác nhận`                                                                                                             |

Sau khi Xác nhận: badge → `Kế thừa`, ô nhập biến mất, **quyền riêng bị xoá** và thay bằng quyền của cha
(đã kiểm chứng: TL có 3 người → khôi phục kế thừa → còn đúng 2 người của thư mục cha). Không có toast.

> ✅ Dùng hàm **`khoiPhucKeThuaTrongModal(page, modal?)`**.

---

## 5. Cập nhật đối tượng phân quyền + Lưu

Điền người vào 5 trường **y hệt** people-picker chỗ khác: click ô nhập → gõ account id → chờ ~5 s
→ `Enter` → `Escape`. Gõ `ecm08` cho ra đúng 1 option `"DN Lê Duy Nam Chuyên viên nghiệp vụ"`.

Bấm `Xác nhận` ở footer:

- Toast **2 lần nối tiếp**: `"Đang xử lý"` → `"Thành công"` (đo bằng MutationObserver)
- Modal Cập nhật quyền **tự đóng**, quay về modal chi tiết TL
- Mở lại modal thì thấy đúng người vừa thêm → đã lưu vào DB

> ✅ Dùng hàm **`themQuyenTrongModalThuMuc(page, account, permTestIds?)`** + **`luuModalThuMuc(page, modal?, label?)`**
> (dùng chung với modal thư mục).

**Hành vi trạng thái kế thừa sau khi lưu** (đã kiểm chứng trực tiếp — trả lời case 609/610):

| Trạng thái TL trước khi lưu | Thao tác                                   | Sau khi lưu                       |
| --------------------------- | -------------------------------------------- | --------------------------------- |
| `Kế thừa`                   | Đặt quyền độc lập → sửa quyền → Xác nhận    | **`Độc lập`** (quyền riêng tư)    |
| `Độc lập`                   | Sửa quyền → Xác nhận                        | **giữ nguyên `Độc lập`**          |

---

## 6. 🚨 Khối "Phân quyền tài liệu" trên màn chi tiết TL bị ẩn

Giống BHS (xem `KHO-TAI-LIEU.md` mục 4.6): trên màn chi tiết Tài liệu, khối
`lbl-access-permission-card` ("Phân quyền tài liệu", gồm 5 `pp-multi-usersRight*` + checkbox
"Break quyền riêng") nằm trong 1 phần tử có thuộc tính **`hidden`** → `getBoundingClientRect()` = 0×0,
mọi `click`/`fill` sẽ timeout. `innerText` vẫn đọc được nên assert `toContainText` có thể **pass giả**.

→ Sau khi tài liệu đã tạo, **chỉ sửa được quyền qua modal này** (hoặc modal Phân quyền nâng cao của BHS).

---

## 7. ⚠️ Chưa khảo sát

- Mở modal này bằng **vai không phải Owner BHS** (ecm05/ecm06): chưa rõ họ có `btn-more` trên màn
  chi tiết TL không (ở modal BHS thì vai VIEW **không** có — xem `KHO-TAI-LIEU.PHAN-QUYEN-THEO-VAI.md`).
  Các case 608–610 chạy bằng các vai này sẽ là phép thử đầu tiên.
- Nút `Hủy` / `btn-close-modal` khi đang sửa dở: chưa thử → chưa rõ có hỏi xác nhận huỷ không.
- Validate: chưa thử bấm `Xác nhận` khi xoá sạch cả 5 trường (vd bỏ hết Owner).
- Menu `...` của **thư mục** → "Kiểm tra phân quyền": chưa thử (case hiện tại đi qua menu dòng
  → "Cập nhật", xem `KHO-TAI-LIEU.MODAL-TAO-THU-MUC.md` mục 6b).
