@fui-org/fui-cli 2.1.4 → 2.2.0

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.
Files changed (217) hide show
  1. package/README.md +27 -7
  2. package/dist/fui-qrb1j9jz.js +397 -0
  3. package/dist/fui.js +1 -1
  4. package/package.json +1 -1
  5. package/skills/fui/SKILL.md +143 -38
  6. package/skills/fui/agents/fui-builder.md +62 -0
  7. package/skills/fui/agents/fui-tester.md +77 -0
  8. package/skills/{fui-skill → fui}/assets/projectdefaultstyle.css +489 -489
  9. package/skills/{fui-skill → fui}/examples/component.vue +162 -162
  10. package/skills/{fui-skill → fui}/references/INDEX.md +5 -4
  11. package/skills/{fui-skill → fui}/references/advanced-techniques.md +170 -168
  12. package/skills/{fui-skill → fui}/references/component-design.md +2 -2
  13. package/skills/{fui-skill → fui}/references/component-quickref.md +78 -78
  14. package/skills/{fui-skill → fui}/references/component-table.md +287 -287
  15. package/skills/{fui-skill → fui}/references/components-dialog.md +191 -191
  16. package/skills/{fui-skill → fui}/references/components-display.md +141 -141
  17. package/skills/{fui-skill → fui}/references/components-echart.md +316 -316
  18. package/skills/{fui-skill → fui}/references/components-input.md +335 -335
  19. package/skills/{fui-skill → fui}/references/controls-patterns.md +2 -2
  20. package/skills/{fui-skill → fui}/references/controls-styling-vocabulary.md +173 -173
  21. package/skills/{fui-skill → fui}/references/db-workflow.md +9 -9
  22. package/skills/{fui-skill → fui}/references/default-function.md +456 -456
  23. package/skills/{fui-skill → fui}/references/design-modes.md +57 -57
  24. package/skills/{fui-skill → fui}/references/echart-templates.md +489 -489
  25. package/skills/{fui-skill → fui}/references/fastproject.md +99 -99
  26. package/skills/{fui-skill → fui}/references/fullstack-workflow.md +314 -313
  27. package/skills/{fui-skill → fui}/references/module-data-patterns.md +117 -117
  28. package/skills/{fui-skill → fui}/references/module-json-anatomy.md +132 -132
  29. package/skills/{fui-skill → fui}/references/module-structure.md +144 -144
  30. package/skills/{fui-skill → fui}/references/new-session.md +1 -2
  31. package/skills/{fui-skill → fui}/references/pdfmake.md +60 -60
  32. package/skills/{fui-skill → fui}/references/permission-system.md +1 -1
  33. package/skills/{fui-skill → fui}/references/platform-architecture.md +269 -269
  34. package/skills/{fui-skill → fui}/references/project-config.md +303 -303
  35. package/skills/{fui-skill → fui}/references/project-provisioning.md +4 -4
  36. package/skills/{fui-skill → fui}/references/script-map.md +262 -262
  37. package/skills/{fui-skill → fui}/references/sql-clr-functions.md +225 -225
  38. package/skills/{fui-skill → fui}/references/system-design.md +2 -2
  39. package/skills/{fui-skill → fui}/references/tapi-permission-patterns.md +156 -156
  40. package/skills/{fui-skill → fui}/references/tapi-reference.md +474 -474
  41. package/skills/fui/references/team-run.md +141 -0
  42. package/skills/fui/references/tools-registry.md +211 -0
  43. package/skills/{fui-skill → fui}/references/ui-crosswindow-patterns.md +321 -321
  44. package/skills/{fui-skill → fui}/references/ui-dialog-patterns.md +255 -255
  45. package/skills/{fui-skill → fui}/references/ui-layout-patterns.md +176 -176
  46. package/skills/{fui-skill → fui}/references/ui-patterns.md +303 -303
  47. package/skills/{fui-skill → fui}/references/ui-screenshot-review.md +95 -95
  48. package/skills/{fui-skill → fui}/references/ui-table-cell-patterns.md +318 -318
  49. package/skills/{fui-skill → fui}/references/verification.md +11 -11
  50. package/skills/{fui-skill → fui}/references/watcher-patterns.md +163 -163
  51. package/skills/{fui-skill → fui}/references/websocket-realtime.md +271 -271
  52. package/skills/{fui-skill → fui}/scripts/component-3.0.js +2715 -2715
  53. package/skills/{fui-skill → fui}/scripts/component.js +3147 -3147
  54. package/skills/{fui-skill → fui}/scripts/componentTable-3.0.js +1038 -1038
  55. package/skills/{fui-skill → fui}/scripts/componentTable.js +891 -891
  56. package/skills/{fui-skill → fui}/scripts/defaultfunction-3.0.js +866 -866
  57. package/skills/{fui-skill → fui}/scripts/defaultfunction.js +1051 -1051
  58. package/skills/{fui-skill → fui}/scripts/fastproject-3.0.js +870 -870
  59. package/skills/{fui-skill → fui}/scripts/fastproject.js +828 -828
  60. package/dist/fui-h4z7zhk2.js +0 -381
  61. package/skills/fui-skill/SKILL.md +0 -139
  62. package/skills/fui-skill/references/tools-registry.md +0 -84
  63. /package/skills/{fui-skill → fui}/assets/projectdefaultstyle-3.0.css +0 -0
  64. /package/skills/{fui-skill → fui}/design-md/airbnb/DESIGN.md +0 -0
  65. /package/skills/{fui-skill → fui}/design-md/airbnb/README.md +0 -0
  66. /package/skills/{fui-skill → fui}/design-md/airtable/DESIGN.md +0 -0
  67. /package/skills/{fui-skill → fui}/design-md/airtable/README.md +0 -0
  68. /package/skills/{fui-skill → fui}/design-md/apple/DESIGN.md +0 -0
  69. /package/skills/{fui-skill → fui}/design-md/apple/README.md +0 -0
  70. /package/skills/{fui-skill → fui}/design-md/asu/DESIGN.md +0 -0
  71. /package/skills/{fui-skill → fui}/design-md/asu/README.md +0 -0
  72. /package/skills/{fui-skill → fui}/design-md/binance/DESIGN.md +0 -0
  73. /package/skills/{fui-skill → fui}/design-md/binance/README.md +0 -0
  74. /package/skills/{fui-skill → fui}/design-md/bmw/DESIGN.md +0 -0
  75. /package/skills/{fui-skill → fui}/design-md/bmw/README.md +0 -0
  76. /package/skills/{fui-skill → fui}/design-md/bmw-m/DESIGN.md +0 -0
  77. /package/skills/{fui-skill → fui}/design-md/bmw-m/README.md +0 -0
  78. /package/skills/{fui-skill → fui}/design-md/bugatti/DESIGN.md +0 -0
  79. /package/skills/{fui-skill → fui}/design-md/bugatti/README.md +0 -0
  80. /package/skills/{fui-skill → fui}/design-md/cal/DESIGN.md +0 -0
  81. /package/skills/{fui-skill → fui}/design-md/cal/README.md +0 -0
  82. /package/skills/{fui-skill → fui}/design-md/claude/DESIGN.md +0 -0
  83. /package/skills/{fui-skill → fui}/design-md/claude/README.md +0 -0
  84. /package/skills/{fui-skill → fui}/design-md/clay/DESIGN.md +0 -0
  85. /package/skills/{fui-skill → fui}/design-md/clay/README.md +0 -0
  86. /package/skills/{fui-skill → fui}/design-md/clickhouse/DESIGN.md +0 -0
  87. /package/skills/{fui-skill → fui}/design-md/clickhouse/README.md +0 -0
  88. /package/skills/{fui-skill → fui}/design-md/cohere/DESIGN.md +0 -0
  89. /package/skills/{fui-skill → fui}/design-md/cohere/README.md +0 -0
  90. /package/skills/{fui-skill → fui}/design-md/coinbase/DESIGN.md +0 -0
  91. /package/skills/{fui-skill → fui}/design-md/coinbase/README.md +0 -0
  92. /package/skills/{fui-skill → fui}/design-md/composio/DESIGN.md +0 -0
  93. /package/skills/{fui-skill → fui}/design-md/composio/README.md +0 -0
  94. /package/skills/{fui-skill → fui}/design-md/cursor/DESIGN.md +0 -0
  95. /package/skills/{fui-skill → fui}/design-md/cursor/README.md +0 -0
  96. /package/skills/{fui-skill → fui}/design-md/elevenlabs/DESIGN.md +0 -0
  97. /package/skills/{fui-skill → fui}/design-md/elevenlabs/README.md +0 -0
  98. /package/skills/{fui-skill → fui}/design-md/expo/DESIGN.md +0 -0
  99. /package/skills/{fui-skill → fui}/design-md/expo/README.md +0 -0
  100. /package/skills/{fui-skill → fui}/design-md/ferrari/DESIGN.md +0 -0
  101. /package/skills/{fui-skill → fui}/design-md/ferrari/README.md +0 -0
  102. /package/skills/{fui-skill → fui}/design-md/figma/DESIGN.md +0 -0
  103. /package/skills/{fui-skill → fui}/design-md/figma/README.md +0 -0
  104. /package/skills/{fui-skill → fui}/design-md/framer/DESIGN.md +0 -0
  105. /package/skills/{fui-skill → fui}/design-md/framer/README.md +0 -0
  106. /package/skills/{fui-skill → fui}/design-md/fui/DESIGN.md +0 -0
  107. /package/skills/{fui-skill → fui}/design-md/hashicorp/DESIGN.md +0 -0
  108. /package/skills/{fui-skill → fui}/design-md/hashicorp/README.md +0 -0
  109. /package/skills/{fui-skill → fui}/design-md/ibm/DESIGN.md +0 -0
  110. /package/skills/{fui-skill → fui}/design-md/ibm/README.md +0 -0
  111. /package/skills/{fui-skill → fui}/design-md/intercom/DESIGN.md +0 -0
  112. /package/skills/{fui-skill → fui}/design-md/intercom/README.md +0 -0
  113. /package/skills/{fui-skill → fui}/design-md/kraken/DESIGN.md +0 -0
  114. /package/skills/{fui-skill → fui}/design-md/kraken/README.md +0 -0
  115. /package/skills/{fui-skill → fui}/design-md/lamborghini/DESIGN.md +0 -0
  116. /package/skills/{fui-skill → fui}/design-md/lamborghini/README.md +0 -0
  117. /package/skills/{fui-skill → fui}/design-md/linear.app/DESIGN.md +0 -0
  118. /package/skills/{fui-skill → fui}/design-md/linear.app/README.md +0 -0
  119. /package/skills/{fui-skill → fui}/design-md/lovable/DESIGN.md +0 -0
  120. /package/skills/{fui-skill → fui}/design-md/lovable/README.md +0 -0
  121. /package/skills/{fui-skill → fui}/design-md/mastercard/DESIGN.md +0 -0
  122. /package/skills/{fui-skill → fui}/design-md/mastercard/README.md +0 -0
  123. /package/skills/{fui-skill → fui}/design-md/meta/DESIGN.md +0 -0
  124. /package/skills/{fui-skill → fui}/design-md/meta/README.md +0 -0
  125. /package/skills/{fui-skill → fui}/design-md/minimax/DESIGN.md +0 -0
  126. /package/skills/{fui-skill → fui}/design-md/minimax/README.md +0 -0
  127. /package/skills/{fui-skill → fui}/design-md/mintlify/DESIGN.md +0 -0
  128. /package/skills/{fui-skill → fui}/design-md/mintlify/README.md +0 -0
  129. /package/skills/{fui-skill → fui}/design-md/miro/DESIGN.md +0 -0
  130. /package/skills/{fui-skill → fui}/design-md/miro/README.md +0 -0
  131. /package/skills/{fui-skill → fui}/design-md/mistral.ai/DESIGN.md +0 -0
  132. /package/skills/{fui-skill → fui}/design-md/mistral.ai/README.md +0 -0
  133. /package/skills/{fui-skill → fui}/design-md/mongodb/DESIGN.md +0 -0
  134. /package/skills/{fui-skill → fui}/design-md/mongodb/README.md +0 -0
  135. /package/skills/{fui-skill → fui}/design-md/nike/DESIGN.md +0 -0
  136. /package/skills/{fui-skill → fui}/design-md/nike/README.md +0 -0
  137. /package/skills/{fui-skill → fui}/design-md/notion/DESIGN.md +0 -0
  138. /package/skills/{fui-skill → fui}/design-md/notion/README.md +0 -0
  139. /package/skills/{fui-skill → fui}/design-md/nvidia/DESIGN.md +0 -0
  140. /package/skills/{fui-skill → fui}/design-md/nvidia/README.md +0 -0
  141. /package/skills/{fui-skill → fui}/design-md/ollama/DESIGN.md +0 -0
  142. /package/skills/{fui-skill → fui}/design-md/ollama/README.md +0 -0
  143. /package/skills/{fui-skill → fui}/design-md/opencode.ai/DESIGN.md +0 -0
  144. /package/skills/{fui-skill → fui}/design-md/opencode.ai/README.md +0 -0
  145. /package/skills/{fui-skill → fui}/design-md/pinterest/DESIGN.md +0 -0
  146. /package/skills/{fui-skill → fui}/design-md/pinterest/README.md +0 -0
  147. /package/skills/{fui-skill → fui}/design-md/playstation/DESIGN.md +0 -0
  148. /package/skills/{fui-skill → fui}/design-md/playstation/README.md +0 -0
  149. /package/skills/{fui-skill → fui}/design-md/posthog/DESIGN.md +0 -0
  150. /package/skills/{fui-skill → fui}/design-md/posthog/README.md +0 -0
  151. /package/skills/{fui-skill → fui}/design-md/raycast/DESIGN.md +0 -0
  152. /package/skills/{fui-skill → fui}/design-md/raycast/README.md +0 -0
  153. /package/skills/{fui-skill → fui}/design-md/renault/DESIGN.md +0 -0
  154. /package/skills/{fui-skill → fui}/design-md/renault/README.md +0 -0
  155. /package/skills/{fui-skill → fui}/design-md/replicate/DESIGN.md +0 -0
  156. /package/skills/{fui-skill → fui}/design-md/replicate/README.md +0 -0
  157. /package/skills/{fui-skill → fui}/design-md/resend/DESIGN.md +0 -0
  158. /package/skills/{fui-skill → fui}/design-md/resend/README.md +0 -0
  159. /package/skills/{fui-skill → fui}/design-md/revolut/DESIGN.md +0 -0
  160. /package/skills/{fui-skill → fui}/design-md/revolut/README.md +0 -0
  161. /package/skills/{fui-skill → fui}/design-md/runwayml/DESIGN.md +0 -0
  162. /package/skills/{fui-skill → fui}/design-md/runwayml/README.md +0 -0
  163. /package/skills/{fui-skill → fui}/design-md/sanity/DESIGN.md +0 -0
  164. /package/skills/{fui-skill → fui}/design-md/sanity/README.md +0 -0
  165. /package/skills/{fui-skill → fui}/design-md/sentry/DESIGN.md +0 -0
  166. /package/skills/{fui-skill → fui}/design-md/sentry/README.md +0 -0
  167. /package/skills/{fui-skill → fui}/design-md/shopify/DESIGN.md +0 -0
  168. /package/skills/{fui-skill → fui}/design-md/shopify/README.md +0 -0
  169. /package/skills/{fui-skill → fui}/design-md/slack/DESIGN.md +0 -0
  170. /package/skills/{fui-skill → fui}/design-md/spacex/DESIGN.md +0 -0
  171. /package/skills/{fui-skill → fui}/design-md/spacex/README.md +0 -0
  172. /package/skills/{fui-skill → fui}/design-md/spotify/DESIGN.md +0 -0
  173. /package/skills/{fui-skill → fui}/design-md/spotify/README.md +0 -0
  174. /package/skills/{fui-skill → fui}/design-md/starbucks/DESIGN.md +0 -0
  175. /package/skills/{fui-skill → fui}/design-md/starbucks/README.md +0 -0
  176. /package/skills/{fui-skill → fui}/design-md/stripe/DESIGN.md +0 -0
  177. /package/skills/{fui-skill → fui}/design-md/stripe/README.md +0 -0
  178. /package/skills/{fui-skill → fui}/design-md/supabase/DESIGN.md +0 -0
  179. /package/skills/{fui-skill → fui}/design-md/supabase/README.md +0 -0
  180. /package/skills/{fui-skill → fui}/design-md/superhuman/DESIGN.md +0 -0
  181. /package/skills/{fui-skill → fui}/design-md/superhuman/README.md +0 -0
  182. /package/skills/{fui-skill → fui}/design-md/tesla/DESIGN.md +0 -0
  183. /package/skills/{fui-skill → fui}/design-md/tesla/README.md +0 -0
  184. /package/skills/{fui-skill → fui}/design-md/theverge/DESIGN.md +0 -0
  185. /package/skills/{fui-skill → fui}/design-md/theverge/README.md +0 -0
  186. /package/skills/{fui-skill → fui}/design-md/together.ai/DESIGN.md +0 -0
  187. /package/skills/{fui-skill → fui}/design-md/together.ai/README.md +0 -0
  188. /package/skills/{fui-skill → fui}/design-md/uber/DESIGN.md +0 -0
  189. /package/skills/{fui-skill → fui}/design-md/uber/README.md +0 -0
  190. /package/skills/{fui-skill → fui}/design-md/vercel/DESIGN.md +0 -0
  191. /package/skills/{fui-skill → fui}/design-md/vercel/README.md +0 -0
  192. /package/skills/{fui-skill → fui}/design-md/vodafone/DESIGN.md +0 -0
  193. /package/skills/{fui-skill → fui}/design-md/vodafone/README.md +0 -0
  194. /package/skills/{fui-skill → fui}/design-md/voltagent/DESIGN.md +0 -0
  195. /package/skills/{fui-skill → fui}/design-md/voltagent/README.md +0 -0
  196. /package/skills/{fui-skill → fui}/design-md/warp/DESIGN.md +0 -0
  197. /package/skills/{fui-skill → fui}/design-md/warp/README.md +0 -0
  198. /package/skills/{fui-skill → fui}/design-md/webflow/DESIGN.md +0 -0
  199. /package/skills/{fui-skill → fui}/design-md/webflow/README.md +0 -0
  200. /package/skills/{fui-skill → fui}/design-md/wired/DESIGN.md +0 -0
  201. /package/skills/{fui-skill → fui}/design-md/wired/README.md +0 -0
  202. /package/skills/{fui-skill → fui}/design-md/wise/DESIGN.md +0 -0
  203. /package/skills/{fui-skill → fui}/design-md/wise/README.md +0 -0
  204. /package/skills/{fui-skill → fui}/design-md/x.ai/DESIGN.md +0 -0
  205. /package/skills/{fui-skill → fui}/design-md/x.ai/README.md +0 -0
  206. /package/skills/{fui-skill → fui}/design-md/zapier/DESIGN.md +0 -0
  207. /package/skills/{fui-skill → fui}/design-md/zapier/README.md +0 -0
  208. /package/skills/{fui-skill → fui}/examples/f-table-patterns.json +0 -0
  209. /package/skills/{fui-skill → fui}/examples/module-patterns.json +0 -0
  210. /package/skills/{fui-skill → fui}/examples/project-patterns.json +0 -0
  211. /package/skills/{fui-skill → fui}/references/coding-standards.md +0 -0
  212. /package/skills/{fui-skill → fui}/references/db-table-design.md +0 -0
  213. /package/skills/{fui-skill → fui}/references/fsheet.md +0 -0
  214. /package/skills/{fui-skill → fui}/references/tapi-file-api.md +0 -0
  215. /package/skills/{fui-skill → fui}/references/ui-templates.md +0 -0
  216. /package/skills/{fui-skill → fui}/scripts/fechart.js +0 -0
  217. /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
