@shanyucoder/flowgrid 0.1.9 → 0.1.11
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/adapters/nextjs/codegen/runners/lib/resolve-hub-id.mjs +7 -4
- package/adapters/nextjs/unitgen/runners/README.md +4 -4
- package/adapters/nextjs/unitgen/runners/generate.mjs +1 -1
- package/adapters/nuxt4/codegen/runners/lib/resolve-hub-id.mjs +7 -4
- package/adapters/nuxt4/unitgen/runners/README.md +4 -4
- package/adapters/nuxt4/unitgen/runners/generate.mjs +1 -1
- package/adapters/shared/common-gen.mjs +1 -1
- package/adapters/shared/resolve-hub-id.mjs +7 -4
- package/bin/flowgrid.mjs +43 -9
- package/bin/lib/audit-run.mjs +5 -1
- package/bin/lib/docs-hub-locale.mjs +9 -0
- package/bin/lib/init-scaffold.mjs +30 -2
- package/dist/docs/mcp/tools.js +4 -4
- package/dist/docs/mcp/tools.js.map +1 -1
- package/dist/docs/scan/ids.d.ts +1 -1
- package/dist/docs/scan/ids.js +9 -8
- package/dist/docs/scan/ids.js.map +1 -1
- package/dist/docs/scan/route.js +2 -2
- package/dist/docs/scan/route.js.map +1 -1
- package/dist/graph/mcp/tools.js +1 -1
- package/dist/graph/mcp/tools.js.map +1 -1
- package/engines/cases/render-cases.mjs +34 -26
- package/engines/docs/lib/audit-hub-prd.mjs +136 -0
- package/engines/docs/lib/audit-risks-catalog.mjs +142 -0
- package/engines/docs/lib/docs-hub-locale.mjs +100 -0
- package/engines/docs/lib/render-bundle-markdown.mjs +8 -1
- package/engines/docs/vitepress/config.ts +4 -4
- package/engines/spec/lib/audit-bundle-gaps.mjs +38 -7
- package/engines/spec/lib/audit-flow-gaps.mjs +2 -2
- package/engines/spec/lib/audit-legacy-gaps.mjs +10 -0
- package/engines/spec/lib/bundle-schema.mjs +3 -1
- package/engines/testcase/_staging-from-portal/runners/README.md +3 -3
- package/engines/testcase/_staging-from-portal/runners/generate.mjs +2 -2
- package/engines/testcase/_staging-from-portal/runners/lib/read-testcase.mjs +1 -1
- package/engines/testcase/runners/README.md +3 -3
- package/engines/testcase/runners/generate.mjs +2 -2
- package/engines/testcase/runners/lib/read-testcase.mjs +1 -1
- package/engines/testcase/runners/lib/resolve-hub-id.mjs +6 -6
- package/harness/be/skills/audit-api/SKILL.md +1 -1
- package/harness/be/skills/grill-api-unit/SKILL.md +1 -1
- package/harness/common/skills/legacy/SKILL.md +2 -2
- package/harness/docs/extracts/architecture-core.md +2 -0
- package/harness/docs/extracts/common-scope.md +8 -8
- package/harness/docs/extracts/extract-registry.docs.json +7 -3
- package/harness/docs/extracts/product-id-convention.md +58 -0
- package/harness/docs/extracts/spec-core.md +1 -1
- package/harness/docs/extracts/spec-prd-lite.md +11 -13
- package/harness/docs/extracts/spec-requirement.md +1 -1
- package/harness/docs/extracts/tpl-adoption-inventory.md +32 -0
- package/harness/docs/extracts/tpl-module.md +9 -40
- package/harness/docs/extracts/tpl-overview-prd.md +11 -0
- package/harness/docs/extracts/tpl-risk-register.md +28 -0
- package/harness/docs/extracts/tpl-surface-prd.md +7 -0
- package/harness/docs/rules/agent-compliance.mdc +1 -1
- package/harness/docs/rules/docs-hub.mdc +1 -1
- package/harness/docs/rules/flowgrid-process.mdc +1 -1
- package/harness/docs/skills/adopt/SKILL.md +43 -11
- package/harness/docs/skills/api-update/SKILL.md +1 -1
- package/harness/docs/skills/background-logic/SKILL.md +1 -1
- package/harness/docs/skills/common-spec/SKILL.md +1 -1
- package/harness/docs/skills/cross-service/SKILL.md +1 -1
- package/harness/docs/skills/db-erd/SKILL.md +1 -1
- package/harness/docs/skills/grill/SKILL.md +2 -0
- package/harness/docs/skills/grill-hub-prd/SKILL.md +38 -0
- package/harness/docs/skills/module/SKILL.md +9 -6
- package/harness/docs/skills/overview/SKILL.md +7 -6
- package/harness/docs/skills/risk-register/SKILL.md +29 -0
- package/harness/docs/skills/spec/SKILL.md +5 -4
- package/harness/docs/skills/surfaces/SKILL.md +4 -1
- package/harness/docs/skills/{business-process → user-flow}/SKILL.md +8 -8
- package/harness/fe/skills/gen-common/SKILL.md +1 -1
- package/harness/fe/skills/grill-prototype/SKILL.md +1 -1
- package/harness/fe/skills/prototype/SKILL.md +6 -6
- package/harness/fe/skills/unit/SKILL.md +4 -4
- package/harness/shared/SSOT_AGENT_PROTOCOL.md +1 -1
- package/harness/tests/extracts/grill-scenario-flow.md +2 -2
- package/harness/tests/skills/grill-testcase/SKILL.md +2 -2
- package/harness/tests/skills/scenario/SKILL.md +11 -11
- package/harness/tests/skills/testcase/SKILL.md +2 -2
- package/harness/tests/templates/SC.example.md +26 -26
- package/harness/tests/templates/TC.example.yaml +3 -3
- package/lexicon/registry-tags.en.txt +2 -2
- package/package.json +1 -1
- package/templates/project-skeleton/architecture/03-business-process/index.md +3 -0
- package/templates/project-skeleton/architecture/{03-business-process → 03-user-flows}/FLOW-login.md +16 -16
- package/templates/project-skeleton/architecture/{03-business-process → 03-user-flows}/FLOW-template.md +6 -7
- package/templates/project-skeleton/architecture/04-solution-strategy/index.md +1 -1
- package/templates/project-skeleton/architecture/08-cross-cutting/security.md +1 -1
- package/templates/project-skeleton/architecture/11-risks/index.md +7 -17
- package/templates/project-skeleton/architecture/11-risks/risk-register.md +34 -0
- package/templates/project-skeleton/architecture/12-glossary/index.md +5 -3
- package/templates/project-skeleton/architecture/model/workspace.dsl +7 -7
- package/templates/project-skeleton/overview/index.md +21 -43
- package/templates/project-skeleton/overview/operational-areas/_template.md +15 -22
- package/templates/project-skeleton/surfaces/_module-index.template.md +40 -0
- package/templates/project-skeleton/surfaces/_surface-index.template.md +44 -0
- package/templates/shared/bundle-authoring.md +6 -3
- package/templates/shared/default-layout.ejs +108 -62
- package/templates/shared/feature.bundle.yaml +15 -6
- package/templates/shared/ir/generated/spec.md +24 -24
- package/templates/shared/tpl-api-contract.md +5 -5
- package/templates/tests-skeleton/cases/README.md +1 -1
- package/templates/tests-skeleton/catalog/locale.yaml +16 -15
- package/templates/tests-skeleton/tpl-testcase-plan.md +6 -6
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SURFACE-TEMPLATE
|
|
3
|
+
surfaceCode: ADM
|
|
4
|
+
title: "Surface channel name"
|
|
5
|
+
status: draft
|
|
6
|
+
contentLocale: vi
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Surface: [Channel name]
|
|
10
|
+
|
|
11
|
+
> Section titles are **English**. Prose uses `contentLocale` / `docs-hub.locale.yaml`.
|
|
12
|
+
|
|
13
|
+
## Goals {#goals}
|
|
14
|
+
|
|
15
|
+
[Value of this channel — one paragraph.]
|
|
16
|
+
|
|
17
|
+
## Background {#background}
|
|
18
|
+
|
|
19
|
+
[Who uses this channel and in what context.]
|
|
20
|
+
|
|
21
|
+
## Scope {#scope}
|
|
22
|
+
|
|
23
|
+
**In scope:**
|
|
24
|
+
|
|
25
|
+
- [CMP modules on this surface]
|
|
26
|
+
|
|
27
|
+
**Out of scope:**
|
|
28
|
+
|
|
29
|
+
- [Other surface or integration-only scope]
|
|
30
|
+
|
|
31
|
+
## Modules (CMP)
|
|
32
|
+
|
|
33
|
+
| CMP | Summary | Primary flow |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `CMP-…` | […] | `FLOW-…` (if any) |
|
|
36
|
+
|
|
37
|
+
## User flows on this surface
|
|
38
|
+
|
|
39
|
+
- Catalog: [`architecture/03-user-flows/`](/architecture/03-user-flows/)
|
|
40
|
+
- Shared on surface: `common/user-flows/FLOW-*.md` (when ≥2 modules share)
|
|
41
|
+
|
|
42
|
+
## Features overview
|
|
43
|
+
|
|
44
|
+
[1–2 paragraphs: how to read the hub — detail per screen in bundles and `ir/generated/spec.md`.]
|
|
@@ -8,8 +8,11 @@ Hub: `docs/templates/feature.bundle.yaml` · split: `pnpm spec:split`
|
|
|
8
8
|
|-----|---------|
|
|
9
9
|
| `page-id` | Screen identity (`cmp-adm-000-01`). Split copies this onto `ir/spec.yaml` as `page-id` (not `id`, so it does not collide with requirement/section ids). Legacy bundles may still use `id`. |
|
|
10
10
|
| `summary` | Phải trình bày dạng bullet. Bắt buộc có các tiêu đề (chuẩn Arc42 business): **mục tiêu nghiệp vụ** (business_goals), **các bên liên quan** (stakeholders), **kịch bản người dùng** (user_journey), **bối cảnh** (description, input liên kết cross-page/module, output) và **cách giải quyết** (tùy chọn). Mục đích để 100% Non-tech Stakeholder hiểu và duyệt. |
|
|
11
|
-
| `
|
|
12
|
-
| `nonGoals` | **
|
|
11
|
+
| `scopeIn` | **Khuyến nghị** — trong phạm vi màn/phase (PRD). Audit `WARN_NO_SCOPE_IN`. |
|
|
12
|
+
| `nonGoals` | **Khuyến nghị** — ngoài phạm vi màn/phase (PRD). Audit `WARN_NO_NON_GOALS`. |
|
|
13
|
+
| `userFlows` | **Khuyến nghị** — link `FLOW-*` (`03-user-flows` / `common/user-flows`) hoặc journey ngắn. Audit `WARN_NO_USER_FLOWS` khi multi-screen. |
|
|
14
|
+
| `nfr` | **Khuyến nghị** — hiệu năng, bảo mật. Audit `WARN_NO_NFR`. |
|
|
15
|
+
| _(rủi ro)_ | **Không** khai báo trên bundle. SSOT: `architecture/11-risks/risk-register.md` — `/risk-register`. Key `risks:` → audit `WARN_BUNDLE_RISKS_FORBIDDEN`. |
|
|
13
16
|
| `userStories` | **Khối User Stories chuyên sâu cho màn hình:** `primary`, `contextAndHandoff` (+ `screenAccess`), `scenarios` (5 kịch bản chuẩn + **scenario thứ 6 “Affordances UX”** khi màn có delete/filter/breadcrumb/disabled/import — xem `feature.bundle.yaml`), `acceptanceCriteria` (kèm checkbox UX khi áp dụng). **Split:** `pnpm spec:split` copy nguyên khối sang `ir/spec.yaml` (business prose); **không** tự sinh từ `design` — Agent phải cập nhật `userStories` khi bổ sung DSL/`#needs-component`/audit `UX_*`. Render → `## User Stories & Screen Journey` trong Markdown. |
|
|
14
17
|
| `spec` | Design v1 — **`entities`**, **`relationships`**, actors, requirements, `ui.routes`, **`ui.list` / `ui.form` / `ui.detail`**, `acceptance`. **Không** author `spec.api` — API SSOT là `api/<seq>/01-backend-spec.yaml`. |
|
|
15
18
|
| `gen` | **Bắt buộc trước flowgrid gen:** `codegen.profile` (`auth` login/forgot/reset; `change-password`; `public`; `not-found`/`error`; `list`/`create`/`admin-crud`) + entity/module, `tags`, derived `ui.*`. `/grill-dev` ghi. Endpoint `action` ghi trên **01**, không trên bundle. |
|
|
@@ -285,7 +288,7 @@ actions:
|
|
|
285
288
|
trigger: button_click
|
|
286
289
|
apiRefs: [ feature.create ]
|
|
287
290
|
# tags: ["#reuse-api"]
|
|
288
|
-
# reuseFrom: surfaces/admin/CMP-01/
|
|
291
|
+
# reuseFrom: surfaces/admin/CMP-ADM-AUTH-01/01/01/01/api/01/01-backend-spec.yaml
|
|
289
292
|
onSuccess:
|
|
290
293
|
- Navigate to list page
|
|
291
294
|
- Show success toast "Created successfully"
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
const pageId = spec['page-id'] || spec.id || '';
|
|
3
3
|
const docStatus = spec.status || 'draft';
|
|
4
4
|
const docOwner = spec.owner || '';
|
|
5
|
-
const
|
|
5
|
+
const hasScopeIn = spec.scopeIn && String(spec.scopeIn).trim();
|
|
6
6
|
const hasNonGoals = spec.nonGoals && String(spec.nonGoals).trim();
|
|
7
7
|
const hasStories = !!spec.userStories;
|
|
8
|
+
const hasNfr = spec.nfr && String(spec.nfr).trim();
|
|
9
|
+
const hasUserFlows = spec.userFlows && String(spec.userFlows).trim();
|
|
10
|
+
const qaOpen = spec['Q&A'] && String(spec['Q&A']).trim();
|
|
11
|
+
const hasEntities = spec.entities && (Array.isArray(spec.entities) ? spec.entities.length : true);
|
|
8
12
|
const pageSections = spec.sections || spec.design?.sections || [];
|
|
9
13
|
const pageActions = spec.actions || spec.design?.actions || [];
|
|
10
14
|
const pageStateMatrix = spec.stateMatrix || spec.design?.stateMatrix;
|
|
@@ -21,23 +25,28 @@
|
|
|
21
25
|
const hasLegacy = spec.legacy && (spec.legacy.behaviors?.length || spec.legacy.fields?.length || spec.legacy.evidence?.length);
|
|
22
26
|
const hasDebt = spec.pendingTechDebt && spec.pendingTechDebt.length;
|
|
23
27
|
|
|
28
|
+
const contentLocale = context.docsContentLocale || 'vi';
|
|
24
29
|
const toc = [];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
if (
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
if (
|
|
39
|
-
if (
|
|
40
|
-
|
|
30
|
+
let n = 1;
|
|
31
|
+
const addToc = (label, anchor) => { toc.push(`${n}. [${label}](#${anchor})`); n += 1; };
|
|
32
|
+
addToc('Overview', 'overview');
|
|
33
|
+
if (hasScopeIn) addToc('In scope', 'scope-in');
|
|
34
|
+
if (hasNonGoals) addToc('Out of scope', 'scope-out');
|
|
35
|
+
if (hasStories) addToc('User stories & requirements', 'user-stories--screen-journey');
|
|
36
|
+
addToc('Data & integrations / API', 'data-integrations');
|
|
37
|
+
if (hasUserFlows) addToc('User flows', 'user-flows');
|
|
38
|
+
if (hasNfr) addToc('Non-functional (NFR)', 'nfr');
|
|
39
|
+
if (hasState) addToc('State & permissions matrix', 'state--permission-matrix');
|
|
40
|
+
if (hasListColumns) addToc('List columns', 'list-columns');
|
|
41
|
+
if (hasListExtras) addToc('Filters & pagination', 'list-filters-pagination');
|
|
42
|
+
if (hasValidation) addToc('Data dictionary & validation', 'data-dictionary--validation');
|
|
43
|
+
if (hasCustomWidget) addToc('Custom widgets', 'custom-widgets');
|
|
44
|
+
if (hasActions) addToc('Action flows', 'action-flows');
|
|
45
|
+
else if (pageSections.length) addToc('Page composition', 'page-composition');
|
|
46
|
+
if (qaOpen) addToc('Appendix — open questions', 'appendix-open-questions');
|
|
47
|
+
if (hasReview) addToc('Review notes', 'review-notes');
|
|
48
|
+
if (hasLegacy) addToc('Legacy evidence', 'legacy-evidence');
|
|
49
|
+
if (hasDebt) addToc('Tech debt', 'pending-tech-debt');
|
|
41
50
|
|
|
42
51
|
const route = spec.ui?.routes?.[0];
|
|
43
52
|
const routePath = route?.path || '';
|
|
@@ -45,13 +54,13 @@
|
|
|
45
54
|
%>
|
|
46
55
|
# <%= spec.title || spec.id %>
|
|
47
56
|
|
|
48
|
-
##
|
|
57
|
+
## Table of contents
|
|
49
58
|
|
|
50
59
|
<%= toc.join('\n') %>
|
|
51
60
|
|
|
52
61
|
---
|
|
53
62
|
|
|
54
|
-
##
|
|
63
|
+
## Overview {#overview}
|
|
55
64
|
|
|
56
65
|
| | |
|
|
57
66
|
| --- | --- |
|
|
@@ -75,36 +84,29 @@
|
|
|
75
84
|
%>
|
|
76
85
|
<%= screenLine %>
|
|
77
86
|
|
|
78
|
-
- **QA inbox:** [
|
|
87
|
+
- **QA inbox:** [Open questions](../../qa/index.md) (same function leaf)
|
|
79
88
|
- **Generated (VitePress):** [spec](./spec.md) · [data-model](./data-model.md) · [api](./api.md) _(khi có `01`)_
|
|
80
89
|
|
|
81
90
|
<% if (spec.summary) { %>
|
|
82
91
|
<%= spec.summary %>
|
|
83
92
|
<% } %>
|
|
84
93
|
|
|
85
|
-
<% if (
|
|
86
|
-
##
|
|
94
|
+
<% if (hasScopeIn) { %>
|
|
95
|
+
## In scope {#scope-in}
|
|
87
96
|
|
|
88
|
-
<%= spec.
|
|
97
|
+
<%= spec.scopeIn %>
|
|
89
98
|
|
|
90
99
|
<% } %>
|
|
91
100
|
|
|
92
101
|
<% if (hasNonGoals) { %>
|
|
93
|
-
##
|
|
102
|
+
## Out of scope {#scope-out}
|
|
94
103
|
|
|
95
104
|
<%= spec.nonGoals %>
|
|
96
105
|
|
|
97
106
|
<% } %>
|
|
98
107
|
|
|
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
|
-
|
|
106
108
|
<% if (spec.userStories) { %>
|
|
107
|
-
## User
|
|
109
|
+
## User stories & requirements {#user-stories--screen-journey}
|
|
108
110
|
|
|
109
111
|
<% if (typeof spec.userStories === 'string') { %>
|
|
110
112
|
<%= spec.userStories %>
|
|
@@ -112,29 +114,29 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
112
114
|
<% spec.userStories.forEach(story => { %>
|
|
113
115
|
### <%= story.title || story.name || 'User Story' %>
|
|
114
116
|
<% if (story.asA || story.role) { %>
|
|
115
|
-
> **
|
|
116
|
-
> **
|
|
117
|
-
>
|
|
117
|
+
> **As a** <%= story.asA || story.role %>,
|
|
118
|
+
> **I want** <%= story.iWant || story.action %>,
|
|
119
|
+
> **so that** <%= story.soThat || story.benefit %>.
|
|
118
120
|
<% } else if (story.story) { %>
|
|
119
121
|
> <%= story.story %>
|
|
120
122
|
<% } %>
|
|
121
123
|
|
|
122
124
|
<% if (story.preConditions && story.preConditions.length) { %>
|
|
123
|
-
|
|
125
|
+
**Pre-conditions:**
|
|
124
126
|
<% (Array.isArray(story.preConditions) ? story.preConditions : [story.preConditions]).forEach(p => { %>
|
|
125
127
|
- <%= p %>
|
|
126
128
|
<% }); %>
|
|
127
129
|
<% } %>
|
|
128
130
|
|
|
129
131
|
<% if (story.scenarios && story.scenarios.length) { %>
|
|
130
|
-
**
|
|
132
|
+
**Scenarios:**
|
|
131
133
|
<% story.scenarios.forEach(sc => { %>
|
|
132
134
|
- **<%= sc.name || sc.title %>:** <%= sc.description || sc.steps || sc %>
|
|
133
135
|
<% }); %>
|
|
134
136
|
<% } %>
|
|
135
137
|
|
|
136
138
|
<% if (story.acceptanceCriteria && story.acceptanceCriteria.length) { %>
|
|
137
|
-
**
|
|
139
|
+
**Acceptance criteria:**
|
|
138
140
|
<% (Array.isArray(story.acceptanceCriteria) ? story.acceptanceCriteria : [story.acceptanceCriteria]).forEach(ac => { %>
|
|
139
141
|
- [ ] <%= ac %>
|
|
140
142
|
<% }); %>
|
|
@@ -143,34 +145,34 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
143
145
|
<% }); %>
|
|
144
146
|
<% } else if (typeof spec.userStories === 'object') { %>
|
|
145
147
|
<% if (spec.userStories.primary) { %>
|
|
146
|
-
###
|
|
147
|
-
> **
|
|
148
|
-
> **
|
|
149
|
-
>
|
|
148
|
+
### Primary user story
|
|
149
|
+
> **As a** <%= spec.userStories.primary.asA || spec.userStories.primary.role %>,
|
|
150
|
+
> **I want** <%= spec.userStories.primary.iWant || spec.userStories.primary.action %>,
|
|
151
|
+
> **so that** <%= spec.userStories.primary.soThat || spec.userStories.primary.benefit %>.
|
|
150
152
|
<% } %>
|
|
151
153
|
|
|
152
154
|
<% if (spec.userStories.contextAndHandoff) { %>
|
|
153
|
-
###
|
|
155
|
+
### Screen access & handoff
|
|
154
156
|
<% if (spec.userStories.contextAndHandoff.screenAccess) { %>
|
|
155
157
|
<% const sa = spec.userStories.contextAndHandoff.screenAccess; %>
|
|
156
|
-
- **
|
|
158
|
+
- **Access type:** `<%= sa.accessType || 'N/A' %>`
|
|
157
159
|
<% if (sa.accessType === 'directRoute' && sa.directRoute) { %>
|
|
158
|
-
-
|
|
160
|
+
- **Path:** `<%= sa.directRoute.routePath || '' %>` <%= sa.directRoute.isPublic ? '(public)' : '(authenticated)' %>
|
|
159
161
|
<% } else if (sa.accessType === 'sidebarMenu' && sa.sidebarMenu) { %>
|
|
160
|
-
- **
|
|
162
|
+
- **Sidebar menu:** <%= sa.sidebarMenu.menuHierarchy ? sa.sidebarMenu.menuHierarchy.join(' > ') : '' %> > **<%= sa.sidebarMenu.menuLabel || '' %>**
|
|
161
163
|
<% } else if (sa.accessType === 'contextualAction' && sa.contextualAction) { %>
|
|
162
|
-
- **
|
|
164
|
+
- **Triggered by:** <%= sa.contextualAction.triggerControl || '' %> on screen `<%= sa.contextualAction.sourceScreen || '' %>` (navigation: `<%= sa.contextualAction.navigationType || 'pageRedirect' %>`)
|
|
163
165
|
<% } %>
|
|
164
166
|
<% } %>
|
|
165
|
-
- **
|
|
167
|
+
- **Source screen:** `<%= spec.userStories.contextAndHandoff.sourceScreen || 'Direct' %>`
|
|
166
168
|
<% if (spec.userStories.contextAndHandoff.inputsReceived && spec.userStories.contextAndHandoff.inputsReceived.length) { %>
|
|
167
|
-
- **
|
|
169
|
+
- **Inputs received:** <%= Array.isArray(spec.userStories.contextAndHandoff.inputsReceived) ? spec.userStories.contextAndHandoff.inputsReceived.join(' · ') : spec.userStories.contextAndHandoff.inputsReceived %>
|
|
168
170
|
<% } %>
|
|
169
|
-
-
|
|
171
|
+
- **Next screen on success:** `<%= spec.userStories.contextAndHandoff.nextScreenOnSuccess || 'None' %>`
|
|
170
172
|
<% } %>
|
|
171
173
|
|
|
172
174
|
<% if (spec.userStories.scenarios && spec.userStories.scenarios.length) { %>
|
|
173
|
-
###
|
|
175
|
+
### Scenarios
|
|
174
176
|
<% spec.userStories.scenarios.forEach(sc => { %>
|
|
175
177
|
#### <%= sc.name || sc.title %>
|
|
176
178
|
<% if (sc.steps && sc.steps.length) { %>
|
|
@@ -184,7 +186,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
184
186
|
<% } %>
|
|
185
187
|
|
|
186
188
|
<% if (spec.userStories.acceptanceCriteria && spec.userStories.acceptanceCriteria.length) { %>
|
|
187
|
-
###
|
|
189
|
+
### Acceptance criteria
|
|
188
190
|
<% spec.userStories.acceptanceCriteria.forEach(ac => { %>
|
|
189
191
|
- [ ] <%= ac %>
|
|
190
192
|
<% }); %>
|
|
@@ -193,6 +195,37 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
193
195
|
<% } %>
|
|
194
196
|
<% } %>
|
|
195
197
|
|
|
198
|
+
## Data & integrations / API {#data-integrations}
|
|
199
|
+
|
|
200
|
+
Backend contract: `api/<seq>/01-backend-spec.yaml` on the same function leaf (not merged into bundle).
|
|
201
|
+
|
|
202
|
+
- **Data model (read):** [data-model.md](./data-model.md)
|
|
203
|
+
- **API (read):** [api.md](./api.md) — summary from `01-backend-spec.yaml` when present
|
|
204
|
+
- **Codegen SSOT:** `ir/design.yaml` + `01` + bundle `db` fields
|
|
205
|
+
|
|
206
|
+
<% if (hasEntities) { %>
|
|
207
|
+
### Entities on this screen (summary)
|
|
208
|
+
|
|
209
|
+
```yaml
|
|
210
|
+
<%= context.stringify(spec.entities).trim() %>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
<% } %>
|
|
214
|
+
|
|
215
|
+
<% if (hasUserFlows) { %>
|
|
216
|
+
## User flows {#user-flows}
|
|
217
|
+
|
|
218
|
+
<%= spec.userFlows %>
|
|
219
|
+
|
|
220
|
+
<% } %>
|
|
221
|
+
|
|
222
|
+
<% if (hasNfr) { %>
|
|
223
|
+
## Non-functional (NFR) {#nfr}
|
|
224
|
+
|
|
225
|
+
<%= spec.nfr %>
|
|
226
|
+
|
|
227
|
+
<% } %>
|
|
228
|
+
|
|
196
229
|
<%
|
|
197
230
|
const page = {
|
|
198
231
|
nav: spec.nav || spec.design?.nav,
|
|
@@ -204,21 +237,25 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
204
237
|
%>
|
|
205
238
|
|
|
206
239
|
<% if (pageStateMatrix) { %>
|
|
207
|
-
##
|
|
240
|
+
## State & permissions matrix {#state--permission-matrix}
|
|
208
241
|
|
|
209
242
|
<%- context.renderStateMatrixTable(pageStateMatrix) %>
|
|
210
243
|
|
|
211
244
|
<% } %>
|
|
212
245
|
|
|
246
|
+
<% if (hasListColumns || hasListExtras || hasValidation || hasCustomWidget || hasActions || pageSections.length) { %>
|
|
247
|
+
> **Note (business reviewers):** Sections below are technical screen detail (dev/QA). Skip when reviewing scope and user stories only.
|
|
248
|
+
|
|
249
|
+
<% } %>
|
|
213
250
|
<%
|
|
214
251
|
const listColumnsMd = context.renderListColumnsTable && context.renderListColumnsTable(listUi);
|
|
215
252
|
if (listColumnsMd) {
|
|
216
253
|
%>
|
|
217
|
-
##
|
|
254
|
+
## List columns {#list-columns}
|
|
218
255
|
|
|
219
256
|
<%- listColumnsMd %>
|
|
220
257
|
|
|
221
|
-
<p><strong>DB
|
|
258
|
+
<p><strong>Multi-table DB detail:</strong> <a href="./data-model.md">data-model.md</a> · SSOT: <code>*.bundle.yaml</code> + <code>ir/design.yaml</code>.</p>
|
|
222
259
|
|
|
223
260
|
<% } %>
|
|
224
261
|
|
|
@@ -227,16 +264,16 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
227
264
|
const paginationMd = context.renderListPaginationBlock && context.renderListPaginationBlock(listUi);
|
|
228
265
|
if (filtersMd || paginationMd) {
|
|
229
266
|
%>
|
|
230
|
-
##
|
|
267
|
+
## List filters & pagination {#list-filters-pagination}
|
|
231
268
|
|
|
232
269
|
<% if (filtersMd) { %>
|
|
233
|
-
###
|
|
270
|
+
### Filters
|
|
234
271
|
|
|
235
272
|
<%- filtersMd %>
|
|
236
273
|
|
|
237
274
|
<% } %>
|
|
238
275
|
<% if (paginationMd) { %>
|
|
239
|
-
###
|
|
276
|
+
### Pagination
|
|
240
277
|
|
|
241
278
|
<%- paginationMd %>
|
|
242
279
|
|
|
@@ -248,7 +285,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
248
285
|
const validationTableMd = context.renderValidationDictionaryTable(pageSections);
|
|
249
286
|
if (validationTableMd) {
|
|
250
287
|
%>
|
|
251
|
-
##
|
|
288
|
+
## Data dictionary & validation {#data-dictionary--validation}
|
|
252
289
|
|
|
253
290
|
<%- validationTableMd %>
|
|
254
291
|
|
|
@@ -268,19 +305,28 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
268
305
|
const actionFlowsMd = context.renderActionFlowsDetailed(pageActions);
|
|
269
306
|
if (actionFlowsMd) {
|
|
270
307
|
%>
|
|
271
|
-
##
|
|
308
|
+
## Action flows {#action-flows}
|
|
272
309
|
|
|
273
310
|
<%- actionFlowsMd %>
|
|
274
311
|
|
|
275
312
|
<% } else { %>
|
|
276
|
-
##
|
|
313
|
+
## Page composition {#page-composition}
|
|
277
314
|
|
|
278
315
|
<%- context.renderPageComposition(page, { businessOnly: true }) %>
|
|
279
316
|
<% } %>
|
|
280
317
|
|
|
318
|
+
<% if (qaOpen) { %>
|
|
319
|
+
## Appendix — open questions {#appendix-open-questions}
|
|
320
|
+
|
|
321
|
+
Câu hỏi treo (SSOT): [qa/index.md](../../qa/index.md)
|
|
322
|
+
|
|
323
|
+
**ID trên leaf:** `<%= spec['Q&A'] %>`
|
|
324
|
+
|
|
325
|
+
<% } %>
|
|
326
|
+
|
|
281
327
|
<%
|
|
282
328
|
const skipKeys = new Set([
|
|
283
|
-
'id', 'page-id', 'title', 'summary', 'successMetrics', 'nonGoals', 'userStories', 'acceptance', 'ui', 'design', 'legacy', 'review', 'template',
|
|
329
|
+
'id', 'page-id', 'title', 'summary', 'successMetrics', 'scopeIn', 'nonGoals', 'userFlows', 'nfr', 'userStories', 'acceptance', 'ui', 'design', 'legacy', 'review', 'template', 'entities', 'relationships',
|
|
284
330
|
'nav', 'sections', 'zones', 'behavior', 'actions', 'stateMatrix', 'grillStatus', 'specOrigin',
|
|
285
331
|
'status', 'owner', 'schema', 'qa', 'Q&A', 'pendingTechDebt', 'i18n', 'gen', '_meta'
|
|
286
332
|
]);
|
|
@@ -6,7 +6,7 @@ title: Feature title
|
|
|
6
6
|
status: draft
|
|
7
7
|
owner: portal-team
|
|
8
8
|
|
|
9
|
-
# 1.
|
|
9
|
+
# 1. TỔNG QUAN NGHIỆP VỤ (PRD — mặt đọc BA)
|
|
10
10
|
summary: |
|
|
11
11
|
- 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.]
|
|
12
12
|
- các bên liên quan (stakeholders): [Ai dùng, ai hưởng lợi, ai quản lý?]
|
|
@@ -17,13 +17,22 @@ summary: |
|
|
|
17
17
|
- output: [Kết quả đầu ra]
|
|
18
18
|
- cách giải quyết (solution): [Tuỳ chọn. Ghi kỹ thuật phức tạp nếu có.]
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
20
|
+
scopeIn: |
|
|
21
|
+
- [Trong phạm vi màn/phase này — bullet rõ việc màn làm]
|
|
22
|
+
- [VD: xem danh sách, lọc, mở chi tiết từ dòng bảng]
|
|
23
23
|
|
|
24
24
|
nonGoals: |
|
|
25
|
-
- [
|
|
26
|
-
- [VD: không
|
|
25
|
+
- [Ngoài phạm vi — tránh scope creep]
|
|
26
|
+
- [VD: không export Excel tại màn list — defer QA/debt]
|
|
27
|
+
|
|
28
|
+
userFlows: |
|
|
29
|
+
- [Luồng người dùng — link FLOW-* trong architecture/03-user-flows hoặc common/user-flows]
|
|
30
|
+
|
|
31
|
+
nfr: |
|
|
32
|
+
- **Hiệu năng:** [VD: danh sách < 2s với 10k bản ghi — phân trang server]
|
|
33
|
+
- **Bảo mật:** [VD: RBAC theo role; audit log thao tác nhạy cảm]
|
|
34
|
+
|
|
35
|
+
# Rủi ro: KHÔNG khai báo trên bundle — chỉ architecture/11-risks/risk-register.md
|
|
27
36
|
|
|
28
37
|
userStories:
|
|
29
38
|
primary:
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
|
|
2
2
|
# Feature title
|
|
3
3
|
|
|
4
|
-
##
|
|
5
|
-
|
|
6
|
-
1. [
|
|
7
|
-
2. [
|
|
8
|
-
3. [
|
|
9
|
-
4. [User stories
|
|
10
|
-
5. [
|
|
11
|
-
6. [
|
|
12
|
-
7. [
|
|
13
|
-
8. [
|
|
14
|
-
9. [
|
|
4
|
+
## Table of contents
|
|
5
|
+
|
|
6
|
+
1. [Overview](#overview)
|
|
7
|
+
2. [Success metrics](#success-metrics)
|
|
8
|
+
3. [Out of scope](#non-goals)
|
|
9
|
+
4. [User stories & requirements](#user-stories--screen-journey)
|
|
10
|
+
5. [State & permissions matrix](#state--permission-matrix)
|
|
11
|
+
6. [List columns](#list-columns)
|
|
12
|
+
7. [Data dictionary & validation](#data-dictionary--validation)
|
|
13
|
+
8. [Custom widgets](#custom-widgets)
|
|
14
|
+
9. [Action flows](#action-flows)
|
|
15
15
|
- [API SSOT](#api-ssot)
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Overview {#overview}
|
|
20
20
|
|
|
21
21
|
| | |
|
|
22
22
|
| --- | --- |
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
##
|
|
47
|
+
## Success metrics {#success-metrics}
|
|
48
48
|
|
|
49
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
50
|
- [Bỏ bullet nếu chưa có số — ghi qualitative metric]
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
##
|
|
56
|
+
## Out of scope {#scope-out}
|
|
57
57
|
|
|
58
58
|
- [Phạm vi KHÔNG làm trên màn/phase này — tránh scope creep]
|
|
59
59
|
- [VD: không xử lý export Excel tại màn list — defer QA/debt]
|
|
@@ -68,14 +68,14 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
68
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
69
|
|
|
70
70
|
|
|
71
|
-
## User
|
|
71
|
+
## User stories & requirements {#user-stories--screen-journey}
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
###
|
|
76
|
-
> **
|
|
77
|
-
> **
|
|
78
|
-
>
|
|
75
|
+
### Primary user story
|
|
76
|
+
> **As a** [persona / role],
|
|
77
|
+
> **I want** [primary action on this screen],
|
|
78
|
+
> **so that** [business outcome].
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
|
|
@@ -192,7 +192,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
192
192
|
|
|
193
193
|
|
|
194
194
|
|
|
195
|
-
##
|
|
195
|
+
## State & permissions matrix {#state--permission-matrix}
|
|
196
196
|
|
|
197
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
198
|
| --- | --- | --- | --- |
|
|
@@ -204,7 +204,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
204
204
|
|
|
205
205
|
|
|
206
206
|
|
|
207
|
-
##
|
|
207
|
+
## List columns {#list-columns}
|
|
208
208
|
|
|
209
209
|
| Nhãn cột | Key | Ý nghĩa nghiệp vụ | Mục đích UI | Widget / render | Sort | DB (schema.field) |
|
|
210
210
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
@@ -215,7 +215,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
215
215
|
|
|
216
216
|
|
|
217
217
|
|
|
218
|
-
##
|
|
218
|
+
## Data dictionary & validation {#data-dictionary--validation}
|
|
219
219
|
|
|
220
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
221
|
| --- | --- | --- | --- | --- | --- |
|
|
@@ -229,7 +229,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
229
229
|
|
|
230
230
|
<div id="custom-widgets"></div>
|
|
231
231
|
|
|
232
|
-
##
|
|
232
|
+
## Custom widgets {#custom-widgets}
|
|
233
233
|
|
|
234
234
|
### Khối Tùy Biến (`custom`)
|
|
235
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
|
|
@@ -238,7 +238,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
|
|
|
238
238
|
|
|
239
239
|
|
|
240
240
|
|
|
241
|
-
##
|
|
241
|
+
## Action flows {#action-flows}
|
|
242
242
|
|
|
243
243
|
### Lưu & Xác Nhận
|
|
244
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ủ
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Read before writing (global)
|
|
10
10
|
|
|
11
11
|
| Ai | Đọc gì trên VitePress / docs hub | Khi nào mở YAML `01` |
|
|
12
12
|
|----|-----------------------------------|----------------------|
|
|
@@ -19,7 +19,7 @@ Sau `flowgrid split` + `flowgrid render`: sidebar leaf **Spec · W-*** · **Data
|
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Function leaf trio
|
|
23
23
|
|
|
24
24
|
```text
|
|
25
25
|
surfaces/<surface>/CMP-*/<NN…>/<slug>/
|
|
@@ -42,7 +42,7 @@ surfaces/<surface>/CMP-*/<NN…>/<slug>/
|
|
|
42
42
|
|
|
43
43
|
---
|
|
44
44
|
|
|
45
|
-
##
|
|
45
|
+
## `01` structure (field summary)
|
|
46
46
|
|
|
47
47
|
Mẫu đầy đủ: [`backend-api.yaml`](./backend-api.yaml) (sau `flowgrid init` → `.flowgrid/templates/backend-api.yaml`).
|
|
48
48
|
|
|
@@ -108,7 +108,7 @@ Skill: `/openapi` · Redoc/Swagger UI: `openapi_build_ui` (tùy hub).
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
-
## YAML
|
|
111
|
+
## Safe YAML
|
|
112
112
|
|
|
113
113
|
- Chuỗi có `:` → bọc `"..."`.
|
|
114
114
|
- Chạy `flowgrid api:check` trước handoff.
|
|
@@ -116,7 +116,7 @@ Skill: `/openapi` · Redoc/Swagger UI: `openapi_build_ui` (tùy hub).
|
|
|
116
116
|
|
|
117
117
|
---
|
|
118
118
|
|
|
119
|
-
##
|
|
119
|
+
## FE bundle link
|
|
120
120
|
|
|
121
121
|
Trên bundle chỉ khai báo **hành vi UI** và `apiRefs`; chi tiết endpoint nằm trên `01`:
|
|
122
122
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Đặt `TC-*.yaml` mirror path function trên docs-hub (bỏ prefix `surfaces/`).
|
|
4
4
|
|
|
5
|
-
Ví dụ docs: `surfaces/admin/CMP-ADM-
|
|
5
|
+
Ví dụ docs: `surfaces/admin/CMP-ADM-ORD-01/02/01/login/` → `cases/admin/CMP-ADM-ORD-01/02/01/login/TC-*.yaml`
|
|
6
6
|
|
|
7
7
|
- SSOT ghi: `TC-*.yaml` (`schemaVersion: 2`) — copy mẫu từ `../_templates/TC.example.yaml` (init từ harness).
|
|
8
8
|
- SSOT đọc QA/Dev: `pnpm cases:render` → `TC-*.md` cùng thư mục — **không sửa tay** MD.
|