# Modal "Tạo mới 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-27** trên sitdev, tài khoản `ecm01`.

> 📌 **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 BHS → tab **Cấu trúc hồ sơ**:

| Cách                          | Thao tác                                                                                            |
| ----------------------------- | ----------------------------------------------------------------------------------------------------- |
| Tài liệu ở gốc BHS            | Hover `.ant-dropdown-trigger` **đầu tiên** trong `.ant-tabs-tabpane-active` → click `mni-them-tai-lieu` |
| Tài liệu bên trong 1 thư mục  | Hover dòng thư mục → hover `[data-testid^="btn-more-action-"]` → click `mni-them-tai-lieu`           |

**Chờ ~12 s** sau khi click — modal này nặng nhất trong màn (nạp metadata + khối upload file).

Header modal: dòng 1 = `"Tạo mới"` (nút), dòng 2 = `"Tạo mới tài liệu"`.
`lbl-modal-title` chứa cụm `"Tạo mới"`.

---

## 2. Field trong modal

Khối form chính có testId `frm-thong-tin-chung-tai-lieu`.

| Nhãn                        | testId                       | required | Ghi chú                                                                |
| --------------------------- | ---------------------------- | :------: | ------------------------------------------------------------------------ |
| **Tên tài liệu**            | `txt-ten-tai-lieu`           |    ✔     | —                                                                       |
| Mã tài liệu                 | ❌ *(read-only, không testId)* |        | Hệ thống tự sinh dạng `<Mã BHS>/TL001` → DEV bổ sung testId             |
| Số hiệu tài liệu            | `txt-so-hieu-tai-lieu`       |          | Placeholder `Nhập số hiệu tài liệu`                                     |
| Thư mục lưu trữ trong hồ sơ | `sel-folder-storage`         |          | Chọn thư mục chứa. Option hiển thị **có prefix mã** → dùng `matchMode="contains"` |
| **Loại tài liệu**           | `sel-loai-tai-lieu`          |    ✔     | —                                                                       |
| Đơn vị gửi                  | `txt-don-vi-xuat-ban`        |          | —                                                                       |
| Đơn vị nhận                 | `txt-don-vi-phe-duyet`       |          | —                                                                       |
| Người ký                    | `txt-nguoi-dang-ky`          |          | **Là text input**, không phải people-picker                             |
| Hình thức tài liệu          | `sel-dang-van-ban-tl`        |          | —                                                                       |
| Độ mật                      | `sel-security-level`         |          | Mặc định `Thường`                                                       |
| Trạng thái hiển thị         | `sel-trang-thai`             |          | Mặc định `Public`                                                       |
| Ngày tài liệu               | `datetime-ngay-ban-hanh`     |          | Placeholder `Chọn ngày`                                                 |
| Ngày hiệu lực từ            | `datetime-effective-date`    |          | —                                                                       |
| Ngày hết hiệu lực           | `dtp-ngay-het-han`           |          | ⚠️ prefix `dtp-` khác 2 field trên (`datetime-`) — không đồng nhất      |
| Số trang/bản                | `txt-so-trang-ban`           |          | —                                                                       |
| Số bản                      | `txt-so-ban`                 |          | —                                                                       |
| Thời hạn lưu trữ            | `sel-storage-term`           |          | —                                                                       |
| Từ khóa                     | `sel-tags-tu-khoa`           |          | tags multi                                                              |
| Ghi chú                     | `txa-ghi-chu`                |          | —                                                                       |
| Có bản vật lý               | `chk-co-ban-vat-ly`          |          | checkbox `input#basic_availableStorage`, **mặc định đã tick**. Bỏ tick → **`sel-hardCopyStatus` biến mất khỏi DOM** |
| *(động)* Ngày tài liệu      | `datetime-Ngaytailieu`       |          | Metadata động theo cấu trúc hồ sơ — **không hard-code**                 |
| Tình trạng bản cứng         | `sel-hardCopyStatus`         |          | ⚠️ **trùng testId** với field trên form BHS → scope theo modal. Chỉ tồn tại khi `chk-co-ban-vat-ly` được tick |