- CREATE PROCEDURE spAPI_StudentList
112
- @Keyword nvarchar(200) = NULL,
113
- @ClassID varchar(20) = NULL,
114
- @sys_UserID varchar(9)
115
- AS BEGIN
116
- SELECT s.StudentID, s.FullName, s.ClassID, c.ClassName, s.GPA
117
- FROM tblStudent s
118
- JOIN tblClass c ON s.ClassID = c.ClassID
119
- WHERE (@Keyword IS NULL OR s.FullName LIKE '%' + @Keyword + '%')
120
- AND (@ClassID IS NULL OR s.ClassID = @ClassID)
121
- ORDER BY s.FullName
122
- END
123
- ```
124
-
125
- **INSERT:**
126
-
127
- ```sql
128
- CREATE PROCEDURE spAPI_StudentInsert
129
- @StudentID varchar(9),
130
- @FullName nvarchar(100),
131
- @ClassID varchar(20),
132
- @sys_UserID varchar(9)
133
- AS BEGIN
134
- IF EXISTS (SELECT 1 FROM tblStudent WHERE StudentID = @StudentID)
135
- BEGIN
136
- RAISERROR(N'Mã sinh viên đã tồn tại', 16, 1)
137
- RETURN
138
- END
139
- INSERT INTO tblStudent (StudentID, FullName, ClassID, CreateUser, CreateTime)
140
- VALUES (@StudentID, @FullName, @ClassID, @sys_UserID, GETDATE())
141
- SELECT mess = N'Thêm thành công', id = @StudentID
142
- END
143
- ```
144
-
145
- **DELETE:**
146
-
147
- ```sql
148
- CREATE PROCEDURE spAPI_StudentDelete
149
- @url1_StudentID varchar(9),
150
- @sys_UserID varchar(9)
151
- AS BEGIN
152
- DELETE FROM tblStudent WHERE StudentID = @url1_StudentID
153
- SELECT mess = N'Đã xoá', id = @url1_StudentID
154
- END
155
- ```
156
-
157
- **GET (detail, convert_to_object):**
158
-
159
- ```sql
160
- CREATE PROCEDURE spAPI_StudentGet
161
- @url1_StudentID varchar(9),
162
- @sys_UserID varchar(9)
163
- AS BEGIN
164
- SELECT convert_to_object = '',
165
- s.StudentID, s.FullName, s.ClassID, c.ClassName, s.GPA, s.Email
166
- FROM tblStudent s
167
- JOIN tblClass c ON s.ClassID = c.ClassID
168
- WHERE s.StudentID = @url1_StudentID
169
- END
170
- ```
171
-
172
- > `convert_to_object = ''` when the SP returns exactly one record → response is an object instead of `data[0][0]`.
173
-
174
- Deploy with `fui sp save` + `fui sp deploy` (never call the endpoint to "try it" — see step 6).
175
-
176
- **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.
177
-
178
- `fui sp deploy` **auto-grants** new procedures (not FUNCTIONs — functions need no EXECUTE for `[public]`), then **re-checks `sys.database_permissions`** and reports:
179
-
180
- - `✓ [public] đã có quyền EXECUTE trên "..."` — done.
181
- - `❗ [public] CHƯA có quyền EXECUTE ...` — the output prints a runnable command; copy verbatim:
182
- `fui exec "GRANT EXECUTE ON [dbo].[spAPI_StudentList] TO [public] AS [dbo]" --db ...`
183
- - Grant failed but deploy succeeded → **the SP is already on the server**; run the GRANT above, **do not redeploy**.
184
-
185
- 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.
186
-
187
- **Output:** all step-3 SPs deployed.
188
-
189
- ---
190
-
191
- ## Step 5 — Replace hard-coded data with API bindings
192
-
193
- Keep the step-2 layout; replace `data[]` literals with named `IN`/`OUT`/`CALLBACK` actions on the deployed SPs.
194
-
195
- **5.1 Actions replacing hard-coded data**
196
-
197
- ```json
198
- "data": [
199
- { "keyword": "" },
200
- { "classId": "" },
201
- { "studentList": [] },
202
-
203
- {
204
- "fetchStudents": {
205
- "API": "/{apiName}/StudentList",
206
- "IN": { "Keyword": "vueData.keyword", "ClassID": "vueData.classId" },
207
- "OUT": "studentList"
208
- }
209
- },
210
- {
211
- "saveStudent": {
212
- "API": "/{apiName}/StudentInsert",
213
- "IN": {
214
- "StudentID": "vueData.form.StudentID",
215
- "FullName": "vueData.form.FullName",
216
- "ClassID": "vueData.form.ClassID"
217
- },
218
- "CALLBACK": [
219
- { "CALL": "vueData.fetchStudents" },
220
- { "MESS": "Thêm thành công" },
221
- { "EXE": "vueData.dialogOpen = false" }
222
- ]
223
- }
224
- },
225
- {
226
- "deleteStudent": {
227
- "API": "/{apiName}/StudentDelete/`{{vueData.selectedID}}",
228
- "CONFIRM": "Bạn có chắc muốn xoá sinh viên này?",
229
- "CALLBACK": { "CALL": "vueData.fetchStudents" }
230
- }
231
- }
232
- ]
233
- ```
234
-
235
- **5.2 `watch` to reload when filters change**
236
-
237
- ```json
238
- "watch": {
239
- "keyword": { "CALL": "vueData.fetchStudents" },
240
- "classId": { "CALL": "vueData.fetchStudents" }
241
- }
242
- ```
243
-
244
- **5.3** Append `{ "CALL": "vueData.fetchStudents" }` at the end of `data[]` to auto-load on open.
245
-
246
- **Output:** `module.json` fully wired, layout unchanged from step 2.
247
-
248
- ---
249
-
250
- ## Step 6 — Static verification and finish
251
-
252
- Pre-publish checklist — **no step calls a real API/SP/DML**:
253
-
254
- **API/SP** — full checklist (naming, params, security, `GRANT EXECUTE`, deploy workflow): [verification.md](verification.md). Here:
255
-
256
- - [ ] `AUTH_` on endpoints that need no login
257
- - [ ] `fui sp verify` per new SP — reads the real body, infers response shape/error contract, checks params, emits a `--mocks` snippet (no endpoint call)
258
- - [ ] **Write** APIs: read the body and reason (WHERE, transaction, RAISERROR) — no trial run
259
-
260
- **module.json:**
261
-
262
- - [ ] Each action's `API` path matches `/{apiName}/{FunctionName}`
263
- - [ ] `IN` params match SP param names exactly (case-sensitive)
264
- - [ ] Route params (url1, url2) go via the URL, not `IN`
265
- - [ ] `OUT` receives the right type (array vs object per SP) — check against the shape inferred by `fui sp verify`
266
- - [ ] `watch` declared for auto-reload filters
267
- - [ ] `data[]` declares the initial fetch action and ends with a startup `{ "CALL": ... }`
268
-
269
- **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.)
270
-
271
- **Output:** module works end-to-end, UI ↔ API connected, zero real calls during verification.
272
-
273
- ---
274
-
275
- ## Complete output example
276
-
277
- ### SPs (step 4)
278
-
279
- 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.
280
-
281
- ```sql
282
- -- 1. Danh sách (file spAPI_StudentList.sql)
283
- CREATE PROCEDURE spAPI_StudentList @Keyword nvarchar(200), @ClassID varchar(20), @sys_UserID varchar(9)
284
- AS BEGIN
285
- SELECT s.StudentID, s.FullName, s.ClassID, s.GPA
286
- FROM tblStudent s WHERE (@Keyword = '' OR s.FullName LIKE '%'+@Keyword+'%') AND (@ClassID = '' OR s.ClassID = @ClassID)
287
- END
288
- ```
289
-
290
- ```sql
291
- -- 2. Thêm mới (file spAPI_StudentInsert.sql)
292
- CREATE PROCEDURE spAPI_StudentInsert @StudentID varchar(9), @FullName nvarchar(100), @ClassID varchar(20), @sys_UserID varchar(9)
293
- AS BEGIN
294
- INSERT INTO tblStudent(StudentID, FullName, ClassID, CreateUser, CreateTime) VALUES(@StudentID, @FullName, @ClassID, @sys_UserID, GETDATE())
295
- SELECT mess = N'Thêm thành công'
296
- END
297
- ```
298
-
299
- ### module.json actions (step 5)
300
-
301
- ```json
302
- "fetchStudents": { "API": "/me/StudentList", "IN": { "Keyword": "vueData.keyword" }, "OUT": "studentList" },
303
- "addStudent": { "API": "/me/StudentInsert", "IN": { "StudentID": "vueData.form.StudentID", "FullName": "vueData.form.FullName", "ClassID": "vueData.form.ClassID" }, "CALLBACK": [ { "CALL": "vueData.fetchStudents" }, { "MESS": "Đã thêm" } ] }
304
- ```
305
-
306
- ---
307
-
308
- ## Key notes
309
-
310
- - **`{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).
311
- - **Route params** (`@url1_`) go via the URL, not `IN`, using the backtick prefix: `"API": "/me/StudentDelete/\`{{vueData.selectedID}}"`
312
- - **SP with multiple SELECTs** → `data[0]`, `data[1]`... — declare explicitly in `OUT` or split with `EXE`.
313
- - **`convert_to_object = ''`** → flat object response: read `vueData.studentDetail.FullName`, not `vueData.studentDetail[0].FullName`.
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`.