@fui-org/fui-cli 1.3.1 → 2.0.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 (111) hide show
  1. package/README.md +19 -3
  2. package/dist/fui-bmg5pnmq.js +379 -0
  3. package/dist/fui.js +1 -1
  4. package/package.json +7 -6
  5. package/skills/fui/SKILL.md +9 -41
  6. package/skills/fui-skill/SKILL.md +95 -225
  7. package/skills/fui-skill/assets/projectdefaultstyle-3.0.css +555 -0
  8. package/skills/fui-skill/assets/projectdefaultstyle.css +207 -235
  9. package/skills/fui-skill/references/INDEX.md +105 -137
  10. package/skills/fui-skill/references/advanced-techniques.md +76 -68
  11. package/skills/fui-skill/references/coding-standards.md +56 -56
  12. package/skills/fui-skill/references/component-design.md +166 -173
  13. package/skills/fui-skill/references/component-quickref.md +61 -60
  14. package/skills/fui-skill/references/component-table.md +128 -117
  15. package/skills/fui-skill/references/components-dialog.md +55 -56
  16. package/skills/fui-skill/references/components-display.md +24 -30
  17. package/skills/fui-skill/references/components-echart.md +186 -261
  18. package/skills/fui-skill/references/components-input.md +72 -96
  19. package/skills/fui-skill/references/controls-patterns.md +196 -342
  20. package/skills/fui-skill/references/controls-styling-vocabulary.md +130 -97
  21. package/skills/fui-skill/references/db-table-design.md +24 -28
  22. package/skills/fui-skill/references/db-workflow.md +191 -390
  23. package/skills/fui-skill/references/default-function.md +169 -128
  24. package/skills/fui-skill/references/design-modes.md +35 -63
  25. package/skills/fui-skill/references/echart-templates.md +204 -196
  26. package/skills/fui-skill/references/fastproject.md +62 -60
  27. package/skills/fui-skill/references/fsheet.md +109 -124
  28. package/skills/fui-skill/references/fullstack-workflow.md +90 -128
  29. package/skills/fui-skill/references/module-data-patterns.md +31 -40
  30. package/skills/fui-skill/references/module-json-anatomy.md +47 -52
  31. package/skills/fui-skill/references/module-structure.md +80 -196
  32. package/skills/fui-skill/references/new-session.md +49 -51
  33. package/skills/fui-skill/references/pdfmake.md +17 -17
  34. package/skills/fui-skill/references/permission-system.md +89 -108
  35. package/skills/fui-skill/references/platform-architecture.md +128 -153
  36. package/skills/fui-skill/references/project-config.md +102 -134
  37. package/skills/fui-skill/references/project-provisioning.md +139 -244
  38. package/skills/fui-skill/references/script-map.md +208 -242
  39. package/skills/fui-skill/references/sql-clr-functions.md +98 -97
  40. package/skills/fui-skill/references/system-design.md +63 -88
  41. package/skills/fui-skill/references/tapi-file-api.md +46 -52
  42. package/skills/fui-skill/references/tapi-permission-patterns.md +51 -53
  43. package/skills/fui-skill/references/tapi-reference.md +132 -207
  44. package/skills/fui-skill/references/tools-registry.md +84 -460
  45. package/skills/fui-skill/references/ui-crosswindow-patterns.md +79 -75
  46. package/skills/fui-skill/references/ui-dialog-patterns.md +98 -72
  47. package/skills/fui-skill/references/ui-layout-patterns.md +26 -26
  48. package/skills/fui-skill/references/ui-patterns.md +71 -83
  49. package/skills/fui-skill/references/ui-screenshot-review.md +63 -62
  50. package/skills/fui-skill/references/ui-table-cell-patterns.md +61 -59
  51. package/skills/fui-skill/references/ui-templates.md +16 -23
  52. package/skills/fui-skill/references/verification.md +236 -246
  53. package/skills/fui-skill/references/watcher-patterns.md +30 -63
  54. package/skills/fui-skill/references/websocket-realtime.md +83 -66
  55. package/skills/fui-skill/scripts/component-3.0.js +298 -131
  56. package/skills/fui-skill/scripts/component.js +277 -271
  57. package/skills/fui-skill/scripts/componentTable-3.0.js +182 -53
  58. package/skills/fui-skill/scripts/componentTable.js +171 -49
  59. package/skills/fui-skill/scripts/defaultfunction-3.0.js +88 -3
  60. package/skills/fui-skill/scripts/defaultfunction.js +88 -3
  61. package/skills/fui-skill/scripts/fsheet.js +38 -0
  62. package/dist/fui-x7kph78r.js +0 -420
  63. package/skills/fui-skill/README.md +0 -112
  64. package/skills/fui-skill/metadata.json +0 -75
  65. package/skills/fui-skill-cli/SKILL.md +0 -139
  66. package/skills/fui-skill-cli/references/INDEX.md +0 -110
  67. package/skills/fui-skill-cli/references/advanced-techniques.md +0 -168
  68. package/skills/fui-skill-cli/references/coding-standards.md +0 -112
  69. package/skills/fui-skill-cli/references/component-design.md +0 -448
  70. package/skills/fui-skill-cli/references/component-quickref.md +0 -78
  71. package/skills/fui-skill-cli/references/component-table.md +0 -248
  72. package/skills/fui-skill-cli/references/components-dialog.md +0 -191
  73. package/skills/fui-skill-cli/references/components-display.md +0 -141
  74. package/skills/fui-skill-cli/references/components-echart.md +0 -316
  75. package/skills/fui-skill-cli/references/components-input.md +0 -335
  76. package/skills/fui-skill-cli/references/controls-patterns.md +0 -701
  77. package/skills/fui-skill-cli/references/controls-styling-vocabulary.md +0 -137
  78. package/skills/fui-skill-cli/references/db-table-design.md +0 -73
  79. package/skills/fui-skill-cli/references/db-workflow.md +0 -288
  80. package/skills/fui-skill-cli/references/default-function.md +0 -425
  81. package/skills/fui-skill-cli/references/design-modes.md +0 -57
  82. package/skills/fui-skill-cli/references/echart-templates.md +0 -489
  83. package/skills/fui-skill-cli/references/fastproject.md +0 -99
  84. package/skills/fui-skill-cli/references/fsheet.md +0 -203
  85. package/skills/fui-skill-cli/references/fullstack-workflow.md +0 -313
  86. package/skills/fui-skill-cli/references/module-data-patterns.md +0 -117
  87. package/skills/fui-skill-cli/references/module-json-anatomy.md +0 -132
  88. package/skills/fui-skill-cli/references/module-structure.md +0 -141
  89. package/skills/fui-skill-cli/references/new-session.md +0 -85
  90. package/skills/fui-skill-cli/references/pdfmake.md +0 -60
  91. package/skills/fui-skill-cli/references/permission-system.md +0 -150
  92. package/skills/fui-skill-cli/references/platform-architecture.md +0 -269
  93. package/skills/fui-skill-cli/references/project-config.md +0 -303
  94. package/skills/fui-skill-cli/references/project-provisioning.md +0 -278
  95. package/skills/fui-skill-cli/references/script-map.md +0 -262
  96. package/skills/fui-skill-cli/references/sql-clr-functions.md +0 -225
  97. package/skills/fui-skill-cli/references/system-design.md +0 -89
  98. package/skills/fui-skill-cli/references/tapi-file-api.md +0 -185
  99. package/skills/fui-skill-cli/references/tapi-permission-patterns.md +0 -156
  100. package/skills/fui-skill-cli/references/tapi-reference.md +0 -474
  101. package/skills/fui-skill-cli/references/tools-registry.md +0 -84
  102. package/skills/fui-skill-cli/references/ui-crosswindow-patterns.md +0 -321
  103. package/skills/fui-skill-cli/references/ui-dialog-patterns.md +0 -255
  104. package/skills/fui-skill-cli/references/ui-layout-patterns.md +0 -176
  105. package/skills/fui-skill-cli/references/ui-patterns.md +0 -303
  106. package/skills/fui-skill-cli/references/ui-screenshot-review.md +0 -95
  107. package/skills/fui-skill-cli/references/ui-table-cell-patterns.md +0 -318
  108. package/skills/fui-skill-cli/references/ui-templates.md +0 -22
  109. package/skills/fui-skill-cli/references/verification.md +0 -236
  110. package/skills/fui-skill-cli/references/watcher-patterns.md +0 -163
  111. package/skills/fui-skill-cli/references/websocket-realtime.md +0 -271