> ⚠️ **Nhóm testId trùng với panel lọc của tab Cấu trúc hồ sơ**: `txt-so-hieu-tai-lieu`,
> `sel-dang-van-ban-tl`, `txt-don-vi-phe-duyet`, `txt-don-vi-xuat-ban`.
> Nếu panel lọc đang mở khi modal này bật → `page.getByTestId(...)` bị strict-mode violation.
> **Luôn scope `page.locator(".ant-modal-content:visible").last()`**, không dùng `pw.inputText(...)` trần.

> ⚠️ Quy ước prefix của màn này **không khớp `tests/README.md`**: `datetime-` / `dtp-` thay vì `date-`,
> `sel-tags-tu-khoa` (gạch nối) thay vì `sel-tags-tuKhoa`. `PW.batchInput` **không route được**
> `datetime-` / `dtp-` / `chk-` → phải gọi helper trực tiếp.

---

## 3. Khối "Tài liệu số" (upload file)

| testId                        | Mô tả                                                                              |
| ----------------------------- | ------------------------------------------------------------------------------------ |
| `sec-tai-lieu-so`             | Section (+ `sec-tai-lieu-so-title`)                                                  |
| `sec-upload-tai-lieu-dinh-kem`| Vùng upload                                                                          |
| `file-upload-tai-lieu`        | Input/nút **Tải lên**                                                                |
| `tbl-danh-sach-file-dinh-kem` | Bảng file đã đính kèm                                                                |
| `empty-file-dinh-kem`         | Trạng thái rỗng: *"Chưa có tài liệu, chọn "Tải lên" hoặc "Kéo thả" để tạo tài liệu"* |
| `loading-atm`, `loading-noi-dung-tai-lieu` | Placeholder loading                                                     |

Cột bảng file: `""`, `STT`, `Tên tệp`, `""`, `Dung lượng`, `Thời gian upload`, `Người upload`,
`Trạng thái OCR toàn văn`, `Tài liệu bóc tách`, `Nhãn Purview`, `""`.

### 3a. Upload file — đã kiểm chứng

⚠️ `file-upload-tai-lieu` là **`div`** (nút "Tải lên"), **không phải `input[type=file]`** →
`setInputFiles` trên nó sẽ lỗi. Input thật **không có testId**:

```ts
const pane = page.locator(".ant-tabs-tabpane-active").last();
await pane.locator('input[type="file"]').first().setInputFiles("/duong/dan/file.txt");
await page.waitForTimeout(10_000); // upload + render dòng mới
```

- Có **2** `input[type="file"]` trong pane (dùng `.first()`)
- `accept` = `.docx,.xlsx,.png,.jpg,.txt,.jpeg,.pptx,.rar,.zip,.cad,.mp3,.mp4,.mkv,.mov,.m4a,.pdf`,
  `multiple = true` (đo lại 2026-08-04 — **không còn** biến thể chữ hoa `.DOCX/.PDF`, và **không có
  `.doc`, `.xls`**)
- 🚨 **Có toast `"Tải tài liệu lên thành công."`** — dòng "không có toast" trước đây là **sai**;
  ngoài ra file **trùng** sẽ bật dialog *"Danh sách tài liệu trùng…"* chặn luồng.
  Chi tiết cơ chế upload xem **[`KHO-TAI-LIEU.TAB-TAI-LIEU-SO.md`](./KHO-TAI-LIEU.TAB-TAI-LIEU-SO.md)**
  (upload sau khi tài liệu đã lưu làm ở **tab "Tài liệu số"** của màn chi tiết TL).

### 3b. testId xuất hiện SAU khi có file

