# Modal "Khôi phục Cấu trúc hồ sơ" — Quản lý cấu trúc đã xóa

> 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-08-14** trên sitdev
> (`https://sunecm-dev.sharepoint.vn/#`), tài khoản `ecm01` (Thủ thư — "Nguyễn Minh Hoàng (Giám đốc)"),
> trên 1 BHS đang ở tình trạng **Hoạt động**. Kịch bản đã chạy thật **2 vòng**:
> tạo tài liệu → xoá tài liệu → mở "Quản lý cấu trúc đã xóa" → tick → Khôi phục → tài liệu về lại cây.

> 📌 **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 vào đây.

---

## 1. Cách mở

Từ màn chi tiết **Bộ hồ sơ** → hover `btn-more` ở **header modal BHS** → click
**`btn-quan-ly-cau-truc-da-xoa`** ("Quản lý cấu trúc đã xóa").

| Điểm cần nhớ | Chi tiết |
| --- | --- |
| Chỉ có ở menu của **BHS** | Menu `btn-more` của **bảng Cấu trúc hồ sơ** chỉ có `mni-keo-tha`, `mni-nhap-tu-excel`, `mni-xuat-excel` — **không** có mục này (đo 2026-08-14) |
| Scope `btn-more` | Ở tab Cấu trúc hồ sơ có **2** phần tử `btn-more` → dùng `openMoreMenu(page, "record")` (xem `KHO-TAI-LIEU.md` mục 4.3) |
| Menu của TÀI LIỆU khác | Màn chi tiết 1 **tài liệu** có mục riêng `btn-quan-ly-file-da-xoa` ("Quản lý file đã xóa") — **khác** mục này, chưa khảo sát |

Modal mở **đè lên** modal BHS. 2 modal là **anh em** trong DOM (mỗi cái 1 `.ant-modal-root`),
không lồng nhau → lọc theo tiêu đề là an toàn.

Chờ ~3–5 s sau khi click để bảng nạp dữ liệu.

---

## 2. Cấu trúc modal

| Phần | Locator | Ghi chú |
| --- | --- | --- |
| Tiêu đề | `getByTestId("lbl-modal-title")` = **`"Khôi phục Cấu trúc hồ sơ"`** | ⚠️ testId này **trùng** với modal BHS phía sau → phải scope theo modal |
| Nút X (đóng) | `getByTestId("btn-close-modal")` | — |
| Bảng | `.ant-table` (`ant-table-small`) | Cột: `""` (checkbox), `Danh sách`, `Thời gian xóa`, `Người thực hiện`, `Vị trí` |
| Nút **Khôi phục** | ❌ **không có testId** — `getByRole("button", { name: "Khôi phục", exact: true })` | Nút primary ở footer. **`disabled` khi chưa tick dòng nào** |
| Nút **Đóng** | ❌ **không có testId** — `getByRole("button", { name: "Đóng" })` | — |
| Rỗng | `.ant-table` có class `ant-table-empty`, tbody hiển thị **`"Chưa có dữ liệu"`** | Không có element `.ant-empty` |

> Ngoài `lbl-modal-title` / `btn-close-modal` (và `avatar-*` trong ô "Người thực hiện"),
> **modal không có `data-testid` nào khác** → đã ghi vào `KHO-TAI-LIEU.TODO-DEV.md`.

### Nội dung 1 dòng (đo trên DOM thật)

| `td` | Cột | Ví dụ giá trị |
| :--: | --- | --- |
| 0 | checkbox | `label.ant-checkbox-wrapper` |
| 1 | Danh sách | `AT-KS-DAXOA-001` — **tên item**, không có mã prefix (`A.`, `G.`…) |
| 2 | Thời gian xóa | `16:26 14/08/2026` (`HH:mm dd/MM/yyyy`) |
| 3 | Người thực hiện | `Nguyễn Minh Hoàng (Giám đốc)` — tên hiển thị + chức danh, kèm avatar |
| 4 | Vị trí | `Cấu trúc Hồ sơ ATT-067-HĐSL` — với item xoá ở **gốc BHS** |

> ⚠️ **Chưa khảo sát**: giá trị cột **Vị trí** khi xoá item nằm **bên trong 1 thư mục**
> (dự đoán hiển thị đường dẫn thư mục — chưa kiểm chứng), và trường hợp **xoá cả thư mục có con**
> (danh sách liệt kê 1 dòng cho thư mục cha hay 1 dòng cho từng item con?).
> Có phân trang hay ô tìm kiếm không cũng chưa rõ — lúc khảo sát danh sách chỉ có 1 dòng,
> **không** thấy `.ant-pagination` và **không** có ô input nào.

### 🚨 Checkbox chỉ hiện khi **hover dòng**

Đo trên DOM: khi không hover, `span.ant-checkbox` có **`display: none`** (bounding box `0×0`) →
Playwright coi là *not visible*, click thẳng vào `input.ant-checkbox-input` hoặc
`label.ant-checkbox-wrapper` sẽ **timeout**. Hover dòng thì `display` đổi thành `flex` (16×16).