@@ -1,48 +1,20 @@
1
- # Watcher Patterns (FUI Best Practice)
1
+ # Watcher Patterns
2
2
 
3
- > File này sở hữu: **`watch`: `v_old`, deep-watch, cascading, chống race**. Cú pháp action xem [controls-patterns.md](controls-patterns.md).
3
+ > Owns: **`watch`: `v_old`, deep-watch, cascading, race control**. Action syntax: [controls-patterns.md](controls-patterns.md).
4
4
 
5
- Hướng này dùng cho các case:
6
- - Form phụ thuộc nhiều cấp (Tỉnh/Thành -> Quận/Huyện -> Phường/Xã)
7
- - Bộ lọc theo thuộc tính (status, loại, ngày, keyword, ...)
5
+ Use for multi-level dependent forms (Province → District → Ward) and attribute filters (status, type, date, keyword...). Goals: correct FUI structure (`data`, `watch`, `controls`, `set`), no watcher loops, fewer redundant API calls, maintainable large modules.
8
6
 
9
- Mục tiêu:
10
- - Đúng cấu trúc FUI (`module.json`: `data`, `watch`, `controls`, `set`)
11
- - Tránh vòng lặp watcher
12
- - Giảm gọi API thừa
13
- - Dễ bảo trì khi module lớn
7
+ ## 1. Rules
14
8
 
