---
description: Khảo sát 1 màn hình bằng Playwright MCP rồi sinh file tài liệu + file function theo đúng mẫu dự án
---

# Khảo sát màn hình & sinh tài liệu (screen-instructions)

Bạn nhận **đường dẫn màn hình + yêu cầu** từ người dùng, dùng **Playwright MCP** khảo sát màn hình đó trên app chạy thật, rồi tạo ra bộ tài liệu kỹ thuật **giống hệt khuôn** các màn đã có (`KHO_TAI_LIEU.*`, `TANG.*`, `TRA_CUU.*`) trong `sun-ecm/src/screen-instructions/`.

Đầu vào của người dùng nằm ở cuối file. Thường gồm: **đường dẫn/URL màn hình** (vd `/settings/compartments`), **tên màn / mã UC**, và **phạm vi cần khảo sát** (toàn bộ màn, hay chỉ 1 modal/luồng).

---

## Nguyên tắc tối thượng — KHÔNG BỊA

> Đây là điểm quan trọng nhất, phân biệt tài liệu tốt với tài liệu vô dụng.

- **Chỉ ghi những gì đã TỰ THẤY** qua Playwright MCP (snapshot/DOM) hoặc đọc trực tiếp trong source (nếu người dùng cung cấp đường dẫn repo app).
- **TUYỆT ĐỐI không suy đoán testId, selector, placeholder, tên nút.** Không thấy thì đi mở màn/modal ra xem, không tự nghĩ.
- Phần do **người dùng mô tả miệng** hoặc **chưa mở tới được** → vẫn ghi vào tài liệu nhưng **đánh dấu `⚠️` + câu "chưa tự khảo sát, cần xác nhận trước khi viết case"** (đúng như mục 6e-1, 6e-2 trong `KHO_TAI_LIEU.md`). Ghi rõ *cái gì* còn thiếu để lần sau khảo sát tiếp.
- Nếu môi trường lỗi (SSO kẹt, trang trắng, không mở được modal) → **dừng và báo người dùng**, không viết tài liệu dựa trên phỏng đoán.

---

## Bước 0 — Đọc tài liệu nền TRƯỚC khi làm gì khác

Đọc bằng công cụ file (không phải MCP):

1. `sun-ecm/tests/README.md` — fixtures/account, `TIMEOUT`, helper `PW`, **quy ước prefix testId** (`txt-`, `sel-`, `tree-sel-`, `pp-multi-`…), selector AntD dùng chung. **Mọi file bạn sinh ra phải bám các quy ước này.**
2. Một file mẫu **cùng độ phức tạp** với màn sắp khảo sát:
   - Màn danh mục CRUD đơn giản (List + Modal) → mẫu `TANG.md` + `TANG.function.ts`.
   - Màn có trang chi tiết / nhiều modal / phân quyền → mẫu `KHO_TAI_LIEU.md` + `KHO_TAI_LIEU.function.ts`.
3. `CLAUDE.md` (gốc repo) — bảng "Tài liệu theo màn hình" và mục "Thêm màn hình mới".

---

## Bước 1 — Chốt thông tin đầu vào

Từ `$ARGUMENTS` xác định:

| Cần chốt | Cách lấy |
| --- | --- |
| **Đường dẫn màn** (path sau `#`, vd `/settings/compartments`) | Người dùng đưa. Thiếu → hỏi. |
| **`TEN_MAN_HINH`** (SLUG hoa, gạch dưới — vd `KHO_TAI_LIEU`) | Suy từ tên màn; là tên 2 file `.md`/`.function.ts` và tiêu đề bảng. Không chắc → hỏi. |
| **`ten-man-hinh`** (kebab, cho tên skill) | Bản thường hoá của trên. |
| **UC liên quan** | Người dùng đưa (vd UC9, UC77). |
| **Phạm vi** | Toàn màn hay 1 phần? Ảnh hưởng độ sâu khảo sát. |
| **BASE_URL** | Đọc `sun-ecm/.env.sit` / `.env.sitdev` / `.env.development`. URL đầy đủ để mở MCP = `BASE_URL` + path. Nếu có nhiều môi trường, hỏi người dùng dùng cái nào. |

Chỉ hỏi lại khi thật sự thiếu; suy được thì suy rồi nói rõ giả định.

---

## Bước 2 — Khảo sát bằng Playwright MCP

> App yêu cầu đăng nhập (SSO). Browser của MCP **không** tự dùng `playwright/.auth/*.json`.
> Mở màn xong nếu gặp trang login → **báo người dùng đăng nhập thủ công trên cửa sổ MCP** rồi tiếp tục (account tham khảo trong `account.json`, vai mặc định `ecm01` Thủ thư / `ecm09` Admin). Không tự nhập mật khẩu trừ khi người dùng bảo.

Trình tự cho **mỗi** khu vực (màn danh sách, từng modal, từng tab):

1. `browser_navigate` tới `BASE_URL` + path. Chờ init (trang nặng JS).
2. `browser_snapshot` — đọc cây accessibility để nắm bố cục, nhãn nút, form.
3. **Gặt toàn bộ `data-testid` đang có trên trang** bằng `browser_evaluate`:
   ```js
   () => [...document.querySelectorAll('[data-testid]')].map(e => ({
     id: e.getAttribute('data-testid'),
     tag: e.tagName.toLowerCase(),
     text: (e.innerText || e.getAttribute('placeholder') || '').trim().slice(0, 50),
   }))
   ```