| testId                                 | Mô tả                                        |
| -------------------------------------- | ---------------------------------------------- |
| `btn-tai-ve`                           | Tải về                                        |
| `btn-tai-xuong-toan-bo`                | Tải xuống toàn bộ                             |
| `btn-mo-menu-thao-tac`                 | 🐞 **lặp ở MỖI dòng file** (không phải nút chung của bảng — đo 2026-08-04) → scope theo dòng |
| `btn-xem-truoc-row-<uuid>`             | Xem trước file                                |
| `lbl-ten-tep-row-<uuid>`               | Tên tệp                                       |
| `lbl-dung-luong-row-<uuid>`            | Dung lượng                                    |
| `lbl-ngay-upload-row-<uuid>`           | Thời gian upload                              |
| `lbl-nguoi-upload-row-<uuid>`          | Người upload                                  |
| `tag-ocr-status-row-<uuid>`            | Trạng thái OCR toàn văn                       |
| `chk-tai-lieu-boc-tach-row-<uuid>`     | Checkbox "Tài liệu bóc tách"                  |
| `tag-purview-label-row-<uuid>`         | Nhãn Purview                                  |
| `btn-menu-row-<uuid>`                  | Menu "..." của dòng file                      |

`<uuid>` là id của file (dạng UUID) → bám bằng `[data-testid^="lbl-ten-tep-row-"]` hoặc filter theo tên tệp.

**Menu `btn-menu-row-<uuid>`** (hover dòng → hover nút):
`mni-tai-xuong` (Tải xuống), `mni-cap-nhat-thuoc-tinh` (Cập nhật thuộc tính),
`mni-quan-ly-phien-ban` (Quản lý phiên bản), `mni-xem-noi-dung-boc-tach` (Xem nội dung bóc tách),
`mni-xoa` (Xóa).

---

## 4. Khối "Phân quyền tài liệu"

`lbl-access-permission-card` (+ `-title`). Khác modal thư mục — **ở đây people-picker CÓ testId**:

| Quyền          | testId                        |
| -------------- | ----------------------------- |
| Quyền Owner    | `pp-multi-usersRightOwner`    |
| Quyền tạo mới  | `pp-multi-usersRightAdd`      |
| Quyền cập nhật | `pp-multi-usersRightEdit`     |
| Quyền tải file | `pp-multi-usersRightDownload` |
| Quyền Xem      | `pp-multi-usersRightViewers`  |

Mặc định các picker hiển thị **chip kế thừa từ BHS** (Owner = người tạo BHS + các account đã gán).

| Element             | Locator                                | Ghi chú                                                            |
| ------------------- | -------------------------------------- | -------------------------------------------------------------------- |
| **Break quyền riêng** | ❌ `input#basic_hasUniquePermission`  | Checkbox tách khỏi kế thừa. **Không có testId** → DEV bổ sung        |

> ⚠️ Giống modal thư mục: tick "Break quyền riêng" **không xoá** các chip đã kế thừa —
> muốn tài liệu chỉ còn account mình gán thì phải xoá chip trước (`clearInherited: true`).

> ⚠️ **`pp-multi-usersRightOwner` … trùng testId với form BHS phía sau** → luôn scope theo modal.

---

## 5. Khối "Thông tin liên quan"

Giống trên form BHS: `lbl-related-info`, `btn-add-related-ecm`, `btn-add-related-more-options`.
Chi tiết 2 pop-up xem mục 5 của [`KHO-TAI-LIEU.md`](./KHO-TAI-LIEU.md).

---

## 6. Lưu

Nút **Lưu lại** = `btn-save` (⚠️ trùng testId với `btn-save` của modal BHS → scope theo modal).

**Thành công** — điểm cần đặc biệt lưu ý:

