@fui-org/fui-cli 0.2.0 → 0.4.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 +20 -2
  2. package/dist/fui.js +347 -44
  3. package/package.json +15 -3
  4. package/skills/fui/SKILL.md +149 -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,415 @@
1
+ # Default Functions
2
+
3
+ > File này sở hữu: **hàm tiện ích FUI dùng được trong `FUN`/`EXE`/`script.js`** (openWindow, ajaxCALL, jsonToExcel, groupBy...). Vị trí trong source xem [script-map.md](script-map.md).
4
+
5
+ Các hàm và biến global được định nghĩa trong `defaultfunction.js` — tự động load trong mọi module FUI V2.
6
+
7
+ ---
8
+
9
+ ## Global Variables
10
+
11
+ | Variable | Mô tả |
12
+ |---|---|
13
+ | `$isMobile` | `true` nếu user agent là thiết bị mobile |
14
+ | `$isPhone` | `true` nếu là điện thoại (không tính tablet) |
15
+ | `$isApple` | `true` nếu là thiết bị Apple (iPad, iPhone, iPod) |
16
+
17
+ ---
18
+
19
+ ## Dialog & Thông báo
20
+
21
+ ### confirm(obj)
22
+
23
+ Hiển thị hộp thoại xác nhận OK/Cancel có style. Wrapper của `$.confirm` (jquery-confirm).
24
+
25
+ | Param | Mô tả |
26
+ |---|---|
27
+ | `title` | Tiêu đề dialog |
28
+ | `message` | Nội dung |
29
+ | `action` | Function chạy khi nhấn OK |
30
+ | `cancel` | Function chạy khi nhấn Cancel (tùy chọn) |
31
+ | `type` | Màu: `'blue'` (mặc định), `'red'`, `'green'`, `'orange'` |
32
+ | `icon` | Icon class, ví dụ `'mdi mdi-delete'` |
33
+
34
+ ```js
35
+ confirm({
36
+ title: 'Xác nhận xóa',
37
+ message: 'Bạn có chắc muốn xóa bản ghi này không?',
38
+ type: 'red',
39
+ icon: 'mdi mdi-delete',
40
+ action: function() {
41
+ CALL(vueData.actDelete);
42
+ }
43
+ });
44
+ ```
45
+
46
+ ---
47
+
48
+ ### showMessage(obj)
49
+
50
+ Hiển thị hộp thoại thông báo (chỉ có nút Close). Wrapper của `$.dialog`.
51
+
52
+ | Param | Mô tả |
53
+ |---|---|
54
+ | `title` | Tiêu đề |
55
+ | `message` | Nội dung |
56
+ | `type` | Màu: `'blue'`, `'red'`, `'green'` |
57
+ | `icon` | Icon class |
58
+ | `onclose` | FUI action object chạy sau khi đóng dialog |
59
+
60
+ ```js
61
+ showMessage({ title: 'Thông báo', message: 'Lưu thành công!' });
62
+
63
+ showMessage({
64
+ title: 'Lỗi',
65
+ message: 'Không tìm thấy dữ liệu',
66
+ type: 'red',
67
+ icon: 'mdi mdi-alert-circle'
68
+ });
69
+
70
+ // Chạy action sau khi đóng
71
+ showMessage({
72
+ title: 'Hoàn tất',
73
+ message: 'Import xong!',
74
+ onclose: vueData.actReload
75
+ });
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Navigation & Window
81
+
82
+ ### pushRouter(router)
83
+
84
+ Cập nhật URL trên thanh địa chỉ trình duyệt (không reload trang). Dùng để đồng bộ URL với trạng thái UI hiện tại.
85
+
86
+ ```js
87
+ pushRouter('/fp/don-hang/danh-sach');
88
+ ```
89
+
90
+ ---
91
+
92
+ ### openWindow(obj)
93
+
94
+ Mở một cửa sổ FUI (`f-window`) overlay trên trang hiện tại.
95
+
96
+ | Param | Mô tả |
97
+ |---|---|
98
+ | `url` | URL nội dung cửa sổ (bắt buộc) |
99
+ | `title` | Tiêu đề cửa sổ |
100
+ | `id` | ID định danh (tự sinh nếu bỏ qua) — dùng để tránh mở trùng |
101
+ | `width` | Chiều rộng (px hoặc %) |
102
+ | `height` | Chiều cao |
103
+
104
+ ```js
105
+ // Cách đơn giản — chỉ truyền URL
106
+ openWindow('/fp/san-pham/chi-tiet?id=123');
107
+
108
+ // Đầy đủ options
109
+ openWindow({
110
+ id: 'winChiTiet',
111
+ url: '/fp/san-pham/chi-tiet?id=' + vueData.selectedId,
112
+ title: 'Chi tiết sản phẩm',
113
+ width: '80%'
114
+ });
115
+ ```
116
+
117
+ > Nếu gọi từ bên trong một `f-window` (iframe), FUI tự động chuyển sang `windowSendMessage('#PARENT', 'openWindow', obj)` để mở ở cửa sổ cha.
118
+
119
+ ---
120
+
121
+ ### redirect(obj, query)
122
+
123
+ Chuyển hướng trang hiện tại sang URL khác.
124
+
125
+ ```js
126
+ redirect('/fp/trang-chu');
127
+ redirect({ url: '/fp/don-hang', query: true }); // giữ lại query params hiện tại
128
+ ```
129
+
130
+ ---
131
+
132
+ ### reload()
133
+
134
+ Tải lại trang hiện tại.
135
+
136
+ ```js
137
+ reload();
138
+ ```
139
+
140
+ ---
141
+
142
+ ### windowSendMessage(window, cmd, data)
143
+
144
+ Gửi `postMessage` đến một cửa sổ/iframe khác. Dùng để giao tiếp giữa các `f-window`.
145
+
146
+ | Param | Mô tả |
147
+ |---|---|
148
+ | `window` | Object window, `'#PARENT'` (cửa sổ cha), hoặc `'#iframeId'` (ID của iframe) |
149
+ | `cmd` | Tên lệnh |
150
+ | `data` | Dữ liệu gửi kèm |
151
+
152
+ ```js
153
+ // Gửi lên cửa sổ cha
154
+ windowSendMessage('#PARENT', 'refreshData', { id: vueData.selectedId });
155
+
156
+ // Gửi đến iframe có id="iframeChild"
157
+ windowSendMessage('#iframeChild', 'loadItem', { itemId: 5 });
158
+ ```
159
+
160
+ ---
161
+
162
+ ## AJAX
163
+
164
+ ### ajaxCALL(URL, DATA, callBack, errorCallBack, header)
165
+
166
+ Gọi API bằng AJAX POST. URL chỉ cần truyền path — hệ thống tự ghép domain qua `fixURL`.
167
+
168
+ ```js
169
+ ajaxCALL('/SP_GetDonHang', { maKH: vueData.maKH }, function(res) {
170
+ vueData.danhSach = res.data;
171
+ });
172
+
173
+ // Với error handler
174
+ ajaxCALL('/SP_SaveDonHang', vueData.form,
175
+ function(res) { Vue.$toast.success('Lưu thành công', { position: 'top' }); },
176
+ function(err) { showMessage({ title: 'Lỗi', message: err.message, type: 'red' }); }
177
+ );
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Xuất file
183
+
184
+ ### jsonToExcel(Obj)
185
+
186
+ Xuất dữ liệu JSON ra file `.xlsx`. Tự động load ExcelJS nếu chưa có.
187
+
188
+ **Single sheet:**
189
+ ```js
190
+ jsonToExcel({
191
+ data: vueData.danhSach, // Array of objects
192
+ filename: 'danh-sach.xlsx',
193
+ worksheet: 'DanhSach'
194
+ });
195
+ ```
196
+
197
+ **Multi sheet:**
198
+ ```js
199
+ jsonToExcel({
200
+ filename: 'bao-cao.xlsx',
201
+ sheets: [
202
+ { name: 'Sheet1', data: vueData.list1 },
203
+ { name: 'Sheet2', data: vueData.list2 }
204
+ ]
205
+ });
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Data Utilities
211
+
212
+ ### groupBy(arrayObj, groupField, sumArr)
213
+
214
+ Group mảng object theo một field và tính tổng các cột số. Trả về mảng đã group kèm `count`.
215
+
216
+ ```js
217
+ // groupBy(data, 'Khoa', ['SLNghiHoc', 'SLTotNghiep'])
218
+ var result = groupBy(vueData.data, 'phongBan', ['soLuong', 'doanhThu']);
219
+ // result: [{ phongBan: 'A', soLuong: 10, doanhThu: 5000, count: 3 }, ...]
220
+ ```
221
+
222
+ > Với nhu cầu group phức tạp hơn, dùng `_.groupBy` của Lodash trực tiếp.
223
+
224
+ ---
225
+
226
+ ### findInArray(obj)
227
+
228
+ Tìm phần tử trong mảng theo điều kiện.
229
+
230
+ ```js
231
+ var item = findInArray({ array: vueData.list, value: { id: 5 } });
232
+ ```
233
+
234
+ > Thường dùng `_.find(array, condition)` của Lodash thay thế vì ngắn gọn hơn.
235
+
236
+ ---
237
+
238
+ ### fillData(obj)
239
+
240
+ Copy dữ liệu từ mảng nguồn vào object đích theo key và mapping.
241
+
242
+ - **Parameters:** `{ src, des, key, map }` — src: array nguồn, des: object đích, key: giá trị khớp, map: mapping field
243
+
244
+ ---
245
+
246
+ ### buildHeader(obj, mapCol)
247
+
248
+ Tự sinh mảng `headers` cho `f-table` từ dữ liệu. Không truyền `mapCol` → suy header từ key của bản ghi đầu tiên (cột số tự gán `el: 't-num'`); có `mapCol` → map từ `{ text, value }`.
249
+
250
+ ```js
251
+ // Tự dựng header từ dữ liệu API
252
+ vueData.headers = buildHeader(vueData.dsData);
253
+ ```
254
+
255
+ ### capacityText(numb)
256
+
257
+ Đổi số byte → chuỗi dung lượng (`'1.5 Mb'`, `'320 Kb'`) — dùng hiển thị kích thước file.
258
+
259
+ ### appCommand(cmdData)
260
+
261
+ Gửi lệnh sang app native (React Native / iOS webkit / Android) khi trang FUI nhúng trong app di động.
262
+
263
+ ---
264
+
265
+ ## PDF
266
+
267
+ ### printPDF(obj)
268
+
269
+ Tạo và in/tải PDF dùng `pdfMake`. Tự động load pdfMake nếu chưa có.
270
+
271
+ ```js
272
+ printPDF({
273
+ data: docDefinition, // pdfMake docDefinition object
274
+ download: true, // true = tải file, false = mở viewer
275
+ callBack: function() { console.log('Done'); }
276
+ });
277
+ ```
278
+
279
+ ---
280
+
281
+ ## Clipboard & String
282
+
283
+ ### copyToClipboard(textToCopy)
284
+
285
+ Copy chuỗi vào clipboard. Tự động dùng `navigator.clipboard` nếu có (HTTPS), fallback về `execCommand`.
286
+
287
+ ```js
288
+ copyToClipboard(vueData.maHoaDon);
289
+ copyToClipboard(JSON.stringify(vueData.data));
290
+ ```
291
+
292
+ ---
293
+
294
+ ### fileNameClear(fname)
295
+
296
+ Xóa các ký tự đặc biệt khỏi tên file (chỉ giữ chữ, số, khoảng trắng, dấu gạch).
297
+
298
+ ```js
299
+ var safeName = fileNameClear(vueData.tenFile); // "report (2025)!" → "report 2025"
300
+ ```
301
+
302
+ ---
303
+
304
+ ### hashCode(s)
305
+
306
+ Tạo hash số nguyên từ chuỗi (dùng để generate key duy nhất).
307
+
308
+ ```js
309
+ var key = hashCode('unique-string-' + Date.now());
310
+ ```
311
+
312
+ ---
313
+
314
+ ### generateID()
315
+
316
+ Tạo ID ngẫu nhiên dạng chuỗi.
317
+
318
+ ```js
319
+ var newId = 'win_' + generateID();
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Cookie & Auth
325
+
326
+ ### getCookie(cname) / setCookie(name, value, days, domain)
327
+
328
+ Đọc/ghi cookie trình duyệt.
329
+
330
+ ```js
331
+ var token = getCookie('awt');
332
+ setCookie('theme', 'dark', 30); // hết hạn sau 30 ngày
333
+ setCookie('lang', 'vi', 365, '.fui.vn'); // share cookie toàn domain
334
+ ```
335
+
336
+ ---
337
+
338
+ ### logout(cookiesName, domain, url)
339
+
340
+ Xóa cookie và chuyển hướng đến trang login.
341
+
342
+ ```js
343
+ logout(); // xóa cookie 'awt', redirect về login mặc định
344
+ logout('myToken', '.fui.vn', '/login');
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Quyền hạn
350
+
351
+ ### rightTest(rightObject)
352
+
353
+ Kiểm tra user hiện tại có quyền hạn yêu cầu không. So sánh với `vueData.user`.
354
+
355
+ ```js
356
+ // Kiểm tra user có role 'admin' hoặc 'manager'
357
+ if (rightTest({ role: ['admin', 'manager'] })) {
358
+ vueData.showDelete = true;
359
+ }
360
+
361
+ // Kiểm tra nhiều điều kiện cùng lúc (AND)
362
+ if (rightTest({ dept: 'IT', level: [1, 2] })) { ... }
363
+ ```
364
+
365
+ ---
366
+
367
+ ## Color Utilities
368
+
369
+ ### colorLib(color)
370
+
371
+ Utility xử lý màu sắc (hex, rgb, hsl). Dùng chủ yếu trong chart.
372
+
373
+ ```js
374
+ var c = colorLib('#4e73df');
375
+ c.hexString() // '#4e73df'
376
+ c.rgbString() // 'rgb(78, 115, 223)'
377
+ c.lighten(0.2) // làm sáng 20%
378
+ c.darken(0.1) // làm tối 10%
379
+ c.alpha(0.5).rgbString() // 'rgba(78, 115, 223, 0.5)'
380
+ ```
381
+
382
+ ### transparentize(value, opacity)
383
+
384
+ Shortcut tạo màu trong suốt từ mã màu.
385
+
386
+ ```js
387
+ transparentize('#4e73df', 0.5) // 'rgba(78, 115, 223, 0.5)'
388
+ ```
389
+
390
+ ---
391
+
392
+ ## Script Loader
393
+
394
+ ### loadScripts(scriptsArray, callbackFunc)
395
+
396
+ Load thêm script từ URL và chạy callback khi xong. Dùng để load thư viện on-demand.
397
+
398
+ ```js
399
+ loadScripts(['/include/pdfmake/pdfmake.min.js', '/include/pdfmake/vfs_fonts.js'], function() {
400
+ printPDF({ data: docDef, download: true });
401
+ });
402
+ ```
403
+
404
+ ---
405
+
406
+ ## Device Detection
407
+
408
+ ### extractHostname(url) / getDomainWithoutSubdomain(url)
409
+
410
+ Trích xuất hostname/domain từ URL. Dùng nội bộ, ít dùng trực tiếp.
411
+
412
+ ```js
413
+ extractHostname('https://api.fui.vn/fp/test') // 'api.fui.vn'
414
+ getDomainWithoutSubdomain('https://api.fui.vn') // 'fui.vn'
415
+ ```
@@ -0,0 +1,85 @@
1
+ # Design System — App-mode vs Web-mode
2
+
3
+ > File này sở hữu: **quyết định chế độ thẩm mỹ, giới hạn `border-radius`, vòng tích luỹ
4
+ > `{projectId}/DESIGN.md`**. Về class/prop/style cụ thể được duyệt xem
5
+ > [controls-styling-vocabulary.md](controls-styling-vocabulary.md). Về anatomy dialog/form chuẩn
6
+ > dùng tool `design_read("fui")`.
7
+
8
+ Trước khi viết bất kỳ `controls` nào, xác định module thuộc chế độ nào. Đây là quyết định đầu tiên
9
+ và **cứng** — hai chế độ có bộ quy tắc thẩm mỹ riêng, không được vay mượn qua lại.
10
+
11
+ ---
12
+
13
+ ## 1. Hai chế độ
14
+
15
+ | | **App-mode** (mặc định) | **Web-mode** |
16
+ |---|---|---|
17
+ | Áp dụng cho | Phần mềm quản lý: CRUD, danh sách, form, dashboard nội bộ, phân quyền | Landing page, trang giới thiệu sản phẩm, trang công khai (thường `HTMLOnly: true`) |
18
+ | Nguồn thẩm mỹ | **DUY NHẤT** `design_read("fui")` + `controls-styling-vocabulary.md` | Brand DESIGN.md (`design_list` → `design_read("<brand>")`) |
19
+ | Brand DESIGN.md (nike, linear, bmw...) | **CẤM** — kể cả khi "chỉ lấy màu" | Được phép, chọn theo tone/ngành |
20
+ | Style/class tự chế | **CẤM** (theo `controls-styling-vocabulary.md`) | Được phép trong `header.html` |
21
+ | Ưu tiên component | `f-*` / `v-*` theo chuẩn FUI | Tự do HTML/CSS |
22
+
23
+ **Tín hiệu nhận diện Web-mode**: `HTMLOnly: true` trong `_moduleInfo.json`, hoặc user mô tả
24
+ "landing page / trang giới thiệu / trang chủ công khai". Mọi trường hợp còn lại → App-mode.
25
+ Không chắc → **hỏi user**.
26
+
27
+ ⚠️ `HTMLOnly: true` không chỉ là tín hiệu thẩm mỹ: ở chế độ đó server **không nhúng sẵn** thư viện
28
+ JS/CSS nào của V2/V3 (kể cả Vue, Vuetify, jQuery, lodash, MDI, `projectdefaultstyle.css`) — cần gì
29
+ phải tự khai trong chính module. Xem [module-structure.md §HTMLOnly=true: KHÔNG có thư viện nào được
30
+ nhúng sẵn](module-structure.md#htmlonlytrue-không-có-thư-viện-nào-được-nhúng-sẵn) **trước khi** viết
31
+ thẻ đầu tiên.
32
+
33
+ **Bắt buộc với App-mode**: trước khi viết/chỉnh sửa layout, dialog, form → nạp `design_read("fui")`
34
+ (nếu chưa nạp trong phiên) để lấy anatomy chuẩn (dialog `v-dialog` phong cách f-dialog, form
35
+ floating label, bảng màu/icon), đồng thời đọc `controls-styling-vocabulary.md`. AI phải hoàn tất bước
36
+ đọc này **trước khi phác thảo hoặc viết control**, không vừa thiết kế vừa đoán quy tắc từ trí nhớ.
37
+
38
+ Trước khi bắt đầu thiết kế, tự xác nhận đã hiểu ba nhóm quy tắc giao diện cốt lõi trong các file chủ
39
+ sở hữu: kích thước icon và từ vựng styling (`controls-styling-vocabulary.md`), anatomy/action của
40
+ dialog (`design_read("fui")` + `ui-dialog-patterns.md`), và canh dọc control trong một hàng
41
+ (`controls-patterns.md` §Canh giữa control theo chiều dọc). Nếu task có dialog thì phải đọc thêm
42
+ `ui-dialog-patterns.md` trước khi phác thảo dialog.
43
+
44
+ ---
45
+
46
+ ## 2. Override theo project — `{projectId}/DESIGN.md`
47
+
48
+ Nếu `{projectId}/DESIGN.md` tồn tại trong workspace → file đó **ghi đè** quy tắc cùng chủ đề của
49
+ chế độ đang dùng (những gì không đề cập vẫn theo chuẩn chung). **Luôn kiểm tra file này trước khi
50
+ thiết kế.**
51
+
52
+ ---
53
+
54
+ ## 3. Vòng tích luỹ chuẩn thiết kế (BẮT BUỘC)
55
+
56
+ Khi user góp ý/chê một chi tiết UI ("dialog phải...", "nút này màu...", "đừng dùng..."):
57
+
58
+ 1. Sửa module theo góp ý.
59
+ 2. **Ghi ngay quy tắc đó vào `{projectId}/DESIGN.md`** (tạo file nếu chưa có — theo template ở cuối
60
+ `design_read("fui")`), diễn đạt thành **quy tắc tổng quát + ví dụ JSON đúng/sai**, không chỉ ghi
61
+ lại tình huống cụ thể.
62
+ 3. Nếu quy tắc rõ ràng áp dụng cho MỌI project (không riêng project này) → nói với user: "quy tắc
63
+ này nên đưa vào chuẩn chung `fui/DESIGN.md`" và đề xuất nội dung — **chỉ user quyết định** cập
64
+ nhật chuẩn chung.
65
+
66
+ Nhờ vòng này, góp ý của user không bị mất sau phiên chat — lần thiết kế sau tự động tuân theo.
67
+
68
+ ---
69
+
70
+ ## 4. Giới hạn `border-radius`
71
+
72
+ Áp dụng cho **MỌI brand/style, kể cả khi DESIGN.md của brand đề xuất giá trị lớn hơn**:
73
+
74
+ | Loại khung | `border-radius` tối đa |
75
+ |---|---|
76
+ | Khung lớn tổng quát (card container, dialog, section wrapper, panel chính) | `12px` |
77
+ | Khung vừa (button lớn, input group, panel con, toolbar) | `8px` |
78
+ | Khung nhỏ (button nhỏ, chip, badge, tag, icon container) | `6px` |
79
+
80
+ Nếu brand DESIGN.md đề xuất `border-radius` lớn hơn mức tương ứng ở trên (ví dụ các brand có bo
81
+ tròn mạnh như Airbnb, Notion, Nike...) → **hạ xuống mức tối đa cho phép**, không áp dụng nguyên giá
82
+ trị gốc.
83
+
84
+ **Ngoại lệ**: phần tử tròn hoàn toàn (avatar, icon button dạng chấm tròn) dùng `border-radius: 50%`
85
+ — không thuộc phạm vi giới hạn này.