improve BA skill

This commit is contained in:
Leonard-ThindPad-P50
2026-09-09 06:34:57 +07:00
parent 119792967c
commit 2c7bcde741
42 changed files with 5429 additions and 54 deletions

View File

@@ -45,14 +45,15 @@ viết và đề xuất khung dựa trên biến thể gần nhất — không i
## Cú pháp
```
/ba-3-specification <US-id...> [--product <loại>] [--only srs|ac|nfr|api] [--lang vi,en,ko] [--out <path>] [go]
/ba-3-specification <US-id...> [--product <loại>] [--only srs|ac|nfr|api|uiconv|wf] [--proto <path|url>] [--lang vi,en,ko] [--out <path>] [go]
```
| Tham số | Ý nghĩa |
|---|---|
| `<US-id...>` | Một hoặc vài US. **Quá 3 US một lần thì chất lượng giảm rõ rệt** |
| `--product` | Ghi đè `PRODUCT` của profile cho lần chạy này. Nhiều loại: `--product screen,api-service` |
| `--only` | Chỉ sinh một loại tài liệu |
| `--only` | Chỉ sinh một loại tài liệu. `uiconv` và `wf` chỉ có với `screen` |
| `--proto` | Prototype tham chiếu: thư mục ảnh, file HTML, link Figma, hoặc mục SAD (`docs/sections/07-giao-dien.md`). Không có ⇒ skill tự tìm; vẫn không có ⇒ WF là đề xuất của BA |
| `--lang` | Ngôn ngữ text hiển thị. Bỏ qua nếu `PRODUCT` không có giao diện |
| `go` | Bỏ bước dừng xác nhận input |
@@ -71,15 +72,38 @@ Ví dụ:
- `BR` — quy tắc nghiệp vụ đã chốt
- `RBAC` — ma trận phân quyền
**Rất nên có:**
- Quy ước UI của dự án (nếu có) — để khỏi tự phát minh lại
**Rất nên có (với `screen`):**
- **Prototype tham chiếu** — Figma, HTML, ảnh, hoặc mục *Thiết kế giao diện* của SAD. Có nó,
WF của BA là bản ghi lại + đối chiếu, và **đội design không phải làm lại phần bố cục**
- Design system dự án dùng (tên + version) — để `UICONV` §11 map thành phần
- Quy ước UI sẵn có của dự án — `UICONV` chỉ bổ khuyết, không chép lại
- Danh sách mã lỗi đã dùng — để tiếp số, không mở dãy mới
- Glossary — để dùng đúng thuật ngữ
- Wireframe/prototype nếu đã có
**Nếu dự án có sẵn guideline** (như `GLOBAL_UI_CONVENTION.md`, `GLOBAL_NFR.md` của Berriz),
nói rõ đường dẫn khi gọi. Skill sẽ tuân thủ thay vì tự định nghĩa.
## `UICONV` và `WF` — phần "thay design" của bộ skill
Hai artifact chỉ sinh khi `PRODUCT = screen`:
| Artifact | Ở đâu | Sinh khi nào | Trả lời câu gì |
|---|---|---|---|
| `UICONV_<PROJECT>.md` | `00-index/` | **Một lần** ở lần chạy ba-3 đầu tiên; các lần sau bổ sung | Toàn project phân trang, toast, định dạng, trạng thái rỗng, từ vựng thành phần… thế nào |
| `WF_<US>.md` + `.html` | `03-specification/` | Mỗi US, sau bảng thành phần và bảng text | Từng màn hình: vùng nào chứa thành phần nào, trông ra sao ở từng trạng thái, khác prototype ở đâu |
Mức độ "thay design" tuỳ đầu vào:
| Đầu vào | WF là gì | Đội design còn làm gì |
|---|---|---|
| Prototype đã duyệt + design system | Bản ghi lại và đối chiếu — dev dựng UI từ WF + SRS + design system | Chỉ xử lý dòng lệch ở WF §5 |
| Prototype đã duyệt, chưa có design system | Như trên, nhưng `UICONV` §11 chưa map được component | Chọn/dựng design system, visual |
| Chỉ có mô tả UI dạng văn bản (SAD §7) | Bản ghi lại ở mức vùng; kích thước, thứ tự là đề xuất BA | Duyệt WF, làm visual |
| Không có gì | **Đề xuất của BA**, đánh dấu ✏️ | Thiết kế bố cục thật, hoặc duyệt đề xuất BA rồi làm visual |
WF **không bao giờ** thay phần visual (màu, font, icon, motion). Mục §6 của WF ghi rõ phần
Designer còn phải làm — để không ai tưởng "có WF là xong design".
## Quy trình 7 bước — bạn tham gia ở đâu
| Bước | Skill làm | Bạn làm |
@@ -91,6 +115,8 @@ nói rõ đường dẫn khi gọi. Skill sẽ tuân thủ thay vì tự định
| 5. Mã lỗi & text | Sinh bảng, đòi nguyên văn từng chuỗi | Chốt giọng văn với PO/thiết kế |
| 6. NFR | Rà 7 nhóm, đòi số đo + cách verify | Hỏi người dùng ngưỡng chấp nhận được |
| 7. API contract | Đề xuất contract, đánh dấu điểm cần BE chốt | Gửi BE xác nhận |
| 8. `UICONV` *(screen, lần đầu)* | Điền quy ước từ design system / prototype, đánh dấu ô chưa chọn | Chốt với Designer, ký |
| 9. `WF` *(screen)* | Bản đồ prototype ↔ màn hình, bảng vùng, HTML low-fi, **bảng lệch §5** | Đưa Designer duyệt (✏️) hoặc PO xác nhận bảng lệch (🎨) |
🔴 **Bước 3 và Bước 5 là hai bước cần bạn đi hỏi nhiều nhất.** Skill sẽ liệt kê chính xác
những gì còn thiếu dưới dạng `OQ` — đó là danh sách việc của bạn.
@@ -98,11 +124,15 @@ những gì còn thiếu dưới dạng `OQ` — đó là danh sách việc củ
## Bạn sẽ nhận được gì
```
ba-output/<PROJECT>/00-index/
└── UICONV_<PROJECT>_v1.0.md ← quy ước giao diện, một lần cho cả project (screen)
ba-output/<PROJECT>/03-specification/
├── SRS_<US-id>_v1.0.md ← tài liệu chính
├── AC_<US-id>_v1.0.md ← nếu tách riêng
├── NFR_<US-id>_v1.0.md
└── API_<US-id>_v1.0.md
├── API_<US-id>_v1.0.md
├── WF_<US-id>_v1.0.md ← bố cục từng màn hình + bảng lệch prototype ↔ SRS (screen)
└── WF_<US-id>_v1.0.html ← render low-fi, mở bằng trình duyệt, đổi khung nhìn/trạng thái (screen)
```
Cộng năm bảng in ra màn hình: truy vết US→AC→BR→field→mã lỗi · tự chấm G3 · checklist
@@ -184,6 +214,8 @@ Contract BA đề xuất là **giả định**, không phải sự thật.
4. **QA xác nhận mọi AC đều test được** — chữ ký hay bị bỏ qua nhất *(bỏ được ở `RIGOR = light`)*
5. Đã ký đủ theo `RIGOR`: `light` chỉ PO · `standard` PO + Tech Lead + QA · `strict` thêm
Bảo mật/Pháp chế, và API **phải do BE xác nhận**
6. *(screen)* `WF` §5 không còn lệch Tồn tại/Hành vi chưa quyết; tập `C-id` khớp hai chiều với
SRS; `UICONV` và `WF` (chế độ ✏️) có chữ ký Designer
Rồi bàn giao cho dev và chuyển sang `/ba-4-delivery-support`.

View File