15
- ## 1. Nguyên tắc chuẩn
9
+ 1. **Watch the smallest input key** — an ID or filter field (`provinceID`, `districtID`, `filter.status`), not a large object unless needed.
10
+ 2. **Split actions by level** — `handleProvinceChange` only resets dependents and calls `apiLoadDistricts`; `handleDistrictChange` only resets the level below and calls `apiLoadWards`; `applyFilters` holds the final list-loading logic.
11
+ 3. **`deep-watch` selectively** — only small filter objects (e.g. `filter`). Never deep-watch large arrays like `items`, `tableData`.
12
+ 4. **No self-made loops** — never watch the action's own output var; never watch `items` and write `items` in the callback.
13
+ 5. **Guard before API calls** — `IF/THEN/ELSE` to validate input (e.g. no province selected → don't load districts).
14
+ 6. **Debounce/race control in `script.js`** — watchers only orchestrate; debounce and stale-response-overwrite protection live in JS helpers.
15
+ 7. **Force literal strings only via `IN`** — in an action with `IN`, a string without spaces may be read as an expression → use `` `value`` or `"'value'"`. Full rules: [controls-patterns.md §0](controls-patterns.md#0-value-resolution-rules-fundamental).
16
16
 
17
- 1. Chỉ watch key đầu vào nhỏ nhất
18
- - Watch ID hoặc field filter (`provinceID`, `districtID`, `filter.status`), không watch cả object lớn nếu không cần.
19
-
20
- 2. Tách action theo tầng
21
- - `handleProvinceChange` chỉ reset dữ liệu phụ thuộc và gọi `apiLoadDistricts`.
22
- - `handleDistrictChange` chỉ reset cấp dưới và gọi `apiLoadWards`.
23
- - `applyFilters` gom logic tải list cuối.
24
-
25
- 3. Dùng `deep-watch` có chọn lọc
26
- - Chỉ dùng cho object filter nhỏ (ví dụ `filter`).
27
- - Không deep-watch mảng lớn như `items`, `tableData`.
28
-
29
- 4. Không để watcher tự tạo loop
30
- - Không watch biến output của chính action đó.
31
- - Không watch `items` rồi trong callback lại ghi `items`.
32
-
33
- 5. Gắn điều kiện trước khi gọi API
34
- - Dùng `IF/THEN/ELSE` để kiểm tra đầu vào hợp lệ (ví dụ chưa chọn tỉnh thì không gọi huyện).
35
-
36
- 6. Tách debounce/race-control sang `script.js`
37
- - Watcher giữ vai trò điều phối.
38
- - Debounce hoặc chống response cũ ghi đè response mới đặt trong helper JS.
39
-
40
- 7. Literal string chỉ ép khi truyền qua `IN`
41
- - Trong action có `IN`, chuỗi không có khoảng trắng có thể bị core hiểu là expression → dùng
42
- `` `value`` hoặc `"'value'"`. Quy tắc đầy đủ:
43
- [controls-patterns.md §0](controls-patterns.md#0-value-resolution-rules-fundamental).
44
-
45
- ## 2. Pattern A: Cascading địa giới hành chính
17
+ ## 2. Pattern A: administrative-area cascade
46
18
 
47
19
  ```json
48
20
  {
@@ -108,12 +80,9 @@ Mục tiêu:
108
80
  }
109
81
  ```
110
82
 
111
- Điểm chính:
112
- - Watch theo key cụ thể.
113
- - Reset cấp dưới trước khi gọi API cấp dưới.
114
- - Có điều kiện tránh gọi API khi null.
83
+ Key points: watch specific keys; reset lower levels before calling their API; guard against calling with null.
115
84
 
116
- ## 3. Pattern B: Filter object + deep-watch
85
+ ## 3. Pattern B: filter object + deep-watch
117
86
 
118
87
  ```json
119
88
  {
@@ -153,26 +122,24 @@ Mục tiêu:
153
122
  }
154
123
  ```
155
124
 
156
- Khi dùng pattern này:
157
- - `filter` nên nhỏ và ổn định.
158
- - Nếu có text search nhập liên tục, nên debounce trong `script.js`.
125
+ Keep `filter` small and stable. Continuous text-search input → debounce in `script.js`.
159
126
 
160
- ## 4. Debounce khuyến nghị (script.js)
127
+ ## 4. Recommended debounce (script.js)
161
128
 
162
129
  ```javascript
163
130
  var filterTimer = null;
164
131
 
165
132
  function debounceFilter(input) {
166
- var callActionName = input && input.callActionName;
167
- var waitMs = input && input.waitMs;
168
- clearTimeout(filterTimer);
169
- filterTimer = setTimeout(function () {
170
- CALL(vueData[callActionName]);
171
- }, waitMs || 350);
133
+ var callActionName = input && input.callActionName;
134
+ var waitMs = input && input.waitMs;
135
+ clearTimeout(filterTimer);
136
+ filterTimer = setTimeout(function () {
137
+ CALL(vueData[callActionName]);
138
+ }, waitMs || 350);
172
139
  }
173
140
  ```
174
141
 
175
- Ví dụ action trong `module.json`:
142
+ Action in `module.json`:
176
143
 
177
144
  ```json
178
145
  {
@@ -186,11 +153,11 @@ Ví dụ action trong `module.json`:
186
153
  }
187
154
  ```
188
155
 
189
- ## 5. Checklist QA cho watcher
156
+ ## 5. Watcher QA checklist
190
157
 
191
- 1. Watch key có đủ nhỏ chưa (ID/field thay vì object lớn)?
192
- 2. Có reset đúng dữ liệu phụ thuộc trước khi gọi API?
193
- 3. Có guard `IF` trước API khi input null/rỗng?
194
- 4. Có nguy cơ loop watcher không?
195
- 5. Có cần debounce cho input text không?
196
- 6. Nếu action có `IN`, đã áp dụng literal string rule cho giá trị text không khoảng trắng chưa?
158
+ 1. Watch key small enough (ID/field, not a large object)?
159
+ 2. Dependents reset before calling the API?
160
+ 3. `IF` guard before API when input is null/empty?
161
+ 4. Any watcher loop risk?
162
+ 5. Text input needs debounce?
163
+ 6. Action with `IN`: literal string rule applied to space-free text values?
@@ -1,47 +1,41 @@
1
1
  # FUI Real-time Messaging — WebSocket (SignalR)
2
2
 
3
- > File này sở hữu: **real-time/SignalR: `webSocketJoinGroup`, `webSocket_Send`**.
3
+ > Owns: **real-time/SignalR: `webSocketJoinGroup`, `webSocket_Send`**.
4
4
 
5
- FUI cung cấp cơ chế real-time messaging dựa trên **SignalR** (không phải WebSocket API thuần của trình duyệt), host tại domain riêng `ws.fui.vn`. Toàn bộ logic client đã có sẵn trong `defaultfunction.js` (global, không cần viết lại) — chỉ cần khai báo đúng cấu trúc trong `module.json` và gọi 3 hàm sẵn có: `webSocketConnection`, `webSocketJoinGroup`, `webSocket_Send`.
5
+ Real-time uses **SignalR** (not the browser WebSocket API), hosted at `ws.fui.vn`. Client logic is already global in `defaultfunction.js` — do not rewrite it. Declare the right structure in `module.json` and use the 3 built-ins: `webSocketConnection`, `webSocketJoinGroup`, `webSocket_Send`.
6
6
 
7
7
  ---
8
8
 
9
- ## 1. Import bắt buộc
9
+ ## 1. Required imports
10
10
 
11
- Trước khi dùng, phải thêm 2 file JS qua `file_import_new` — **đúng thứ tự** (dòng dưới phụ thuộc dòng trên):
11
+ Add 2 JS files with `fui import new` — **in this order** (second depends on first):
12
12
 
13
- | Thứ tự | File | Ghi chú |
14
- |---|---|---|
15
- | 1 (sort nhỏ hơn) | `https://ws.fui.vn/Scripts/jquery.signalR-2.4.1.min.js` | SignalR client core |
16
- | 2 (sort lớn hơn) | `https://ws.fui.vn/signalr/hubs` | Hub proxy tự sinh từ server — vẫn là nội dung JS dù URL không có đuôi `.js` |
13
+ | Order | File | Note |
14
+ | --------------- | ------------------------------------------------------- | ------------------------------------------------------------------ |
15
+ | 1 (lower sort) | `https://ws.fui.vn/Scripts/jquery.signalR-2.4.1.min.js` | SignalR client core |
16
+ | 2 (higher sort) | `https://ws.fui.vn/signalr/hubs` | Server-generated hub proxy — JS content despite no `.js` extension |
17
17
 
18
+ ```bash
19
+ fui import new --type .js --content-type link --name "https://ws.fui.vn/Scripts/jquery.signalR-2.4.1.min.js" --sort 100
20
+ fui import new --type .js --content-type link --name "https://ws.fui.vn/signalr/hubs" --sort 110
18
21
  ```
19
- file_import_new({
20
- fileType: ".js", contentType: "link",
21
- fileName: "https://ws.fui.vn/Scripts/jquery.signalR-2.4.1.min.js",
22
- fileSort: 100
23
- })
24
- file_import_new({
25
- fileType: ".js", contentType: "link",
26
- fileName: "https://ws.fui.vn/signalr/hubs",
27
- fileSort: 110
28
- })
29
- ```
30
22
 
31
- > jQuery đã có sẵn trong FUI runtime — không cần import thêm. Kiểm tra `file_import_list` trước để tránh add trùng (giống quy trình `f-echart` trong [components-echart.md](components-echart.md)).
23
+ See `fui import new --help`.
24
+
25
+ > jQuery is already in the FUI runtime — do not import it. Run `fui import list` first to avoid duplicates (same flow as `f-echart` in [components-echart.md](components-echart.md)).
32
26
 
33
27
  ---
34
28
 
35
- ## 2. Cơ chế tự động kết nối khi module load
29
+ ## 2. Auto-connect on module load
36
30
 
37
- FUI's bootstrap (`buildApp` trong `fastproject.js`) kiểm tra `vueData.webSocket` ngay sau khi `data[]` chạy xong:
31
+ FUI bootstrap (`buildApp` in `fastproject.js`) checks `vueData.webSocket` right after `data[]` runs:
38
32
 
39
- - Nếu `vueData.webSocket` tồn tại → tự tạo `vueData.webSocketState = { code, text }` và gọi `webSocketConnection('https://ws.fui.vn/signalr', vueData.webSocket, vueData.webSocketState)` + `webSocketJoinGroup(vueData.webSocket)` — **không cần tự gọi 2 hàm này ở bootstrap**, chỉ cần khai báo đúng object `webSocket` trong `data[]`.
40
- - `webSocketState.text` phản ánh trạng thái kết nối: `Reconnecting` / `Connected` / `Disconnected`. Có thể bind trực tiếp lên UI để debug (`innerHTML: "{{webSocketState}}"`).
33
+ - If `vueData.webSocket` exists → creates `vueData.webSocketState = { code, text }` and calls `webSocketConnection('https://ws.fui.vn/signalr', vueData.webSocket, vueData.webSocketState)` + `webSocketJoinGroup(vueData.webSocket)`. **Do not call these yourself at bootstrap** — just declare `webSocket` in `data[]`.
34
+ - `webSocketState.text`: `Reconnecting` / `Connected` / `Disconnected`. Bind to UI for debugging (`innerHTML: "{{webSocketState}}"`).
41
35
 
42
36
  ---
43
37
 
44
- ## 3. Khai báo group cần join — trong `data[]`
38
+ ## 3. Declare groups to join — in `data[]`
45
39
 
46
40
  ```json
47
41
  "data": [
@@ -57,18 +51,18 @@ FUI's bootstrap (`buildApp` trong `fastproject.js`) kiểm tra `vueData.webSocke
57
51
  ]
58
52
  ```
59
53
 
60
- - Mỗi **key** của object `webSocket` là tên một group sẽ được join khi module load.
61
- - **Kiểu giá trị quyết định cách nhận tin nhắn:**
62
- - Giá trị là **array** (`[]`) → tin nhắn mới được **push** vào cuối mảng (giữ lịch sử, dùng cho chat log, danh sách sự kiện...).
63
- - Giá trị là **`null` hoặc object** (`{}`) → tin nhắn mới **ghi đè** giá trị hiện tại (chỉ giữ tin mới nhất, dùng cho trạng thái/counter/notification đơn).
64
- - Group name có thể là chuỗi literal (`nhom1`) hoặc **dynamic qua `{{ }}` interpolation ngay trong key** — `"{{DGroup}}"` sẽ được resolve thành giá trị hiện tại của `DGroup` (ví dụ `device-abc`), cho phép join theo group riêng của user/thiết bị.
65
- - `` `ThuNha `` / `` `device-abc `` dùng backtick để ép literal string — xem [Value Resolution Rules](controls-patterns.md#0-value-resolution-rules-fundamental).
54
+ - Each **key** of `webSocket` = a group joined on load.
55
+ - **Value type sets receive behavior:**
56
+ - **array** (`[]`) → new messages **pushed** to the end (history: chat log, event list...).
57
+ - **`null` or object** (`{}`) → new message **overwrites** (latest only: status/counter/single notification).
58
+ - Group name: literal (`nhom1`) or **dynamic via `{{ }}` in the key** — `"{{DGroup}}"` resolves to `DGroup`'s current value (e.g. `device-abc`), for per-user/device groups.
59
+ - `` `ThuNha `` / `` `device-abc `` use backtick to force a literal string — see [Value Resolution Rules](controls-patterns.md#0-value-resolution-rules-fundamental).
66
60
 
67
61
  ---
68
62
 
69
- ## 4. Join thêm group tại runtime — `FUN: webSocketJoinGroup`
63
+ ## 4. Join more groups at runtime — `FUN: webSocketJoinGroup`
70
64
 
71
- Ngoài khai báo tĩnh ở mục 3, có thể join thêm group bất cứ lúc nào (kể cả ngay trong `data[]` dưới dạng action tự chạy khi load — xem [ui-patterns.md §1](ui-patterns.md)):
65
+ Join any time, including as an auto-run action in `data[]` (see [ui-patterns.md §1](ui-patterns.md)):
72
66
 
73
67
  ```json
74
68
  {
@@ -80,14 +74,14 @@ Ngoài khai báo tĩnh ở mục 3, có thể join thêm group bất cứ lúc n
80
74
  }
81
75
  ```
82
76
 
83
- - `IN` ở đây **chính là `groupObj`** (tham số đầu tiên của `webSocketJoinGroup(groupObj, timeout, callBackFunc)`) — không map theo tên tham số như hàm khác. Vì vậy `FUN` + `IN` **không truyền được `timeout`/`callBackFunc`** — nếu cần custom timeout hoặc callback sau khi join xong, dùng `EXE` raw JS: `"EXE": "webSocketJoinGroup({Nhom3:null}, 3000, function(){ ... })"`.
84
- - Dùng cách này khi group cần join phát sinh **có điều kiện** hoặc **sau khi** module đã load (ví dụ: trong một action, sau khi API trả về ID phòng/chat cần join), thay vì chỉ khai báo tĩnh trong `webSocket: {}`.
77
+ - `IN` **is the `groupObj`** (first param of `webSocketJoinGroup(groupObj, timeout, callBackFunc)`), not mapped by param name. So `FUN` + `IN` **cannot pass `timeout`/`callBackFunc`** — for those use raw `EXE`: `"EXE": "webSocketJoinGroup({Nhom3:null}, 3000, function(){ ... })"`.
78
+ - Use for groups that are **conditional** or known only **after** load (e.g. after an API returns a room/chat ID), instead of static `webSocket: {}`.
85
79
 
86
80
  ---
87
81
 
88
- ## 5. Nhận tin nhắn
82
+ ## 5. Receive messages
89
83
 
90
- ### watch (khuyến nghị — trigger logic khi có tin mới)
84
+ ### watch (recommended — run logic on new message)
91
85
 
92
86
  ```json
93
87
  "watch": {
@@ -96,9 +90,9 @@ Ngoài khai báo tĩnh ở mục 3, có thể join thêm group bất cứ lúc n
96
90
  }
97
91
  ```
98
92
 
99
- > **Lưu ý quan trọng:** key trong `watch` **không được** dùng `{{ }}` template (watch key là literal cố định, không interpolate) — phải ghi trực tiếp giá trị đã resolve của group (ví dụ `webSocket.device-abc`, không phải `webSocket.{{DGroup}}`).
93
+ > **Important:** `watch` keys **cannot** use `{{ }}` (literal, not interpolated) — write the resolved group (`webSocket.device-abc`, not `webSocket.{{DGroup}}`).
100
94
 
101
- ### Bind trực tiếp lên UI (hiển thị raw, không cần watch)
95
+ ### Bind directly to UI (raw, no watch)
102
96
 
103
97
  ```json
104
98
  {
@@ -107,7 +101,7 @@ Ngoài khai báo tĩnh ở mục 3, có thể join thêm group bất cứ lúc n
107
101
  }
108
102
  ```
109
103
 
110
- ### Debug toàn bộ state
104
+ ### Debug all state
111
105
 
112
106
  ```json
113
107
  { "el": "div", "attr": {}, "innerHTML": "{{webSocket}}" }
@@ -115,11 +109,11 @@ Ngoài khai báo tĩnh ở mục 3, có thể join thêm group bất cứ lúc n
115
109
 
116
110
  ---
117
111
 
118
- ## 6. Gửi tin nhắn — `webSocket_Send`
112
+ ## 6. Send — `webSocket_Send`
119
113
 
120
- `webSocket_Send(obj)` nhận **một object** `{ group, data }` — `data` có thể là object, array, hoặc string, hàm tự chọn API SignalR phù hợp (`messageSendObject` / `messageSendArray` / `messageSend`).
114
+ `webSocket_Send(obj)` takes **one object** `{ group, data }`; `data` may be object, array or string — the function picks the SignalR API (`messageSendObject` / `messageSendArray` / `messageSend`).
121
115
 
122
- ### Cách 1 — `FUN` + `IN` (payload object)
116
+ ### Way 1 — `FUN` + `IN` (object payload)
123
117
 
124
118
  ```json
125
119
  {
@@ -131,13 +125,13 @@ Ngoài khai báo tĩnh ở mục 3, có thể join thêm group bất cứ lúc n
131
125
  }
132
126
  ```
133
127
 
134
- ### Cách 2 — `EXE` raw JS (khi cần đọc giá trị động từ vueData)
128
+ ### Way 2 — raw `EXE` (read dynamic vueData values)
135
129
 
136
130
  ```json
137
131
  { "EXE": "webSocket_Send({ group:'nhom2', data:vueData.messText })" }
138
132
  ```
139
133
 
140
- ### Cách 3 — payload dạng array
134
+ ### Way 3 — array payload
141
135
 
142
136
  ```json
143
137
  {
@@ -146,15 +140,15 @@ Ngoài khai báo tĩnh ở mục 3, có thể join thêm group bất cứ lúc n
146
140
  }
147
141
  ```
148
142
 
149
- ### Gọi trực tiếp trong `script.js` (ngoài action engine)
143
+ ### Directly in `script.js` (outside the action engine)
150
144
 
151
145
  ```js
152
- webSocket_Send({ group: 'Nhom3', data: 'Gui noi dung từ JS' })
146
+ webSocket_Send({ group: "Nhom3", data: "Gui noi dung từ JS" });
153
147
  ```
154
148
 
155
- ### Cách 4 — Gửi qua HTTP API (không cần client tự giữ kết nối SignalR)
149
+ ### Way 4 — HTTP API (no client SignalR connection needed)
156
150
 
157
- FUI cũng expose endpoint REST để gửi tin nhắn tới group mà không cần gọi `webSocket_Send` từ client:
151
+ REST endpoint to send to a group:
158
152
 
159
153
  ```json
160
154
  {
@@ -163,17 +157,17 @@ FUI cũng expose endpoint REST để gửi tin nhắn tới group mà không c
163
157
  }
164
158
  ```
165
159
 
166
- Group name cũng interpolate được trực tiếp trong URL:
160
+ Group name interpolates in the URL:
167
161
 
168
162
  ```json
169
163
  { "API": "https://ws.fui.vn/api/messageSend/{{DGroup}}" }
170
164
  ```
171
165
 
172
- Dùng cách này khi muốn gửi tin nhắn từ một action/nơi không tiện dùng `FUN`/`EXE` (ví dụ gọi từ server, hoặc muốn tách hẳn việc gửi ra khỏi client hiện có kết nối SignalR hay không).
166
+ Use when `FUN`/`EXE` is inconvenient (e.g. server-side sends, or decoupling sending from whether the client holds a SignalR connection).
173
167
 
174
168
  ---
175
169
 
176
- ## 7. Ví dụ đầy đủ (tham khảo)
170
+ ## 7. Full example
177
171
 
178
172
  ```json
179
173
  {
@@ -211,7 +205,10 @@ Dùng cách này khi muốn gửi tin nhắn từ một action/nơi không tiệ
211
205
  "attr": {
212
206
  "label": "send - nhom2",
213
207
  ":action": [
214
- { "FUN": "webSocket_Send", "IN": { "group": "`nhom1", "data": { "text": "doi tuong 1", "text2": "doi tuong 2" } } },
208
+ {
209
+ "FUN": "webSocket_Send",
210
+ "IN": { "group": "`nhom1", "data": { "text": "doi tuong 1", "text2": "doi tuong 2" } }
211
+ },
215
212
  { "EXE": "webSocket_Send({ group:'nhom2', data:vueData.messText})" },
216
213
  { "FUN": "webSocket_Send", "IN": { "group": "`nhom3", "data": ["mang 1", "mang 2"] } }
217
214
  ]
@@ -222,18 +219,38 @@ Dùng cách này khi muốn gửi tin nhắn từ một action/nơi không tiệ
222
219
  {
223
220
  "prop": "",
224
221
  "cols": [
225
- { "el": "v-textarea", "col": {}, "attr": { "v-model": "webSocket.nhom2", "label": "Nội dung nhận nhom2", ":required": false, "rows": 5 } }
222
+ {
223
+ "el": "v-textarea",
224
+ "col": {},
225
+ "attr": { "v-model": "webSocket.nhom2", "label": "Nội dung nhận nhom2", ":required": false, "rows": 5 }
226
+ }
226
227
  ]
227
228
  },
228
- { "prop": "", "cols": [ { "el": "div", "col": {}, "attr": {}, "innerHTML": "{{webSocket}}" } ] },
229
+ { "prop": "", "cols": [{ "el": "div", "col": {}, "attr": {}, "innerHTML": "{{webSocket}}" }] },
229
230
  {
230
231
  "prop": "",
231
232
  "cols": [
232
- { "el": "f-button", "col": {}, "attr": { "label": "send by API - nhom1", ":action": [ { "API": "https://ws.fui.vn/api/messageSend/nhom1", "IN": { "text": "test 1", "text2": "test 2" } } ] } },
233
- { "el": "f-button", "col": {}, "attr": { ":label": "'send by API - ' + DGroup", ":action": [ { "API": "https://ws.fui.vn/api/messageSend/{{DGroup}}" } ] } }
233
+ {
234
+ "el": "f-button",
235
+ "col": {},
236
+ "attr": {
237
+ "label": "send by API - nhom1",
238
+ ":action": [
239
+ { "API": "https://ws.fui.vn/api/messageSend/nhom1", "IN": { "text": "test 1", "text2": "test 2" } }
240
+ ]
241
+ }
242
+ },
243
+ {
244
+ "el": "f-button",
245
+ "col": {},
246
+ "attr": {
247
+ ":label": "'send by API - ' + DGroup",
248
+ ":action": [{ "API": "https://ws.fui.vn/api/messageSend/{{DGroup}}" }]
249
+ }
250
+ }
234
251
  ]
235
252
  },
236
- { "prop": "", "cols": [ { "el": "div", "col": {}, "attr": {}, "innerHTML": "{{webSocketState}}" } ] }
253
+ { "prop": "", "cols": [{ "el": "div", "col": {}, "attr": {}, "innerHTML": "{{webSocketState}}" }] }
237
254
  ]