- Modal **không đóng**, mà **chuyển thành màn chi tiết Tài liệu**
- `lbl-modal-title` đổi thành `"<Mã tài liệu>\n<Tình trạng>"`, vd `"ee-001-BĐSBA_T1_T005_33/TL001\nKhai báo"`
- 🚨 **`page.url()` bị thay bằng URL của TÀI LIỆU** (`?itemId=<id tài liệu>`) — mất `recordUrl` của BHS.
  → Phải lưu `recordUrl` từ trước, và quay lại BHS bằng `openBoHoSo(page, recordUrl)` (2 bước, xem mục 4.0 file chính).

**Màn chi tiết Tài liệu** có:

| testId              | Mô tả                                        |
| ------------------- | ---------------------------------------------- |
| `tab-general`       | Tab **Thông tin**                              |
| `tab-digitizedATM`  | Tab **Tài liệu số**                            |
| `tab-history`       | Tab **Lịch sử** — timeline "Lịch sử hoạt động", không phải bảng AntD |
| `btn-save`          | Lưu lại                                        |
| `btn-more`          | "..." → menu tài liệu (xem mục 4.3 file chính) |
| `btn-close-modal`   | Đóng                                           |

> 🚨 **3 tab này là `input[type="radio"]` ẩn** (`input.ant-radio-button-input`), **không click trực
> tiếp được** — Playwright báo `Element is outside of the viewport`. Phải click vào `label` bọc ngoài:
>
> ```ts
> await modal.getByTestId("tab-digitizedATM")
>   .locator('xpath=ancestor::label[1]').click();
> ```
>
> (Khác với tab của BHS — `lbl-tab-thongTin` / `lbl-tab-cauTrucHoSo` là `span`, click thẳng được.)

---

## 7. Dùng hàm

```ts
// Tài liệu ở gốc BHS:
await createTaiLieu(admin, pw, recordUrl, `AT-TL-1-${ts}`);

// Bên trong 1 thư mục (mở từ nút "Tạo mới" của toolbar rồi chọn `sel-folder-storage`):
await createTaiLieu(admin, pw, recordUrl, `AT-TL-2-${ts}`, { folderName: `AT-TM-1-${ts}` });

// Bên trong 1 thư mục, mở từ MENU DÒNG của thư mục (app tự điền `sel-folder-storage`) —
// bắt buộc với vai chỉ có quyền tại thư mục đó vì toolbar không có nút "Tạo mới":
await createTaiLieu(ecm06, pw, recordUrl, `AT-TL-4-${ts}`, {
  folderName: `AT-TM-1-${ts}`,
  moTuMenuDongThuMuc: true,
});

// Đọc lại giá trị field ngay trước khi Lưu (Loại tài liệu do hàm tự chọn option đầu):
const values = await createTaiLieu(admin, pw, recordUrl, `AT-TL-5-${ts}`, {
  readFields: ["sel-loai-tai-lieu"],
});
values["sel-loai-tai-lieu"]; // → nhãn option đã chọn

// Có phân quyền riêng:
await createTaiLieu(admin, pw, recordUrl, `AT-TL-3-${ts}`, {
  quyenRieng: true,
  perm: { account: "ecm05", permTestId: DOC_PERM.VIEW, clearInherited: true },
});
```

---

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

- Luồng **OCR** sau khi upload (`tag-ocr-status-row-<uuid>` chuyển trạng thái ra sao, mất bao lâu).
- Các mục menu file: `mni-quan-ly-phien-ban`, `mni-xem-noi-dung-boc-tach`, `mni-cap-nhat-thuoc-tinh`.
- `btn-mo-menu-thao-tac` (menu thao tác chung): hover không mở được dropdown trong lần thử —
  có thể cần `click()` thay vì `hover()`.
- Menu `mni-tao-shortcut` / `mni-nhan-ban` / `mni-chuyen-len` / `mni-chuyen-xuong` của dòng tài liệu.
- Tick/bỏ tick `chk-co-ban-vat-ly` **khi TẠO MỚI** (mới thử trên màn chi tiết tài liệu đã lưu).