@@ -1,6 +1,6 @@
---
name: ba-3-specification
description: Giai đoạn 3 của quy trình BA — viết đặc tả chi tiết tới mức dev code được và QA test được. Viết SRS/FRD cho một hoặc nhiều User Story, với PART 2 thay đổi theo loại sản phẩm - màn hình và bảng field (screen), endpoint và hợp đồng dữ liệu (api-service), luồng dữ liệu và data contract (data-pipeline), metric và ngưỡng chấp nhận (ml-model), lịch và idempotency (batch-job) - cộng acceptance criteria Given/When/Then đủ cả luồng lỗi, bảng mã lỗi, yêu cầu phi chức năng, API contract. Kích hoạt khi người dùng nói "viết SRS", "đặc tả US này", "viết acceptance criteria", "field spec", "bảng mã lỗi", "NFR", "API contract", "data contract", "spec cho dev", "ngưỡng chấp nhận cho mô hình". Input là BACKLOG đã qua G2; output vào ba-output/<PROJECT>/03-specification/ và phải qua Gate G3 (Ready for Dev) trước khi dev bắt đầu.
description: Giai đoạn 3 của quy trình BA — viết đặc tả chi tiết tới mức dev code được và QA test được. Viết SRS/FRD cho một hoặc nhiều User Story, với PART 2 thay đổi theo loại sản phẩm - màn hình và bảng field (screen), endpoint và hợp đồng dữ liệu (api-service), luồng dữ liệu và data contract (data-pipeline), metric và ngưỡng chấp nhận (ml-model), lịch và idempotency (batch-job) - cộng acceptance criteria Given/When/Then đủ cả luồng lỗi, bảng mã lỗi, yêu cầu phi chức năng, API contract; với sản phẩm có giao diện còn sinh quy ước giao diện cấp project (UICONV) và wireframe low-fi (WF markdown + HTML) đối chiếu với prototype nếu có. Kích hoạt khi người dùng nói "viết SRS", "đặc tả US này", "viết acceptance criteria", "field spec", "bảng mã lỗi", "NFR", "API contract", "data contract", "spec cho dev", "ngưỡng chấp nhận cho mô hình", "wireframe", "bố cục màn hình", "đối chiếu prototype", "quy ước UI". Input là BACKLOG đã qua G2 (+ prototype/design system nếu có); output vào ba-output/<PROJECT>/03-specification/ và phải qua Gate G3 (Ready for Dev) trước khi dev bắt đầu.
---
# GĐ3 · SPECIFICATION — Đặc tả chi tiết
@@ -10,7 +10,10 @@ Mục tiêu: **viết tới mức dev code được mà không phải đoán, QA
Đây là sản phẩm chính của nghề BA và là gate nghiêm nhất. Một chỗ mơ hồ lọt qua G3 sẽ
thành một bug hoặc một CR — với chi phí gấp nhiều lần chi phí làm rõ ở đây.
Output: `SRS` · `AC` · `NFR` · `API` (+ `WF` nếu có) trong `ba-output/<PROJECT>/03-specification/`
Output: `SRS` · `AC` · `NFR` · `API` trong `ba-output/<PROJECT>/03-specification/`.
Khi `PRODUCT = screen` thêm: `UICONV` (một lần cho cả project, ở `00-index/`) và `WF` (mỗi US,
gồm `.md` + `.html` low-fi) — hai artifact này là phần **thay vai trò design ở mức bố cục và
trạng thái** khi dự án có prototype tham chiếu, và là **đề xuất chờ Designer duyệt** khi không có.
## Bốn nguyên tắc bất di bất dịch
@@ -44,21 +47,34 @@ Ví dụ minh hoạ cho từng bước, ở nhiều domain khác nhau: `examples
4. **Phạm vi lần chạy** — liệt kê đích danh `US` sẽ đặc tả. **Quá 3 US một lần chạy thì
chất lượng giảm rõ rệt** — đề xuất chia.
5. **Biến thể PART 2 sẽ nạp** — theo `PRODUCT` (Bước 2). US có nhiều loại ⇒ nêu đủ.
6. **Guideline dự án cần tuân thủ** — tìm và nêu: quy ước UI, bộ NFR chuẩn, glossary, danh
sách mã lỗi đã dùng. Không có ⇒ nói rõ sẽ tự định nghĩa và cần ai duyệt.
6. **Guideline dự án cần tuân thủ** — tìm và nêu: `00-index/UICONV_<PROJECT>.md`, quy ước UI
sẵn có của dự án (vd `GLOBAL_UI_CONVENTION.md`), bộ NFR chuẩn, glossary, danh sách mã lỗi
đã dùng. **Chưa có `UICONV` mà `PRODUCT = screen` ⇒ lần chạy này phải sinh nó trước (Bước 8)**
và nêu ai duyệt (Designer + PO).
7. **Ngôn ngữ hiển thị** — chỉ hỏi khi `PRODUCT` có giao diện cho người. Không có ⇒ ghi N/A.
8. **Prototype / design system tham chiếu** — chỉ khi `PRODUCT = screen`. Tìm theo thứ tự:
tham số `--proto`, thư mục `design/` hoặc `prototype/` trong repo, mục *Thiết kế giao diện*
của SAD (`docs/sections/07-*.md`, `docs/SAD.md` §7), file Figma/HTML/ảnh người dùng đưa.
Trình bảng `Nguồn | Định dạng | Phiên bản | Phủ màn hình nào`. Không có ⇒ nói rõ **WF sẽ là
đề xuất của BA và cần Designer duyệt trước G3**; có ⇒ prototype là nguồn sự thật về bố cục.
Rồi **hỏi xác nhận** bảy điểm trên.
Rồi **hỏi xác nhận** tám điểm trên.
Bỏ qua khi lệnh có `go`.
## Thực hiện — 7 bước
## Thực hiện — 9 bước
Bước 1–7 cho mọi `PRODUCT`. Bước 8–9 chỉ khi `PRODUCT = screen`; loại khác ghi "N/A" trong
báo cáo cuối, không bỏ im lặng.
### Bước 1 — Khung SRS và tóm tắt nghiệp vụ
Dùng `templates/srs.md`. **Không bỏ mục nào** — mục không áp dụng thì ghi "N/A" kèm lý do,
đừng xoá; xoá mục làm người đọc không biết là đã cân nhắc hay đã quên.
Với `PRODUCT = screen`: §2.3.6 (trạng thái), §4.3 (định dạng) và mọi quy ước phân trang, toast,
vị trí nút **tham chiếu `UICONV`** thay vì định nghĩa lại. Muốn khác ⇒ ghi vào `UICONV` §12.
Viết mục **Tóm tắt nghiệp vụ** cho PO đọc: US này giải quyết `RQ` nào, người dùng được gì,
khác hiện tại chỗ nào. Ngôn ngữ nghiệp vụ, không có tên bảng dữ liệu, không có tên component.
Đây là hiện thực hoá quy tắc W10 — một tài liệu, ba người đọc.
@@ -215,9 +231,60 @@ Ba điểm phải chốt, hay bị bỏ:
- **Thời gian** dạng gì, múi giờ nào — UTC hay giờ địa phương?
- **Phân trang** offset hay cursor, có `hasNext`/`total` không?
### Bước 8 — Quy ước giao diện cấp project (`UICONV`) — chỉ `screen`
Dùng `templates/ui-convention.md`, ghi vào `00-index/UICONV_<PROJECT>.md`. **Sinh một lần** ở
lần chạy ba-3 đầu tiên của project; các lần sau chỉ đọc và bổ sung §10 (dãy mã lỗi), §11 (từ
vựng thành phần), §12 (ngoại lệ).
Nguồn điền, theo thứ tự ưu tiên: design system của dự án > prototype tham chiếu > guideline
sẵn có > **hỏi** (ghi `OQ`, không tự chọn). Mọi ô "chọn một" phải chọn xong — còn dấu `/`
giữa các phương án là chưa làm.
🔴 `UICONV` là **hợp đồng giữa BA và Design**: BA sở hữu text và trạng thái, Design sở hữu
layout và từ vựng thành phần. Cả hai ký. Dự án không có Designer ⇒ PO ký thay và ghi `DEC-nn`.
### Bước 9 — Wireframe & bố cục (`WF`) — chỉ `screen`
Dùng `templates/wireframe.md` (bảng) + `templates/wireframe.html` (render low-fi). Ghi
`WF_<US>_v1.0.md` và `WF_<US>_v1.0.html` vào `03-specification/`. Làm **sau** Bước 3 và 5, vì
WF chỉ sắp xếp những thành phần và text đã có trong SRS.
**Hai chế độ, phải khai rõ ở dòng `Nguồn bố cục` của header:**
| Chế độ | Khi nào | WF là gì | Ai duyệt |
|---|---|---|---|
| 🎨 **Prototype tham chiếu** | Có Figma / HTML / ảnh / mô tả UI đã duyệt | **Bản ghi lại** bố cục của prototype bằng bảng + đối chiếu với SRS | PO (Designer đã duyệt prototype) |
| ✏️ **BA tự dựng** | Không có prototype | **Đề xuất** bố cục theo `UICONV` | **Designer bắt buộc** ở `standard`+ |
Bốn việc, theo thứ tự:
1. **Bản đồ prototype ↔ SCR** (§1): mỗi màn hình trong `SRS` §2.1 có trong prototype không,
ở frame/trang nào. Màn hình prototype không có ⇒ BA tự dựng phần đó và đánh dấu.
2. **Bảng vùng + vị trí thành phần** (§3.x.1–2) cho **mọi** `SCR`, kể cả modal và trang lỗi.
🔴 Tập `C-id` phải **khớp hai chiều** với bảng thành phần `SRS` §2.3.1 — chạy
`grep -o "C[0-9][0-9]"` trên cả hai file và so tập. Thành phần có trong prototype mà SRS
không có ⇒ **không tự thêm vào SRS**, ghi lệch ở §5 cho PO quyết.
3. **Trạng thái, responsive, tương tác trình bày** (§3.x.3–6): hai trạng thái rỗng phải **trông**
khác nhau; mọi con số (ms, s, px) có nguồn `UICONV`/prototype, không có ⇒ `OQ`.
4. **Sinh HTML** từ bảng: mỗi `SCR` một `<section id>`, mỗi vùng `data-zone`, mỗi thành phần
`data-c` đúng C-id, mỗi trạng thái một khối `data-state`. Thành phần **chỉ có ở prototype**
(lệch loại Tồn tại ở §5) vẽ đúng chỗ prototype đặt nó bằng `<span class="proto-only"
data-ref="WF §5 #n">`, **không gán `data-c`** — khung đỏ đứt để PO nhìn thấy ngay quyết định
còn thiếu. Giữ nguyên `<style>`/`<script>` của template để mọi WF trong project trông giống
nhau. Không màu thương hiệu, không font.
**§5 Lệch prototype ↔ SRS là mục quan trọng nhất của WF.** Phân loại: Tồn tại · Hành vi · Bố
cục · Text · Trạng thái thiếu. Bên thắng theo W11: SRS thắng Tồn tại/Hành vi/Text; prototype
thắng Bố cục. Lệch Tồn tại/Hành vi chưa có người quyết ⇒ 🔴 chặn G3. Bảng rỗng vẫn phải ghi
"đã đối chiếu n màn hình, không lệch".
Điều **không** làm ở bước này: chọn màu, font, icon, khoảng cách chính xác, motion — ghi vào
§6 "Designer còn phải làm". Có prototype phủ hết ⇒ ghi "Không".
## Trước khi kết thúc
In năm thứ:
In năm thứ (sáu khi `PRODUCT = screen`):
**① Bảng truy vết** `US` → `AC` → `BR` → `field` → `mã lỗi`. Ô trống là chỗ chưa đặc tả xong.
@@ -234,10 +301,17 @@ bảng ràng buộc của PART 2 hoặc trong bảng mã lỗi ⇒ G3 không th
**⑤ `OQ` mở** — cái nào ảnh hưởng tới hành vi hệ thống thì đánh dấu 🔴 chặn G3.
**⑥ Đối chiếu WF ↔ SRS ↔ UICONV** *(chỉ `screen`)* — in: tập `C-id` lệch hai chiều (phải rỗng) ·
số dòng §5 của WF theo loại và số dòng chưa có người quyết · mục nào của SRS định nghĩa lại
thứ đã có trong `UICONV` mà không có dòng ở `UICONV` §12 · WF đang ở chế độ 🎨 hay ✏️ và
Designer đã ký chưa.
Nhắc chữ ký theo `RIGOR` (`../ba-lifecycle/references/workflow.md` §2 — G3):
`light` không cần chữ ký QA · `standard` cần **PO + Tech Lead + QA** · `strict` thêm
**Bảo mật/Pháp chế**, và API **phải do BE xác nhận**, không chấp nhận contract BA tự đề xuất.
Ở mức `standard` trở lên, QA phải xác nhận *mọi AC đều test được*.
`PRODUCT = screen` ở `standard`+ thêm **Designer** ký `UICONV` và `WF` (WF ở chế độ 🎨 thì
Designer đã duyệt prototype, chỉ cần PO xác nhận bản đồ §1 và §5 không còn lệch mở).
## Bẫy thường gặp
@@ -250,6 +324,15 @@ sai. Mỗi field phải truy về một `BR` hoặc một câu trả lời của
**Mô tả hành vi bằng ảnh.** Wireframe minh hoạ bố cục; hành vi nằm ở bảng thành phần và AC
(quy tắc W11). Viết "xem hình" ở cột hành vi là chưa đặc tả.
**Chép prototype vào SRS.** Prototype có nút mà backlog không có ⇒ đó là scope creep bằng hình
ảnh. Ghi vào WF §5, để PO quyết, không tự thêm thành phần vào bảng SRS.
**Mỗi SRS tự đặt quy ước.** Phân trang, vị trí toast, định dạng ngày phải tham chiếu `UICONV`.
Ba SRS với ba kiểu phân trang là ba màn hình người dùng phải học lại.
**WF không có bảng lệch.** WF chỉ có hình mà không có §5 là chưa đối chiếu — và đúng chỗ
prototype khác SRS là chỗ dev sẽ tự chọn một bên.
**Trộn "ẩn" với "disable".** Ba trạng thái khác nhau (W7), và người dùng phản ứng rất khác:
nút biến mất làm họ tưởng mất quyền, nút mờ có tooltip làm họ hiểu vì sao chưa bấm được.

View File

@@ -6,7 +6,9 @@
> **Tiêu chí G3 riêng của biến thể này** *(thay cho dòng "bảng field" và "wireframe" ở
> [workflow.md §2](../../../ba-lifecycle/references/workflow.md))*:
> mỗi màn hình có bảng thành phần đủ 8 cột · mỗi field có bảng field đủ 10 cột · hai trạng
> thái rỗng khác nhau · mọi thành phần có điều kiện ẩn/khoá/read-only rõ ràng.
> thái rỗng khác nhau · mọi thành phần có điều kiện ẩn/khoá/read-only rõ ràng · **có `WF_<US>`
> (md + html) phủ mọi `SCR`, tập `C-id` khớp hai chiều, bảng lệch prototype ↔ SRS không còn
> dòng Tồn tại/Hành vi chưa quyết** · quy ước chung tham chiếu `UICONV_<PROJECT>`.
---
@@ -47,7 +49,8 @@ quay lại có giữ trạng thái danh sách không, huỷ giữa chừng thì
## 2.3 SCR-01 — <Tên màn hình>
**Wireframe:** `WF_SCR-01.png` *(bố cục — hành vi xem bảng bên dưới)*
**Bố cục:** `WF_<US>_v1.0.md` §3.x · `WF_<US>_v1.0.html#SCR-01` · prototype: *(frame/trang,
hoặc "✏️ BA tự dựng")*. Hành vi xem bảng bên dưới, không suy từ hình (W11).
### 2.3.1 Bảng thành phần
@@ -147,9 +150,12 @@ lỗi trải nghiệm nghiêm trọng và rất hay xảy ra khi spec không nó
### 2.3.6 Trạng thái rỗng, đang tải, lỗi
*Cách trình bày (skeleton/spinner/banner) theo `UICONV` §6 — ở đây chỉ ghi **text riêng** cho
đối tượng của màn hình này và nút hành động. Trông thế nào, ở vùng nào: `WF` §3.x.3.*
| Trạng thái | Hiển thị gì | Text nguyên văn | Nút hành động |
|---|---|---|---|
| Đang tải | | | |
| Đang tải | theo UICONV §6 | | |
| **Chưa có dữ liệu nào** | | "Chưa có bản ghi nào. Tạo bản ghi đầu tiên?" | ✅ Tạo mới |
| **Bộ lọc không khớp** | | "Không tìm thấy kết quả phù hợp với bộ lọc." | ✅ Xoá lọc |
| Lỗi tải dữ liệu | | | ✅ Thử lại |

