@fui-org/fui-cli 0.2.0 → 0.3.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 (212) hide show
  1. package/README.md +13 -1
  2. package/dist/fui.js +160 -43
  3. package/package.json +4 -2
  4. package/skills/fui/SKILL.md +131 -66
  5. package/skills/fui-skill/README.md +112 -0
  6. package/skills/fui-skill/SKILL.md +269 -0
  7. package/skills/fui-skill/assets/projectdefaultstyle.css +518 -0
  8. package/skills/fui-skill/design-md/airbnb/DESIGN.md +545 -0
  9. package/skills/fui-skill/design-md/airbnb/README.md +5 -0
  10. package/skills/fui-skill/design-md/airtable/DESIGN.md +554 -0
  11. package/skills/fui-skill/design-md/airtable/README.md +5 -0
  12. package/skills/fui-skill/design-md/apple/DESIGN.md +562 -0
  13. package/skills/fui-skill/design-md/apple/README.md +5 -0
  14. package/skills/fui-skill/design-md/asu/DESIGN.md +179 -0
  15. package/skills/fui-skill/design-md/asu/README.md +107 -0
  16. package/skills/fui-skill/design-md/binance/DESIGN.md +634 -0
  17. package/skills/fui-skill/design-md/binance/README.md +5 -0
  18. package/skills/fui-skill/design-md/bmw/DESIGN.md +544 -0
  19. package/skills/fui-skill/design-md/bmw/README.md +5 -0
  20. package/skills/fui-skill/design-md/bmw-m/DESIGN.md +503 -0
  21. package/skills/fui-skill/design-md/bmw-m/README.md +5 -0
  22. package/skills/fui-skill/design-md/bugatti/DESIGN.md +454 -0
  23. package/skills/fui-skill/design-md/bugatti/README.md +5 -0
  24. package/skills/fui-skill/design-md/cal/DESIGN.md +542 -0
  25. package/skills/fui-skill/design-md/cal/README.md +5 -0
  26. package/skills/fui-skill/design-md/claude/DESIGN.md +589 -0
  27. package/skills/fui-skill/design-md/claude/README.md +5 -0
  28. package/skills/fui-skill/design-md/clay/DESIGN.md +541 -0
  29. package/skills/fui-skill/design-md/clay/README.md +5 -0
  30. package/skills/fui-skill/design-md/clickhouse/DESIGN.md +544 -0
  31. package/skills/fui-skill/design-md/clickhouse/README.md +5 -0
  32. package/skills/fui-skill/design-md/cohere/DESIGN.md +451 -0
  33. package/skills/fui-skill/design-md/cohere/README.md +5 -0
  34. package/skills/fui-skill/design-md/coinbase/DESIGN.md +570 -0
  35. package/skills/fui-skill/design-md/coinbase/README.md +5 -0
  36. package/skills/fui-skill/design-md/composio/DESIGN.md +506 -0
  37. package/skills/fui-skill/design-md/composio/README.md +5 -0
  38. package/skills/fui-skill/design-md/cursor/DESIGN.md +537 -0
  39. package/skills/fui-skill/design-md/cursor/README.md +5 -0
  40. package/skills/fui-skill/design-md/elevenlabs/DESIGN.md +504 -0
  41. package/skills/fui-skill/design-md/elevenlabs/README.md +5 -0
  42. package/skills/fui-skill/design-md/expo/DESIGN.md +526 -0
  43. package/skills/fui-skill/design-md/expo/README.md +5 -0
  44. package/skills/fui-skill/design-md/ferrari/DESIGN.md +531 -0
  45. package/skills/fui-skill/design-md/ferrari/README.md +5 -0
  46. package/skills/fui-skill/design-md/figma/DESIGN.md +578 -0
  47. package/skills/fui-skill/design-md/figma/README.md +5 -0
  48. package/skills/fui-skill/design-md/framer/DESIGN.md +544 -0
  49. package/skills/fui-skill/design-md/framer/README.md +5 -0
  50. package/skills/fui-skill/design-md/fui/DESIGN.md +532 -0
  51. package/skills/fui-skill/design-md/hashicorp/DESIGN.md +575 -0
  52. package/skills/fui-skill/design-md/hashicorp/README.md +5 -0
  53. package/skills/fui-skill/design-md/ibm/DESIGN.md +550 -0
  54. package/skills/fui-skill/design-md/ibm/README.md +5 -0
  55. package/skills/fui-skill/design-md/intercom/DESIGN.md +546 -0
  56. package/skills/fui-skill/design-md/intercom/README.md +5 -0
  57. package/skills/fui-skill/design-md/kraken/DESIGN.md +125 -0
  58. package/skills/fui-skill/design-md/kraken/README.md +5 -0
  59. package/skills/fui-skill/design-md/lamborghini/DESIGN.md +288 -0
  60. package/skills/fui-skill/design-md/lamborghini/README.md +5 -0
  61. package/skills/fui-skill/design-md/linear.app/DESIGN.md +548 -0
  62. package/skills/fui-skill/design-md/linear.app/README.md +5 -0
  63. package/skills/fui-skill/design-md/lovable/DESIGN.md +298 -0
  64. package/skills/fui-skill/design-md/lovable/README.md +5 -0
  65. package/skills/fui-skill/design-md/mastercard/DESIGN.md +365 -0
  66. package/skills/fui-skill/design-md/mastercard/README.md +5 -0
  67. package/skills/fui-skill/design-md/meta/DESIGN.md +683 -0
  68. package/skills/fui-skill/design-md/meta/README.md +5 -0
  69. package/skills/fui-skill/design-md/minimax/DESIGN.md +746 -0
  70. package/skills/fui-skill/design-md/minimax/README.md +5 -0
  71. package/skills/fui-skill/design-md/mintlify/DESIGN.md +852 -0
  72. package/skills/fui-skill/design-md/mintlify/README.md +5 -0
  73. package/skills/fui-skill/design-md/miro/DESIGN.md +825 -0
  74. package/skills/fui-skill/design-md/miro/README.md +5 -0
  75. package/skills/fui-skill/design-md/mistral.ai/DESIGN.md +773 -0
  76. package/skills/fui-skill/design-md/mistral.ai/README.md +5 -0
  77. package/skills/fui-skill/design-md/mongodb/DESIGN.md +767 -0
  78. package/skills/fui-skill/design-md/mongodb/README.md +5 -0
  79. package/skills/fui-skill/design-md/nike/DESIGN.md +575 -0
  80. package/skills/fui-skill/design-md/nike/README.md +5 -0
  81. package/skills/fui-skill/design-md/notion/DESIGN.md +821 -0
  82. package/skills/fui-skill/design-md/notion/README.md +5 -0
  83. package/skills/fui-skill/design-md/nvidia/DESIGN.md +640 -0
  84. package/skills/fui-skill/design-md/nvidia/README.md +5 -0
  85. package/skills/fui-skill/design-md/ollama/DESIGN.md +539 -0
  86. package/skills/fui-skill/design-md/ollama/README.md +5 -0
  87. package/skills/fui-skill/design-md/opencode.ai/DESIGN.md +521 -0
  88. package/skills/fui-skill/design-md/opencode.ai/README.md +5 -0
  89. package/skills/fui-skill/design-md/pinterest/DESIGN.md +597 -0
  90. package/skills/fui-skill/design-md/pinterest/README.md +5 -0
  91. package/skills/fui-skill/design-md/playstation/DESIGN.md +661 -0
  92. package/skills/fui-skill/design-md/playstation/README.md +5 -0
  93. package/skills/fui-skill/design-md/posthog/DESIGN.md +690 -0
  94. package/skills/fui-skill/design-md/posthog/README.md +5 -0
  95. package/skills/fui-skill/design-md/raycast/DESIGN.md +669 -0
  96. package/skills/fui-skill/design-md/raycast/README.md +5 -0
  97. package/skills/fui-skill/design-md/renault/DESIGN.md +589 -0
  98. package/skills/fui-skill/design-md/renault/README.md +5 -0
  99. package/skills/fui-skill/design-md/replicate/DESIGN.md +616 -0
  100. package/skills/fui-skill/design-md/replicate/README.md +5 -0
  101. package/skills/fui-skill/design-md/resend/DESIGN.md +585 -0
  102. package/skills/fui-skill/design-md/resend/README.md +5 -0
  103. package/skills/fui-skill/design-md/revolut/DESIGN.md +636 -0
  104. package/skills/fui-skill/design-md/revolut/README.md +5 -0
  105. package/skills/fui-skill/design-md/runwayml/DESIGN.md +244 -0
  106. package/skills/fui-skill/design-md/runwayml/README.md +5 -0
  107. package/skills/fui-skill/design-md/sanity/DESIGN.md +357 -0
  108. package/skills/fui-skill/design-md/sanity/README.md +5 -0
  109. package/skills/fui-skill/design-md/sentry/DESIGN.md +551 -0
  110. package/skills/fui-skill/design-md/sentry/README.md +5 -0
  111. package/skills/fui-skill/design-md/shopify/DESIGN.md +516 -0
  112. package/skills/fui-skill/design-md/shopify/README.md +5 -0
  113. package/skills/fui-skill/design-md/slack/DESIGN.md +482 -0
  114. package/skills/fui-skill/design-md/spacex/DESIGN.md +363 -0
  115. package/skills/fui-skill/design-md/spacex/README.md +5 -0
  116. package/skills/fui-skill/design-md/spotify/DESIGN.md +246 -0
  117. package/skills/fui-skill/design-md/spotify/README.md +5 -0
  118. package/skills/fui-skill/design-md/starbucks/DESIGN.md +580 -0
  119. package/skills/fui-skill/design-md/starbucks/README.md +5 -0
  120. package/skills/fui-skill/design-md/stripe/DESIGN.md +487 -0
  121. package/skills/fui-skill/design-md/stripe/README.md +5 -0
  122. package/skills/fui-skill/design-md/supabase/DESIGN.md +462 -0
  123. package/skills/fui-skill/design-md/supabase/README.md +5 -0
  124. package/skills/fui-skill/design-md/superhuman/DESIGN.md +448 -0
  125. package/skills/fui-skill/design-md/superhuman/README.md +5 -0
  126. package/skills/fui-skill/design-md/tesla/DESIGN.md +286 -0
  127. package/skills/fui-skill/design-md/tesla/README.md +5 -0
  128. package/skills/fui-skill/design-md/theverge/DESIGN.md +339 -0
  129. package/skills/fui-skill/design-md/theverge/README.md +5 -0
  130. package/skills/fui-skill/design-md/together.ai/DESIGN.md +633 -0
  131. package/skills/fui-skill/design-md/together.ai/README.md +5 -0
  132. package/skills/fui-skill/design-md/uber/DESIGN.md +636 -0
  133. package/skills/fui-skill/design-md/uber/README.md +5 -0
  134. package/skills/fui-skill/design-md/vercel/DESIGN.md +736 -0
  135. package/skills/fui-skill/design-md/vercel/README.md +5 -0
  136. package/skills/fui-skill/design-md/vodafone/DESIGN.md +538 -0
  137. package/skills/fui-skill/design-md/vodafone/README.md +5 -0
  138. package/skills/fui-skill/design-md/voltagent/DESIGN.md +521 -0
  139. package/skills/fui-skill/design-md/voltagent/README.md +5 -0
  140. package/skills/fui-skill/design-md/warp/DESIGN.md +526 -0
  141. package/skills/fui-skill/design-md/warp/README.md +5 -0
  142. package/skills/fui-skill/design-md/webflow/DESIGN.md +588 -0
  143. package/skills/fui-skill/design-md/webflow/README.md +5 -0
  144. package/skills/fui-skill/design-md/wired/DESIGN.md +497 -0
  145. package/skills/fui-skill/design-md/wired/README.md +5 -0
  146. package/skills/fui-skill/design-md/wise/DESIGN.md +544 -0
  147. package/skills/fui-skill/design-md/wise/README.md +5 -0
  148. package/skills/fui-skill/design-md/x.ai/DESIGN.md +465 -0
  149. package/skills/fui-skill/design-md/x.ai/README.md +5 -0
  150. package/skills/fui-skill/design-md/zapier/DESIGN.md +537 -0
  151. package/skills/fui-skill/design-md/zapier/README.md +5 -0
  152. package/skills/fui-skill/examples/component.vue +162 -0
  153. package/skills/fui-skill/examples/f-table-patterns.json +331 -0
  154. package/skills/fui-skill/examples/module-patterns.json +973 -0
  155. package/skills/fui-skill/examples/project-patterns.json +222 -0
  156. package/skills/fui-skill/metadata.json +75 -0
  157. package/skills/fui-skill/references/INDEX.md +144 -0
  158. package/skills/fui-skill/references/advanced-techniques.md +160 -0
  159. package/skills/fui-skill/references/coding-standards.md +112 -0
  160. package/skills/fui-skill/references/component-design.md +455 -0
  161. package/skills/fui-skill/references/component-quickref.md +77 -0
  162. package/skills/fui-skill/references/component-table.md +276 -0
  163. package/skills/fui-skill/references/components-dialog.md +192 -0
  164. package/skills/fui-skill/references/components-display.md +147 -0
  165. package/skills/fui-skill/references/components-echart.md +391 -0
  166. package/skills/fui-skill/references/components-input.md +359 -0
  167. package/skills/fui-skill/references/controls-patterns.md +847 -0
  168. package/skills/fui-skill/references/controls-styling-vocabulary.md +140 -0
  169. package/skills/fui-skill/references/db-table-design.md +77 -0
  170. package/skills/fui-skill/references/db-workflow.md +504 -0
  171. package/skills/fui-skill/references/default-function.md +415 -0
  172. package/skills/fui-skill/references/design-modes.md +85 -0
  173. package/skills/fui-skill/references/echart-templates.md +481 -0
  174. package/skills/fui-skill/references/fastproject.md +97 -0
  175. package/skills/fui-skill/references/fsheet.md +218 -0
  176. package/skills/fui-skill/references/fullstack-workflow.md +351 -0
  177. package/skills/fui-skill/references/module-data-patterns.md +126 -0
  178. package/skills/fui-skill/references/module-json-anatomy.md +137 -0
  179. package/skills/fui-skill/references/module-structure.md +260 -0
  180. package/skills/fui-skill/references/new-session.md +108 -0
  181. package/skills/fui-skill/references/pdfmake.md +60 -0
  182. package/skills/fui-skill/references/permission-system.md +169 -0
  183. package/skills/fui-skill/references/platform-architecture.md +294 -0
  184. package/skills/fui-skill/references/project-config.md +335 -0
  185. package/skills/fui-skill/references/project-provisioning.md +383 -0
  186. package/skills/fui-skill/references/script-map.md +296 -0
  187. package/skills/fui-skill/references/sql-clr-functions.md +224 -0
  188. package/skills/fui-skill/references/system-design.md +116 -0
  189. package/skills/fui-skill/references/tapi-file-api.md +191 -0
  190. package/skills/fui-skill/references/tapi-permission-patterns.md +158 -0
  191. package/skills/fui-skill/references/tapi-reference.md +549 -0
  192. package/skills/fui-skill/references/tools-registry.md +460 -0
  193. package/skills/fui-skill/references/ui-crosswindow-patterns.md +317 -0
  194. package/skills/fui-skill/references/ui-dialog-patterns.md +229 -0
  195. package/skills/fui-skill/references/ui-layout-patterns.md +176 -0
  196. package/skills/fui-skill/references/ui-patterns.md +315 -0
  197. package/skills/fui-skill/references/ui-screenshot-review.md +94 -0
  198. package/skills/fui-skill/references/ui-table-cell-patterns.md +316 -0
  199. package/skills/fui-skill/references/ui-templates.md +29 -0
  200. package/skills/fui-skill/references/verification.md +246 -0
  201. package/skills/fui-skill/references/watcher-patterns.md +196 -0
  202. package/skills/fui-skill/references/websocket-realtime.md +254 -0
  203. package/skills/fui-skill/scripts/component-3.0.js +2549 -0
  204. package/skills/fui-skill/scripts/component.js +3142 -0
  205. package/skills/fui-skill/scripts/componentTable-3.0.js +909 -0
  206. package/skills/fui-skill/scripts/componentTable.js +769 -0
  207. package/skills/fui-skill/scripts/defaultfunction-3.0.js +781 -0
  208. package/skills/fui-skill/scripts/defaultfunction.js +966 -0
  209. package/skills/fui-skill/scripts/fastproject-3.0.js +870 -0
  210. package/skills/fui-skill/scripts/fastproject.js +828 -0
  211. package/skills/fui-skill/scripts/fechart.js +890 -0
  212. package/skills/fui-skill/scripts/fsheet.js +1330 -0