238
255
  }
239
256
  ],
@@ -243,12 +260,12 @@ Dùng cách này khi muốn gửi tin nhắn từ một action/nơi không tiệ
243
260
 
244
261
  ---
245
262
 
246
- ## 8. Checklist khi thêm real-time messaging vào module
263
+ ## 8. Checklist
247
264
 
248
- - [ ] `file_import_list` kiểm tra đã có `jquery.signalR-2.4.1.min.js` + `signalr/hubs` chưa
249
- - [ ] Thiếu → `file_import_new` cả 2, đúng thứ tự sort (signalR trước, hubs sau)
250
- - [ ] Khai báo `data[].webSocket = { groupName: [] | null | {} }` — chọn kiểu giá trị theo nhu cầu (lịch sử vs. giá trị mới nhất)
251
- - [ ] Group cần join runtime/điều kiện → `FUN: webSocketJoinGroup` (không dùng được `timeout`/`callBackFunc` qua `IN`, cần thì dùng `EXE`)
252
- - [ ] Nhận tin: `watch` (key literal, không `{{ }}`) hoặc bind trực tiếp `v-model="webSocket.<group>"`
253
- - [ ] Gửi tin: `FUN: webSocket_Send` (object/array payload) hoặc `EXE` khi cần đọc `vueData` động, hoặc `API: https://ws.fui.vn/api/messageSend/<group>` nếu muốn gửi qua HTTP thay vì client SignalR
254
- - [ ] Không quên `vueData.webSocketState` nếu cần hiển thị trạng thái kết nối cho user
265
+ - [ ] `fui import list`: are `jquery.signalR-2.4.1.min.js` + `signalr/hubs` present?
266
+ - [ ] Missing → `fui import new` both, sort order signalR first, hubs second
267
+ - [ ] Declare `data[].webSocket = { groupName: [] | null | {} }` — value type by need (history vs latest)
268
+ - [ ] Runtime/conditional groups → `FUN: webSocketJoinGroup` (no `timeout`/`callBackFunc` via `IN`; use `EXE` if needed)
269
+ - [ ] Receive: `watch` (literal key, no `{{ }}`) or bind `v-model="webSocket.<group>"`
270
+ - [ ] Send: `FUN: webSocket_Send` (object/array), `EXE` for dynamic `vueData`, or `API: https://ws.fui.vn/api/messageSend/<group>` for HTTP instead of client SignalR
271
+ - [ ] Use `vueData.webSocketState` if the user needs to see connection status