View File

@@ -6,8 +6,8 @@
| **Date** | YYYY-MM-DD |
| **Author** | <BA> (skill ba-3-specification) |
| **Status** | 🟡 Draft |
| **Approved by** | PO: — · Tech Lead: — · QA: — |
| **Source** | BACKLOG_… v1.0 · BR_… v1.0 · RBAC_… v1.0 · IMPACT_… v1.0 |
| **Approved by** | PO: — · Tech Lead: — · QA: — · Designer: — *(chỉ khi PRODUCT = screen)* |
| **Source** | BACKLOG_… v1.0 · BR_… v1.0 · RBAC_… v1.0 · IMPACT_… v1.0 · UICONV_… v1.0 *(screen)* |
| **Scope** | US-0nn |
| **Profile** | `screen · brownfield · standard` *(PRODUCT · LIFECYCLE · RIGOR)* |
| **Biến thể PART 2** | `srs-part2/screen.md` |
@@ -21,8 +21,10 @@
> **Quy ước đọc tài liệu này** *(quy tắc W11 — chỉ áp dụng khi `PRODUCT = screen`)*
> Bảng thành phần quyết định **một phần tử có tồn tại hay không** và **nó hành xử thế nào**.
> Wireframe quyết định **nó nằm ở đâu, to bằng nào**. Khi hai thứ mâu thuẫn: bảng thắng về
> sự tồn tại và hành vi, hình thắng về bố cục — và mâu thuẫn đó phải được báo cho BA.
> `WF_<US>.md` + `.html` (và prototype nó tham chiếu) quyết định **nó nằm ở đâu, to bằng nào,
> trông ra sao ở từng trạng thái**. `UICONV_<PROJECT>.md` quyết định **quy ước chung** (phân
> trang, toast, định dạng) — tài liệu này chỉ ghi phần khác. Khi mâu thuẫn: bảng thắng về sự
> tồn tại và hành vi, WF thắng về bố cục — và mâu thuẫn đó phải có dòng ở `WF` §5.
---
@@ -199,6 +201,8 @@ nội dung cảnh báo vận hành** — đặc tả ở PART 2 của biến th
| 8 | Không còn OQ mở ảnh hưởng hành vi | | |
| 9 | Không còn `TBD` trong bảng đặc tả PART 2 và bảng mã lỗi | | |
| 10 | Đã áp đúng bảng "Bớt ở light" / "Thêm ở strict" theo `RIGOR` | | |
| 11 | *(screen)* `WF` có cho mọi `SCR`, tập `C-id` khớp hai chiều, §5 không còn lệch Tồn tại/Hành vi chưa quyết | | |
| 12 | *(screen)* Quy ước chung tham chiếu `UICONV`; chỗ khác có dòng ở `UICONV` §12 | | |
**Quét bắt buộc trước khi nộp:**

