Giao diện
US-P03: Quản trị trạng thái tool và nhóm tool ​
Thuộc FS: 00-overview.md — Nền tảng dùng chung của cụm tool SAPP (as-built @
e77c47a) Phần chung (tổng quan, roles, flow tổng, related features, NFR, Open Questions, References) xem ở Overview. File này chỉ chứa nội dung riêng của US.
| Field | Value |
|---|---|
| US ID | US-P03 |
| Role thực hiện | R2 - Admin CMS (đăng nhập CMS, có quyền tools / tool_groups) — Fact từ code |
| Phase | As-built (đã triển khai) |
| Priority | N/A — as-built |
| Status | Draft |
| NFR liên quan | NFR-P-02, NFR-P-04 — xem Overview §5 |
| OQ liên quan | OQ-P-12, OQ-P-14, OQ-P-16, OQ-P-17, OQ-P-18 — xem Overview §6 |
Nguồn inventory: A-10, A-11.
1. US Statement ​
text
As a Admin CMS
I want tạo / sửa / xoá tool và nhóm tool, bật/tắt, ẩn/hiện và đặt khoá bí mật cho từng tool
So that [Assumption] tôi quyết định tool nào trong workspace SAPP được mở cho người dùng tool và ở điều kiện nào (OQ-P-18)2. Dependencies & Definition Of Ready ​
N/A — đã triển khai (Mode B).
Dependency runtime: tài khoản CMS có quyền tools:* (và tool_groups:* để quản lý nhóm).
3. Business Rules ​
| ID | Rule | Áp dụng ở | Đã cover bởi (AC) | Nguồn code (Mode B/C) |
|---|---|---|---|---|
| BR-01 | Quản trị tool yêu cầu đăng nhập CMS và quyền tools tương ứng (đọc / tạo / sửa / xoá); quản trị nhóm yêu cầu quyền tool_groups | M1, M2, M3 | AC-M1.1, EC-M1.1 | be/apps/api/src/tool/tool.controller.ts:35-137, be/apps/api/src/tool-group/tool-group.controller.ts:32-116 |
| BR-02 | Mã tool là duy nhất; tạo hoặc đổi mã trùng bị từ chối (23002) | M2 - field Code | AC-M2.2 | be/libs/modules/src/lib/tool/service.ts:35-44, :162-174 |
| BR-03 | Mỗi tool có mã vĩnh viễn dạng sapp.NNN gắn với một route; mã không bao giờ bị dùng lại hay đánh số lại. FE chỉ giữ mã + tên, mọi trạng thái do bảng tools quyết | M2 - field Code | AC-M2.1 | fe/src/data/constants/tool-codes.ts:4-20, fe/CLAUDE.md:3-22 |
| BR-04 | Gán tool vào nhóm không tồn tại bị từ chối (24001); gửi nhóm rỗng = gỡ tool khỏi nhóm | M3 - field Nhóm | AC-M3.1 | be/libs/modules/src/lib/tool/service.ts:24-32, :171-174, :183 |
| BR-05 | Bật/tắt, ẩn/hiện ở nhóm áp cho mọi tool con (hiệu lực = nhóm AND tool) | M3 - nhóm | AC-M3.2 | be/libs/modules/src/lib/tool/service.ts:105-113, app/lib/screens/tool_group_form_screen.dart:181-190 |
| BR-06 | Xoá nhóm → tool con trở thành "không thuộc nhóm" (không bị xoá) | M3 - xoá nhóm | AC-M3.3 | be/libs/database/src/lib/entities/tool/tool.entity.ts:34-39 |
| BR-07 | Form tạo tool (web): Code, Name bắt buộc; Enabled / Visible mặc định Yes; Secret Type mặc định none; Secret Value chỉ được gửi khi loại khoá khác none và có nhập | M2 | AC-M2.1, AC-M2.3 | fe/src/ui/components/cms/(tools)/create-form.tsx:25-86 |
| BR-08 | Form sửa tool (web): chọn loại khoá none thì xoá giá trị khoá cũ; loại khác mà để trống Secret Value thì giữ nguyên giá trị khoá cũ | M2 | AC-M2.4 | fe/src/ui/components/cms/(tools)/update-form.tsx:78-95 |
| BR-09 | Màn web không có trường nhóm — gán/gỡ nhóm và quản lý nhóm chỉ làm được trên app mobile (hoặc gọi API) | M2, M3 | AC-M3.1 | fe/src/ui/components/cms/(tools)/create-form.tsx:25-70 (không có field nhóm), app/lib/screens/tool_form_screen.dart:187-191 |
| BR-10 | Danh sách tool web hiện cột Code, Name, Enabled, Visible, Lock (badge loại khoá), Secret (che mặc định), Updated; lọc theo Enabled / Visible / Secret Type; phân trang mặc định 10 | M1 | AC-M1.1, AC-M1.2 | fe/src/ui/pages/cms/(tools)/page.tsx:46-78, :170-224 |
| BR-11 | Xoá tool phải xác nhận "Delete tool?" — "… will be deleted. This cannot be undone." | M1 - action Xoá | AC-M1.3 | fe/src/ui/pages/cms/(tools)/page.tsx:247-258 |
| BR-12 | Tool mới seed qua migration mặc định bật + hiện + không khoá (13 mã sapp.001–sapp.013; 011–013 gắn nhóm sapp) | Dữ liệu | AC-M2.5 | be/libs/database/migrations/1783700000000-create-tools.ts:6-15, :33, :41, be/libs/database/migrations/1786300000000-seed-tool-sapp-26297.ts:14-19 |
4. Design ​
a. Mối liên hệ giữa các màn & User Flow ​
text
[CMS web /cms/tools] M1 Danh sách tool ──Create/Edit──→ M2 Form tool ──Submit──→ M1 (reload + toast)
└─Delete─→ xác nhận → M1
[App mobile — quản trị tool] M3 Danh sách tool + nhóm ──→ form tool (có "Nhóm") / form nhóm
Kết quả → bảng tools / tool_groups (DB mind_keep) → US-P02 đọc qua /tools/publicb. Chi tiết từng màn ​
Màn M1 - Danh sách tool (web /cms/tools) ​
| Field | Value |
|---|---|
| Figma frame | N/A — as-built, không có Figma |
| Trigger vào màn | Admin mở /cms/tools ("Manage tool availability, visibility, and secret locks") |
| Mô tả ngắn | Bảng tool có lọc, tạo / sửa / xoá |
UI States ​
| State | Khi nào | Hiển thị | Figma frame |
|---|---|---|---|
| Default / Has data | Tải xong | Bảng "Tools" với các cột BR-10 | N/A |
| Loading | Đang tải (timeout 10 s) | Trạng thái tải của bảng chung | N/A |
| Empty | Không có tool khớp lọc | Bảng rỗng của template chung | N/A |
| Error | API lỗi | Hộp lỗi "Failed to load tools" + thông điệp; lỗi token → nút "Login again" chuyển /login | N/A |
| Disabled | N/A | — | N/A |
Fields ​
| Field | Required | Type | Default | Format | Validation | Error Message | Visibility | Depends On |
|---|---|---|---|---|---|---|---|---|
| Code | — | Text (mono) | — | sapp.NNN | — | — | Always | — |
| Name | — | Text | — | — | — | — | Always | — |
| Enabled / Visible | — | Boolean | — | Yes/No | — | — | Always | — |
| Lock | — | Badge | None | tên loại khoá | — | — | Always | Secret Type |
| Secret | — | Giá trị che | che | — | — | — | Always (che) | ⚠️ Observed — giá trị khoá trả về dạng rõ qua API admin (OQ-P-12) |
| Updated | — | Datetime | — | — | — | — | Always | — |
Filter / Search / Sort ​
| Loại | Theo field | Kiểu | Default | Notes |
|---|---|---|---|---|
| Filter | Enabled | Yes / No (chọn đúng 1 mới lọc) | Tất cả | fe/src/ui/pages/cms/(tools)/page.tsx:31-35, :59-66 |
| Filter | Visible | Yes / No | Tất cả | :67-73 |
| Filter | Secret Type | none / fixed_value / year_of_date | Tất cả | :74-77 |
| Search | code hoặc name | free-text | — | Placeholder "Search by code or name…" (:174) |
| Sort | — | — | Mặc định của API | Không có điều khiển sắp xếp trên màn |
| Pagination | — | — | 10 / trang | :46-47 |
Actions ​
A1 - Tạo tool ​
| Item | Description |
|---|---|
| Trigger | Bấm "Create tool" → M2 |
| Behavior | POST /api/v1/tools (BR-02, BR-07). Ghi bảng tools — DB mind_keep (một DB, không theo môi trường SAPP) |
| Success Result | Bảng tải lại, "Tool created — The tool has been created successfully." |
Error cases:
| Case | Expected Handling |
|---|---|
| Mã trùng | Lỗi 23002 "Tool code already exists" |
| Không có quyền | Lỗi quyền của CMS |
A2 - Sửa tool ​
| Item | Description |
|---|---|
| Trigger | Bấm sửa một dòng → M2 (điền sẵn) |
| Behavior | PATCH /api/v1/tools/:id (BR-02, BR-08). Ghi bảng tools — DB mind_keep |
| Success Result | "Tool updated — The tool has been updated successfully." |
Error cases:
| Case | Expected Handling |
|---|---|
| Đổi mã trùng tool khác | 23002 |
| Tool đã bị xoá | 23001 "Tool not found" |
A3 - Xoá tool ​
| Item | Description |
|---|---|
| Trigger | Bấm xoá → xác nhận "Delete tool" (BR-11) |
| Behavior | DELETE /api/v1/tools/:id. Xoá dòng trong bảng tools — DB mind_keep |
| Success Result | "Tool deleted — The tool has been deleted successfully." Tool đó trở thành "chưa mở" với người dùng (fail-closed, US-P02 BR-04) |
Error cases:
| Case | Expected Handling |
|---|---|
| Tool không tồn tại | 23001 |
Acceptance Criteria của màn M1 ​
AC-M1.1 - Xem và lọc danh sách ​
gherkin
Given Admin có quyền tools:read
When mở /cms/tools và chọn lọc Enabled = No
Then bảng chỉ hiện tool đang tắt, đủ cột Code, Name, Enabled, Visible, Lock, Secret, UpdatedMaps to:
- Business Rule: BR-01, BR-10
AC-M1.2 - Giá trị khoá được che ​
gherkin
Given một tool có khoá "fixed_value"
When Admin xem bảng
Then cột Secret hiển thị dạng che; cột Lock hiện badge "fixed value"Maps to:
- Business Rule: BR-10
AC-M1.3 - Xoá có xác nhận ​
gherkin
Given Admin bấm xoá tool "SAPP 23954…" (sapp.008)
When hộp xác nhận "Delete tool?" hiện và Admin bấm "Delete tool"
Then tool bị xoá và bảng tải lạiMaps to:
- Business Rule: BR-11
Negative AC / Edge Cases của màn M1 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M1.1 | Phiên CMS hết hạn | Hộp lỗi "Failed to load tools" + nút "Login again" → /login |
| EC-M1.2 | Xoá một tool sapp.NNN đang dùng | Tool bị đóng ngay với người dùng ở lần tải trang sau; mã không được dùng lại cho tool khác (BR-03) |
Màn M2 - Form tạo / sửa tool (web) ​
| Field | Value |
|---|---|
| Figma frame | N/A — as-built, không có Figma |
| Trigger vào màn | "Create tool" hoặc sửa một dòng ở M1 |
| Mô tả ngắn | Modal "Create New Tool" / sửa tool |
UI States ​
| State | Khi nào | Hiển thị | Figma frame |
|---|---|---|---|
| Default | Mở form | Các field ở bảng dưới; sửa thì điền sẵn | N/A |
| Loading | Đang gửi | Trạng thái gửi của form chung | N/A |
| Error | API từ chối | Hộp lỗi với thông điệp BE | N/A |
| Empty / Has data / Disabled | N/A — form | — | N/A |
Fields ​
| Field | Required | Type | Default | Format | Validation | Error Message | Visibility | Depends On |
|---|---|---|---|---|---|---|---|---|
| Code | Yes | Text | — (placeholder "e.g. sapp.001") | <family>.NNN | Duy nhất (BR-02) | "Tool code already exists" | Always | — |
| Name | Yes | Text | — ("Human-readable name") | — | Bắt buộc | Bắt buộc của form | Always | — |
| Enabled | Yes | Select Yes/No | Yes | — | — | — | Always | — |
| Visible | Yes | Select Yes/No | Yes | — | — | — | Always | — |
| Secret Type | Yes | Select none / fixed_value / year_of_date | none | — | — | — | Always | — |
| Secret Value | No | Text | rỗng ("Backing lock value (leave blank for no lock)") | Với year_of_date: một năm, vd 2026 | Chỉ gửi khi loại ≠ none (BR-07/BR-08) | — | Always | Secret Type |
Filter / Search / Sort ​
N/A — không phải màn List.
Actions ​
A1 - Submit form ​
| Item | Description |
|---|---|
| Trigger | Bấm "Create" / "Update" |
| Behavior | Như M1 A1 / A2 |
| Success Result | Form đóng, M1 tải lại |
Error cases:
| Case | Expected Handling |
|---|---|
| Thiếu Code / Name | Form không gửi |
| Lỗi BE | Hộp lỗi |
Acceptance Criteria của màn M2 ​
AC-M2.1 - Tạo tool mặc định ​
gherkin
Given Admin mở "Create New Tool"
When nhập Code "sapp.014", Name và giữ mặc định
Then tool được tạo với Enabled = Yes, Visible = Yes, Secret Type = none, không có giá trị khoáMaps to:
- Business Rule: BR-03, BR-07
AC-M2.2 - Mã trùng ​
gherkin
Given đã có tool mã sapp.001
When Admin tạo tool mới mã sapp.001
Then bị từ chối với lỗi 23002 "Tool code already exists"Maps to:
- Business Rule: BR-02
AC-M2.3 - Đặt khoá năm của ngày ​
gherkin
Given Admin chọn Secret Type = year_of_date và Secret Value = "2026"
When lưu
Then người dùng mở tool phải chọn một ngày thuộc năm 2026 để mở khoá (US-P02 BR-07)Maps to:
- Business Rule: BR-07
AC-M2.4 - Bỏ khoá / giữ khoá khi sửa ​
gherkin
Given tool đang khoá fixed_value
When Admin sửa và chọn Secret Type = none
Then giá trị khoá cũ bị xoá
And nếu Admin chỉ đổi Name mà để trống Secret Value (loại vẫn fixed_value) thì giá trị khoá cũ được giữMaps to:
- Business Rule: BR-08
AC-M2.5 - Tool seed sẵn ​
gherkin
Given DB vừa chạy migration tạo bảng tools
When Admin mở /cms/tools
Then thấy các mã sapp.001–sapp.013 ở trạng thái bật, hiện, không khoá (trừ khi đã được sửa sau đó — OQ-P-14)Maps to:
- Business Rule: BR-12
Negative AC / Edge Cases của màn M2 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M2.1 | Chọn fixed_value nhưng để trống Secret Value khi tạo | Tool lưu với loại khoá nhưng không có giá trị; mọi mã nhập đều sai (không có giá trị khoá để khớp) — hành vi suy từ code be/libs/modules/src/lib/tool/service.ts:239-240 |
| EC-M2.2 | ⚠️ Observed — giá trị khoá lưu dạng rõ trong DB và trả lại qua API admin | Hành vi hiện tại (be/libs/database/src/lib/entities/tool/tool.entity.ts:31-32; OQ-P-12) |
Màn M3 - Quản trị tool và nhóm tool (app mobile) ​
| Field | Value |
|---|---|
| Figma frame | N/A — as-built, không có Figma |
| Trigger vào màn | Admin mở màn quản trị tool trên app mobile |
| Mô tả ngắn | Danh sách tool + nhóm; form tool có trường "Nhóm"; form nhóm có bật/tắt, ẩn/hiện toàn bộ tool trong nhóm. Inventory chỉ ghi điểm vào — chi tiết UI ngoài phạm vi truy vết |
UI States ​
N/A — ngoài phạm vi truy vết chi tiết (inventory §1 chỉ ghi điểm vào app/lib/screens/tool_admin_list_screen.dart:17).
Fields ​
| Field | Required | Type | Default | Format | Validation | Error Message | Visibility | Depends On |
|---|---|---|---|---|---|---|---|---|
| Nhóm (form tool) | No | Dropdown | "Không thuộc nhóm" | — | Nhóm phải tồn tại (BR-04) | 24001 "Tool group not found" | Always | — |
| Mã nhóm (code) | Yes | Text | — ("ví dụ sapp") | — | Duy nhất (24002) | "Tool group code already exists" | Form nhóm | — |
| Tên nhóm / Mô tả | Tên: Yes | Text | — | — | — | — | Form nhóm | — |
| Bật / Hiện (nhóm) | — | Switch | Bật / Hiện | — | Áp cho mọi tool con (BR-05) | — | Form nhóm | — |
Nguồn: app/lib/screens/tool_form_screen.dart:167-242, app/lib/screens/tool_group_form_screen.dart:152-190, be/libs/database/src/lib/entities/tool/tool-group.entity.ts:5-24.
Filter / Search / Sort ​
N/A — ngoài phạm vi truy vết chi tiết.
Actions ​
A1 - Gán / gỡ nhóm cho tool ​
| Item | Description |
|---|---|
| Trigger | Chọn "Nhóm" trong form tool rồi lưu |
| Behavior | PATCH /api/v1/tools/:id với nhóm (BR-04). Ghi bảng tools — DB mind_keep |
| Success Result | Tool thuộc nhóm; hiệu lực theo BR-05 |
Error cases:
| Case | Expected Handling |
|---|---|
| Nhóm không tồn tại | 24001 |
A2 - Tạo / sửa / xoá nhóm ​
| Item | Description |
|---|---|
| Trigger | Form nhóm / "Xoá nhóm" (xác nhận "Xoá nhóm") |
| Behavior | /api/v1/tool-groups CRUD. Ghi bảng tool_groups — DB mind_keep |
| Success Result | Nhóm cập nhật; xoá nhóm → tool con không nhóm (BR-06) |
Error cases:
| Case | Expected Handling |
|---|---|
| Mã nhóm trùng | 24002 |
Acceptance Criteria của màn M3 ​
AC-M3.1 - Gán và gỡ nhóm ​
gherkin
Given tool sapp.001 không thuộc nhóm
When Admin chọn nhóm "sapp" trên app và lưu, rồi sau đó chọn "Không thuộc nhóm"
Then lần đầu tool thuộc nhóm "sapp"; lần sau tool không còn nhóm
And trên màn web /cms/tools không có trường nào để làm việc nàyMaps to:
- Business Rule: BR-04, BR-09
AC-M3.2 - Tắt nhóm tắt mọi tool con ​
gherkin
Given nhóm "sapp" chứa sapp.011–sapp.013, cả ba đang bật
When Admin tắt nhóm "sapp"
Then người dùng mở một trong ba tool thấy "Tool đã bị tắt"Maps to:
- Business Rule: BR-05
AC-M3.3 - Xoá nhóm không xoá tool ​
gherkin
Given nhóm "sapp" chứa 3 tool
When Admin xoá nhóm
Then 3 tool vẫn còn, ở trạng thái không thuộc nhómMaps to:
- Business Rule: BR-06
Negative AC / Edge Cases của màn M3 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M3.1 | Ẩn nhóm | Mọi tool con biến mất khỏi danh sách public (US-P02 BR-03) |
| EC-M3.2 | ⚠️ Observed — Tool ẩn vẫn kiểm mã / đọc trạng thái qua route theo mã | Hành vi hiện tại (OQ-P-06) |
5. Cross-feature AC ​
AC-X.1 - Trạng thái admin quyết định tool nào mở cho mọi FS nhóm ​
gherkin
Given Admin tắt tool sapp.008
When người dùng mở tool 23954
Then cổng chặn (US-P02) hiện "Tool đã bị tắt" — nhưng API của FS 23954 vẫn gọi được trực tiếp (⚠️ Observed, NFR-P-01)Maps to:
- REL-04 (Be Affected - FS 23954), áp tương tự REL-01…REL-06