@shanyucoder/flowgrid 0.1.5 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/adapters/laravel/registries/codegen.registry.json +9 -9
- package/bin/flowgrid.mjs +326 -115
- package/bin/lib/agent-mcp.mjs +4 -0
- package/bin/lib/agent-profiles.mjs +30 -6
- package/bin/lib/audit-run.mjs +1 -1
- package/bin/lib/cli-update.mjs +48 -8
- package/bin/lib/doctor.mjs +90 -3
- package/bin/lib/harness-overlay.mjs +12 -5
- package/bin/lib/harness-sync.mjs +17 -3
- package/bin/lib/init-adapters.mjs +75 -0
- package/bin/lib/init-scaffold.mjs +9 -0
- package/bin/lib/inject-consumer-scripts.mjs +115 -0
- package/bin/lib/merge-stack-config.mjs +89 -0
- package/bin/lib/project-gitignore.mjs +1 -0
- package/bin/lib/repo-maps-align.mjs +203 -0
- package/dist/graph/config/load-config.js +7 -2
- package/dist/graph/config/load-config.js.map +1 -1
- package/dist/graph/mcp/tools.js +1 -1
- package/dist/graph/mcp/tools.js.map +1 -1
- package/dist/graph/registry/load-registries.d.ts +1 -0
- package/dist/graph/registry/load-registries.js +14 -1
- package/dist/graph/registry/load-registries.js.map +1 -1
- package/engines/cases/render-cases.mjs +67 -5
- package/engines/docs/lib/render-api-summary-markdown.mjs +28 -0
- package/engines/docs/lib/render-bundle-markdown.mjs +77 -2
- package/engines/docs/lib/render-data-model-markdown.mjs +171 -0
- package/engines/docs/lib/render-design-tables.mjs +89 -7
- package/engines/docs/lib/render-template.mjs +6 -0
- package/engines/docs/render-docs.mjs +6 -2
- package/engines/docs/vitepress/config.ts +7 -7
- package/engines/docs/vitepress/surfaces-nav.mjs +45 -14
- package/engines/openapi/check-backend-spec.mjs +2 -2
- package/engines/openapi/lib/markdown-table.mjs +8 -0
- package/engines/openapi/lib/render-backend-spec-markdown.mjs +78 -0
- package/engines/registry-sync/be-capabilities-sync.mjs +118 -0
- package/engines/registry-sync/fe-design-sync.mjs +258 -0
- package/engines/registry-sync/run-registry-sync.mjs +107 -0
- package/engines/shared/e2e-output-layout.mjs +68 -0
- package/engines/shared/flowgrid-e2e-root.mjs +19 -0
- package/engines/shared/resolve-flowgrid-context.mjs +176 -0
- package/engines/spec/lib/audit-api-gaps.mjs +1 -1
- package/engines/spec/lib/audit-bundle-gaps.mjs +92 -3
- package/engines/spec/lib/audit-db-tables.mjs +529 -0
- package/engines/spec/lib/audit-e2e-coverage.mjs +88 -30
- package/engines/spec/lib/bundle-schema.mjs +4 -1
- package/engines/spec/split-bundle.mjs +11 -1
- package/engines/testcase/runners/generate-api.mjs +23 -23
- package/engines/testcase/runners/generate.mjs +19 -17
- package/engines/testcase/runners/lib/bootstrap-context.mjs +44 -0
- package/engines/testcase/runners/lib/write-files.mjs +37 -9
- package/harness/agents/antigravity/rules/antigravity-mcp.mdc +12 -0
- package/harness/agents/gemini/rules/gemini-mcp.mdc +11 -0
- package/harness/agents/gemini_antigravity/rules/antigravity-mcp.mdc +8 -6
- package/harness/be/skills/{grill-api → audit-api}/SKILL.md +8 -7
- package/harness/common/extracts/artifact-graph.md +2 -2
- package/harness/common/extracts/artifactgraph-phase-hooks.md +2 -2
- package/harness/common/extracts/docs-mark-detect.md +2 -2
- package/harness/common/extracts/entity-relationship.md +23 -0
- package/harness/common/rules/flowgrid-ux-common.mdc +3 -3
- package/harness/common/skills/configure-repo-maps/SKILL.md +4 -2
- package/harness/docs/extracts/agent-execution-protocol.md +1 -1
- package/harness/docs/extracts/api-codegen-readiness.md +34 -0
- package/harness/docs/extracts/api-codegen-tags.md +30 -0
- package/harness/docs/extracts/api-contract.md +43 -0
- package/harness/docs/extracts/api-spec-sync.md +35 -0
- package/harness/docs/extracts/artifactgraph-hooks-docs.md +1 -1
- package/harness/docs/extracts/call-external.md +16 -0
- package/harness/docs/extracts/common-scope.md +9 -10
- package/harness/docs/extracts/db-audit-wizard.md +45 -0
- package/harness/docs/extracts/derived-data.md +18 -0
- package/harness/docs/extracts/design-leaf-signoff.md +16 -0
- package/harness/docs/extracts/extract-registry.docs.json +9 -1
- package/harness/docs/extracts/spec-core.md +7 -3
- package/harness/docs/extracts/spec-evolution.md +21 -0
- package/harness/docs/extracts/spec-prd-lite.md +19 -0
- package/harness/docs/extracts/spec-requirement.md +6 -2
- package/harness/docs/extracts/spec-ssot-prep.md +25 -0
- package/harness/docs/extracts/tpl-module.md +12 -0
- package/harness/docs/extracts/verify-gate.md +33 -0
- package/harness/docs/extracts/wire-spec-feedback.md +31 -0
- package/harness/docs/rules/agent-compliance.mdc +1 -1
- package/harness/docs/rules/team-flow-spec.mdc +3 -4
- package/harness/docs/skills/adopt/SKILL.md +2 -0
- package/harness/docs/skills/api/SKILL.md +4 -5
- package/harness/docs/skills/api-spec/SKILL.md +19 -6
- package/harness/docs/skills/api-update/SKILL.md +3 -3
- package/harness/docs/skills/architecture/SKILL.md +1 -1
- package/harness/docs/skills/business-process/SKILL.md +2 -0
- package/harness/docs/skills/common/SKILL.md +2 -2
- package/harness/docs/skills/common-spec/SKILL.md +10 -47
- package/harness/docs/skills/db-erd/SKILL.md +26 -0
- package/harness/docs/skills/grill/SKILL.md +28 -22
- package/harness/docs/skills/grill-api/SKILL.md +4 -6
- package/harness/docs/skills/grill-api-spec/SKILL.md +44 -24
- package/harness/docs/skills/grill-bqa/SKILL.md +28 -13
- package/harness/docs/skills/grill-common-spec/SKILL.md +10 -35
- package/harness/docs/skills/grill-dev/SKILL.md +7 -6
- package/harness/docs/skills/grill-docs/SKILL.md +10 -3
- package/harness/docs/skills/module/SKILL.md +3 -1
- package/harness/docs/skills/openapi/SKILL.md +2 -1
- package/harness/docs/skills/overview/SKILL.md +7 -1
- package/harness/docs/skills/spec/SKILL.md +42 -9
- package/harness/docs/skills/update-spec/SKILL.md +4 -1
- package/harness/fe/extracts/wire-audit-loop.md +72 -0
- package/harness/fe/extracts/wire-phase.md +45 -0
- package/harness/fe/rules/platform-design-vocabulary.mdc +1 -1
- package/harness/fe/rules/team-flow-prototype.mdc +8 -4
- package/harness/fe/skills/gen-common/SKILL.md +11 -84
- package/harness/fe/skills/grill-prototype/SKILL.md +51 -25
- package/harness/fe/skills/grill-test/SKILL.md +78 -20
- package/harness/fe/skills/grill-wire/SKILL.md +81 -0
- package/harness/fe/skills/prototype/SKILL.md +3 -2
- package/harness/fe/skills/wire/SKILL.md +7 -2
- package/harness/shared/AGENTS.md +3 -3
- package/harness/shared/SSOT_AGENT_PROTOCOL.md +3 -3
- package/harness/tests/extracts/grill-api-hook.md +69 -0
- package/harness/tests/extracts/grill-scenario-flow.md +39 -0
- package/harness/tests/extracts/grill-screen-tc.md +40 -0
- package/harness/tests/extracts/testcase-gen-cli.md +57 -0
- package/harness/tests/extracts/testcase-plan.md +29 -0
- package/harness/tests/extracts/tests-verify-gate.md +29 -0
- package/harness/tests/extracts/wire-test-handoff.md +37 -0
- package/harness/tests/skills/grill-testcase/SKILL.md +1 -0
- package/harness/tests/skills/test-api/SKILL.md +14 -6
- package/harness/tests/skills/testcase/SKILL.md +3 -1
- package/harness/tests/templates/TC.example-api.yaml +7 -1
- package/harness/tests/templates/TC.example.yaml +4 -1
- package/harness/tests/templates/tpl-testcase-plan.md +75 -0
- package/package.json +1 -1
- package/stacks/fastapi.json +1 -0
- package/stacks/laravel.json +1 -0
- package/stacks/nestjs.json +72 -0
- package/stacks/nextjs-nest.json +1 -0
- package/stacks/nuxt4-nest.json +1 -0
- package/templates/project-skeleton/architecture/03-business-process/FLOW-template.md +2 -0
- package/templates/project-skeleton/overview/index.md +68 -2
- package/templates/project-skeleton/overview/operational-areas/_template.md +37 -0
- package/templates/project-skeleton/surfaces/common/data-model/index.md +10 -2
- package/templates/shared/api-03-mock.stub.yaml +14 -0
- package/templates/shared/backend-api.bundle.yaml +3 -0
- package/templates/shared/backend-api.yaml +4 -0
- package/templates/shared/be-capabilities.registry.base.json +8 -0
- package/templates/shared/bundle-authoring.md +39 -3
- package/templates/shared/default-layout.ejs +131 -18
- package/templates/shared/design-spec.yaml +2 -3
- package/templates/shared/design.registry.base.json +38 -0
- package/templates/shared/feature.bundle.yaml +16 -9
- package/templates/shared/ir/generated/spec.md +281 -0
- package/templates/shared/qa-item.yaml +1 -1
- package/templates/shared/tpl-api-contract.md +133 -0
- package/templates/shared/tpl-screen-data-model.md +76 -0
- package/templates/tests-skeleton/cases/README.md +4 -0
- package/templates/tests-skeleton/catalog/locale.yaml +9 -0
- package/templates/tests-skeleton/tpl-testcase-plan.md +9 -0
- package/harness/docs/skills/api-integration/SKILL.md +0 -110
- package/harness/docs/skills/grill-integration-spec/SKILL.md +0 -51
|
@@ -1,5 +1,64 @@
|
|
|
1
|
+
<%
|
|
2
|
+
const pageId = spec['page-id'] || spec.id || '';
|
|
3
|
+
const docStatus = spec.status || 'draft';
|
|
4
|
+
const docOwner = spec.owner || '';
|
|
5
|
+
const hasMetrics = spec.successMetrics && String(spec.successMetrics).trim();
|
|
6
|
+
const hasNonGoals = spec.nonGoals && String(spec.nonGoals).trim();
|
|
7
|
+
const hasStories = !!spec.userStories;
|
|
8
|
+
const pageSections = spec.sections || spec.design?.sections || [];
|
|
9
|
+
const pageActions = spec.actions || spec.design?.actions || [];
|
|
10
|
+
const pageStateMatrix = spec.stateMatrix || spec.design?.stateMatrix;
|
|
11
|
+
const hasState = !!pageStateMatrix;
|
|
12
|
+
const listUi = spec.ui?.list || {};
|
|
13
|
+
const hasListColumns = !!(context.renderListColumnsTable && context.renderListColumnsTable(listUi));
|
|
14
|
+
const hasListFilters = !!(context.renderListFiltersTable && context.renderListFiltersTable(listUi));
|
|
15
|
+
const hasListPagination = !!(listUi.pagination && typeof listUi.pagination === 'object');
|
|
16
|
+
const hasListExtras = hasListFilters || hasListPagination;
|
|
17
|
+
const hasValidation = !!(context.renderValidationDictionaryTable && context.renderValidationDictionaryTable(pageSections));
|
|
18
|
+
const hasCustomWidget = !!(context.renderCustomWidgetSpecs && context.renderCustomWidgetSpecs(pageSections));
|
|
19
|
+
const hasActions = pageActions && pageActions.length;
|
|
20
|
+
const hasReview = spec.review && (spec.review.layoutNotes || spec.review.summary);
|
|
21
|
+
const hasLegacy = spec.legacy && (spec.legacy.behaviors?.length || spec.legacy.fields?.length || spec.legacy.evidence?.length);
|
|
22
|
+
const hasDebt = spec.pendingTechDebt && spec.pendingTechDebt.length;
|
|
23
|
+
|
|
24
|
+
const toc = [];
|
|
25
|
+
toc.push('1. [Tổng quan](#overview)');
|
|
26
|
+
if (hasMetrics) toc.push('2. [Chỉ số thành công](#success-metrics)');
|
|
27
|
+
if (hasNonGoals) toc.push('3. [Phạm vi không làm](#non-goals)');
|
|
28
|
+
if (hasStories) toc.push('4. [User stories và hành trình màn hình](#user-stories--screen-journey)');
|
|
29
|
+
if (hasState) toc.push('5. [Ma trận trạng thái và phân quyền](#state--permission-matrix)');
|
|
30
|
+
if (hasListColumns) toc.push('6. [Bảng cột danh sách](#list-columns)');
|
|
31
|
+
if (hasListExtras) toc.push('6b. [Bộ lọc & phân trang](#list-filters-pagination)');
|
|
32
|
+
if (hasValidation) toc.push(`${hasListColumns || hasListExtras ? '7' : '6'}. [Từ điển dữ liệu và validation](#data-dictionary--validation)`);
|
|
33
|
+
if (hasCustomWidget) toc.push(`${hasListColumns ? '8' : '7'}. [Widget tùy biến](#custom-widgets)`);
|
|
34
|
+
const actionNum = hasListColumns ? (hasCustomWidget ? '9' : '8') : (hasCustomWidget ? '8' : '7');
|
|
35
|
+
if (hasActions) toc.push(`${actionNum}. [Luồng hành động](#action-flows)`);
|
|
36
|
+
else if (pageSections.length) toc.push(`${actionNum}. [Bố cục màn hình](#page-composition)`);
|
|
37
|
+
if (hasReview) toc.push('9. [Ghi chú review](#review-notes)');
|
|
38
|
+
if (hasLegacy) toc.push('10. [Legacy evidence](#legacy-evidence)');
|
|
39
|
+
if (hasDebt) toc.push('11. [Tech debt](#pending-tech-debt)');
|
|
40
|
+
toc.push('- [API SSOT](#api-ssot)');
|
|
41
|
+
|
|
42
|
+
const route = spec.ui?.routes?.[0];
|
|
43
|
+
const routePath = route?.path || '';
|
|
44
|
+
const testId = route?.pageTestId || '';
|
|
45
|
+
%>
|
|
1
46
|
# <%= spec.title || spec.id %>
|
|
2
47
|
|
|
48
|
+
## Mục lục (Contents)
|
|
49
|
+
|
|
50
|
+
<%= toc.join('\n') %>
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Tổng quan {#overview}
|
|
55
|
+
|
|
56
|
+
| | |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| **Page ID** | `<%= pageId || '—' %>` |
|
|
59
|
+
| **Status** | <%= docStatus %> |
|
|
60
|
+
| **Owner** | <%= docOwner || '—' %> |
|
|
61
|
+
|
|
3
62
|
<% if (context.testcases && context.testcases.length) { %>
|
|
4
63
|
- **Testcase plans:** <%= context.testcases.map(tc => '`' + (tc.data.title || tc.data.id || tc.file) + '`').join(' · ') %> (hub: [base_test](https://github.com/raintr91/base_test))
|
|
5
64
|
<% } else { %>
|
|
@@ -7,24 +66,45 @@
|
|
|
7
66
|
<% } %>
|
|
8
67
|
|
|
9
68
|
<%
|
|
10
|
-
const route = spec.ui?.routes?.[0];
|
|
11
|
-
const path = route?.path || '';
|
|
12
|
-
const testId = route?.pageTestId || '';
|
|
13
69
|
let screenLine = '- **Screen:** None';
|
|
14
|
-
if (
|
|
15
|
-
const screen = context.renderScreenLink ? context.renderScreenLink(
|
|
70
|
+
if (routePath) {
|
|
71
|
+
const screen = context.renderScreenLink ? context.renderScreenLink(routePath) : '`' + routePath + '`';
|
|
16
72
|
const suffix = testId ? ` · \`${testId}\`` : '';
|
|
17
73
|
screenLine = `- **Screen:** ${screen}${suffix}`;
|
|
18
74
|
}
|
|
19
75
|
%>
|
|
20
76
|
<%= screenLine %>
|
|
21
77
|
|
|
78
|
+
- **QA inbox:** [Danh sách câu hỏi treo](../../qa/index.md) (cùng function leaf)
|
|
79
|
+
- **Generated (VitePress):** [spec](./spec.md) · [data-model](./data-model.md) · [api](./api.md) _(khi có `01`)_
|
|
80
|
+
|
|
22
81
|
<% if (spec.summary) { %>
|
|
23
82
|
<%= spec.summary %>
|
|
24
83
|
<% } %>
|
|
25
84
|
|
|
85
|
+
<% if (hasMetrics) { %>
|
|
86
|
+
## Chỉ số thành công {#success-metrics}
|
|
87
|
+
|
|
88
|
+
<%= spec.successMetrics %>
|
|
89
|
+
|
|
90
|
+
<% } %>
|
|
91
|
+
|
|
92
|
+
<% if (hasNonGoals) { %>
|
|
93
|
+
## Phạm vi không làm {#non-goals}
|
|
94
|
+
|
|
95
|
+
<%= spec.nonGoals %>
|
|
96
|
+
|
|
97
|
+
<% } %>
|
|
98
|
+
|
|
99
|
+
## API SSOT {#api-ssot}
|
|
100
|
+
|
|
101
|
+
Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function leaf (không gộp vào bundle). Sau split: `ir/design.yaml` chiếu endpoint; OpenAPI/mock theo stack.
|
|
102
|
+
|
|
103
|
+
- **Data model (review, multi-table):** [data-model.md](./data-model.md) — bảng/cột; SSOT codegen = `01` + `ir/design.yaml` `db`.
|
|
104
|
+
- **API (đọc trên site):** [api.md](./api.md) — tóm tắt endpoint từ `01-backend-spec.yaml` (khi có file).
|
|
105
|
+
|
|
26
106
|
<% if (spec.userStories) { %>
|
|
27
|
-
## User Stories & Screen Journey
|
|
107
|
+
## User Stories & Screen Journey {#user-stories--screen-journey}
|
|
28
108
|
|
|
29
109
|
<% if (typeof spec.userStories === 'string') { %>
|
|
30
110
|
<%= spec.userStories %>
|
|
@@ -114,11 +194,6 @@
|
|
|
114
194
|
<% } %>
|
|
115
195
|
|
|
116
196
|
<%
|
|
117
|
-
// 1. Executive Summary & Screen Journey
|
|
118
|
-
const pageSections = spec.sections || spec.design?.sections || [];
|
|
119
|
-
const pageActions = spec.actions || spec.design?.actions || [];
|
|
120
|
-
const pageStateMatrix = spec.stateMatrix || spec.design?.stateMatrix;
|
|
121
|
-
|
|
122
197
|
const page = {
|
|
123
198
|
nav: spec.nav || spec.design?.nav,
|
|
124
199
|
sections: pageSections,
|
|
@@ -129,17 +204,51 @@
|
|
|
129
204
|
%>
|
|
130
205
|
|
|
131
206
|
<% if (pageStateMatrix) { %>
|
|
132
|
-
## Ma Trận Trạng Thái Giao Diện & Phân Quyền (State & Permission Matrix)
|
|
207
|
+
## Ma Trận Trạng Thái Giao Diện & Phân Quyền (State & Permission Matrix) {#state--permission-matrix}
|
|
133
208
|
|
|
134
209
|
<%- context.renderStateMatrixTable(pageStateMatrix) %>
|
|
135
210
|
|
|
136
211
|
<% } %>
|
|
137
212
|
|
|
213
|
+
<%
|
|
214
|
+
const listColumnsMd = context.renderListColumnsTable && context.renderListColumnsTable(listUi);
|
|
215
|
+
if (listColumnsMd) {
|
|
216
|
+
%>
|
|
217
|
+
## Bảng cột danh sách (List columns) {#list-columns}
|
|
218
|
+
|
|
219
|
+
<%- listColumnsMd %>
|
|
220
|
+
|
|
221
|
+
<p><strong>DB chi tiết / multi-table:</strong> <a href="./data-model.md">data-model.md</a> · SSOT ghi: <code>*.bundle.yaml</code> + <code>ir/design.yaml</code>.</p>
|
|
222
|
+
|
|
223
|
+
<% } %>
|
|
224
|
+
|
|
225
|
+
<%
|
|
226
|
+
const filtersMd = context.renderListFiltersTable && context.renderListFiltersTable(listUi);
|
|
227
|
+
const paginationMd = context.renderListPaginationBlock && context.renderListPaginationBlock(listUi);
|
|
228
|
+
if (filtersMd || paginationMd) {
|
|
229
|
+
%>
|
|
230
|
+
## Bộ lọc & phân trang (List filters & pagination) {#list-filters-pagination}
|
|
231
|
+
|
|
232
|
+
<% if (filtersMd) { %>
|
|
233
|
+
### Bộ lọc
|
|
234
|
+
|
|
235
|
+
<%- filtersMd %>
|
|
236
|
+
|
|
237
|
+
<% } %>
|
|
238
|
+
<% if (paginationMd) { %>
|
|
239
|
+
### Phân trang
|
|
240
|
+
|
|
241
|
+
<%- paginationMd %>
|
|
242
|
+
|
|
243
|
+
<% } %>
|
|
244
|
+
|
|
245
|
+
<% } %>
|
|
246
|
+
|
|
138
247
|
<%
|
|
139
248
|
const validationTableMd = context.renderValidationDictionaryTable(pageSections);
|
|
140
249
|
if (validationTableMd) {
|
|
141
250
|
%>
|
|
142
|
-
## Danh Mục Trường Nhập Liệu & Quy Tắc Kiểm Tra Hợp Lệ (Data Dictionary & Validation)
|
|
251
|
+
## Danh Mục Trường Nhập Liệu & Quy Tắc Kiểm Tra Hợp Lệ (Data Dictionary & Validation) {#data-dictionary--validation}
|
|
143
252
|
|
|
144
253
|
<%- validationTableMd %>
|
|
145
254
|
|
|
@@ -149,6 +258,8 @@
|
|
|
149
258
|
const customWidgetMd = context.renderCustomWidgetSpecs(pageSections);
|
|
150
259
|
if (customWidgetMd) {
|
|
151
260
|
%>
|
|
261
|
+
<div id="custom-widgets"></div>
|
|
262
|
+
|
|
152
263
|
<%- customWidgetMd %>
|
|
153
264
|
|
|
154
265
|
<% } %>
|
|
@@ -157,17 +268,19 @@
|
|
|
157
268
|
const actionFlowsMd = context.renderActionFlowsDetailed(pageActions);
|
|
158
269
|
if (actionFlowsMd) {
|
|
159
270
|
%>
|
|
160
|
-
## Đặc Tả Quy Trình Hành Động (Action Flows)
|
|
271
|
+
## Đặc Tả Quy Trình Hành Động (Action Flows) {#action-flows}
|
|
161
272
|
|
|
162
273
|
<%- actionFlowsMd %>
|
|
163
274
|
|
|
164
275
|
<% } else { %>
|
|
276
|
+
## Bố cục màn hình {#page-composition}
|
|
277
|
+
|
|
165
278
|
<%- context.renderPageComposition(page, { businessOnly: true }) %>
|
|
166
279
|
<% } %>
|
|
167
280
|
|
|
168
281
|
<%
|
|
169
282
|
const skipKeys = new Set([
|
|
170
|
-
'id', 'page-id', 'title', 'summary', 'userStories', 'acceptance', 'ui', 'design', 'legacy', 'review', 'template',
|
|
283
|
+
'id', 'page-id', 'title', 'summary', 'successMetrics', 'nonGoals', 'userStories', 'acceptance', 'ui', 'design', 'legacy', 'review', 'template',
|
|
171
284
|
'nav', 'sections', 'zones', 'behavior', 'actions', 'stateMatrix', 'grillStatus', 'specOrigin',
|
|
172
285
|
'status', 'owner', 'schema', 'qa', 'Q&A', 'pendingTechDebt', 'i18n', 'gen', '_meta'
|
|
173
286
|
]);
|
|
@@ -187,7 +300,7 @@
|
|
|
187
300
|
<% }); %>
|
|
188
301
|
|
|
189
302
|
<% if (spec.pendingTechDebt && spec.pendingTechDebt.length) { %>
|
|
190
|
-
## Pending tech debt
|
|
303
|
+
## Pending tech debt {#pending-tech-debt}
|
|
191
304
|
|
|
192
305
|
| ID | Status | Summary |
|
|
193
306
|
| --- | --- | --- |
|
|
@@ -198,7 +311,7 @@
|
|
|
198
311
|
<% } %>
|
|
199
312
|
|
|
200
313
|
<% if (spec.review && (spec.review.layoutNotes || spec.review.summary)) { %>
|
|
201
|
-
## Review — layout notes
|
|
314
|
+
## Review — layout notes {#review-notes}
|
|
202
315
|
|
|
203
316
|
<% if (spec.review.layoutNotes) { %>
|
|
204
317
|
<% if (Array.isArray(spec.review.layoutNotes)) { %>
|
|
@@ -210,7 +323,7 @@
|
|
|
210
323
|
<% } %>
|
|
211
324
|
|
|
212
325
|
<% if (spec.legacy && (spec.legacy.behaviors?.length || spec.legacy.fields?.length || spec.legacy.evidence?.length)) { %>
|
|
213
|
-
## Legacy evidence
|
|
326
|
+
## Legacy evidence {#legacy-evidence}
|
|
214
327
|
|
|
215
328
|
<% if (spec.legacy.behaviors && spec.legacy.behaviors.length) { %>
|
|
216
329
|
### Behaviors
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
# After /grill-docs → see docs/templates/spec.yaml (portal-gen-ready).
|
|
1
|
+
# DEPRECATED — không author leaf mới. SSOT: feature.bundle.yaml + bundle-authoring.md
|
|
2
|
+
# File giữ cho repo cũ / tham chiếu shape spec (design v1). /spec skill → chỉ bundle.
|
|
4
3
|
|
|
5
4
|
id: role-page-function
|
|
6
5
|
title: Feature title
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"canonicalSystem": "flowgrid-fe",
|
|
4
|
+
"description": "Design DSL registry (UI shells, #ui, Mo*). Refresh with flowgrid registry:sync after base changes.",
|
|
5
|
+
"defaults": {
|
|
6
|
+
"shellByProfile": {
|
|
7
|
+
"list": "DataListPage",
|
|
8
|
+
"create": "DataFormPage",
|
|
9
|
+
"detail": "DataDetailPage",
|
|
10
|
+
"admin-crud": "DataListPage",
|
|
11
|
+
"auth": "AuthShell"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"shells": {
|
|
15
|
+
"DataListPage": {
|
|
16
|
+
"tag": "#shell: DataListPage",
|
|
17
|
+
"portal": { "status": "planned", "path": "components/organisms/DataListPage.vue" },
|
|
18
|
+
"portalGen": {
|
|
19
|
+
"templateStandard": "list/page-standard",
|
|
20
|
+
"templateCustom": "list/page-custom"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"DataFormPage": {
|
|
24
|
+
"tag": "#shell: DataFormPage",
|
|
25
|
+
"portal": { "status": "planned", "path": "components/organisms/DataFormPage.vue" }
|
|
26
|
+
},
|
|
27
|
+
"DataDetailPage": {
|
|
28
|
+
"tag": "#shell: DataDetailPage",
|
|
29
|
+
"portal": { "status": "planned", "path": "components/organisms/DataDetailPage.vue" }
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"fieldWidgets": {},
|
|
33
|
+
"componentAliases": {},
|
|
34
|
+
"aliasIndex": {},
|
|
35
|
+
"components": {},
|
|
36
|
+
"composables": {},
|
|
37
|
+
"helpers": {}
|
|
38
|
+
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Authoring: docs/templates/bundle-authoring.md
|
|
3
|
-
# /spec MUST fill inventory when information exists. grill-* only re-checks gaps/conflicts.
|
|
1
|
+
# portal-feature-bundle/v1 — Authoring: bundle-authoring.md · Split: flowgrid split <path>
|
|
4
2
|
|
|
5
3
|
schema: portal-feature-bundle/v1
|
|
6
4
|
page-id: role-domain-function
|
|
@@ -19,7 +17,14 @@ summary: |
|
|
|
19
17
|
- output: [Kết quả đầu ra]
|
|
20
18
|
- cách giải quyết (solution): [Tuỳ chọn. Ghi kỹ thuật phức tạp nếu có.]
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
successMetrics: |
|
|
21
|
+
- [Chỉ số đo được khi màn/feature đạt mục tiêu — VD: thời gian hoàn tất thao tác, tỷ lệ lỗi validation]
|
|
22
|
+
- [Bỏ bullet nếu chưa có số — ghi qualitative metric]
|
|
23
|
+
|
|
24
|
+
nonGoals: |
|
|
25
|
+
- [Phạm vi KHÔNG làm trên màn/phase này — tránh scope creep]
|
|
26
|
+
- [VD: không xử lý export Excel tại màn list — defer QA/debt]
|
|
27
|
+
|
|
23
28
|
userStories:
|
|
24
29
|
primary:
|
|
25
30
|
asA: "[Persona / Role - vd: Nhân viên Vận hành / Khách hàng / Quản trị viên]"
|
|
@@ -27,10 +32,7 @@ userStories:
|
|
|
27
32
|
soThat: "[Mục đích kinh doanh và giá trị thực tế đạt được]"
|
|
28
33
|
|
|
29
34
|
contextAndHandoff:
|
|
30
|
-
#
|
|
31
|
-
# - directRoute: Dành cho Login, Register, Public/Guest Page (mô tả path router)
|
|
32
|
-
# - sidebarMenu: Dành cho Dashboard, CRUD List, Admin Pages (mô tả menu đa cấp dropdown + label text)
|
|
33
|
-
# - contextualAction: Dành cho Detail, Edit Modal, Settings (mô tả nút bấm xxx từ màn hình A)
|
|
35
|
+
# screenAccess: directRoute | sidebarMenu | contextualAction — xem bundle-authoring.md
|
|
34
36
|
screenAccess:
|
|
35
37
|
accessType: sidebarMenu # directRoute | sidebarMenu | contextualAction
|
|
36
38
|
directRoute:
|
|
@@ -74,7 +76,6 @@ userStories:
|
|
|
74
76
|
- "Hệ thống khóa nút và hiển thị loading để chống bấm đúp (double submit prevention)."
|
|
75
77
|
- "Lưu dữ liệu thành công (không phải xóa): Toast hoặc inline alert; điều hướng theo Handoff. Thao tác xóa — xem scenario UX affordances bên dưới."
|
|
76
78
|
|
|
77
|
-
# Thêm scenario này khi màn có delete/breadcrumb/filter/disabled reason / import CSV (sau audit UX_* hoặc DSL gap).
|
|
78
79
|
- name: "Affordances UX chuẩn portal (UX — khi áp dụng)"
|
|
79
80
|
steps:
|
|
80
81
|
- "Breadcrumb / ngữ cảnh: Người dùng luôn biết đang ở module và bản ghi nào (trail hoặc back + title rõ)."
|
|
@@ -194,6 +195,12 @@ gen:
|
|
|
194
195
|
testIds: null
|
|
195
196
|
|
|
196
197
|
design:
|
|
198
|
+
# dataModel: # optional — multi-table review; xem tpl-screen-data-model.md
|
|
199
|
+
# erdRef: "<LCA>/common/db-erd.md"
|
|
200
|
+
# tables:
|
|
201
|
+
# - schema: records
|
|
202
|
+
# roleOnScreen: read-write
|
|
203
|
+
# summary: "Hồ sơ chính trên form"
|
|
197
204
|
inherits: null
|
|
198
205
|
shell:
|
|
199
206
|
tag: "#shell: DataListPage" # AuthPage | AppShell
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
|
|
2
|
+
# Feature title
|
|
3
|
+
|
|
4
|
+
## Mục lục (Contents)
|
|
5
|
+
|
|
6
|
+
1. [Tổng quan](#overview)
|
|
7
|
+
2. [Chỉ số thành công](#success-metrics)
|
|
8
|
+
3. [Phạm vi không làm](#non-goals)
|
|
9
|
+
4. [User stories và hành trình màn hình](#user-stories--screen-journey)
|
|
10
|
+
5. [Ma trận trạng thái và phân quyền](#state--permission-matrix)
|
|
11
|
+
6. [Bảng cột danh sách](#list-columns)
|
|
12
|
+
7. [Từ điển dữ liệu và validation](#data-dictionary--validation)
|
|
13
|
+
8. [Widget tùy biến](#custom-widgets)
|
|
14
|
+
9. [Luồng hành động](#action-flows)
|
|
15
|
+
- [API SSOT](#api-ssot)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Tổng quan {#overview}
|
|
20
|
+
|
|
21
|
+
| | |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| **Page ID** | `role-domain-function` |
|
|
24
|
+
| **Status** | draft |
|
|
25
|
+
| **Owner** | portal-team |
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
- **Testcase plans:** [base_test](https://github.com/raintr91/base_test) (`pnpm cases:render` on tests hub) — see docs-hub TESTS-HUB
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
- **Screen:** None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
- mục tiêu nghiệp vụ (business_goals): [Nêu rõ vấn đề đang giải quyết và giá trị kinh tế/nghiệp vụ mang lại. Viết sâu sắc để Stakeholder hiểu rõ vì sao phải làm.]
|
|
36
|
+
- các bên liên quan (stakeholders): [Ai dùng, ai hưởng lợi, ai quản lý?]
|
|
37
|
+
- kịch bản người dùng (user_journey): [Kể câu chuyện người dùng trải qua các bước trên màn hình bằng ngôn ngữ đời thường.]
|
|
38
|
+
- bối cảnh (context):
|
|
39
|
+
- description: [Phân tích sâu luồng nghiệp vụ chi tiết]
|
|
40
|
+
- input: [Dữ liệu đầu vào. CHÚ Ý liên kết cross-page / cross-module nếu có]
|
|
41
|
+
- output: [Kết quả đầu ra]
|
|
42
|
+
- cách giải quyết (solution): [Tuỳ chọn. Ghi kỹ thuật phức tạp nếu có.]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## Chỉ số thành công {#success-metrics}
|
|
48
|
+
|
|
49
|
+
- [Chỉ số đo được khi màn/feature đạt mục tiêu — VD: thời gian hoàn tất thao tác, tỷ lệ lỗi validation]
|
|
50
|
+
- [Bỏ bullet nếu chưa có số — ghi qualitative metric]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Phạm vi không làm {#non-goals}
|
|
57
|
+
|
|
58
|
+
- [Phạm vi KHÔNG làm trên màn/phase này — tránh scope creep]
|
|
59
|
+
- [VD: không xử lý export Excel tại màn list — defer QA/debt]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## API SSOT {#api-ssot}
|
|
65
|
+
|
|
66
|
+
Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function leaf (không gộp vào bundle). Sau split: `ir/design.yaml` chiếu endpoint; OpenAPI/mock theo stack.
|
|
67
|
+
|
|
68
|
+
- **Data model (review, multi-table):** [data-model.md](./data-model.md) — bảng/cột tách khỏi spec BA; SSOT codegen = `01` + `ir/design.yaml` `db`.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## User Stories & Screen Journey {#user-stories--screen-journey}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### User Story Chính
|
|
76
|
+
> **Là một** [Persona / Role - vd: Nhân viên Vận hành / Khách hàng / Quản trị viên],
|
|
77
|
+
> **Tôi muốn** [Hành động chính trên màn hình: xem danh sách, lọc, tạo mới, cập nhật, phê duyệt...],
|
|
78
|
+
> **Để** [Mục đích kinh doanh và giá trị thực tế đạt được].
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
### Cách thức Truy cập & Chuyển giao Màn hình (Screen Access & Handoff)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
- **Loại truy cập (Access Type):** `sidebarMenu`
|
|
86
|
+
|
|
87
|
+
- **Menu Sidebar:** Quản trị hệ thống > Quản lý Người dùng > **Danh sách tài khoản**
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
- **Màn hình nguồn (Source Screen):** `W-ADM-LIST-01`
|
|
91
|
+
|
|
92
|
+
- **Dữ liệu tiếp nhận (Inputs):** customer_id · booking_id
|
|
93
|
+
|
|
94
|
+
- **Điều hướng tiếp theo (Next Screen):** `W-ADM-DETAIL-01`
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
### Kịch bản Chi tiết trên Màn hình (Scenarios)
|
|
99
|
+
|
|
100
|
+
#### Khởi tạo & Tải dữ liệu ban đầu (Initial Load)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
- Kiểm tra quyền hạn người dùng (Role / Permissions) đối với màn hình.
|
|
104
|
+
|
|
105
|
+
- Tải danh sách dữ liệu theo bộ lọc mặc định hoặc bind thông tin bản ghi theo ID nhận được.
|
|
106
|
+
|
|
107
|
+
- Hiển thị trạng thái tải (Skeleton / Spinner), nếu không có dữ liệu hiển thị Empty State.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
#### Tương tác Nhập liệu & Thẩm định Dữ liệu (Input & Validation)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
- Người dùng nhập liệu các trường bắt buộc và tùy chọn trên form.
|
|
115
|
+
|
|
116
|
+
- Hệ thống validate trực tiếp trên giao diện (Inline error) khi nhập sai định dạng hoặc bỏ trống.
|
|
117
|
+
|
|
118
|
+
- Các trường có logic phụ thuộc tự động cập nhật hoặc mở thêm vùng nhập (Dynamic visibility / calculation).
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
#### Nộp dữ liệu & Hoàn tất Thành công (Happy Path Submit)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
- Người dùng nhấn nút thực thi chính (Primary Action Button).
|
|
126
|
+
|
|
127
|
+
- Hệ thống khóa nút và hiển thị loading để chống bấm đúp (double submit prevention).
|
|
128
|
+
|
|
129
|
+
- Lưu dữ liệu thành công (không phải xóa): Toast hoặc inline alert; điều hướng theo Handoff. Thao tác xóa — xem scenario UX affordances bên dưới.
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
#### Affordances UX chuẩn portal (UX — khi áp dụng)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
- Breadcrumb / ngữ cảnh: Người dùng luôn biết đang ở module và bản ghi nào (trail hoặc back + title rõ).
|
|
137
|
+
|
|
138
|
+
- Danh sách: Tìm kiếm/lọc (nếu có) và phân trang; empty state khác 'không có kết quả filter'.
|
|
139
|
+
|
|
140
|
+
- Cột trạng thái: Hiển thị chip/badge có chữ, không chỉ màu.
|
|
141
|
+
|
|
142
|
+
- Hành động trên dòng bị khóa: Có lý do hiển thị (tooltip/badge/chip), không chỉ nút disabled im lặng.
|
|
143
|
+
|
|
144
|
+
- Xóa / xóa hàng loạt: Hộp thoại xác nhận chặn → gọi API → hộp thoại kết quả (thành công/lỗi) bắt người dùng xác nhận — không chỉ toast.
|
|
145
|
+
|
|
146
|
+
- Import CSV (nếu có): Chọn file → kiểm tra → xác nhận → báo kết quả; lỗi theo dòng có thể tải log.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
#### Xử lý Ngoại lệ & Lỗi Giao diện (Exceptions & Edge Cases)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
- Xung đột dữ liệu / Đã bị sửa đổi bởi người khác (409 Conflict): Cảnh báo và tải lại.
|
|
154
|
+
|
|
155
|
+
- Lỗi kết nối / Máy chủ (Network / 5xx error): Giữ nguyên dữ liệu đã nhập trên form, không bắt nhập lại.
|
|
156
|
+
|
|
157
|
+
- Hết hạn phiên làm việc (Session timeout): Yêu cầu xác thực lại và bảo lưu tạm bản nháp.
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
#### Tác vụ Ngầm Kích hoạt từ Màn hình (Background / Async Logic - Tùy chọn)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
- Nếu thao tác kích hoạt xử lý ngầm (gửi tin nhắn, tạo job đồng bộ): Hệ thống đẩy sự kiện vào hàng đợi.
|
|
165
|
+
|
|
166
|
+
- Cập nhật trạng thái hiển thị trên màn hình thành 'Đang xử lý ngầm' (Processing) để người dùng theo dõi.
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
### Tiêu chí Nghiệm thu (Acceptance Criteria)
|
|
174
|
+
|
|
175
|
+
- [ ] [ ] Người dùng có quyền truy cập xem được toàn bộ thông tin hợp lệ.
|
|
176
|
+
|
|
177
|
+
- [ ] [ ] Form chặn nộp khi thiếu các trường bắt buộc và thông báo lỗi rõ ràng.
|
|
178
|
+
|
|
179
|
+
- [ ] [ ] Khi nộp thành công (không phải delete), dữ liệu được lưu đúng và chuyển trang mượt mà.
|
|
180
|
+
|
|
181
|
+
- [ ] [ ] (Khi có delete) Confirm trước xóa và result dialog sau API — không toast-only.
|
|
182
|
+
|
|
183
|
+
- [ ] [ ] (Khi có action disabled theo rule) Người dùng hiểu lý do bị khóa (copy/badge/tooltip).
|
|
184
|
+
|
|
185
|
+
- [ ] [ ] (Khi list/detail drill-down) Breadcrumb hoặc ngữ cảnh điều hướng đủ cho deep link.
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
## Ma Trận Trạng Thái Giao Diện & Phân Quyền (State & Permission Matrix) {#state--permission-matrix}
|
|
196
|
+
|
|
197
|
+
| Trạng Thái Bản Ghi (Record Status) | Trạng Thái Trường Form (Fields State) | Nút Hành Động Khả Dụng (Visible Buttons) | Ghi Chú Phân Quyền RBAC (Role Overrides) |
|
|
198
|
+
| --- | --- | --- | --- |
|
|
199
|
+
| `DRAFT` | ✏️ Cho phép sửa (Editable) | `btn_save_draft`, `btn_submit_record`, `btn_cancel` | Áp dụng cho mọi vai trò |
|
|
200
|
+
| `PENDING_APPROVAL` | 🔒 Chỉ đọc (Readonly) | `btn_cancel_request` | **manager**: Nút khả dụng [`btn_approve`, `btn_reject`] |
|
|
201
|
+
| `APPROVED` | 🔒 Chỉ đọc (Readonly) | `btn_print`, `btn_export` | Áp dụng cho mọi vai trò |
|
|
202
|
+
| `REJECTED` | ✏️ Cho phép sửa (Editable) | `btn_resubmit`, `btn_delete` | Áp dụng cho mọi vai trò |
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
## Bảng cột danh sách (List columns) {#list-columns}
|
|
208
|
+
|
|
209
|
+
| Nhãn cột | Key | Ý nghĩa nghiệp vụ | Mục đích UI | Widget / render | Sort | DB (schema.field) |
|
|
210
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
211
|
+
| Trạng thái | `status` | Tình trạng hoạt động của tài khoản trong hệ thống để quản lý quyền đăng nhập và giao dịch | Cho user thấy tài khoản còn hoạt động hay đã bị tạm khóa | chip / custom | Không | — |
|
|
212
|
+
|
|
213
|
+
<p><strong>DB chi tiết / multi-table:</strong> <a href="./data-model.md">data-model.md</a> · SSOT ghi: <code>*.bundle.yaml</code> + <code>ir/design.yaml</code>.</p>
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
## Danh Mục Trường Nhập Liệu & Quy Tắc Kiểm Tra Hợp Lệ (Data Dictionary & Validation) {#data-dictionary--validation}
|
|
219
|
+
|
|
220
|
+
| Tên Trường (Label) | Mã Kỹ Thuật (Key) | Kiểu (Type) | Bắt Buộc? | Ràng Buộc & Quy Tắc Hợp Lệ (Rules) | Thông Báo Lỗi Inline (Messages) |
|
|
221
|
+
| --- | --- | --- | --- | --- | --- |
|
|
222
|
+
| Mã hồ sơ | `N/A` | input | Bắt buộc | Kiểu: `slug_uppercase`<br>Regex: `^[A-Z0-9_-]{5,20}$`<br>Độ dài [5, 20]<br>Unique DB: `/api/v1/records/check-duplicate-code` | Required: "Vui lòng nhập mã hồ sơ."<br>"Mã hồ sơ chỉ được gồm chữ in hoa, chữ số và ký tự gạch (- _)"<br>"Độ dài mã hồ sơ bắt buộc từ 5 đến 20 ký tự"<br>DB: "Mã hồ sơ này đã tồn tại trên hệ thống. Vui lòng chọn mã khác." |
|
|
223
|
+
| Tên hồ sơ | `N/A` | input | Bắt buộc | Kiểu: `text_clean`<br>Độ dài [3, 100] | Required: "Vui lòng nhập tên hồ sơ."<br>"Tên hồ sơ bắt buộc từ 3 đến 100 ký tự" |
|
|
224
|
+
| Cấp độ dịch vụ | `N/A` | select | Bắt buộc | Kiểu: `enum` | Required: "Vui lòng chọn cấp độ dịch vụ." |
|
|
225
|
+
| Yêu cầu đặc biệt cho gói VIP | `N/A` | textarea | Tùy chọn | Độ dài [0, 500]<br>Phụ thuộc: `service_level == 'PREMIUM'` | Required: "Vui lòng nhập yêu cầu đặc biệt khi đăng ký gói VIP."<br>"Yêu cầu đặc biệt không được vượt quá 500 ký tự." |
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
<div id="custom-widgets"></div>
|
|
231
|
+
|
|
232
|
+
## Đặc Tả Khối Giao Diện Tùy Biến (Custom UI Blocks)
|
|
233
|
+
|
|
234
|
+
### Khối Tùy Biến (`custom`)
|
|
235
|
+
- **Mục đích thao tác:** Theo dõi tiến độ xử lý và xem chi tiết phản hồi từng bước duyệt
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
## Đặc Tả Quy Trình Hành Động (Action Flows) {#action-flows}
|
|
242
|
+
|
|
243
|
+
### Lưu & Xác Nhận
|
|
244
|
+
- **Mục đích thao tác:** Thẩm định toàn bộ form và gửi dữ liệu lên máy chủ
|
|
245
|
+
- **Vị trí hiển thị:** `form_footer` | **Trigger:** `click` | **Variant:** `primary`
|
|
246
|
+
|
|
247
|
+
### Hủy Bỏ
|
|
248
|
+
- **Mục đích thao tác:** Hủy thao tác tạo mới và quay lại trang trước
|
|
249
|
+
- **Vị trí hiển thị:** `form_footer` | **Trigger:** `click` | **Variant:** `outline`
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
## actors
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
```yaml
|
|
259
|
+
[]
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
## requirements
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
- "[Ghi chú: Bắt buộc định nghĩa State Machine, và UI Permissions vào thuộc tính
|
|
269
|
+
states của từng item trong design.sections. KHÔNG liệt kê chung chung ở đây]"
|
|
270
|
+
- "Edge Cases: [Bắt buộc định nghĩa ngoại lệ như lỗi luồng, data hỏng,
|
|
271
|
+
concurrency]"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Copy to qa/open/QA-<bundle.id>-NNNN.yaml — do not leave this file in qa/open/.
|
|
2
2
|
id: QA-cmp-adm-002-02-01-02-0001
|
|
3
3
|
kind: customer # customer | choice | tech-debt
|
|
4
|
-
skill: spec # spec | grill-bqa | grill-dev | grill-docs | api-spec | api-update
|
|
4
|
+
skill: spec # spec | grill-bqa | grill-dev | grill-docs | api-spec | api-update
|
|
5
5
|
target:
|
|
6
6
|
path: surfaces/<surface>/CMP-*/<NN…>/<slug>.bundle.yaml
|
|
7
7
|
at: design.zones.main.items.title.purpose
|