View File

@@ -0,0 +1,211 @@
# UICONV — Quy ước giao diện — <PROJECT>
| | |
|---|---|
| **Version** | 1.0 |
| **Date** | YYYY-MM-DD |
| **Author** | <BA> (skill ba-3-specification, activity `uiconv`) |
| **Status** | 🟡 Draft |
| **Approved by** | Designer: — · PO: — |
| **Source** | <prototype / design system / SAD §7 / GLOBAL_UI_CONVENTION của dự án / DEC-nn> |
| **Scope** | Toàn project — mọi `SRS` và `WF` có `PRODUCT = screen` |
| **Profile** | `screen · … · …` |
## Change Log
| Version | Date | Người sửa | Thay đổi | CR |
|---|---|---|---|---|
| 1.0 | | | Bản đầu | — |
> **Vai trò của tài liệu này.** Một project có nhiều `SRS`, mỗi lần chạy tối đa 3 US. Không có
> tài liệu này thì mỗi `SRS` tự đặt một kiểu phân trang, một vị trí toast, một định dạng ngày.
> `SRS` và `WF` **tham chiếu** mục ở đây thay vì định nghĩa lại; muốn lệch phải ghi vào §12.
>
> **Thứ tự ưu tiên khi lệch:** Design system của dự án (nếu có) > Prototype đã duyệt > `UICONV`
> > `SRS` từng US. `SRS` chỉ được ghi khác `UICONV` khi có dòng ở §12.
---
## 0. Nguồn
| | |
|---|---|
| **Design system** | *(tên + version + link, hoặc "Chưa có — dev chọn theo NFR, quy ước ở đây là ràng buộc tối thiểu")* |
| **Prototype tham chiếu** | *(đường dẫn / URL / mục SAD, phiên bản, ngày)* |
| **Guideline sẵn có của dự án** | *(vd: GLOBAL_UI_CONVENTION.md — nếu có thì file này chỉ bổ khuyết, không chép lại)* |
| **Ai sở hữu quy ước** | BA đề xuất · **Designer duyệt** · PO duyệt phần text/giọng văn |
| **Nền tảng** | Web desktop / web responsive / app di động — *(theo BRIEF)* |
## 1. Khung layout
| | |
|---|---|
| **Grid** | *(số cột · gutter px · max-width px)* |
| **Header cố định** | Có / Không — chứa: *(logo, tìm kiếm toàn cục, ngôn ngữ, tài khoản…)* |
| **Sidebar / menu** | Trái cố định / thu gọn được / không có |
| **Footer** | Có / Không — chứa: |
| Breakpoint | Khoảng (px) | Sidebar | Bảng dữ liệu | Form |
|---|---|---|---|---|
| Desktop | ≥ 1024 | Hiện | Bảng | 2 cột |
| Tablet | 768–1023 | Thu gọn icon | Bảng cuộn ngang | 1 cột |
| Mobile | < 768 | Ẩn sau nút menu | **Card** | 1 cột |
## 2. Điều hướng
| Câu hỏi | Quy ước |
|---|---|
| Quay lại từ màn hình chi tiết/form | Nút Back **và** breadcrumb — cả hai |
| Quay lại có giữ trạng thái danh sách (trang, bộ lọc, sắp xếp)? | ✅ Giữ, qua query string |
| Vào bằng URL trực tiếp với id không tồn tại | Trang trạng thái "Không tìm thấy" + nút về danh sách |
| Vào bằng URL trực tiếp không có quyền | Trang "Không có quyền" + nút về trang chủ; **không** lộ tên bản ghi |
| Rời màn hình khi form đang dở | Modal xác nhận "Rời trang? Thay đổi chưa lưu sẽ mất." |
| Menu với chức năng không có quyền | ❌ Ẩn (W7) |
| Mở chi tiết | Cùng tab, đổi URL |
## 3. Danh sách
| | |
|---|---|
| **Kiểu phân trang** | Offset (`page`, `size`) / Cursor / Cuộn vô hạn |
| **Số dòng mỗi trang** | Mặc định … · tuỳ chọn … |
| **Sắp xếp mặc định** | *(vd: ngày tạo giảm dần)* |
| **Bấm vào dòng** | Mở chi tiết / Chỉ nút "Chi tiết" mới mở |
| **Cột hành động** | Cột cuối, icon + tooltip / Menu ⋯ |
| **Chọn nhiều dòng** | Checkbox cột đầu, thanh hành động hàng loạt xuất hiện phía trên |
| **Tìm kiếm** | Gõ rồi Enter / Gợi ý sau khi ngừng gõ … ms |
| **Bộ lọc** | Thanh ngang trên bảng (desktop) · nút "Lọc" mở bottom-sheet (mobile) |
| **Cột trên mobile** | Bảng → card; card hiện tối đa … trường: *(liệt kê thứ tự ưu tiên chung)* |
## 4. Form
| | |
|---|---|
| **Vị trí nút** | Lưu (nút chính) **bên phải**, Huỷ (nút phụ) bên trái nút Lưu |
| **Đánh dấu bắt buộc** | Dấu `*` màu cảnh báo sau nhãn |
| **Thời điểm validate** | Khi rời field (blur) **và** khi bấm Lưu |
| **Vị trí message lỗi** | Ngay dưới field, thay chỗ hint; field viền cảnh báo |
| **Lỗi nhiều field** | Focus vào field lỗi đầu tiên, cuộn tới nó |
| **Lỗi từ server không gắn field** | Banner đầu form |
| **Sau lỗi mạng/5xx** | 🔴 **Giữ nguyên dữ liệu đã nhập**, mở lại nút Lưu |
| **Đang gửi** | Khoá toàn form, spinner trong nút Lưu, nhãn đổi "Đang lưu…" |
| **Huỷ khi có thay đổi** | Modal xác nhận |
| **Nhãn field** | Trên field (không đặt cạnh trái) |
| **Nút Lưu khi form chưa hợp lệ** | Bấm được, hiện lỗi / 🔒 disable — *(chọn một)* |
## 5. Phản hồi hệ thống
| Loại | Dùng khi | Vị trí | Thời lượng | Có nút đóng |
|---|---|---|---|---|
| **Toast thành công** | Lưu/xoá thành công | Góc trên phải (desktop) · trên cùng (mobile) | 3 s tự đóng | ✅ |
| **Toast lỗi** | Lỗi không gắn field, có thể thử lại | Như trên | Không tự đóng | ✅ |
| **Modal xác nhận** | Hành động không hoàn tác được (xoá, gửi duyệt) | Giữa màn | — | Nút chính bên **phải**; hành động nguy hiểm dùng nút nguy hiểm |
| **Inline dưới field** | Lỗi validation | Dưới field | Tới khi sửa | — |
| **Banner trong vùng** | Lỗi tải một vùng dữ liệu | Trong vùng đó | Tới khi thử lại | Nút Thử lại |
| **Trang trạng thái** | 404 / 403 / lỗi toàn trang | Toàn trang | — | Nút về danh sách/trang chủ |
## 6. Trạng thái chuẩn
| Trạng thái | Hiển thị | Text mặc định (VI) | Text (EN) | Nút |
|---|---|---|---|---|
| Đang tải danh sách | Skeleton … dòng | — | — | — |
| Đang tải chi tiết/form | Skeleton theo bố cục | — | — | — |
| Đang tải cục bộ (dropdown, tính lại) | Spinner nhỏ tại chỗ | — | — | — |
| **Rỗng — chưa có dữ liệu nào** | Minh hoạ + câu dẫn + nút chính | "Chưa có <đối tượng> nào." | | Tạo mới |
| **Rỗng — bộ lọc không khớp** | Câu dẫn + nút phụ *(không minh hoạ, để khác loại trên)* | "Không tìm thấy kết quả phù hợp với bộ lọc." | | Xoá lọc |
| Lỗi tải | Banner trong vùng | "Không tải được dữ liệu." | | Thử lại |
| Không có quyền | Trang trạng thái | "Bạn không có quyền truy cập nội dung này." | | Về trang chủ |
| Không tìm thấy | Trang trạng thái | "Không tìm thấy nội dung yêu cầu." | | Về danh sách |
`SRS` §2.3.6 chỉ ghi **phần khác** với bảng này (vd: câu dẫn riêng cho đối tượng), còn lại tham chiếu.
## 7. Ẩn · khoá · read-only *(quy tắc W7)*
| Trạng thái | Trình bày | Bắt buộc kèm |
|---|---|---|
| `❌ ẩn` | Không render | — |
| `🔒 disable` | Mờ 45%, con trỏ mặc định | **Tooltip nêu lý do** khi hover/focus |
| `👁 read-only` | Giá trị dạng text, không viền ô nhập, nhãn giữ nguyên | — |
## 8. Định dạng hiển thị
*`SRS` §4.3 tham chiếu bảng này; chỉ ghi khác khi có dòng ở §12.*
| Loại | Định dạng | Ví dụ | Ghi chú |
|---|---|---|---|
| Ngày | `dd/MM/yyyy` | 30/08/2026 | |
| Ngày giờ | `dd/MM/yyyy HH:mm` | 30/08/2026 14:05 | **Múi giờ hiển thị:** … *(theo người dùng / cố định UTC+7)* |
| Lưu trữ thời gian | ISO-8601 UTC | | Khớp `API` §1 |
| Số | `#,##0` | 1.234.567 | Dấu phân cách: … |
| Tiền | `#,##0 ₫` | 1.234.567 ₫ | Làm tròn: … · đa tiền tệ: … |
| Phần trăm | `0,0 %` | 12,5 % | |
| Rỗng / null | `—` | | Thống nhất toàn hệ thống |
| Số lớn (id) | Hiển thị nguyên văn chuỗi | | Không format |
## 9. Ngôn ngữ và giọng văn
| | |
|---|---|
| **Ngôn ngữ hỗ trợ** | VI (mặc định) · EN · … — thiếu bản dịch ⇒ hiện **khoá** hay **VI**? *(chọn một)* |
| **Xưng hô** | "bạn" / không xưng hô |
| **Thông điệp lỗi** | Nói **vì sao** và **làm gì tiếp**; không mã kỹ thuật; kết thúc bằng dấu chấm |
| **Nhãn nút** | Động từ, viết hoa chữ đầu: "Tạo mới", "Lưu", "Huỷ" |
| **Tiêu đề màn hình** | Danh từ: "Danh sách cửa hàng" |
| **Độ dài nhãn tối đa** | … ký tự — để không vỡ ở ngôn ngữ dài (KO/DE) |
| **Khoá i18n** | `<scr>.<thành phần>.<thuộc tính>` — vd `scr01.search.ph` |
## 10. Dãy mã lỗi
| Dãy | Module / phạm vi | Số kế tiếp chưa dùng | Ai cấp |
|---|---|---|---|
| `E-<DOMAIN>-` | | 0001 | BA |
## 11. Từ vựng thành phần
*`WF` §3.x.2 cột "Kiểu trình bày" chỉ dùng từ ở cột đầu. Cột thứ hai map sang design system khi có.*
| Kiểu trình bày | Component trong design system | Dùng khi |
|---|---|---|
| Nút chính | | Một hành động chính mỗi màn hình |
| Nút phụ | | Huỷ, hành động thứ cấp |
| Nút nguy hiểm | | Xoá, huỷ đơn — luôn kèm modal xác nhận |
| Liên kết | | Điều hướng trong text |
| Ô nhập | | |
| Ô nhập số | | |
| Chọn 1 | | ≤ 7 lựa chọn: radio · > 7: dropdown có tìm |
| Chọn nhiều | | |
| Chọn ngày / khoảng ngày | | |
| Công tắc | | Bật/tắt có hiệu lực ngay |
| Bảng | | Desktop |
| Card | | Mobile, hoặc dữ liệu ít cột |
| Badge trạng thái | | Trạng thái vòng đời (BR §3) |
| Tab | | Nhóm nội dung cùng cấp trong một màn hình |
| Modal | | Form ngắn ≤ 6 field, xác nhận |
| Drawer | | Form dài, chi tiết nhanh |
| Bottom-sheet | | Bộ lọc trên mobile |
| Toast | | §5 |
| Skeleton | | §6 |
## 12. Ngoại lệ đã duyệt
*`SRS`/`WF` lệch quy ước ⇒ phải có dòng ở đây. Không có dòng ⇒ lệch là lỗi, không phải quyết định.*
| # | Tài liệu | Mục lệch | Quy ước | Thực tế | Vì sao | Ai duyệt | Ngày |
|---|---|---|---|---|---|---|---|
## 13. Open Questions
| ID | Câu hỏi | Hỏi ai | Từ ngày | Chặn gì |
|---|---|---|---|---|
---
## Tự chấm
| # | Tiêu chí | ☐/✅ |
|---|---|---|
| 1 | Mọi ô "chọn một" đã chọn, không còn dấu `/` giữa các phương án | |
| 2 | §6 có hai trạng thái rỗng **trông khác nhau** | |
| 3 | §8 có múi giờ hiển thị và quy tắc làm tròn tiền | |
| 4 | §11 mọi kiểu trình bày dùng trong `WF` hiện có đều có dòng | |
| 5 | Có chữ ký Designer (hoặc ghi rõ dự án không có Designer ⇒ PO ký thay, `DEC-nn`) | |