```ts
await row.hover();                       // bắt buộc
await row.locator("span.ant-checkbox").click();
// tick xong: tr có class `ant-table-row-selected`, span có class `ant-checkbox-checked`
```

Header bảng cũng có checkbox "chọn tất cả" (`.ant-table-thead label.ant-checkbox-wrapper`) —
cũng ở trạng thái `display:none` khi không hover, **chưa thử click**.

---

## 3. Hành vi khi bấm "Khôi phục"

Kiểm chứng 2 lần liên tiếp trên cùng 1 tài liệu:

| Sau khi bấm | Kết quả đo được |
| --- | --- |
| Dialog xác nhận | **KHÔNG có** — app khôi phục ngay |
| Toast | **KHÔNG có toast nào** (đặt `MutationObserver` rình `.ant-message-notice` từ trước lúc bấm → log rỗng) |
| Modal | **Tự đóng** ngay, chỉ còn lại modal BHS |
| Bảng Cấu trúc hồ sơ | **Tự refresh**, item xoá xuất hiện lại — **không** cần mở lại BHS |
| Vị trí item sau khôi phục | Về đúng chỗ cũ, **giữ nguyên mã prefix** (`G. AT-KS-DAXOA-001`) |
| Danh sách đã xoá | Mở lại modal → dòng vừa khôi phục **biến mất** khỏi danh sách |

> Vì **không có toast**, tín hiệu "khôi phục xong" duy nhất để assert là **modal đóng** +
> **dòng xuất hiện lại trong bảng Cấu trúc hồ sơ**.

---

## 4. Thao tác xoá (nguồn dữ liệu của màn này)

Xoá 1 item ở tab Cấu trúc hồ sơ: hover dòng → `[data-testid^="btn-more-action-"]` → `mni-xoa`.
Chi tiết menu dòng xem `KHO-TAI-LIEU.md` mục 4.4.

**Dialog xác nhận xoá item rỗng** (tài liệu / thư mục không có gì bên trong) — khảo sát MCP 2026-08-14:

| Phần | Giá trị |
| --- | --- |
| Modal | `.ant-modal-confirm`, testId **`mdl-xoa-xac-nhan`** |
| Title | `Xóa` |
| Nội dung | `Bạn có chắc chắn muốn xóa?` (`XOA_CONFIRM.RONG`) |
| Nút huỷ | **`btn-xoa-huy`** — nhãn `Hủy bỏ` |
| Nút xác nhận | **`btn-xoa-xac-nhan`** — nhãn `Xác nhận` |
| Toast sau khi xác nhận | **`"Xóa thành công"`** (`.ant-message-success`) — sống ~3 s |

> ✅ Cập nhật so với bản tài liệu cũ: dialog xoá item **rỗng** **đã có** testId
> (`mdl-xoa-xac-nhan`, `btn-xoa-huy`, `btn-xoa-xac-nhan`) — trước đây ghi là "chưa thấy testId";
> và app **có** bắn toast `"Xóa thành công"` — trước đây ghi là chưa rõ.
> ⚠️ Dialog xoá **thư mục có dữ liệu bên trong** (`XOA_CONFIRM.CO_DU_LIEU_BEN_TRONG`) vẫn **chưa
> khảo sát bằng MCP**.

---

## 5. Hàm dùng chung

| Hàm | Công dụng |
| --- | --- |
| `openQuanLyCauTrucDaXoa(page)` → `Locator` | `btn-more` (BHS) → `btn-quan-ly-cau-truc-da-xoa` → trả locator modal |
| `getModalCauTrucDaXoa(page)` | Locator modal (lọc theo tiêu đề `"Khôi phục Cấu trúc hồ sơ"`) |
| `layDanhSachCauTrucDaXoa(page, modal?)` → `CauTrucDaXoa[]` | Đọc mọi dòng: `{ ten, thoiGianXoa, nguoiThucHien, viTri }` |
| `tickItemCauTrucDaXoa(page, tenItem, modal?)` | Hover dòng → tick checkbox → assert dòng được chọn |
| `getNutKhoiPhuc(page, modal?)` | Locator nút "Khôi phục" (để assert `disabled` khi chưa chọn dòng) |
| `khoiPhucCauTrucDaXoa(page, tenItem(s), opts?)` | Luồng đầy đủ: mở modal → tick → "Khôi phục" → chờ modal đóng |
| `dongModalCauTrucDaXoa(page, modal?)` | Bấm "Đóng" nếu modal còn mở |
| `TOAST_XOA` / `MDL_XOA_XAC_NHAN` / `BTN_XOA_HUY` / `BTN_XOA_XAC_NHAN` | Toast + testId của dialog xoá |
| `MODAL_CAU_TRUC_DA_XOA_TITLE` / `EMPTY_CAU_TRUC_DA_XOA` | Tiêu đề modal + text bảng rỗng (`"Chưa có dữ liệu"`) |
| `xoaItemCauTruc(page, tenItem, opts?)` | (đã có sẵn) Xoá 1 item ở tab Cấu trúc hồ sơ |