@@ -0,0 +1,296 @@
1
+ # FUI Script Map (Quick Lookup)
2
+
3
+ > File này sở hữu: **thư viện bundled sẵn (§0) và bản đồ source script FUI — hàm nào nằm ở file/dòng nào**. Cách dùng từng hàm xem [default-function.md](default-function.md).
4
+
5
+ Mục tiêu: tra cứu nhanh "hàm nào dùng để làm gì" theo từng file runtime trong `scripts/`.
6
+
7
+ > **Bản đồ file theo framework** — tra `_moduleInfo.json.Framework` (`"V2"` hoặc `"V3"`) rồi mở đúng cột:
8
+ >
9
+ > | Nội dung | File V2 | File V3 |
10
+ > |---|---|---|
11
+ > | Core engine (action, API, layout) | `scripts/fastproject.js` | `scripts/fastproject-3.0.js` |
12
+ > | Utility layer (router, export, auth, WS) | `scripts/defaultfunction.js` | `scripts/defaultfunction-3.0.js` |
13
+ > | Component registry (`f-*`, dialog, editor...) | `scripts/component.js` | `scripts/component-3.0.js` |
14
+ > | Table system (`f-table`, `t-*`) | `scripts/componentTable.js` | `scripts/componentTable-3.0.js` |
15
+ > | Biểu đồ (`f-echart`) | `scripts/fechart.js` | `scripts/fechart.js` |
16
+ > | Bảng tính nhập liệu (`f-sheet`) | `scripts/fsheet.js` (dùng chung, xem §5) | `scripts/fsheet.js` (dùng chung, xem §5) |
17
+
18
+ Lưu ý dùng đúng chuẩn FUI:
19
+ - Metadata chính là `module.json` (không dùng `controls.json`).
20
+ - Logic action gọi qua `CALL(vueData.actionName)` hoặc Action Protocol.
21
+ - Layout trong `controls` luôn theo grid wrapper `container > rows > cols`.
22
+ - Event trong JSON dùng `v-on:...` (không dùng `@...`).
23
+ - Literal forcing (`` ` `` or `'...'`) is for Action `IN` mapping, not normal static component props.
24
+
25
+ ## Table of Contents
26
+ - [0) Thư viện nhúng sẵn (FUI V2)](#0-thu-vien-nhung-san-fui-v2)
27
+ - [1) scripts/fastproject.js](#1-scriptsfastprojectjs)
28
+ - [2) scripts/defaultfunction.js](#2-scriptsdefaultfunctionjs)
29
+ - [3) scripts/component.js](#3-scriptscomponentjs)
30
+ - [4) scripts/componentTable.js](#4-scriptscomponenttablejs)
31
+ - [4b) scripts/componentTable-3.0.js](#4b-scriptscomponenttable-30js)
32
+ - [5) scripts/fsheet.js](#5-scriptsfsheetjs)
33
+ - [6) Tra cứu theo tác vụ](#6-tra-cuu-theo-tac-vu)
34
+
35
+ ## 0) Thư viện nhúng sẵn (FUI V2)
36
+
37
+ FUI tự động nhúng các thư viện sau vào **mọi** module — **không cần import thêm**. Luôn tận dụng
38
+ chúng thay vì thêm thư viện tương đương. **Không thêm CDN** cho bất kỳ thư viện nào trong danh sách
39
+ — thêm lại gây xung đột.
40
+
41
+ > ⛔ **Cả mục này CHỈ đúng với `HTMLOnly=false`.** Module `HTMLOnly: true` không được nhúng sẵn một
42
+ > byte nào — không Vue, không Vuetify, không jQuery, không lodash, không MDI, không
43
+ > `projectdefaultstyle.css`. Ở đó thêm CDN **không phải là trùng lặp** mà là điều kiện để trang chạy:
44
+ > xem [module-structure.md §HTMLOnly=true: KHÔNG có thư viện nào được nhúng sẵn](module-structure.md#htmlonlytrue-không-có-thư-viện-nào-được-nhúng-sẵn).
45
+
46
+ ### CSS (tự động load)
47
+
48
+ Roboto font · Material Design Icons (`mdi-*`) · Vuetify CSS · vue-toast-notification CSS · jquery-confirm CSS · FUI projectdefaultstyle.css
49
+
50
+ ### JavaScript globals
51
+
52
+ | Global | Thư viện | Công dụng |
53
+ |---|---|---|
54
+ | `$`, `jQuery` | jQuery 3.6.0 | DOM, AJAX |
55
+ | `$.confirm()` / `$.dialog()` | jquery-confirm | Dialog xác nhận / thông báo có style (thay cho `window.confirm`) |
56
+ | `_` | Lodash 4.17 | Utility: sort, filter, group, clone array/object |
57
+ | `moment` | Moment.js (vi) | Parse và format ngày giờ, locale tiếng Việt sẵn |
58
+ | `numeral` | Numeral.js | Format số và tiền tệ |
59
+ | `Vue.$toast` | vue-toast-notification | Toast thông báo góc màn hình |
60
+
61
+ ### FUI built-in JS (luôn load sau các thư viện trên)
62
+
63
+ `defaultfunction.js` — hàm tiện ích FUI (`jsonToExcel`, `pushRouter`, `openWindow`, `CALL`, `confirm`, `showMessage`, ...)
64
+ `component.js` — đăng ký toàn bộ FUI components (`f-table`, `f-form`, `f-echart`, ...)
65
+ `componentTable.js` — FUI table helpers · `fastproject.js` — FUI project bootstrapper
66
+
67
+ ### Ví dụ dùng phổ biến trong FUI
68
+
69
+ ```js
70
+ // Toast thông báo
71
+ Vue.$toast.success('Lưu thành công', { position: 'top' });
72
+ Vue.$toast.error('Có lỗi xảy ra', { position: 'top' });
73
+
74
+ // Dialog xác nhận (FUI wrapper — dùng hàm confirm() từ defaultfunction.js)
75
+ confirm({
76
+ title: 'Xác nhận xóa',
77
+ message: 'Bạn có chắc muốn xóa không?',
78
+ action: function() { /* xử lý khi OK */ }
79
+ });
80
+
81
+ // Dialog thông báo (FUI wrapper)
82
+ showMessage({ title: 'Thông báo', message: 'Đã lưu thành công' });
83
+
84
+ // Lodash — dùng trong script.js / vueData
85
+ _.find(vueData.list, { id: 5 })
86
+ _.groupBy(vueData.data, 'loai')
87
+ _.sumBy(items, 'soLuong')
88
+ _.cloneDeep(vueData.formData)
89
+ _.debounce(fn, 300)
90
+
91
+ // Moment — format ngày
92
+ moment(val).format('DD/MM/YYYY')
93
+ moment(val).format('DD/MM/YYYY HH:mm')
94
+ moment().subtract(7, 'days').format('YYYY-MM-DD')
95
+
96
+ // Numeral — format số
97
+ numeral(value).format('0,0') // 1,234,567
98
+ numeral(value).format('0,0.0') // 1,234,567.8
99
+ numeral(_.sumBy(items, key)).format('0,0') // tổng cột trong bảng
100
+ ```
101
+
102
+ > Component `f-*` nào cần import thêm JS/CSS ngoài danh sách trên: xem
103
+ > [component-quickref.md](component-quickref.md) §Components cần import JS/CSS.
104
+
105
+ ## 1) scripts/fastproject.js — V3: fastproject-3.0.js
106
+
107
+ Vai trò: core engine để khởi tạo module, dựng DOM từ `module.json.controls`, chạy action, map data và gọi API.
108
+
109
+ Luồng khởi tạo chính:
110
+ 1. `$(document).ready` -> `loadModuleInfo()`
111
+ 2. `loadModuleInfo()` -> merge config + đọc URL/user info
112
+ 3. `createModuleDom()` -> `runAction($moduleUI.data)` + render controls
113
+ 4. `createWatch()` -> gắn watcher từ `$moduleUI.watch`
114
+ 5. Vue instance mount và module chạy
115
+
116
+ | Line | Hàm | Dùng để làm gì |
117
+ |---:|---|---|
118
+ | 65 | `buildParamURL()` | Đọc query string và đẩy params vào `vueData`. |
119
+ | 77 | `loadModuleInfo()` | Nạp config project/module, user info, chuẩn bị tạo app. |
120
+ | 122 | `createModuleDom()` | Tạo layout Vue chính, render controls, init watch. |
121
+ | 210 | `buildModuleUI(controlsList, target)` | Recursively build UI từ JSON controls. |
122
+ | 233 | `buildControl(controlsList, flex, target)` | Render từng control, bind attr/event/import. |
123
+ | 299 | `addImport(importArr)` | Nạp script/css phụ thuộc theo control. |
124
+ | 310 | `createWatch_Data(obj)` | Khởi tạo dữ liệu cho watch config. |
125
+ | 320 | `createWatch(obj)` | Gắn watcher Vue theo config `watch`. |
126
+ | 345 | `buildVisualValForObj(controlAttr, target)` | Bind giá trị động vào attr control. |
127
+ | 373 | `runAction(obj, includeData)` | Action engine tổng: chạy object/array action. |
128
+ | 407 | `vueAction(objAction, includeData, callBack)` | Chạy 1 action cụ thể (IF/API/CALL/EXE/...). |
129
+ | 485 | `mapData(map, target, src, includeData)` | Map dữ liệu giữa object theo rule FUI. |
130
+ | 527 | `defProp(obj)` | Chuẩn hoá property object theo context. |
131
+ | 537 | `bindData(obj)` | Resolve biến động (string/object) từ context hiện tại. |
132
+ | 542 | `getVueData(key, src)` | Lấy value theo path (`vueData.x.y`, `item.x`, template...). Literal forcing is for `IN` mapping; static component props without `:` are already plain strings. |
133
+ | 578 | `setValue(target, key, value, srcData, index)` | Gán dữ liệu theo path vào target object/window. |
134
+ | 612 | `bindDataToString(str, data)` | Resolve template string với dữ liệu runtime. |
135
+ | 619 | `templateCompiled(str, data)` | Compile template lodash-style cho chuỗi động. |
136
+ | 624 | `getWindowData(value)` | Lấy dữ liệu từ window/iframe context. |
137
+ | 638 | `setWindowData(key, value)` | Set dữ liệu cho window/iframe context. |
138
+ | 657 | `runFunction(obj, callback)` | Gọi function JS đã khai báo theo action config. |
139
+ | 665 | `tableActionEvent(ctrl)` | Xử lý action event liên quan table/control. |
140
+ | 687 | `callAPI(objApi, callBack, includeData)` | Gọi API theo cấu hình action (`IN/OUT/CALLBACK`). |
141
+ | 743 | `loginFUN()` | Luồng đăng nhập/chuyển hướng login. |
142
+ | 761 | `errorMess(code, message)` | Hiển thị lỗi chuẩn theo status/message. |
143
+
144
+ ## 2) scripts/defaultfunction.js — V3: defaultfunction-3.0.js
145
+
146
+ Vai trò: utility layer dùng chung cho runtime (router, messaging, data transform, AJAX, export, auth, websocket...).
147
+
148
+ | Line | Hàm | Dùng để làm gì |
149
+ |---:|---|---|
150
+ | 35 | `extractHostname(url)` | Lấy hostname từ URL. |
151
+ | 53 | `getDomainWithoutSubdomain(url)` | Lấy domain gốc không subdomain. |
152
+ | 62 | `loadScripts(scriptsArray, callbackFunc)` | Nạp nhiều script động rồi callback. |
153
+ | 75 | `pushRouter(router)` | Push route vào browser history. |
154
+ | 82 | `CALL(obj, includeData)` | Alias gọi `runAction(...)`. |
155
+ | 86 | `windowSendMessage(window, cmd, data)` | `postMessage` sang parent/window/iframe. |
156
+ | 100 | `appCommand(cmdData)` | Gửi lệnh về native wrapper (RN/WebKit). |
157
+ | 106 | `buildHeader(obj, mapCol)` | Tạo header table từ dataset/map cột. |
158
+ | 130 | `fillData(obj)` | Fill/map dữ liệu từ source sang destination. |
159
+ | 162 | `groupBy(arrayObj, groupField, sumArr)` | Group mảng theo field + cộng tổng cột. |
160
+ | 188 | `chartDataBuild(obj)` | Chuẩn hoá data cho chart tổng quát. |
161
+ | 219 | `chartQABuild(obj)` | Build data chart chuyên QA/report. |
162
+ | 239 | `confirm(obj)` | Hiển thị dialog confirm. |
163
+ | 259 | `showMessage(obj)` | Hiển thị dialog/toast message. |
164
+ | 283 | `rightTest(rightObject)` | Kiểm tra quyền theo user rights. |
165
+ | 301 | `openWindow(obj)` | Mở popup/dialog window FUI. |
166
+ | 326 | `redirect(obj, query)` | Điều hướng trang theo URL/config. |
167
+ | 339 | `reload()` | Reload trang hiện tại. |
168
+ | 343 | `findInArray(obj)` | Tìm phần tử theo điều kiện giá trị. |
169
+ | 348 | `fixURL(url)` | Chuẩn hoá URL (ghép domain API nếu cần). |
170
+ | 354 | `stringAttrToJson(str, removeVueEvent)` | Parse chuỗi attr HTML thành JSON object. |
171
+ | 371 | `json_data_parse(obj, level)` | Parse đệ quy field có prefix `json_data:`. |
172
+ | 407 | `ajaxCALL(URL, DATA, callBack, errorCallBack, header)` | Gọi AJAX POST helper. |
173
+ | 449 | `capacityText(numb)` | Format dung lượng bytes -> KB/MB text. |
174
+ | 454 | `generateID()` | Sinh ID ngẫu nhiên. |
175
+ | 467 | `printPDF(obj)` | Render/in/download PDF bằng `pdfMake`. |
176
+ | 499 | `colorLib(color)` | Bộ công cụ convert/manipulate màu. |
177
+ | 645 | `transparentize(value, opacity)` | Tạo màu trong suốt (alpha). |
178
+ | 650 | `jsonToExcel(Obj)` | Xuất dữ liệu JSON ra Excel. |
179
+ | 746 | `copyToClipboard(textToCopy)` | Copy text vào clipboard. |
180
+ | 770 | `fileNameClear(fname)` | Làm sạch tên file không hợp lệ. |
181
+ | 775 | `hashCode(s)` | Sinh hash code từ string. |
182
+ | 785 | `getCookie(cname)` | Đọc cookie theo tên. |
183
+ | 800 | `setCookie(name, value, days, domain)` | Set cookie có expiry/domain. |
184
+ | 814 | `logout(cookiesName, domain, url)` | Xoá cookie và logout/redirect. |
185
+ | 827 | `webSocketJoinGroup(groupObj, timeout, callBackFunc)` | Join WS group theo payload. |
186
+ | 851 | `webSocketConnection(wsURL, WS, wsState)` | Khởi tạo/duy trì WS connection. |
187
+ | 892 | `webSocket_Send(obj)` | Gửi payload qua websocket hiện tại. |
188
+
189
+ > **Real-time messaging (SignalR)**: hướng dẫn dùng đầy đủ 3 hàm trên — import bắt buộc, khai báo group trong `data[]`, nhận/gửi tin nhắn, ví dụ `module.json` hoàn chỉnh — xem [websocket-realtime.md](websocket-realtime.md).
190
+
191
+ ## 3) scripts/component.js — V3: component-3.0.js
192
+
193
+ Vai trò: đăng ký FUI base components và các component nghiệp vụ (dialog, editor, PDF, excel, chart, media...).
194
+
195
+ Ghi chú:
196
+ - File này cũng chứa `defaultControlAttr` để set attr mặc định theo từng `el`.
197
+ - Nhiều component yêu cầu import ngoài (CKEditor, pdfMake, cropper, chart, ...).
198
+
199
+ | Line | Component | Dùng để làm gì |
200
+ |---:|---|---|
201
+ | 304 | `f-label` | Hiển thị label/text theo format FUI. |
202
+ | 319 | `f-box` | Khối hiển thị dữ liệu dạng box/card đơn giản. |
203
+ | 377 | `f-header` | Header block nhẹ cho section/module. |
204
+ | 386 | `f-title` | Title component chuẩn UI. |
205
+ | 395 | `f-radiobox` | Radio group wrapper. |
206
+ | 425 | `f-menu` | Menu list/navigation control. |
207
+ | 472 | `f-search` | Input tìm kiếm dùng lại. |
208
+ | 536 | `f-button` | Button wrapper chuẩn attr FUI. |
209
+ | 607 | `f-slider` | Slider input/display. |
210
+ | 618 | `f-file-upload` | Upload file cơ bản. |
211
+ | 768 | `f-qrcode` | Render QR code. |
212
+ | 843 | `f-qrcode-reader` | Quét/đọc QR code. |
213
+ | 914 | `f-image-update` | Dialog crop/rotate/upload ảnh. |
214
+ | 1161 | `f-date` | Date picker wrapper. |
215
+ | 1434 | `f-time` | Time picker wrapper. |
216
+ | 1639 | `f-time-counter` | Counter/countdown/clock hiển thị thời gian. |
217
+ | 1775 | `fp-profile` | User profile menu trên header. |
218
+ | 2020 | `header-bar` | Thanh header/menu chính của app/module. |
219
+ | 2277 | `f-chart` | Biểu đồ bản cũ dựng trên canvas Chart.js (props `data`/`type`/`options`). Bản đang dùng là `f-echart` — xem [components-echart.md](components-echart.md). |
220
+ | 2389 | `f-window` | Dynamic window/dialog host. |
221
+ | 2453 | `f-editor` | WYSIWYG editor wrapper (CKEditor). |
222
+ | 2591 | `f-editor-dialog` | Dialog chứa editor toàn màn hình. |
223
+ | 2687 | `f-dialog` | Dialog dynamic dựng form từ `controls`. |
224
+ | 2899 | `f-pdfmake` | Viewer/render PDF bằng `printPDF`. |
225
+ | 2934 | `f-excel-reader` | Upload/đọc Excel/CSV → mảng object: chọn sheet, bỏ N dòng đầu, đặt tên key, giới hạn dung lượng; XLSX lazy-load. Xem [components-input.md](components-input.md). |
226
+
227
+ ## 4) scripts/componentTable.js — V3: componentTable-3.0.js (xem §4b)
228
+
229
+ Vai trò: table system cho FUI, gồm component table chính và cell renderer `t-*`.
230
+
231
+ | Line | Component | Dùng để làm gì |
232
+ |---:|---|---|
233
+ | 1 | `f-table` | Data table duy nhất (CRUD lẫn readonly): search, select, CRUD (`update-api`, `update-form`), export, auto-build headers khi không truyền `headers`, sum row/sum line qua `sumFormat` (object/string), `:height` (xem bên dưới). |
234
+ | 416 | `t-html` | Render ô dạng HTML. |
235
+ | 420 | `t-label` | Render text label thường. |
236
+ | 424 | `t-num` | Render/format số. |
237
+ | 442 | `t-time` | Render/format thời gian. |
238
+ | 452 | `t-boolean` | Render bool bằng icon/màu. |
239
+ | 473 | `t-check` | Cell checkbox có thể tương tác. |
240
+ | 501 | `t-text` | Cell text input inline. |
241
+ | 523 | `t-select` | Cell select inline. |
242
+ | 544 | `t-combobox` | Cell combobox/autocomplete inline. |
243
+ | 616 | `t-menu` | Cell menu action. |
244
+ | 654 | `t-link` | Cell link điều hướng/open dialog. |
245
+ | 678 | `t-button` | Cell button action custom. |
246
+
247
+ **`:height` (`f-table`)** — computed `heightNum`/`tableHeight` + method `updateFillHeight()`: bỏ trống = tự giãn theo số dòng; `<=0` = đo `getBoundingClientRect()` thật rồi fill tới đáy màn hình chừa `abs(n)` px (có `window.resize` listener); `>0` = px cố định.
248
+
249
+ ## 4b) scripts/componentTable-3.0.js
250
+
251
+ Bản **V3** (Vue 3 + Vuetify 3) của cùng bộ component trên — tên đăng ký PascalCase (`FTable`, `THtml`, `TNum`, ...). Framework version của module quyết định file nào được nạp (`_moduleInfo.json`).
252
+
253
+ Khác biệt phải nhớ khi sửa **cả hai** file:
254
+ - Lifecycle: V3 dùng `beforeUnmount` (các component khai cả hai để chạy được ở cả V2 lẫn V3).
255
+ - Class Vuetify: V3 là `text-primary` / `text-subtitle-1` / `text-caption`, V2 là `primary--text` / `subtitle-1` / `caption`; `v-btn outlined` (V2) → `variant="outlined"` (V3).
256
+ - Wrapper cuộn của `v-data-table`: V2 `.v-data-table__wrapper`, V3 `.v-table__wrapper`.
257
+ - Tên biến/computed **không được bắt đầu bằng `_`** nếu bind thẳng trong template V3 (proxy `has` trap chặn) — V3 dùng hậu tố kiểu `hideDefaultFooter_cacl`.
258
+ - `headers` của `f-table`: V2 dùng `{ text, value }`, V3 dùng `{ title, key }` (chuẩn Vuetify 3); slot `body.append` V2 nhận `{ headers }`, V3 nhận `{ columns }`.
259
+
260
+ Kiểm chứng bản V3 sau khi sửa: `node test/v3-table-harness.mjs [sum|fill|auto]` — nạp framework V3 thật từ `https://fui.vn` trong chromium headless nhưng **thay `componentTable-3.0.js` bằng file local**, mọi API bị chặn + trả mock. In ra lỗi JS/`[Vue warn]`, chiều cao thật, nội dung dòng tổng, ô AG Grid, và chụp `test/v3-{mode}.png`.
261
+
262
+ ## 5) scripts/fsheet.js — dùng chung V2 + V3
263
+
264
+ Vai trò: toàn bộ component `f-sheet`, một file **duy nhất** dùng chung cho cả V2 và V3 (khai cả `beforeDestroy` lẫn `beforeUnmount` để chạy được ở cả hai runtime) — không tách theo framework version như các file khác trong bảng ở đầu tài liệu.
265
+
266
+ Import bắt buộc (2 file, trích từ `component.js` `defaultControlAttr["f-sheet"].import`), **đúng thứ tự**:
267
+ ```
268
+ /include/ag-grid/ag-grid-community.min.js
269
+ /include/ag-grid/fsheet.js
270
+ ```
271
+
272
+ | Nội dung | Ghi chú |
273
+ |---|---|
274
+ | `Vue.component('FSheet', {...})` | Component chính — props/cột/wiring API chi tiết ở [fsheet.md](fsheet.md) |
275
+ | 4 cell renderer dựng sẵn (`fsHtmlCellRenderer`/`fsLinkCellRenderer`/`fsButtonCellRenderer`/`fsMenuCellRenderer`) | Tương đương `t-html`/`t-link`/`t-button`/`t-menu` của `f-table` — xem [fsheet.md](fsheet.md) |
276
+ | Hệ thống chọn vùng nhiều ô + fill handle + clipboard TSV tự viết | AG Grid Community không có Cell Selection (thuộc bản Enterprise) nên f-sheet tự điều khiển rồi gắn đúng bộ class CSS của AG Grid (`.ag-cell-range-*`, `.ag-selection-fill-*`, `.ag-fill-handle`) |
277
+
278
+ `:height` dùng cùng 3 chế độ (tự giãn / fill đáy / px cố định) — chi tiết: [fsheet.md](fsheet.md) §Chiều cao.
279
+
280
+ Kiểm chứng `fsheet.js`: `component_preview` (render `f-sheet` cô lập với dữ liệu mẫu, không cần push lên server) hoặc `module_simulate({renderUI:true})` (kiểm trong module thật) — cả hai chạy browser thật, không cần harness riêng.
281
+
282
+ ## 6) Tra cứu theo tác vụ
283
+
284
+ > Cột "Mở file trước" ghi tên file V2. Nếu module là **V3**, thay bằng file `-3.0` tương ứng (xem quy tắc ở đầu tài liệu).
285
+
286
+ | Tác vụ cần làm | Mở file trước (V2 / V3) | Xem mục chính |
287
+ |---|---|---|
288
+ | Module không render đúng layout | `fastproject.js` / `fastproject-3.0.js` | `buildModuleUI`, `buildControl`, `bindData`. |
289
+ | Action không chạy hoặc chạy sai nhánh | `fastproject.js` / `fastproject-3.0.js` | `runAction`, `vueAction`, `mapData`, `getVueData`. |
290
+ | API không ra dữ liệu `OUT` | `fastproject.js` + `defaultfunction.js` (cùng bản V2/V3) | `callAPI`, `fixURL`, `ajaxCALL`. |
291
+ | Table CRUD lỗi Add/Edit/Delete | `componentTable.js` / `componentTable-3.0.js` | `f-table` + `update-api`, `update-form`. |
292
+ | Dialog dynamic không bind dữ liệu | `component.js` / `component-3.0.js` | `f-dialog`, `buildModuleUI` (ở fastproject). |
293
+ | Upload ảnh, crop, lưu file | `component.js` / `component-3.0.js` | `f-image-update`. |
294
+ | Export Excel/PDF | `defaultfunction.js` + `component.js` (cùng bản V2/V3) | `jsonToExcel`, `printPDF`, `f-pdfmake`, `f-excel-reader`. |
295
+ | Sự cố đăng nhập/quyền/menu | `fastproject.js` + `defaultfunction.js` (cùng bản V2/V3) | `loadModuleInfo`, `loginFUN`, `rightTest`. |
296
+ | `f-sheet` không hiện / báo "FSheet is not defined" | `fsheet.js` (dùng chung V2+V3) | Thiếu import — xem [component-quickref.md](component-quickref.md) hoặc [fsheet.md](fsheet.md). |
@@ -0,0 +1,224 @@
1
+ # SQL CLR Functions — Hàm mở rộng nhúng DLL trong SQL Server
2
+
3
+ > File này sở hữu: **hàm SQL CLR: httpCall, sendMail, ImageResize, FileReader, RegexMatch...**
4
+
5
+ Database của project có thể đã cài sẵn các **SQL CLR scalar function** (hàm SQL được backing bởi DLL .NET biên dịch sẵn, đăng ký qua `CREATE ASSEMBLY` + `CREATE FUNCTION ... EXTERNAL NAME`). Khi thấy các hàm này trong `db_schema_read` hoặc trong định nghĩa SP hiện có, có thể **gọi trực tiếp trong T-SQL** như hàm built-in bình thường — không cần cài đặt lại, không cần biết code C# bên trong.
6
+
7
+ Nếu hàm cần dùng không có trong danh sách dưới đây (không tồn tại trong DB), **không tự bịa ra** — báo cho user biết là DB chưa có hàm này.
8
+
9
+ ---
10
+
11
+ ## 1. `SQLImageHelper` — Xử lý ảnh
12
+
13
+ ### `dbo.ImageResize`
14
+
15
+ ```sql
16
+ dbo.ImageResize(@extension nvarchar(10), @OriginalImage varbinary(max), @maxWidth int, @maxHeight int, @preserverAspectRatio bit, @quality int)
17
+ RETURNS varbinary(max)
18
+ ```
19
+
20
+ Resize ảnh nhị phân về kích thước tối đa `@maxWidth` x `@maxHeight`.
21
+
22
+ | Tham số | Ý nghĩa |
23
+ |---|---|
24
+ | `@extension` | Định dạng ảnh output, ví dụ `'jpg'`, `'png'` |
25
+ | `@OriginalImage` | Dữ liệu ảnh gốc dạng `varbinary` |
26
+ | `@maxWidth`, `@maxHeight` | Kích thước tối đa (px) |
27
+ | `@preserverAspectRatio` | `1` = giữ tỉ lệ khung hình, resize theo cạnh dài nhất; `0` = resize ép đúng `maxWidth`x`maxHeight` |
28
+ | `@quality` | Chất lượng nén (thường 1-100) |
29
+
30
+ Trả về `varbinary` — ảnh đã resize, ghi thẳng vào cột `varbinary`/file hoặc trả qua tAPI File.
31
+
32
+ ### `dbo.ImageCrop`
33
+
34
+ ```sql
35
+ dbo.ImageCrop(@OriginalImage varbinary(max), @StartAtX int, @StartAtY int, @Width int, @Height int)
36
+ RETURNS varbinary(max)
37
+ ```
38
+
39
+ Cắt (crop) một vùng chữ nhật từ ảnh gốc, bắt đầu tại `(@StartAtX, @StartAtY)`, kích thước `@Width` x `@Height`.
40
+
41
+ ---
42
+
43
+ ## 2. `SQLHttpHelper` — HTTP call, public value store, gửi mail
44
+
45
+ ### `dbo.publicValueUUID` / `publicValueGet` / `publicValueSet`
46
+
47
+ Một key-value store dùng chung ở tầng server (không phải bảng SQL) — hữu ích để lưu tạm state xuyên suốt nhiều lần gọi SP mà không cần tạo bảng.
48
+
49
+ ```sql
50
+ dbo.publicValueUUID() RETURNS nvarchar(max) -- sinh 1 UUID mới, dùng làm key
51
+ dbo.publicValueGet(@name nvarchar(max)) RETURNS nvarchar(max) -- đọc value theo key
52
+ dbo.publicValueSet(@name nvarchar(max), @Data nvarchar(max)) RETURNS int -- ghi value theo key
53
+ ```
54
+
55
+ ### `dbo.httpCall` / `dbo.httpAPI` — Gọi HTTP request từ T-SQL
56
+
57
+ ```sql
58
+ dbo.httpCall(@Method nvarchar(max), @URL nvarchar(max), @Header nvarchar(max), @Data nvarchar(max))
59
+ RETURNS nvarchar(max)
60
+ ```
61
+
62
+ `httpAPI` có chữ ký tham số **giống hệt** `httpCall` (khác implementation nội bộ — dùng khi cần gọi API nội bộ tAPI, còn `httpCall` dùng cho HTTP tổng quát ra ngoài).
63
+
64
+ | Tham số | Ý nghĩa |
65
+ |---|---|
66
+ | `@Method` | `'GET'`, `'POST'`, `'PUT'`, `'DELETE'`... (không phân biệt hoa thường, mặc định `GET` nếu rỗng) |
67
+ | `@URL` | URL đầy đủ, phải là absolute URL hợp lệ |
68
+ | `@Header` | Danh sách header, xem cú pháp bên dưới — truyền `''` nếu không cần custom header |
69
+ | `@Data` | Body request dạng JSON string — **chỉ được gửi khi Method khác GET/HEAD**; luôn gửi dưới dạng `Content-Type: application/json` trừ khi `@Header` override lại |
70
+
71
+ **Ví dụ:**
72
+
73
+ ```sql
74
+ SELECT dbo.httpCall('POST', 'https://tapi.example.vn/me/auth/ApplicationList', '', '')
75
+ ```
76
+
77
+ #### Cú pháp `@Header` — pipe-delimited `Key: Value`
78
+
79
+ Đây là phần quan trọng nhất khi dùng `httpCall`/`httpAPI`. `@Header` là một chuỗi duy nhất, mỗi cặp header cách nhau bởi `|`, mỗi cặp có dạng `Key: Value` (tách bằng dấu `:` đầu tiên, phần còn lại — kể cả nếu chứa `:` — thuộc về value):
80
+
81
+ ```
82
+ "Authorization: Bearer eyJhbGciOi...|X-Custom-Header: abc123"
83
+ ```
84
+
85
+ Implementation (C#) xử lý như sau — **không phân biệt hoa/thường tên header**, và một số tên có xử lý đặc biệt:
86
+
87
+ | Header name | Hành vi |
88
+ |---|---|
89
+ | `Authorization`, `X-*`, mọi tên khác | Add thẳng vào request headers (`TryAddWithoutValidation`) — dùng cho token, API key |
90
+ | `referer` / `referrer` | Parse thành URI, set `request.Headers.Referrer` |
91
+ | `user-agent` | Ghi đè `User-Agent` mặc định |
92
+ | `accept` | Ghi đè `Accept` |
93
+ | `accept-language` | Ghi đè `Accept-Language` |
94
+ | `accept-encoding` | Ghi đè `Accept-Encoding` |
95
+ | `content-type` | Nếu request có body (`Method` khác GET/HEAD) → set vào `Content.Headers.Content-Type` (ghi đè `application/json` mặc định); nếu không có body → set vào request headers thường |
96
+ | Bất kỳ header nào bắt đầu bằng `content-` (khác `content-type`) | Nếu có body → route vào `Content.Headers` thay vì request headers (theo chuẩn HTTP — các header `Content-*` mô tả nội dung body) |
97
+
98
+ **Ví dụ set Bearer token + custom header:**
99
+
100
+ ```sql
101
+ SELECT dbo.httpCall(
102
+ 'POST',
103
+ 'https://api.example.com/data',
104
+ 'Authorization: Bearer abc123xyz|Content-Type: application/json; charset=utf-8',
105
+ '{"key":"value"}'
106
+ )
107
+ ```
108
+
109
+ **Lưu ý khi ghép nhiều header:** không được có thêm dấu `|` thừa hoặc thiếu — mỗi cặp phải tách rõ bằng đúng 1 dấu `|`, thiếu `:` trong 1 cặp thì cặp đó bị bỏ qua (không lỗi, chỉ silent skip).
110
+
111
+ #### Response format của `httpCall`/`httpAPI`
112
+
113
+ - Nếu HTTP status = **200**: trả về **nguyên văn body** của response (không bọc thêm gì) — có thể là JSON, text thuần, hoặc bất kỳ định dạng nào server trả về.
114
+ - Nếu HTTP status **khác 200** (400, 401, 404, 500...): trả về JSON dạng bọc lỗi:
115
+ ```json
116
+ {"httpErrorCode": 404, "responseData": "<nội dung body, hoặc escaped string nếu không phải JSON>"}
117
+ ```
118
+ → khi parse kết quả trong SP, luôn kiểm tra xem response có field `httpErrorCode` không để biết là lỗi hay thành công.
119
+ - Nếu URL không hợp lệ: trả về `{"httpErrorCode":0,"responseData":"Invalid URL"}`.
120
+ - Timeout/exception khác: trả message lỗi dạng text (không phải JSON), nên khi dùng trong SP cần tự kiểm tra format trước khi `OPENJSON`.
121
+
122
+ ### `dbo.sendMail`
123
+
124
+ ```sql
125
+ dbo.sendMail(@email_to nvarchar(max), @subject nvarchar(max), @content nvarchar(max), @from nvarchar(max), @fromDisplayName nvarchar(max), @smtp_server nvarchar(max), @smtp_port int, @userName nvarchar(max), @Password nvarchar(max), @EnableSSL bit)
126
+ RETURNS nvarchar(max)
127
+ ```
128
+
129
+ Gửi email qua SMTP trực tiếp từ T-SQL.
130
+
131
+ | Tham số | Ý nghĩa |
132
+ |---|---|
133
+ | `@email_to` | Địa chỉ người nhận |
134
+ | `@subject` | Tiêu đề mail |
135
+ | `@content` | Nội dung mail (HTML hoặc text) |
136
+ | `@from` | Địa chỉ email gửi (phải khớp/được phép bởi SMTP account) |
137
+ | `@fromDisplayName` | Tên hiển thị người gửi |
138
+ | `@smtp_server`, `@smtp_port` | Ví dụ `'smtp.gmail.com'`, `587` |
139
+ | `@userName`, `@Password` | Tài khoản SMTP dùng để auth |
140
+ | `@EnableSSL` | `1` = bật SSL/TLS (bắt buộc với Gmail SMTP) |
141
+
142
+ ```sql
143
+ SELECT dbo.sendMail('user@example.vn','Tiêu đề','Nội dung','noreply@example.vn','My System','smtp.gmail.com',587,'noreply@example.vn','app-password',1)
144
+ ```
145
+
146
+ > **Bảo mật:** không hardcode `@Password` SMTP trực tiếp trong SP nếu tránh được — cân nhắc lưu qua `publicValueSet`/`publicValueGet` hoặc config bảng riêng có phân quyền, để không lộ credential khi ai đó xem definition của SP.
147
+
148
+ ---
149
+
150
+ ## 3. `SQLFileSystem` — Đọc/ghi file trên server
151
+
152
+ ```sql
153
+ dbo.FileCreateFolder(@Path nvarchar(max)) RETURNS nvarchar(max)
154
+ dbo.FileReader(@Path nvarchar(max)) RETURNS varbinary(max)
155
+ dbo.FileWriter(@Content varbinary(max), @Path nvarchar(max)) RETURNS nvarchar(max)
156
+ dbo.FileExists(@Path nvarchar(max)) RETURNS bit
157
+ dbo.FileDelete(@Path nvarchar(max)) RETURNS nvarchar(max)
158
+ dbo.FileMove(@SourceFileName nvarchar(max), @DestFileName nvarchar(max)) RETURNS nvarchar(max)
159
+ ```
160
+
161
+ Thao tác trực tiếp trên filesystem của **máy SQL Server** (không phải máy client) — `@Path` là đường dẫn tuyệt đối trên server đó. Dùng khi SP cần lưu file kết quả (ví dụ ảnh đã resize bằng `ImageResize`, PDF xuất ra) vào một thư mục cố định để tAPI File API hoặc IIS phục vụ lại.
162
+
163
+ | Hàm | Ý nghĩa |
164
+ |---|---|
165
+ | `FileCreateFolder` | Tạo thư mục (kể cả nested), trả message kết quả |
166
+ | `FileReader` | Đọc toàn bộ nội dung file, trả `varbinary` |
167
+ | `FileWriter` | Ghi `@Content` (varbinary) vào `@Path`, tạo mới hoặc ghi đè |
168
+ | `FileExists` | Kiểm tra tồn tại, trả `bit` |
169
+ | `FileDelete` | Xóa file |
170
+ | `FileMove` | Di chuyển/đổi tên file từ `@SourceFileName` sang `@DestFileName` |
171
+
172
+ > Vì đây là thao tác trên đĩa server thật, cân nhắc quyền hạn (permission check trong SP) trước khi cho phép `FileDelete`/`FileWriter` chạy theo input từ client — tương tự nguyên tắc guard DDL/DML đã áp dụng cho `db_sql_execute_nonquery`.
173
+
174
+ ---
175
+
176
+ ## 4. `SQLExpansionFunction` — Regex & so sánh chuỗi
177
+
178
+ ```sql
179
+ dbo.RegexReplace(@Input nvarchar(max), @Pattern nvarchar(1000), @Replacement nvarchar(4000)) RETURNS nvarchar(max)
180
+ dbo.RegexIsMatch(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS bit
181
+ dbo.RegexMatch(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS nvarchar(max)
182
+ dbo.RegexMatchArray(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS nvarchar(max)
183
+ dbo.CompareWord(@String1 nvarchar(max), @String2 nvarchar(max)) RETURNS int
184
+ ```
185
+
186
+ | Hàm | Ý nghĩa |
187
+ |---|---|
188
+ | `RegexReplace` | Thay thế theo regex `.NET` — dùng khi T-SQL `REPLACE` không đủ (cần pattern, không phải literal string) |
189
+ | `RegexIsMatch` | Kiểm tra `@Input` có khớp `@Pattern` không, trả `bit` — dùng validate format (email, phone, mã số...) ngay trong SP |
190
+ | `RegexMatch` | Trả về **match đầu tiên** tìm được (`Match.Value` — toàn bộ chuỗi khớp, không tách group), rỗng nếu không khớp |
191
+ | `RegexMatchArray` | Trả về **tất cả match, kèm từng capture group**, dạng **JSON object phẳng** (không phải array) — xem cấu trúc chi tiết bên dưới |
192
+ | `CompareWord` | So sánh độ giống nhau giữa 2 chuỗi, trả `int` (thường là số ký tự khác biệt hoặc similarity score — kiểm tra thực tế qua `SELECT dbo.CompareWord('a','b')` nếu cần biết thang đo chính xác) |
193
+
194
+ `@Pattern` dùng cú pháp regex chuẩn .NET (`\d`, `\w`, `^$`, group `()`...), không phải T-SQL `LIKE` pattern. Cả 4 hàm regex đều **luôn bật cố định** `IgnoreCase | Multiline | Singleline` — nghĩa là mặc định **không phân biệt hoa/thường**, `^`/`$` khớp theo từng dòng, và `.` khớp cả ký tự xuống dòng — không có cách nào tắt các flag này qua tham số.
195
+
196
+ #### Cấu trúc thật của `RegexMatchArray` — JSON object, không phải array
197
+
198
+ Dựa theo code C#, kết quả là một **object phẳng** (`{...}`), không phải `[...]`. Key có dạng `"M{m}G{g}"`:
199
+
200
+ - `m` = số thứ tự match (match thứ 1, 2, 3... trong `@Input`, đánh số từ 1)
201
+ - `g` = số thứ tự group **trong chính match đó** (đánh số từ 1): `G1` luôn là **toàn bộ chuỗi khớp** (tương đương `Match.Groups[0]`, cũng là giá trị mà `RegexMatch` trả về), `G2`, `G3`... là các **capture group** `(...)` trong `@Pattern`, nếu có.
202
+ - Nếu không có match nào, trả về object rỗng `"{}"`.
203
+ - Giá trị mỗi key đã được loại bỏ tab/newline/carriage-return (`\t`, `\n`, `\r`) và escape dấu `"`.
204
+
205
+ Ví dụ: `RegexMatchArray('SDT: 0901 - 0902', '(\d{4})')` (pattern có 1 group) trả về:
206
+
207
+ ```json
208
+ {"M1G1": "0901", "M1G2": "0901", "M2G1": "0902", "M2G2": "0902"}
209
+ ```
210
+
211
+ (ở đây G1 = G2 vì toàn bộ match trùng với group con duy nhất — nếu pattern không có group con nào, mỗi match cũng chỉ sinh ra đúng 1 cặp `G1`.)
212
+
213
+ Vì đây là **object**, không phải array, nên khi parse trong T-SQL:
214
+ - `OPENJSON(@result)` **không có `path` mặc định cho array** — vẫn chạy được nhưng trả về theo schema `key/value/type` (mỗi key `"M1G1"` là 1 row, cột `key` = tên, cột `value` = giá trị) chứ không phải mỗi match 1 row có cấu trúc lồng nhau.
215
+ - Muốn lấy giá trị 1 match/group cụ thể theo tên biết trước, dùng `JSON_VALUE(@result, '$."M1G1"')`.
216
+ - Muốn duyệt tất cả kết quả, dùng `OPENJSON(@result)` không truyền path, rồi tự parse tiền tố `M{m}G{g}` từ cột `key` nếu cần nhóm theo match.
217
+
218
+ ---
219
+
220
+ ## 5. Khi nào dùng các hàm này trong SP
221
+
222
+ - Luôn kiểm tra hàm đã tồn tại trong DB trước (`db_schema_read` filter theo `objectType` function, hoặc `SELECT * FROM sys.objects WHERE type='FS'`) — SQL CLR function chỉ dùng được nếu assembly đã được `CREATE ASSEMBLY` trên DB đó. Không phải DB nào cũng có.
223
+ - Dùng trong `spAPI_*` khi nghiệp vụ cần: gọi API ngoài (`httpCall`), xử lý ảnh upload (`ImageResize`/`ImageCrop`), gửi email thông báo (`sendMail`), validate chuỗi bằng regex (`RegexIsMatch`), hoặc lưu file lên đĩa server (`FileWriter`).
224
+ - Kết quả trả về `nvarchar(max)` dạng JSON (như `httpCall`, `RegexMatchArray`) nên dùng `OPENJSON`/`JSON_VALUE` để parse tiếp trong T-SQL trước khi trả ra ngoài qua tAPI.
@@ -0,0 +1,116 @@
1
+ # System Design — Thiết kế app FUI nhiều module từ đầu
2
+
3
+ > File này sở hữu: **thiết kế app nhiều module từ mô tả: phân rã module, schema-first, menu, phân quyền**. Làm một module cụ thể xem [fullstack-workflow.md](fullstack-workflow.md).
4
+
5
+ Playbook cấp **hệ thống**: khi user mô tả cả một phần mềm (nhiều màn hình/chức năng) chứ không phải một module đơn lẻ. File này điều phối; mỗi module cụ thể vẫn theo [fullstack-workflow.md](fullstack-workflow.md).
6
+
7
+ > Đọc trước khi bắt tay: file này (phân rã hệ thống) → [db-table-design.md](db-table-design.md) (schema) → [tapi-reference.md](tapi-reference.md) (API) → [fullstack-workflow.md](fullstack-workflow.md) (từng module) → [project-config.md](project-config.md) (menu/quyền).
8
+
9
+ ---
10
+
11
+ ## Bước 0 — Làm rõ phạm vi trước khi thiết kế
12
+
13
+ Đừng code ngay. Xác nhận với user:
14
+
15
+ - **Ai dùng?** Vai trò người dùng (admin, nhân viên, khách...) → quyết định phân quyền & menu.
16
+ - **Quy trình nghiệp vụ chính là gì?** Liệt kê các luồng end-to-end (ví dụ: "tạo đơn → duyệt → xuất kho").
17
+ - **Dữ liệu cốt lõi (entity chính)?** Danh sách thực thể + quan hệ (1-n, n-n).
18
+ - **Có sẵn DB/project chưa,** hay dựng mới hoàn toàn?
19
+ - **Có sẵn** → `project_list`, `module_list`, `db_schema_read` để nắm hiện trạng trước.
20
+ - **Dựng mới hoàn toàn** → làm [project-provisioning.md](project-provisioning.md) **trước file này**: chưa có database và chưa có project FUI thì không có gì để thiết kế lên. Quay lại Bước 1 sau khi `db_connect_by_name` và `project_new` đã chạy được.
21
+
22
+ Nếu thiếu thông tin → **hỏi**, không tự giả định rồi dựng cả hệ thống sai hướng.
23
+
24
+ ---
25
+
26
+ ## Bước 1 — Phân rã hệ thống thành module
27
+
28
+ Một "phần mềm" = nhiều module FUI. Quy tắc phân rã:
29
+
30
+ | Nguyên tắc | Áp dụng |
31
+ |---|---|
32
+ | Một module = một màn hình/chức năng mạch lạc | "Quản lý sinh viên", "Báo cáo doanh thu", "Duyệt đơn" — mỗi cái một module |
33
+ | Nhóm theo entity chính | CRUD một entity thường gói trong một module (list + dialog thêm/sửa) |
34
+ | Tách theo vai trò khi luồng khác nhau | Màn hình admin vs màn hình người dùng cuối → module riêng |
35
+ | Dashboard/report tách khỏi màn nhập liệu | Trang tổng hợp (chart) riêng, không nhồi vào CRUD |
36
+
37
+ **Output:** bảng danh sách module — tên, mục đích, entity liên quan, vai trò truy cập.
38
+
39
+ Kiểm tra tính nhất quán: các module cùng project nên theo **cùng convention** (đặt tên control, vị trí toolbar/filter/action). Tham khảo module đã có bằng `module_get` trước khi tự sáng tạo kiểu khác.
40
+
41
+ ---
42
+
43
+ ## Bước 2 — Thiết kế database schema-first
44
+
45
+ Trước khi viết bất kỳ module.json nào, chốt schema tổng thể:
46
+
47
+ 1. Vẽ danh sách bảng + PK + FK cho **toàn bộ** entity (không chỉ một bảng). Theo quy ước [db-table-design.md](db-table-design.md): `tbl` + PascalCase, PK `[Entity]ID`, cột audit `CreateUser/CreateTime/UpdateUser/UpdateTime`.
48
+ 2. Xác định quan hệ n-n → bảng nối (ví dụ `tblUserRole`).
49
+ 3. **`CREATE TABLE` do lập trình viên tự chạy** — MCP chặn, AI trình bày DDL để user duyệt & chạy. Sửa bảng ĐÃ CÓ thì mở theo tiêu chí *mất dữ liệu hay không*: `ALTER TABLE ... ADD` và `DROP CONSTRAINT` chạy thẳng, `ALTER COLUMN` cần `confirmWrite`, còn `DROP COLUMN`/`DROP TABLE`/`SWITCH` vẫn chặn cứng.
50
+ 4. Danh mục/bảng tra cứu (lookup) thiết kế sớm — nhiều module sẽ dùng chung cho dropdown.
51
+
52
+ **Output:** sơ đồ bảng + quan hệ, đã được user xác nhận.
53
+
54
+ ---
55
+
56
+ ## Bước 3 — Thiết kế API (SP) theo entity
57
+
58
+ Map mỗi entity → bộ SP tAPI chuẩn (`List/Get/Insert/Update/Delete` + `AUTH_*Options` cho dropdown). Xem [tapi-reference.md](tapi-reference.md) §1, [fullstack-workflow.md](fullstack-workflow.md) Bước 2-3.
59
+
60
+ Ở cấp hệ thống, chú ý:
61
+ - **SP dùng chung** (danh mục, lookup) → đặt tên trung tính, nhiều module gọi lại.
62
+ - Phân quyền nhất quán qua `@sys_SystemRight`/`@sys_FunctionRight` — xem [tapi-permission-patterns.md](tapi-permission-patterns.md).
63
+
64
+ ---
65
+
66
+ ## Bước 4 — Cấu trúc project: menu, điều hướng, component dùng chung
67
+
68
+ Đây là phần **chỉ có ở cấp hệ thống** (một module đơn lẻ không cần):
69
+
70
+ - **Menu & điều hướng:** cấu hình trong `project.json` (menuLeft/menu/menuStyle). Xem [project-config.md](project-config.md) và `examples/project-patterns.json`.
71
+ - **Component dùng chung nhiều module** → đặt project-scope (`{projectId}/components/`, gọi `component_new`/`component_update` với `projectId` không kèm `moduleId`) để auto-load ở mọi module. Xem [component-design.md](component-design.md).
72
+ - **Điều hướng giữa module:** `pushRouter`/`openWindow` (xem [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md)); truyền ID qua URL, module con tự query.
73
+ - **Phân quyền xuyên module:** thống nhất `SystemRight`/`FunctionRight` theo vai trò đã xác định ở Bước 0 — và **định nghĩa mã thật trước khi gác**, xem Bước 4b.
74
+
75
+ ---
76
+
77
+ ## Bước 4b — Tạo ĐỊNH NGHĨA quyền (làm TRƯỚC khi viết module.json nào có gác quyền)
78
+
79
+ Vai trò xác định ở Bước 0 mới chỉ là ý định. Mã quyền chỉ tồn tại khi có dòng trong `acc.tblSysRight`/`acc.tblFunction` của project — viết `FunctionRight.includes('42')` **không tạo ra** mã 42, và mã không tồn tại làm control biến mất im lặng (chi tiết + lý do các tầng kiểm chứng không bắt được: [permission-system.md](permission-system.md) §"Bắt buộc: mã quyền phải TỒN TẠI trước khi đem gác").
80
+
81
+ Hệ thống mới thường quên bước này vì nó không thuộc DB của project, không nằm trong schema, không có file nào trong workspace nhắc tới.
82
+
83
+ 1. `right_system_list` + `right_function_list` — xem project đã có gì (project mới thường trống; cũng ghi cache `_db/rights.json` **theo khoá `apiName`** để `module_validate` đối chiếu được. Hệ thống gọi nhiều alias thì chạy cho **từng alias**, cache của alias này không dùng cho alias kia).
84
+ 2. Map **mỗi vai trò** ở Bước 0 → một mức `SystemRight` (`right_system_new({ sysRight, note })`); `9` = Project Admin có sẵn, không cần tạo.
85
+ 3. Map **mỗi thao tác cần bật/tắt riêng** (duyệt, xuất báo cáo, xóa…) → một `FunctionRight` (`right_function_new({ functionCode, functionName })`). Đặt bảng `mã → ý nghĩa` vào phần thiết kế để mọi module dùng chung một bộ mã, không mỗi module bịa một dãy số.
86
+ 4. Chốt bảng đó với user **trước khi** code — đổi mã sau khi đã gán quyền cho user thật là việc phải làm tay ngoài MCP.
87
+
88
+ ---
89
+
90
+ ## Bước 5 — Thứ tự triển khai
91
+
92
+ Dựng theo thứ tự giảm rủi ro:
93
+
94
+ 0. **Hạ tầng** (chỉ khi dựng mới hoàn toàn): database + hai tài khoản SQL + alias `acc` + project FUI + quyền quản trị — [project-provisioning.md](project-provisioning.md).
95
+ 1. **Schema + lookup data** trước (mọi thứ phụ thuộc vào đây) — khảo sát bằng `db_sql_execute` (TOP 20).
96
+ 2. **Định nghĩa quyền** (Bước 4b) — SP kiểm `@sys_FunctionRight` và module gác `v-if` đều tham chiếu tới đây; tạo sau thì mọi thứ ở trên đã viết bằng mã chưa tồn tại.
97
+ 3. **SP cho entity cốt lõi**, kiểm chứng tĩnh bằng `db_sp_verify` (đọc thân SP + suy luận, không gọi endpoint sống).
98
+ 4. **Module CRUD entity chính** — dùng làm khuôn mẫu convention cho các module sau.
99
+ 5. **Module phụ thuộc** (báo cáo, dashboard) sau khi dữ liệu đã có.
100
+ 6. **Menu + component dùng chung** ráp cuối để nối các module.
101
+
102
+ Mỗi module: theo trọn [fullstack-workflow.md](fullstack-workflow.md) — phân tích → **Design Review Gate (§Bước 1b)** → UI data cứng → chốt contract → SP → wiring API → kiểm chứng tĩnh.
103
+
104
+ ---
105
+
106
+ ## Checklist cấp hệ thống
107
+
108
+ - [ ] Đã liệt kê đầy đủ module + vai trò truy cập, user xác nhận phạm vi
109
+ - [ ] Schema tổng thể (bảng + PK/FK + bảng nối + lookup) đã chốt trước khi code
110
+ - [ ] Convention thống nhất giữa các module (tham khảo module đã có)
111
+ - [ ] Component dùng chung đặt project-scope, không copy lặp
112
+ - [ ] Menu/điều hướng cấu hình trong project.json
113
+ - [ ] **Đã tạo định nghĩa quyền thật** (`right_system_new`/`right_function_new`) cho mọi vai trò/thao tác, có bảng `mã → ý nghĩa` user đã chốt — không module nào gác bằng mã chưa tồn tại
114
+ - [ ] `module_validate` không còn warning về mã quyền (đã chạy `right_*_list` để có cache đối chiếu)
115
+ - [ ] Phân quyền nhất quán theo vai trò xuyên suốt các SP
116
+ - [ ] Thứ tự triển khai: schema → **định nghĩa quyền** → SP → module lõi → module phụ → menu