View File

@@ -0,0 +1,206 @@
<!--
WF_<US-id>_v1.0.html — wireframe low-fi tự chứa, sinh bởi skill ba-3-specification (activity wf).
QUY TẮC SINH FILE NÀY:
· Mỗi màn hình SCR-xx → một <section class="screen" id="SCR-xx">, theo đúng bảng WF §3.x.1 (vùng) và §3.x.2 (thành phần).
· Mỗi vùng Zn → <div class="zone" data-zone="Zn"> có nhãn vùng.
· Mỗi thành phần C-id → một phần tử có data-c="Cnn" và class theo kiểu trình bày (.btn-primary, .btn-secondary, .input, .select, .table, .card, .badge, .link, .label).
Tập data-c phải KHỚP HAI CHIỀU với bảng thành phần SRS §2.3.1 — đây là thứ ba-traceability kiểm.
· Trạng thái (WF §3.x.3) → các khối data-state="default|loading|empty-none|empty-filter|error|submitting" trong cùng vùng; thanh công cụ ở đầu file bật/tắt.
· Thành phần CHỈ CÓ Ở PROTOTYPE, chưa có trong SRS (WF §5 loại Tồn tại) → vẽ đúng chỗ prototype đặt nó, bọc <span class="proto-only" data-ref="WF §5 #n">…</span>,
KHÔNG gán data-c. Khung đỏ đứt để PO thấy ngay đang thiếu quyết định gì; PO quyết xong ⇒ hoặc gán C-id (SRS bổ sung) hoặc xoá khỏi WF.
· Bố cục nhiều cột (vd danh sách + sidebar tổng kết) → bọc các vùng trong <div class="cols">; ở khung nhìn mobile lớp này tự xếp chồng 1 cột (đúng WF §3.x.4). Không tự viết grid riêng.
· Không tô màu thương hiệu, không font riêng — đây là bố cục, không phải visual. Màu duy nhất được dùng: thang xám + một màu nhấn cho nút chính.
· Text hiển thị lấy nguyên văn từ SRS §4.2 (khoá i18n ghi ở data-key).
· Sửa nội dung mẫu bên dưới; giữ nguyên <style> và <script> để mọi WF trong dự án trông và thao tác giống nhau.
-->
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WF — <US-id> — <Tên US></title>
<style>
:root{--ink:#222;--mute:#777;--line:#bbb;--fill:#eee;--fill2:#f7f7f7;--accent:#3b6ea5;--danger:#b23a3a;--bg:#fafafa}
*{box-sizing:border-box}
body{margin:0;font:14px/1.45 system-ui,Segoe UI,Roboto,sans-serif;color:var(--ink);background:var(--bg)}
header.toolbar{position:sticky;top:0;z-index:9;background:#fff;border-bottom:1px solid var(--line);padding:8px 16px;display:flex;gap:16px;align-items:center;flex-wrap:wrap}
header.toolbar h1{font-size:15px;margin:0 16px 0 0}
header.toolbar label{font-size:12px;color:var(--mute)}
header.toolbar select{font:inherit}
.legend{font-size:12px;color:var(--mute);margin-left:auto}
main{padding:24px 16px;display:grid;gap:40px;justify-items:center}
.screen{width:100%;max-width:1200px;background:#fff;border:1px solid var(--line);border-radius:6px;overflow:hidden}
.screen>h2{margin:0;padding:8px 12px;font-size:13px;font-weight:600;background:var(--fill2);border-bottom:1px dashed var(--line);color:var(--mute)}
.screen>h2 small{font-weight:400;margin-left:8px}
.body{padding:12px;display:grid;gap:12px}
.zone{border:1px dashed var(--line);border-radius:4px;padding:10px;position:relative;background:#fff;min-height:44px}
.zone::before{content:attr(data-zone) " · " attr(data-name);position:absolute;top:-8px;left:8px;background:#fff;padding:0 4px;font-size:10px;color:var(--mute);letter-spacing:.02em}
.row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.row.right{justify-content:flex-end}
.row.between{justify-content:space-between}
[data-c]{position:relative}
[data-c]::after{content:attr(data-c);position:absolute;top:-9px;right:-4px;font-size:9px;background:var(--ink);color:#fff;padding:0 4px;border-radius:3px;line-height:14px;opacity:.85}
.label{font-weight:600;font-size:16px}
.btn{display:inline-block;padding:6px 14px;border:1px solid var(--ink);border-radius:4px;background:#fff;cursor:default;white-space:nowrap}
.btn-primary{background:var(--accent);border-color:var(--accent);color:#fff}
.btn-secondary{background:#fff}
.btn-danger{border-color:var(--danger);color:var(--danger)}
.btn[aria-disabled="true"]{opacity:.45;border-style:dashed}
.link{color:var(--accent);text-decoration:underline}
.input,.select{display:inline-block;min-width:160px;padding:6px 8px;border:1px solid var(--line);border-radius:4px;background:#fff;color:var(--mute)}
.select::after{content:" ▾";float:right}
.field{display:grid;gap:4px}
.field>span{font-size:12px}
.field>span .req{color:var(--danger)}
.hint{font-size:12px;color:var(--mute)}
.table{width:100%;border-collapse:collapse}
.table th,.table td{border:1px solid var(--line);padding:6px 8px;text-align:left;font-size:13px}
.table th{background:var(--fill2)}
.card{border:1px solid var(--line);border-radius:4px;padding:10px;background:#fff}
.badge{display:inline-block;padding:0 8px;border:1px solid var(--line);border-radius:10px;font-size:12px;background:var(--fill2)}
.img{background:repeating-linear-gradient(45deg,var(--fill),var(--fill) 6px,var(--fill2) 6px,var(--fill2) 12px);border:1px solid var(--line);min-height:80px;display:grid;place-items:center;color:var(--mute);font-size:12px}
.skeleton{height:14px;background:linear-gradient(90deg,var(--fill),var(--fill2),var(--fill));border-radius:3px;margin:8px 0}
.state{padding:24px;text-align:center;color:var(--mute);border:1px dashed var(--line);border-radius:4px;display:grid;gap:10px;justify-items:center}
.state.error{border-color:var(--danger);color:var(--danger)}
.state .illus{width:80px;height:60px}
.spinner{display:inline-block;width:14px;height:14px;border:2px solid #fff;border-top-color:transparent;border-radius:50%;vertical-align:-2px;margin-right:6px}
.proto-only{display:inline-block;border:1.5px dashed var(--danger);border-radius:4px;padding:4px 8px;background:#fdf3f3;color:var(--danger);position:relative}
.proto-only::before{content:"chỉ ở prototype — " attr(data-ref);font-size:10px;border:1px solid var(--danger);border-radius:3px;padding:0 4px;margin-right:6px;vertical-align:1px}
.modal{max-width:520px;margin:0 auto;border:1px solid var(--ink);border-radius:6px;background:#fff;box-shadow:0 0 0 2000px rgba(0,0,0,.25)}
.modal .body{padding:16px}
[data-state]{display:none}
[data-state="default"]{display:block}
body[data-view="mobile"] .screen{max-width:390px}
body[data-view="mobile"] .m-hide{display:none!important}
body[data-view="mobile"] .body,body[data-view="mobile"] .cols{grid-template-columns:1fr!important;display:grid}
.cols{display:grid;grid-template-columns:2fr 1fr;gap:12px;align-items:start}
body[data-view="mobile"] .m-stack{flex-direction:column;align-items:stretch}
body[data-view="desktop"] .d-hide{display:none!important}
body[data-view="mobile"] .table{display:none}
body[data-view="mobile"] .cards{display:grid!important;gap:8px}
.cards{display:none}
footer.note{padding:16px;font-size:12px;color:var(--mute);text-align:center}
</style>
</head>
<body data-view="desktop">
<header class="toolbar">
<h1>WF — <US-id> · <Tên US></h1>
<label>Khung nhìn
<select id="view"><option value="desktop">Desktop ≥1024</option><option value="mobile">Mobile &lt;768</option></select>
</label>
<label>Trạng thái
<select id="state">
<option value="default">Mặc định (có dữ liệu)</option>
<option value="loading">Đang tải</option>
<option value="empty-none">Rỗng — chưa có dữ liệu nào</option>
<option value="empty-filter">Rỗng — bộ lọc không khớp</option>
<option value="error">Lỗi tải dữ liệu</option>
<option value="submitting">Đang gửi</option>
</select>
</label>
<span class="legend">Nhãn đen = C-id trong SRS §2.3.1 · khung đứt xám = vùng Zn trong WF §3.x.1 · <b style="color:var(--danger)">khung đứt đỏ</b> = chỉ có ở prototype, chưa có trong SRS (WF §5) · hành vi xem SRS, không suy từ hình</span>
</header>
<main>
<!-- ============================ SCR-01 (mẫu: màn hình danh sách) ============================ -->
<section class="screen" id="SCR-01">
<h2>SCR-01 · <Tên màn hình> <small>Danh sách · vào từ Menu · vai trò: ROLE-01, ROLE-02</small></h2>
<div class="body">
<div class="zone" data-zone="Z1" data-name="Header màn hình">
<div class="row between">
<span class="label" data-c="C01" data-key="scr01.title">Tiêu đề màn hình</span>
<span class="btn btn-primary" data-c="C02" data-key="btn.create">Tạo mới</span>
</div>
</div>
<div class="zone" data-zone="Z2" data-name="Thanh lọc">
<div class="row m-stack">
<span class="input" data-c="C03" data-key="scr01.search.ph">Nhập mã hoặc tên…</span>
<span class="select m-hide" data-c="C04" data-key="scr01.filter.status">Trạng thái: Tất cả</span>
<span class="btn btn-secondary d-hide" data-key="btn.filter">Lọc</span>
</div>
</div>
<div class="zone" data-zone="Z3" data-name="Dữ liệu">
<div data-state="default">
<table class="table" data-c="C05">
<thead><tr><th>Mã</th><th>Tên</th><th>Trạng thái</th><th>Ngày tạo</th><th></th></tr></thead>
<tbody>
<tr><td>STR-001</td><td>Cửa hàng A</td><td><span class="badge">Nháp</span></td><td>30/08/2026</td><td><span class="link">Chi tiết</span></td></tr>
<tr><td>STR-002</td><td>Cửa hàng B</td><td><span class="badge">Đã duyệt</span></td><td>29/08/2026</td><td><span class="link">Chi tiết</span></td></tr>
</tbody>
</table>
<div class="cards" data-c="C05">
<div class="card"><div class="row between"><b>STR-001</b><span class="badge">Nháp</span></div><div>Cửa hàng A</div><div class="hint">30/08/2026</div></div>
<div class="card"><div class="row between"><b>STR-002</b><span class="badge">Đã duyệt</span></div><div>Cửa hàng B</div><div class="hint">29/08/2026</div></div>
</div>
</div>
<div data-state="loading"><div class="skeleton"></div><div class="skeleton" style="width:85%"></div><div class="skeleton" style="width:92%"></div></div>
<div data-state="empty-none"><div class="state"><div class="img illus">minh hoạ</div><span data-key="empty.none">Chưa có bản ghi nào. Tạo bản ghi đầu tiên?</span><span class="btn btn-primary" data-key="btn.create">Tạo mới</span></div></div>
<div data-state="empty-filter"><div class="state"><span data-key="empty.filter">Không tìm thấy kết quả phù hợp với bộ lọc.</span><span class="btn btn-secondary" data-key="btn.clearFilter">Xoá lọc</span></div></div>
<div data-state="error"><div class="state error"><span data-key="error.load">Không tải được dữ liệu.</span><span class="btn btn-secondary" data-key="btn.retry">Thử lại</span></div></div>
</div>
<div class="zone" data-zone="Z4" data-name="Phân trang">
<div class="row between">
<span class="proto-only" data-ref="WF §5 #1">Nút "Xuất Excel" — prototype có, BACKLOG không có US ⇒ PO quyết</span>
<span class="row" data-c="C06"><span class="btn btn-secondary">‹</span><span>1 / 7</span><span class="btn btn-secondary">›</span><span class="select m-hide">20 dòng</span></span>
</div>
</div>
</div>
</section>
<!-- ============================ SCR-03 (mẫu: form trong modal) ============================ -->
<section class="screen" id="SCR-03">
<h2>SCR-03 · <Tên form> <small>Modal · mở từ C02 · vai trò: ROLE-01, ROLE-02</small></h2>
<div class="body">
<div class="modal">
<div class="body">
<div class="zone" data-zone="Z1" data-name="Tiêu đề modal">
<div class="row between"><span class="label" data-c="C01" data-key="scr03.title">Tạo mới</span><span class="link" data-c="C08" data-key="btn.close">✕</span></div>
</div>
<div class="zone" data-zone="Z2" data-name="Form">
<div class="body">
<label class="field"><span data-key="f01.label">Mã <b class="req">*</b></span><span class="input" data-c="F01" data-key="f01.ph">3–20 ký tự, A–Z 0–9 -</span><span class="hint" data-key="f01.hint">Không trùng với mã đã có</span></label>
<label class="field"><span data-key="f02.label">Loại <b class="req">*</b></span><span class="select" data-c="F02">Thường</span></label>
<label class="field"><span data-key="f03.label">Tên</span><span class="input" data-c="F03" data-key="f03.ph">Tên hiển thị</span></label>
</div>
</div>
<div class="zone" data-zone="Z3" data-name="Hành động">
<div class="row right">
<span class="btn btn-secondary" data-c="C07" data-key="btn.cancel">Huỷ</span>
<span data-state="default" style="display:inline"><span class="btn btn-primary" data-c="C09" data-key="btn.save">Lưu</span></span>
<span data-state="submitting"><span class="btn btn-primary" data-c="C09" aria-disabled="true"><i class="spinner"></i>Đang lưu…</span></span>
</div>
</div>
</div>
</div>
<p class="hint">Field F01–F03 tham chiếu bảng field SRS §2.3.3. Thất bại ⇒ giữ nguyên dữ liệu đã nhập (AC-09), lỗi hiện dưới field theo UICONV §4.</p>
</div>
</section>
</main>
<footer class="note">Wireframe low-fi · bố cục và trạng thái · không phải thiết kế visual · nguồn hành vi: SRS_<US-id> · nguồn quy ước: UICONV_<PROJECT></footer>
<script>
(function(){
var view=document.getElementById('view'),state=document.getElementById('state');
function apply(){
document.body.setAttribute('data-view',view.value);
var s=state.value;
document.querySelectorAll('[data-state]').forEach(function(el){
var on=el.getAttribute('data-state')===s || (s==='submitting'&&el.getAttribute('data-state')==='default'&&!el.closest('.zone[data-name="Hành động"]'));
el.style.display=on?(el.tagName==='SPAN'?'inline':'block'):'none';
});
}
view.addEventListener('change',apply);state.addEventListener('change',apply);apply();
})();
</script>
</body>
</html>

View File

@@ -0,0 +1,204 @@
# WF — Wireframe & bố cục — <US-id>
| | |
|---|---|
| **Version** | 1.0 |
| **Date** | YYYY-MM-DD |
| **Author** | <BA> (skill ba-3-specification, activity `wf`) |
| **Status** | 🟡 Draft |
| **Approved by** | Designer: — · PO: — |
| **Source** | SRS_… v1.0 · UICONV_<PROJECT> v1.0 · <prototype: đường dẫn/URL + phiên bản> |
| **Scope** | US-0nn · SCR-01, SCR-02, … |
| **Nguồn bố cục** | 🎨 **Prototype tham chiếu** `<path>` / ✏️ **BA tự dựng — chờ Designer duyệt** |
| **Tệp kèm** | `WF_<US-id>_v1.0.html` *(low-fi render, mở bằng trình duyệt)* |
## Change Log
| Version | Date | Người sửa | Thay đổi | CR |
|---|---|---|---|---|
| 1.0 | | | Bản đầu | — |
> **Quy ước đọc** *(quy tắc W11)*. Bảng thành phần trong `SRS` §2.3.1 quyết định **một phần tử
> có tồn tại không** và **hành xử thế nào**. Tài liệu này và tệp HTML kèm theo quyết định **nó
> nằm ở đâu, thứ tự nào, to bằng nào, trông ra sao ở từng trạng thái**. Mâu thuẫn ⇒ SRS thắng
> về tồn tại/hành vi, WF thắng về bố cục — và mâu thuẫn đó **phải được ghi ở §5**, không im lặng.
---
## 0. Nguồn bố cục và độ tin cậy
| | |
|---|---|
| **Prototype tham chiếu** | *(đường dẫn / URL Figma / thư mục ảnh / mục SAD §7 · phiên bản · ngày · ai cung cấp)* |
| **Định dạng** | Figma / HTML tương tác / ảnh tĩnh / mô tả văn bản / **không có** |
| **Mức phủ** | n / m màn hình của US này có trong prototype |
| **Design system** | *(tên + version, hoặc "chưa có — dùng `UICONV` §11")* |
| **Độ tin cậy** | 🎨 Prototype đã được PO/Design duyệt ⇒ WF là **bản ghi lại** · ✏️ Không có prototype ⇒ WF là **đề xuất của BA**, Designer phải duyệt trước G3 |
🔴 **Có prototype thì prototype là nguồn sự thật về bố cục**, WF chỉ ghi lại và đối chiếu với
SRS. Không có prototype thì WF là đề xuất, và dòng `Approved by: Designer` là **bắt buộc** khi
`PRODUCT = screen` ở mức `standard` trở lên.
## 1. Bản đồ prototype ↔ màn hình
*Một dòng cho mỗi `SCR` trong `SRS` §2.1. Màn hình không có trong prototype ⇒ ghi rõ, và mục
§3 tương ứng do BA tự dựng.*
| SCR | Tên màn hình | Có trong prototype | Frame / trang / mục trong prototype | Mức khớp với SRS | Ghi chú |
|---|---|---|---|---|---|
| SCR-01 | | ✅ / ❌ | *(vd: Figma frame "Cart / Desktop", hoặc SAD §7.1.1 SCR-04)* | ✅ khớp · 🟠 khác bố cục · 🔴 khác tồn tại/hành vi | → §5 nếu 🟠/🔴 |
## 2. Khung bố cục chung
*Lấy từ `UICONV` §1 nếu đã có. Chỉ ghi lại phần **khác** hoặc **bổ sung** cho US này.*
| | |
|---|---|
| **Grid** | *(số cột · gutter · max-width, đơn vị px — nguồn: UICONV / prototype)* |
| **Vùng cố định** | Header · Sidebar · Footer — *(có / không, nội dung, tham chiếu UICONV §1)* |
| Breakpoint | Khoảng (px) | Bố cục | Nguồn |
|---|---|---|---|
| Desktop | ≥ 1024 | | UICONV §1 |
| Tablet | 768–1023 | | |
| Mobile | < 768 | | |
---
## 3. Bố cục từng màn hình
*Lặp mục này cho **mọi** `SCR` của US, kể cả modal và trang lỗi.*
### 3.1 SCR-01 — <Tên màn hình>
| | |
|---|---|
| **Loại** | Danh sách / Chi tiết / Form / Modal / Trang trạng thái |
| **Prototype** | *(frame/trang cụ thể, hoặc "✏️ BA tự dựng")* |
| **HTML** | `WF_<US-id>_v1.0.html#SCR-01` |
| **Thành phần (SRS §2.3.1)** | C01 … C0n — *(số lượng phải khớp bảng thành phần)* |
#### 3.1.1 Bảng vùng
*Bảng này thay cho hình vẽ: mỗi vùng là một khối trên màn hình. Tệp HTML render đúng theo bảng này.*
| Vùng | Tên | Vị trí desktop | Vị trí mobile | Chứa thành phần | Chiều rộng | Ưu tiên hiển thị |
|---|---|---|---|---|---|---|
| Z1 | Header màn hình | Hàng 1, full | Hàng 1 | C01 (tiêu đề), C02 (nút Tạo mới) | 100% | 1 |
| Z2 | Thanh lọc | Hàng 2, full | Hàng 2, thu gọn thành nút "Lọc" | C03, C04 | 100% | 2 |
| Z3 | Bảng dữ liệu | Hàng 3, full | Hàng 3, dạng card | C05 | 100% | 1 |
| Z4 | Phân trang | Hàng 4, phải | Hàng 4, giữa | C06 | auto | 3 |
*Ưu tiên hiển thị: 1 = luôn hiện · 2 = có thể gộp/thu gọn ở mobile · 3 = có thể ẩn sau nút.*
#### 3.1.2 Vị trí thành phần
*Mọi `C-id` trong `SRS` §2.3.1 phải có ở đây. `C-id` có ở đây mà không có trong SRS ⇒ lỗi
(thành phần chưa được đặc tả hành vi).*
| C-id | Tên thành phần | Vùng | Thứ tự trong vùng | Kích thước / độ rộng | Kiểu trình bày | Phần tử trong prototype | Khớp |
|---|---|---|---|---|---|---|---|
| C01 | lbl_title | Z1 | 1 | auto | Tiêu đề cấp 1 | | ✅ |
| C02 | btn_create | Z1 | 2 (phải) | auto | **Nút chính** | | ✅ |
| C03 | txt_search | Z2 | 1 | 320px | Ô nhập có icon tìm | | 🟠 |
**Kiểu trình bày** dùng từ vựng của `UICONV` §11 (nút chính / nút phụ / nút nguy hiểm / liên
kết / ô nhập / chọn 1 / chọn nhiều / bảng / card / badge trạng thái …). Không tự đặt tên mới.
#### 3.1.3 Trạng thái hiển thị
*Hành vi và text lấy từ `SRS` §2.3.6 và §4.2. Ở đây chỉ nói **trông thế nào và ở vùng nào**.*
| Trạng thái | Vùng thay đổi | Hiển thị gì | Khoá text (SRS §4.2) | Nút hành động |
|---|---|---|---|---|
| Đang tải lần đầu | Z3 | Skeleton n dòng *(theo UICONV §6)* | — | — |
| **Chưa có dữ liệu nào** | Z3 | Minh hoạ + câu dẫn + nút chính | `empty.none` | C02 Tạo mới |
| **Bộ lọc không khớp** | Z3 | Câu dẫn + nút phụ | `empty.filter` | Xoá lọc |
| Lỗi tải dữ liệu | Z3 | Banner lỗi trong vùng + nút thử lại | `error.load` | Thử lại |
| Đang gửi (form) | Nút gửi | Spinner trong nút, khoá form | — | — |
🔴 Hai trạng thái rỗng **phải trông khác nhau**, không chỉ khác câu chữ.
#### 3.1.4 Responsive
| Breakpoint | Thay đổi so với desktop | Thành phần ẩn / gộp / đổi kiểu |
|---|---|---|
| Tablet | Z2 xuống 2 hàng | — |
| Mobile | Z3 đổi bảng → card; Z2 thu vào nút "Lọc" mở bottom-sheet | C04 gộp vào bottom-sheet |
#### 3.1.5 Tương tác trình bày
*Chỉ phần **trình bày** (hành vi nghiệp vụ đã ở SRS). Mọi con số phải có nguồn — không có ⇒ `OQ`.*
| Thành phần | Sự kiện | Phản hồi trình bày | Ngưỡng / thời lượng | Nguồn |
|---|---|---|---|---|
| C03 txt_search | Gõ | Gợi ý sau khi ngừng gõ | 300 ms | UICONV §4 |
| Toàn màn | Lưu thành công | Toast góc trên phải | 3 s, tự đóng | UICONV §5 |
| C05 bảng | Hover dòng | Đổi nền dòng | — | UICONV §11 |
#### 3.1.6 Thứ tự focus *(màn hình có nhập liệu)*
| Thứ tự | C-id | Ghi chú |
|---|---|---|
| 1 | C03 | Focus mặc định khi vào màn hình |
---
### 3.2 SCR-02 — <Tên màn hình>
*(lặp cấu trúc 3.1)*
---
## 4. Luồng màn hình theo prototype
*Chỉ khi prototype có tương tác. Đối chiếu với sơ đồ điều hướng `SRS` §2.2 — cạnh có trong
prototype mà SRS không có, hoặc ngược lại, ghi vào §5.*
| Từ | Hành động | Đến | Có trong SRS §2.2 | Có trong prototype |
|---|---|---|---|---|
| SCR-01 | Bấm dòng | SCR-02 | ✅ | ✅ |
| SCR-03 | Huỷ giữa chừng | SCR-01 | ✅ | ❌ *(prototype không vẽ nút Huỷ)* → §5 |
## 5. Lệch giữa prototype và SRS
*Bảng bắt buộc, kể cả khi rỗng (ghi "Không phát hiện lệch, đã đối chiếu n màn hình").*
| # | Màn hình | Prototype có | SRS nói | Loại lệch | Bên thắng theo W11 | Xử lý | Ai quyết | Trạng thái |
|---|---|---|---|---|---|---|---|---|
| 1 | SCR-03 | Không có nút Huỷ | C07 btn_cancel tồn tại | Tồn tại | **SRS** | Thêm vào WF, báo Design cập nhật prototype | Designer | ☐ |
| 2 | SCR-01 | Bộ lọc ở sidebar trái | Bảng vùng ghi hàng 2 | Bố cục | **Prototype** | Sửa WF theo prototype | — | ✅ |
| 3 | SCR-01 | Nút "Tạo" | Nhãn "Tạo mới" | Text | **SRS** (BA sở hữu text, W5) | Design đổi nhãn | Designer | ☐ |
Loại lệch: **Tồn tại** · **Hành vi** · **Bố cục** · **Text** · **Trạng thái thiếu**.
Lệch loại *Tồn tại* hoặc *Hành vi* chưa được quyết ⇒ 🔴 **chặn G3**.
## 6. Bàn giao
| | |
|---|---|
| **Designer còn phải làm** | *(visual: màu, typography, icon, minh hoạ trạng thái rỗng, motion… — hoặc "Không: prototype đã phủ toàn bộ")* |
| **Dev dùng WF để** | Dựng layout và chọn component theo §3.x.2; **không** suy ra hành vi từ WF |
| **Không được suy ra từ WF** | Khoảng cách/kích thước chính xác nếu chưa có design system; màu; font |
| **Khi prototype thay đổi** | Design báo BA ⇒ cập nhật §1, §5 và version WF; SRS chỉ đổi nếu lệch loại Tồn tại/Hành vi |
## 7. Open Questions
| ID | Câu hỏi | Hỏi ai | Từ ngày | Chặn gì | 🔴 chặn G3? |
|---|---|---|---|---|---|
---
## Tự chấm
| # | Tiêu chí | ☐/✅ | Ghi chú |
|---|---|---|---|
| 1 | Mọi `SCR` trong `SRS` §2.1 có một mục §3 | | |
| 2 | Mọi `C-id` khớp **hai chiều** giữa `SRS` §2.3.1 và WF §3.x.2 | | *chạy: grep -o "C[0-9][0-9]" cả hai file, so tập* |
| 3 | Mỗi màn hình có §3.x.3 với **hai trạng thái rỗng trông khác nhau** | | |
| 4 | Mỗi màn hình web có responsive ≥ 2 breakpoint | | |
| 5 | Mọi con số ở §3.x.5 có nguồn (UICONV / prototype / OQ) | | |
| 6 | §5 đã điền, mọi lệch Tồn tại/Hành vi có người quyết | | |
| 7 | Tệp HTML mở được, mỗi `SCR` một `<section id>`, mỗi thành phần có `data-c` khớp C-id | | |
| 8 | Kiểu trình bày chỉ dùng từ vựng `UICONV` §11 | | |
| 9 | Có chữ ký Designer *(bắt buộc ở `standard`+ khi WF do BA tự dựng)* | | |