@fui-org/fui-cli 2.1.4 → 2.2.1
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 +16 -7
- package/dist/fui-xg17j3f9.js +396 -0
- package/dist/fui.js +1 -1
- package/package.json +1 -1
- package/skills/fui/SKILL.md +152 -39
- package/skills/{fui-skill → fui}/assets/projectdefaultstyle.css +489 -489
- package/skills/{fui-skill → fui}/examples/component.vue +162 -162
- package/skills/{fui-skill → fui}/references/INDEX.md +4 -5
- package/skills/{fui-skill → fui}/references/advanced-techniques.md +170 -168
- package/skills/{fui-skill → fui}/references/component-design.md +2 -2
- package/skills/{fui-skill → fui}/references/component-quickref.md +78 -78
- package/skills/{fui-skill → fui}/references/component-table.md +287 -287
- package/skills/{fui-skill → fui}/references/components-dialog.md +191 -191
- package/skills/{fui-skill → fui}/references/components-display.md +141 -141
- package/skills/{fui-skill → fui}/references/components-echart.md +316 -316
- package/skills/{fui-skill → fui}/references/components-input.md +335 -335
- package/skills/{fui-skill → fui}/references/controls-patterns.md +2 -2
- package/skills/{fui-skill → fui}/references/controls-styling-vocabulary.md +173 -173
- package/skills/{fui-skill → fui}/references/db-workflow.md +9 -9
- package/skills/{fui-skill → fui}/references/default-function.md +456 -456
- package/skills/{fui-skill → fui}/references/design-modes.md +57 -57
- package/skills/{fui-skill → fui}/references/echart-templates.md +489 -489
- package/skills/{fui-skill → fui}/references/fastproject.md +99 -99
- package/skills/{fui-skill → fui}/references/fullstack-workflow.md +314 -313
- package/skills/{fui-skill → fui}/references/module-data-patterns.md +117 -117
- package/skills/{fui-skill → fui}/references/module-json-anatomy.md +132 -132
- package/skills/{fui-skill → fui}/references/module-structure.md +144 -144
- package/skills/{fui-skill → fui}/references/new-session.md +3 -20
- package/skills/{fui-skill → fui}/references/pdfmake.md +60 -60
- package/skills/{fui-skill → fui}/references/permission-system.md +1 -1
- package/skills/{fui-skill → fui}/references/platform-architecture.md +269 -269
- package/skills/{fui-skill → fui}/references/project-config.md +303 -303
- package/skills/{fui-skill → fui}/references/project-provisioning.md +4 -4
- package/skills/{fui-skill → fui}/references/script-map.md +262 -262
- package/skills/{fui-skill → fui}/references/sql-clr-functions.md +225 -225
- package/skills/{fui-skill → fui}/references/system-design.md +3 -3
- package/skills/{fui-skill → fui}/references/tapi-permission-patterns.md +156 -156
- package/skills/{fui-skill → fui}/references/tapi-reference.md +474 -474
- package/skills/fui/references/tools-registry.md +214 -0
- package/skills/{fui-skill → fui}/references/ui-crosswindow-patterns.md +321 -321
- package/skills/{fui-skill → fui}/references/ui-dialog-patterns.md +255 -255
- package/skills/{fui-skill → fui}/references/ui-layout-patterns.md +176 -176
- package/skills/{fui-skill → fui}/references/ui-patterns.md +303 -303
- package/skills/{fui-skill → fui}/references/ui-screenshot-review.md +95 -95
- package/skills/{fui-skill → fui}/references/ui-table-cell-patterns.md +318 -318
- package/skills/{fui-skill → fui}/references/verification.md +18 -18
- package/skills/{fui-skill → fui}/references/watcher-patterns.md +163 -163
- package/skills/{fui-skill → fui}/references/websocket-realtime.md +271 -271
- package/skills/{fui-skill → fui}/scripts/component-3.0.js +2715 -2715
- package/skills/{fui-skill → fui}/scripts/component.js +3147 -3147
- package/skills/{fui-skill → fui}/scripts/componentTable-3.0.js +1038 -1038
- package/skills/{fui-skill → fui}/scripts/componentTable.js +891 -891
- package/skills/{fui-skill → fui}/scripts/defaultfunction-3.0.js +866 -866
- package/skills/{fui-skill → fui}/scripts/defaultfunction.js +1051 -1051
- package/skills/{fui-skill → fui}/scripts/fastproject-3.0.js +870 -870
- package/skills/{fui-skill → fui}/scripts/fastproject.js +828 -828
- package/dist/fui-h4z7zhk2.js +0 -381
- package/skills/fui-skill/SKILL.md +0 -139
- package/skills/fui-skill/references/tools-registry.md +0 -84
- /package/skills/{fui-skill → fui}/assets/projectdefaultstyle-3.0.css +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/fui/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/slack/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/README.md +0 -0
- /package/skills/{fui-skill → fui}/examples/f-table-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/module-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/project-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/references/coding-standards.md +0 -0
- /package/skills/{fui-skill → fui}/references/db-table-design.md +0 -0
- /package/skills/{fui-skill → fui}/references/fsheet.md +0 -0
- /package/skills/{fui-skill → fui}/references/tapi-file-api.md +0 -0
- /package/skills/{fui-skill → fui}/references/ui-templates.md +0 -0
- /package/skills/{fui-skill → fui}/scripts/fechart.js +0 -0
- /package/skills/{fui-skill → fui}/scripts/fsheet.js +0 -0
|
@@ -1,313 +1,314 @@
|
|
|
1
|
-
# Full-Stack Module Workflow
|
|
2
|
-
|
|
3
|
-
> Owns: **the 6-step UI-first fullstack module workflow (analyze → UI with hard-coded data → lock contract → SP → API wiring → static verification)** and **the Design Review Gate procedure** (SKILL.md §3 holds only the rule "must pass the gate"; the steps live here). tAPI conventions: [tapi-reference.md](tapi-reference.md); multi-module design: [system-design.md](system-design.md).
|
|
4
|
-
|
|
5
|
-
**UI-first:** build UI (FUI) with hard-coded data, confirm visuals with `fui module simulate --render`, THEN lock the API (tAPI) contract and write SPs. Fixing data shape while it is a literal in `data[]` is far cheaper than after `IN`/`OUT`/`watch` depend on it. No step calls a real API/SP to "test" — verification = read + reason + mock ([verification.md](verification.md)). Read [tapi-reference.md](tapi-reference.md) before step 3.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Step 1 — Analyze requirements
|
|
10
|
-
|
|
11
|
-
**1.1 Business need:** list / search / filter? add / edit / delete? report / statistics? workflow (approve, reject, status)?
|
|
12
|
-
|
|
13
|
-
**1.2 Data (initial estimate — locked in step 3):** main entity and linked entities; which fields are displayed, which are input.
|
|
14
|
-
|
|
15
|
-
**1.3 Permissions and auth:**
|
|
16
|
-
|
|
17
|
-
- Login required? (no → use `AUTH_` in the SP)
|
|
18
|
-
- Rights by `sys_SystemRight`? → handle in the SP body
|
|
19
|
-
|
|
20
|
-
**Output:** feature list + main entity names (types/column names not yet locked).
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Step 1b — Design Review Gate (mandatory, before the first line of code)
|
|
25
|
-
|
|
26
|
-
SKILL.md §3 = the **rule** (every new system/page/function passes); this = the **procedure**, for fullstack and UI-only modules.
|
|
27
|
-
|
|
28
|
-
1. **Business efficiency** — steps to finish the task, data truly needed, redundant steps.
|
|
29
|
-
2. **Project patterns** — `fui module list` + `fui module get` on similar modules (layout, control naming, action placement, filter/table/dialog). Match existing pages; **never invent a new style when a convention exists**.
|
|
30
|
-
3. **Sketch the UI** in words/rough layout — controls, placement, dialog/form flow.
|
|
31
|
-
4. **Self-review** — friendly? step count? Add/Edit/Delete, filters, pagination where expected? consistent? Issues → **fix the sketch first**, never in code.
|
|
32
|
-
5. Only then write code (step 2+).
|
|
33
|
-
|
|
34
|
-
**Choose the build approach:**
|
|
35
|
-
|
|
36
|
-
| Approach | When |
|
|
37
|
-
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
38
|
-
| **A — plain module.json** | Simple page: data display, single-table CRUD, basic form. Fewer files, easy to maintain. |
|
|
39
|
-
| **B — Vue component (`uc-*.vue`)** | Complex UI: multi-area dashboard, custom layout, UI logic reused in many places, or `controls` over ~200 lines ([component-design.md](component-design.md)). |
|
|
40
|
-
|
|
41
|
-
> Unspecified → **propose** one with a short reason and **wait for confirmation**; never choose silently.
|
|
42
|
-
|
|
43
|
-
**Component priority**: `f-*` first (`f-button` over `v-btn`, `f-table` over `v-data-table` for EVERY table, `f-echart` for every chart); `v-*` only when no FUI equivalent exists. → [component-quickref.md](component-quickref.md)
|
|
44
|
-
|
|
45
|
-
**Design System gate** (parallel): decide App-mode vs Web-mode and check `{projectId}/DESIGN.md` **before** sketching — separate aesthetic rules; never mix. → [design-modes.md](design-modes.md)
|
|
46
|
-
|
|
47
|
-
**Output:** self-reviewed sketch, approach A/B locked (user-confirmed if we proposed it), aesthetic mode decided.
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Step 2 — Build UI with hard-coded data
|
|
52
|
-
|
|
53
|
-
Whole UI on **literal** data in `data[]`, no API yet. Structure: [module-json-anatomy.md](module-json-anatomy.md), [controls-patterns.md](controls-patterns.md).
|
|
54
|
-
|
|
55
|
-
```json
|
|
56
|
-
"data": [
|
|
57
|
-
{ "keyword": "" },
|
|
58
|
-
{ "classId": "" },
|
|
59
|
-
{ "studentList": [
|
|
60
|
-
{ "StudentID": "SV001", "FullName": "Nguyễn Văn A", "ClassID": "C01", "ClassName": "Lớp 10A", "GPA": 8.5 },
|
|
61
|
-
{ "StudentID": "SV002", "FullName": "Trần Thị B", "ClassID": "C02", "ClassName": "Lớp 10B", "GPA": 7.2 }
|
|
62
|
-
] },
|
|
63
|
-
{ "form": {} },
|
|
64
|
-
{ "dialogOpen": false }
|
|
65
|
-
]
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
`f-table`/`v-dialog`/form patterns: [component-table.md](component-table.md), [ui-templates.md](ui-templates.md).
|
|
69
|
-
|
|
70
|
-
**Visual QA now:** `fui module simulate --render` (vueData override: `fui module simulate --help`). Read screenshot + DOM audit; fix display bugs (horizontal overflow, broken dialog, missing labels...) before moving on.
|
|
71
|
-
|
|
72
|
-
**Output:** `module.json` with complete layout, running on hard-coded data, clean `--render` QA.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## Step 3 — Lock data shape + design API endpoints
|
|
77
|
-
|
|
78
|
-
From the step-2 fields (visually confirmed before becoming an SP contract), lock exact field names + types (→ SQL column types) and the source entity/table of each. Map features to SPs as `spAPI_{Entity}{Action}`; full naming (`spAPI_*` / `spAPI_AUTH_*` / `spAPIFILE_*`, `@url1_`, response contract) is in [tapi-reference.md](tapi-reference.md).
|
|
79
|
-
|
|
80
|
-
| Feature | SP name | Main params |
|
|
81
|
-
| ----------------- | ---------------------------- | ---------------------- |
|
|
82
|
-
| List | `spAPI_{Entity}List` | Filter params (custom) |
|
|
83
|
-
| Detail | `spAPI_{Entity}Get` | `@url1_{EntityID}` |
|
|
84
|
-
| Insert | `spAPI_{Entity}Insert` | Entity fields |
|
|
85
|
-
| Update | `spAPI_{Entity}Update` | ID + fields to update |
|
|
86
|
-
| Delete | `spAPI_{Entity}Delete` | `@url1_{EntityID}` |
|
|
87
|
-
| Lookup / dropdown | `spAPI_AUTH_{Entity}Options` | No auth, returns list |
|
|
88
|
-
|
|
89
|
-
**Example — "Student management"** (matches step-2 fields `StudentID`, `FullName`, `ClassID`, `ClassName`, `GPA`):
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
spAPI_StudentList -- danh sách, lọc theo ClassID + Keyword — trả đúng các cột đã hiển thị ở bước 2
|
|
93
|
-
spAPI_StudentGet -- chi tiết theo url1=StudentID
|
|
94
|
-
spAPI_StudentInsert -- thêm mới
|
|
95
|
-
spAPI_StudentUpdate -- cập nhật
|
|
96
|
-
spAPI_StudentDelete -- xoá theo url1=StudentID
|
|
97
|
-
spAPI_AUTH_ClassOptions -- dropdown danh sách lớp (không cần auth)
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Output:** full SP list; names + params + response shape match the fields used in the UI.
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## Step 4 — Write SQL stored procedures
|
|
105
|
-
|
|
106
|
-
Per tAPI rules ([tapi-reference.md](tapi-reference.md)) and the step-3 contract.
|
|
107
|
-
|
|
108
|
-
**SELECT (list):**
|
|
109
|
-
|
|
110
|
-
```sql
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@
|
|
114
|
-
@
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@
|
|
131
|
-
@
|
|
132
|
-
@
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
@
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
@
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
{ "
|
|
201
|
-
{ "
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
"
|
|
214
|
-
|
|
215
|
-
"
|
|
216
|
-
"
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
{ "
|
|
221
|
-
{ "
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
"
|
|
229
|
-
"
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
"
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
- [ ] `
|
|
258
|
-
- [ ]
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
- [ ] `
|
|
264
|
-
- [ ]
|
|
265
|
-
- [ ]
|
|
266
|
-
- [ ] `
|
|
267
|
-
- [ ] `
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
"
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
- **
|
|
312
|
-
- **
|
|
313
|
-
-
|
|
1
|
+
# Full-Stack Module Workflow
|
|
2
|
+
|
|
3
|
+
> Owns: **the 6-step UI-first fullstack module workflow (analyze → UI with hard-coded data → lock contract → SP → API wiring → static verification)** and **the Design Review Gate procedure** (SKILL.md §3 holds only the rule "must pass the gate"; the steps live here). tAPI conventions: [tapi-reference.md](tapi-reference.md); multi-module design: [system-design.md](system-design.md).
|
|
4
|
+
|
|
5
|
+
**UI-first:** build UI (FUI) with hard-coded data, confirm visuals with `fui module simulate --render`, THEN lock the API (tAPI) contract and write SPs. Fixing data shape while it is a literal in `data[]` is far cheaper than after `IN`/`OUT`/`watch` depend on it. No step calls a real API/SP to "test" — verification = read + reason + mock ([verification.md](verification.md)). Read [tapi-reference.md](tapi-reference.md) before step 3.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Step 1 — Analyze requirements
|
|
10
|
+
|
|
11
|
+
**1.1 Business need:** list / search / filter? add / edit / delete? report / statistics? workflow (approve, reject, status)?
|
|
12
|
+
|
|
13
|
+
**1.2 Data (initial estimate — locked in step 3):** main entity and linked entities; which fields are displayed, which are input.
|
|
14
|
+
|
|
15
|
+
**1.3 Permissions and auth:**
|
|
16
|
+
|
|
17
|
+
- Login required? (no → use `AUTH_` in the SP)
|
|
18
|
+
- Rights by `sys_SystemRight`? → handle in the SP body
|
|
19
|
+
|
|
20
|
+
**Output:** feature list + main entity names (types/column names not yet locked).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Step 1b — Design Review Gate (mandatory, before the first line of code)
|
|
25
|
+
|
|
26
|
+
SKILL.md §3 = the **rule** (every new system/page/function passes); this = the **procedure**, for fullstack and UI-only modules.
|
|
27
|
+
|
|
28
|
+
1. **Business efficiency** — steps to finish the task, data truly needed, redundant steps.
|
|
29
|
+
2. **Project patterns** — `fui module list` + `fui module get` on similar modules (layout, control naming, action placement, filter/table/dialog). Match existing pages; **never invent a new style when a convention exists**.
|
|
30
|
+
3. **Sketch the UI** in words/rough layout — controls, placement, dialog/form flow.
|
|
31
|
+
4. **Self-review** — friendly? step count? Add/Edit/Delete, filters, pagination where expected? consistent? Issues → **fix the sketch first**, never in code.
|
|
32
|
+
5. Only then write code (step 2+).
|
|
33
|
+
|
|
34
|
+
**Choose the build approach:**
|
|
35
|
+
|
|
36
|
+
| Approach | When |
|
|
37
|
+
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
38
|
+
| **A — plain module.json** | Simple page: data display, single-table CRUD, basic form. Fewer files, easy to maintain. |
|
|
39
|
+
| **B — Vue component (`uc-*.vue`)** | Complex UI: multi-area dashboard, custom layout, UI logic reused in many places, or `controls` over ~200 lines ([component-design.md](component-design.md)). |
|
|
40
|
+
|
|
41
|
+
> Unspecified → **propose** one with a short reason and **wait for confirmation**; never choose silently.
|
|
42
|
+
|
|
43
|
+
**Component priority**: `f-*` first (`f-button` over `v-btn`, `f-table` over `v-data-table` for EVERY table, `f-echart` for every chart); `v-*` only when no FUI equivalent exists. → [component-quickref.md](component-quickref.md)
|
|
44
|
+
|
|
45
|
+
**Design System gate** (parallel): decide App-mode vs Web-mode and check `{projectId}/DESIGN.md` **before** sketching — separate aesthetic rules; never mix. → [design-modes.md](design-modes.md)
|
|
46
|
+
|
|
47
|
+
**Output:** self-reviewed sketch, approach A/B locked (user-confirmed if we proposed it), aesthetic mode decided.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Step 2 — Build UI with hard-coded data
|
|
52
|
+
|
|
53
|
+
Whole UI on **literal** data in `data[]`, no API yet. Structure: [module-json-anatomy.md](module-json-anatomy.md), [controls-patterns.md](controls-patterns.md).
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
"data": [
|
|
57
|
+
{ "keyword": "" },
|
|
58
|
+
{ "classId": "" },
|
|
59
|
+
{ "studentList": [
|
|
60
|
+
{ "StudentID": "SV001", "FullName": "Nguyễn Văn A", "ClassID": "C01", "ClassName": "Lớp 10A", "GPA": 8.5 },
|
|
61
|
+
{ "StudentID": "SV002", "FullName": "Trần Thị B", "ClassID": "C02", "ClassName": "Lớp 10B", "GPA": 7.2 }
|
|
62
|
+
] },
|
|
63
|
+
{ "form": {} },
|
|
64
|
+
{ "dialogOpen": false }
|
|
65
|
+
]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`f-table`/`v-dialog`/form patterns: [component-table.md](component-table.md), [ui-templates.md](ui-templates.md).
|
|
69
|
+
|
|
70
|
+
**Visual QA now:** `fui module simulate --render` (vueData override: `fui module simulate --help`). Read screenshot + DOM audit; fix display bugs (horizontal overflow, broken dialog, missing labels...) before moving on.
|
|
71
|
+
|
|
72
|
+
**Output:** `module.json` with complete layout, running on hard-coded data, clean `--render` QA.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Step 3 — Lock data shape + design API endpoints
|
|
77
|
+
|
|
78
|
+
From the step-2 fields (visually confirmed before becoming an SP contract), lock exact field names + types (→ SQL column types) and the source entity/table of each. Map features to SPs as `spAPI_{Entity}{Action}`; full naming (`spAPI_*` / `spAPI_AUTH_*` / `spAPIFILE_*`, `@url1_`, response contract) is in [tapi-reference.md](tapi-reference.md).
|
|
79
|
+
|
|
80
|
+
| Feature | SP name | Main params |
|
|
81
|
+
| ----------------- | ---------------------------- | ---------------------- |
|
|
82
|
+
| List | `spAPI_{Entity}List` | Filter params (custom) |
|
|
83
|
+
| Detail | `spAPI_{Entity}Get` | `@url1_{EntityID}` |
|
|
84
|
+
| Insert | `spAPI_{Entity}Insert` | Entity fields |
|
|
85
|
+
| Update | `spAPI_{Entity}Update` | ID + fields to update |
|
|
86
|
+
| Delete | `spAPI_{Entity}Delete` | `@url1_{EntityID}` |
|
|
87
|
+
| Lookup / dropdown | `spAPI_AUTH_{Entity}Options` | No auth, returns list |
|
|
88
|
+
|
|
89
|
+
**Example — "Student management"** (matches step-2 fields `StudentID`, `FullName`, `ClassID`, `ClassName`, `GPA`):
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
spAPI_StudentList -- danh sách, lọc theo ClassID + Keyword — trả đúng các cột đã hiển thị ở bước 2
|
|
93
|
+
spAPI_StudentGet -- chi tiết theo url1=StudentID
|
|
94
|
+
spAPI_StudentInsert -- thêm mới
|
|
95
|
+
spAPI_StudentUpdate -- cập nhật
|
|
96
|
+
spAPI_StudentDelete -- xoá theo url1=StudentID
|
|
97
|
+
spAPI_AUTH_ClassOptions -- dropdown danh sách lớp (không cần auth)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Output:** full SP list; names + params + response shape match the fields used in the UI.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Step 4 — Write SQL stored procedures
|
|
105
|
+
|
|
106
|
+
Per tAPI rules ([tapi-reference.md](tapi-reference.md)) and the step-3 contract.
|
|
107
|
+
|
|
108
|
+
**SELECT (list):**
|
|
109
|
+
|
|
110
|
+
```sql
|
|
111
|
+
-- No "= NULL" defaults: tAPI passes NULL for a param the client leaves out, and fui sp deploy blocks defaults.
|
|
112
|
+
CREATE PROCEDURE spAPI_StudentList
|
|
113
|
+
@Keyword nvarchar(200),
|
|
114
|
+
@ClassID varchar(20),
|
|
115
|
+
@sys_UserID varchar(9)
|
|
116
|
+
AS BEGIN
|
|
117
|
+
SELECT s.StudentID, s.FullName, s.ClassID, c.ClassName, s.GPA
|
|
118
|
+
FROM tblStudent s
|
|
119
|
+
JOIN tblClass c ON s.ClassID = c.ClassID
|
|
120
|
+
WHERE (@Keyword IS NULL OR s.FullName LIKE '%' + @Keyword + '%')
|
|
121
|
+
AND (@ClassID IS NULL OR s.ClassID = @ClassID)
|
|
122
|
+
ORDER BY s.FullName
|
|
123
|
+
END
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**INSERT:**
|
|
127
|
+
|
|
128
|
+
```sql
|
|
129
|
+
CREATE PROCEDURE spAPI_StudentInsert
|
|
130
|
+
@StudentID varchar(9),
|
|
131
|
+
@FullName nvarchar(100),
|
|
132
|
+
@ClassID varchar(20),
|
|
133
|
+
@sys_UserID varchar(9)
|
|
134
|
+
AS BEGIN
|
|
135
|
+
IF EXISTS (SELECT 1 FROM tblStudent WHERE StudentID = @StudentID)
|
|
136
|
+
BEGIN
|
|
137
|
+
RAISERROR(N'Mã sinh viên đã tồn tại', 16, 1)
|
|
138
|
+
RETURN
|
|
139
|
+
END
|
|
140
|
+
INSERT INTO tblStudent (StudentID, FullName, ClassID, CreateUser, CreateTime)
|
|
141
|
+
VALUES (@StudentID, @FullName, @ClassID, @sys_UserID, GETDATE())
|
|
142
|
+
SELECT mess = N'Thêm thành công', id = @StudentID
|
|
143
|
+
END
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**DELETE:**
|
|
147
|
+
|
|
148
|
+
```sql
|
|
149
|
+
CREATE PROCEDURE spAPI_StudentDelete
|
|
150
|
+
@url1_StudentID varchar(9),
|
|
151
|
+
@sys_UserID varchar(9)
|
|
152
|
+
AS BEGIN
|
|
153
|
+
DELETE FROM tblStudent WHERE StudentID = @url1_StudentID
|
|
154
|
+
SELECT mess = N'Đã xoá', id = @url1_StudentID
|
|
155
|
+
END
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**GET (detail, convert_to_object):**
|
|
159
|
+
|
|
160
|
+
```sql
|
|
161
|
+
CREATE PROCEDURE spAPI_StudentGet
|
|
162
|
+
@url1_StudentID varchar(9),
|
|
163
|
+
@sys_UserID varchar(9)
|
|
164
|
+
AS BEGIN
|
|
165
|
+
SELECT convert_to_object = '',
|
|
166
|
+
s.StudentID, s.FullName, s.ClassID, c.ClassName, s.GPA, s.Email
|
|
167
|
+
FROM tblStudent s
|
|
168
|
+
JOIN tblClass c ON s.ClassID = c.ClassID
|
|
169
|
+
WHERE s.StudentID = @url1_StudentID
|
|
170
|
+
END
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
> `convert_to_object = ''` when the SP returns exactly one record → response is an object instead of `data[0][0]`.
|
|
174
|
+
|
|
175
|
+
Deploy with `fui sp save` + `fui sp deploy` (never call the endpoint to "try it" — see step 6).
|
|
176
|
+
|
|
177
|
+
**NEVER put `GRANT EXECUTE` in the .sql file.** A `CREATE PROCEDURE` body runs **to end of batch**; `_sql_execute_NonQuery` sends one batch and **does not support `GO`** (client directive, not T-SQL). A `GRANT` after `END` is **swallowed into the SP body** — runs on every call, never grants at deploy. No error; the endpoint just returns a permission error while the SP looks right.
|
|
178
|
+
|
|
179
|
+
`fui sp deploy` **auto-grants** new procedures (not FUNCTIONs — functions need no EXECUTE for `[public]`), then **re-checks `sys.database_permissions`** and reports:
|
|
180
|
+
|
|
181
|
+
- `GRANT EXECUTE ON [dbo].[...] TO [public]: applied` (new SP) or `[public] has EXECUTE on "..."` — done.
|
|
182
|
+
- `[public] lacks EXECUTE on "..."` or `deploy OK but grant FAILED` — the output prints a runnable command; copy verbatim:
|
|
183
|
+
`fui exec "GRANT EXECUTE ON [dbo].[spAPI_StudentList] TO [public] AS [dbo]" --db ...`
|
|
184
|
+
- Grant failed but deploy succeeded → **the SP is already on the server**; run the GRANT above, **do not redeploy**.
|
|
185
|
+
|
|
186
|
+
Deploying via `fui exec` with `CREATE PROCEDURE` → **no auto-grant, no tAPI param-cache clear**; fui warns and names the two commands to run next.
|
|
187
|
+
|
|
188
|
+
**Output:** all step-3 SPs deployed.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Step 5 — Replace hard-coded data with API bindings
|
|
193
|
+
|
|
194
|
+
Keep the step-2 layout; replace `data[]` literals with named `IN`/`OUT`/`CALLBACK` actions on the deployed SPs.
|
|
195
|
+
|
|
196
|
+
**5.1 Actions replacing hard-coded data**
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
"data": [
|
|
200
|
+
{ "keyword": "" },
|
|
201
|
+
{ "classId": "" },
|
|
202
|
+
{ "studentList": [] },
|
|
203
|
+
|
|
204
|
+
{
|
|
205
|
+
"fetchStudents": {
|
|
206
|
+
"API": "/{apiName}/StudentList",
|
|
207
|
+
"IN": { "Keyword": "vueData.keyword", "ClassID": "vueData.classId" },
|
|
208
|
+
"OUT": "studentList"
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"saveStudent": {
|
|
213
|
+
"API": "/{apiName}/StudentInsert",
|
|
214
|
+
"IN": {
|
|
215
|
+
"StudentID": "vueData.form.StudentID",
|
|
216
|
+
"FullName": "vueData.form.FullName",
|
|
217
|
+
"ClassID": "vueData.form.ClassID"
|
|
218
|
+
},
|
|
219
|
+
"CALLBACK": [
|
|
220
|
+
{ "CALL": "vueData.fetchStudents" },
|
|
221
|
+
{ "MESS": "Thêm thành công" },
|
|
222
|
+
{ "EXE": "vueData.dialogOpen = false" }
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"deleteStudent": {
|
|
228
|
+
"API": "/{apiName}/StudentDelete/`{{vueData.selectedID}}",
|
|
229
|
+
"CONFIRM": "Bạn có chắc muốn xoá sinh viên này?",
|
|
230
|
+
"CALLBACK": { "CALL": "vueData.fetchStudents" }
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**5.2 `watch` to reload when filters change**
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
"watch": {
|
|
240
|
+
"keyword": { "CALL": "vueData.fetchStudents" },
|
|
241
|
+
"classId": { "CALL": "vueData.fetchStudents" }
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**5.3** Append `{ "CALL": "vueData.fetchStudents" }` at the end of `data[]` to auto-load on open.
|
|
246
|
+
|
|
247
|
+
**Output:** `module.json` fully wired, layout unchanged from step 2.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Step 6 — Static verification and finish
|
|
252
|
+
|
|
253
|
+
Pre-publish checklist — **no step calls a real API/SP/DML**:
|
|
254
|
+
|
|
255
|
+
**API/SP** — full checklist (naming, params, security, `GRANT EXECUTE`, deploy workflow): [verification.md](verification.md). Here:
|
|
256
|
+
|
|
257
|
+
- [ ] `AUTH_` on endpoints that need no login
|
|
258
|
+
- [ ] `fui sp verify` per new SP — reads the real body, infers response shape/error contract, checks params, emits a `--mocks` snippet (no endpoint call)
|
|
259
|
+
- [ ] **Write** APIs: read the body and reason (WHERE, transaction, RAISERROR) — no trial run
|
|
260
|
+
|
|
261
|
+
**module.json:**
|
|
262
|
+
|
|
263
|
+
- [ ] Each action's `API` path matches `/{apiName}/{FunctionName}`
|
|
264
|
+
- [ ] `IN` params match SP param names exactly (case-sensitive)
|
|
265
|
+
- [ ] Route params (url1, url2) go via the URL, not `IN`
|
|
266
|
+
- [ ] `OUT` receives the right type (array vs object per SP) — check against the shape inferred by `fui sp verify`
|
|
267
|
+
- [ ] `watch` declared for auto-reload filters
|
|
268
|
+
- [ ] `data[]` declares the initial fetch action and ends with a startup `{ "CALL": ... }`
|
|
269
|
+
|
|
270
|
+
**Final verification, in order:** `fui module validate` (syntax/rule errors) → `fui module outline` (text review of UI structure) → `fui module simulate --mocks '{...}'` using the `fui sp verify` snippet (action/watch/dialog flow) → if display is still in doubt, a last `fui module simulate --render` with the same `--mocks`. (`fui module check` runs validate + outline + simulate in one step.)
|
|
271
|
+
|
|
272
|
+
**Output:** module works end-to-end, UI ↔ API connected, zero real calls during verification.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Complete output example
|
|
277
|
+
|
|
278
|
+
### SPs (step 4)
|
|
279
|
+
|
|
280
|
+
Each SP is **its own file** via `fui sp save` → `fui sp deploy`. No GRANT in the file, no `GO` (step 4); tAPI params **must not have default values** — `fui sp deploy` hard-blocks them.
|
|
281
|
+
|
|
282
|
+
```sql
|
|
283
|
+
-- 1. Danh sách (file spAPI_StudentList.sql)
|
|
284
|
+
CREATE PROCEDURE spAPI_StudentList @Keyword nvarchar(200), @ClassID varchar(20), @sys_UserID varchar(9)
|
|
285
|
+
AS BEGIN
|
|
286
|
+
SELECT s.StudentID, s.FullName, s.ClassID, s.GPA
|
|
287
|
+
FROM tblStudent s WHERE (@Keyword = '' OR s.FullName LIKE '%'+@Keyword+'%') AND (@ClassID = '' OR s.ClassID = @ClassID)
|
|
288
|
+
END
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
```sql
|
|
292
|
+
-- 2. Thêm mới (file spAPI_StudentInsert.sql)
|
|
293
|
+
CREATE PROCEDURE spAPI_StudentInsert @StudentID varchar(9), @FullName nvarchar(100), @ClassID varchar(20), @sys_UserID varchar(9)
|
|
294
|
+
AS BEGIN
|
|
295
|
+
INSERT INTO tblStudent(StudentID, FullName, ClassID, CreateUser, CreateTime) VALUES(@StudentID, @FullName, @ClassID, @sys_UserID, GETDATE())
|
|
296
|
+
SELECT mess = N'Thêm thành công'
|
|
297
|
+
END
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### module.json actions (step 5)
|
|
301
|
+
|
|
302
|
+
```json
|
|
303
|
+
"fetchStudents": { "API": "/me/StudentList", "IN": { "Keyword": "vueData.keyword" }, "OUT": "studentList" },
|
|
304
|
+
"addStudent": { "API": "/me/StudentInsert", "IN": { "StudentID": "vueData.form.StudentID", "FullName": "vueData.form.FullName", "ClassID": "vueData.form.ClassID" }, "CALLBACK": [ { "CALL": "vueData.fetchStudents" }, { "MESS": "Đã thêm" } ] }
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Key notes
|
|
310
|
+
|
|
311
|
+
- **`{apiName}`** in the API path is the app's tAPI apiName (e.g. `me`, `lms`, `hr`). Ask the user if unknown. **A project may use several apiNames** (one database each) — each alias needs its own `fui db add`, and the alias in the path is the `--db` argument of DB commands ([db-workflow.md](db-workflow.md) §4).
|
|
312
|
+
- **Route params** (`@url1_`) go via the URL, not `IN`, using the backtick prefix: `"API": "/me/StudentDelete/\`{{vueData.selectedID}}"`
|
|
313
|
+
- **SP with multiple SELECTs** → `data[0]`, `data[1]`... — declare explicitly in `OUT` or split with `EXE`.
|
|
314
|
+
- **`convert_to_object = ''`** → flat object response: read `vueData.studentDetail.FullName`, not `vueData.studentDetail[0].FullName`.
|