4. **Cột bảng**: `browser_evaluate` lấy `[...document.querySelectorAll('.ant-table-thead th')].map(t => t.innerText.trim())`.
5. **Mở từng modal / dropdown / tab rồi lặp lại bước 2–4** — nhiều testId chỉ xuất hiện sau khi mở modal (vd form Thêm mới, modal phân quyền). Với dropdown antd, scope panel đang mở `.ant-select-dropdown:visible` cuối cùng để khỏi đọc nhầm dropdown vừa đóng (xem cảnh báo trong `TANG.function.ts`).
6. Với field **không có** `data-testid` (people-picker, dropdown custom…): ghi lại **locator ổn định** đã kiểm chứng (theo `placeholder`, `role`, class AntD, quan hệ sibling…) — đúng cách `KHO_TAI_LIEU.md` mô tả people-picker "Người dùng/Nhóm".
7. Ghi lại **hành vi động**: nút đổi khi có/không quyền, field disable khi sửa, toast thành công/ lỗi (`.ant-message-success` / `.ant-notification-notice-message`), modal confirm khi xoá…

Ưu tiên xác minh trực tiếp mọi testId sẽ đưa vào tài liệu. `browser_take_screenshot` khi cần đối chiếu trực quan.

---

## Bước 3 — Viết `sun-ecm/src/screen-instructions/<TEN_MAN_HINH>.md`

Bám đúng **khung mục** của file mẫu (bỏ mục không áp dụng, giữ nguyên đánh số & giọng văn tiếng Việt):

- **Header**: tên màn + đường dẫn + "Dùng cho UC…"; câu trỏ về `tests/README.md` cho phần dùng chung.
- **Mục 0 — File function đi kèm**: block `import { ... } from "../../../src/screen-instructions/<TEN>.function"` + **bảng liệt kê mọi hàm** (tên + công dụng), kèm quy tắc "thao tác dài > ~10 dòng dùng ở ≥2 spec → viết thành hàm, không copy vào spec".
- **Mục 1 — Thông tin màn hình**: URL, hằng URL/data (nếu có), permission, mã excel/eform… (những gì khảo sát được).
- **Các field của form/modal**: bảng `testId | loại | required | giá trị mẫu | ghi chú` — cột "loại" theo prefix trong README.
- **Button / element**: bảng `testId/locator | mô tả`.
- **Từng modal / pop-up / tab**: cấu trúc DOM, cột bảng, các bước thao tác, cảnh báo (`⚠️`) về strict-mode/timing/scope.
- **Nghiệp vụ khi Lưu / Xoá**: toast, điều kiện chặn, confirm dialog.
- **Phân quyền theo vai** (nếu màn có).
- **Mục cuối — Lưu ý quan trọng**: đánh số các cạm bẫy (hard wait, tách people-picker khỏi `batchInput`, goto reset DOM…).

Đánh dấu `⚠️` + "chưa tự khảo sát" cho mọi phần chưa xác minh trực tiếp.

---

## Bước 4 — Viết `sun-ecm/src/screen-instructions/<TEN_MAN_HINH>.function.ts`

Đóng gói **mọi thao tác dài** khảo sát được thành hàm export, theo đúng style `TANG.function.ts` / `KHO_TAI_LIEU.function.ts`:

- Import chuẩn: `Page`, `expect` từ `@playwright/test`; `TIMEOUT` từ `../constant/timeout`; `PW` từ `../utils/PW`; `const BASE_URL = process.env.BASE_URL!`.
- **Tái sử dụng helper `PW`** (`inputText`, `inputDropDownList`, `inputPeoplePicker`, `batchInput`, `clickButton`…) — không tự viết lại thao tác mà `PW` đã có.
- Mỗi hàm 1 JSDoc tiếng Việt ngắn nêu công dụng + số mục tài liệu tương ứng.
- Assert kết quả bằng toast/message như mẫu; hàm tạo dữ liệu trả về `recordUrl`/giá trị khi cần.
- Helper nội bộ (không export) cho pattern lặp; nhớ scope AntD đúng (`.ant-modal-content:visible` `.last()`, `.ant-select-dropdown:visible` `.last()`).
- Có thể thêm hàm dọn dữ liệu (`deleteXByCodes`) nếu màn hỗ trợ.

Tên hàm trong file phải khớp đúng bảng ở Mục 0 của file `.md`.

---

## Bước 5 — Đăng ký màn hình mới (theo "Thêm màn hình mới" trong CLAUDE.md)

1. Thêm **1 dòng** vào bảng "Tài liệu theo màn hình" trong `CLAUDE.md`: `| <Tên màn> (\`<path>\`) | <UC> | \`sun-ecm/src/screen-instructions/<TEN>.md\` | \`/<ten-man-hinh>\` |`.
2. Tạo skill đọc-tài-liệu tương ứng `.claude/commands/<ten-man-hinh>.md` — **copy khuôn `kho-tai-lieu.md`**, đổi đường dẫn file `.md` và tên màn:
   ```
   Đọc file tài liệu màn hình tại `sun-ecm/src/screen-instructions/<TEN>.md` và dùng nó làm tài liệu kỹ thuật chính cho mọi yêu cầu liên quan đến màn hình "<Tên màn>" (`<path>`).

   Sau khi đọc xong, xác nhận ngắn gọn bạn đã nắm tài liệu rồi hỏi người dùng cần làm gì tiếp theo (nếu không có `$ARGUMENTS`).

   $ARGUMENTS
   ```

---

## Bước 6 — Báo cáo

Tóm tắt: 2 file đã tạo, số hàm export, các testId chính, và **liệt kê rõ những phần còn `⚠️` chưa khảo sát** để người dùng biết cần làm tiếp gì. Đề xuất chạy thử 1 hàm/luồng để nghiệm thu nếu phù hợp.

---

$ARGUMENTS
