@fui-org/fui-cli 1.3.2 → 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 +3 -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-y8an39cn.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,203 +0,0 @@
1
- # f-sheet — Data-Entry Grid (AG Grid)
2
-
3
- > Owns: **`f-sheet` (AG Grid): import, props, columns, built-in cell renderers, range select/fill/clipboard, API wiring**.
4
-
5
- Excel-style entry grid, **entirely AG Grid Community** — customize with AG Grid knowledge (https://www.ag-grid.com/, Community). FUI adds: toolbar (add/delete/save/export Excel), change tracking, range select + fill handle + TSV copy/paste, API wiring.
6
-
7
- > **vs `f-table`** (rows edited via dialog form): `f-sheet` edits **in cells**, for bulk entry (paste rows, fast multi-cell edits). Never hand-write an entry grid as `uc-*.vue`.
8
-
9
- > Source: `scripts/fsheet.js`, **one file for V2 and V3** (`beforeDestroy` + `beforeUnmount`). Template uses **Vuetify 3** props/classes (`variant="text"`, `density="comfortable"`, `text-primary`, `text-subtitle-1`, `text-caption`); on V2 the toolbar works but styling may be off. Uses `ajaxCALL`/`confirm`/`jsonToExcel` ([component-design.md](component-design.md) §12).
10
-
11
- ---
12
-
13
- ## Required import
14
-
15
- Exactly 2 files, **in this order**, via `fui import new` (missing → unusable):
16
-
17
- ```
18
- /include/ag-grid/ag-grid-community.min.js
19
- /include/ag-grid/fsheet.js
20
- ```
21
-
22
- `fui import list` (projectId + moduleId) → `fui import new` missing ones; removing f-sheet and nothing else uses them → `fui import delete`. See [component-quickref.md](component-quickref.md).
23
-
24
- ---
25
-
26
- ## Props
27
-
28
- | Prop (kebab-case in `attr`) | Type | Meaning |
29
- | --------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------ |
30
- | `label` | string | Title, shown on toolbar |
31
- | `:columns` | array | **AG Grid colDefs**. Empty → inferred from `items[0]` (`{ field, headerName: field }`) |
32
- | `:items` | array | rowData |
33
- | `row-key` | string | PK field (default `id`) — tracks changed/new/deleted rows |
34
- | `:height` | number | empty = auto-grow; `<=0` = fill to screen bottom leaving `abs(height)` px; `>0` = fixed px |
35
- | `:allow-add` | bool | Add row button |
36
- | `:allow-delete` | bool | Checkbox column + Delete button |
37
- | `readonly` | bool | Lock all editing (also fill handle) |
38
- | `update-api` | string | tAPI endpoint **saving each changed row** (`ajaxCALL`, auto-prepends `apiDomain`). Missing → Save `disabled` |
39
- | `delete-api` | string | tAPI endpoint deleting one row |
40
- | `:save-extra` | object | Extra params **merged into every save/delete payload** (e.g. `{ LopID: vueData.lopID }`); via `mapData`, dynamic `vueData.x` allowed |
41
- | `:default-item` | object | New-row template on Add; via `mapData`, dynamic `vueData.x` allowed (not only literals) |
42
-
43
- ---
44
-
45
- ## Columns = AG Grid colDef
46
-
47
- | Key | Meaning |
48
- | ----------------------------------- | ----------------------------------------------------------------------------------------------- |
49
- | `field` / `headerName` | data field / label |
50
- | `editable` | **on by default** (`defaultColDef.editable: !readonly`); read-only column → `"editable": false` |
51
- | `width` / `minWidth` / `flex` | default `flex:1`, `minWidth:80` |
52
- | `pinned` | `'left'`/`'right'` |
53
- | `sortable` / `filter` / `resizable` | all on by default |
54
- | `valueFormatter` / `valueGetter` | AG Grid functions |
55
- | `cellEditor` | AG Grid built-in name (e.g. `agDateStringCellEditor`) or own global function name |
56
- | `cellRenderer` | one of 4 f-sheet built-ins (below) or own global function name |
57
- | `type` | **registered columnType name — only `"number"` / `"date"`**, not a free data type |
58
-
59
- Other features (cellClassRules, aggregation...) → AG Grid docs; colDef passes straight through.
60
-
61
- ### columnType: only `number` and `date`
62
-
63
- `type` references `gridOptions.columnTypes`; only two registered (`FS_COLUMN_TYPES`, top of `scripts/fsheet.js`). Unregistered name (`"date_time"`, `"currency"`, `"text"`, `"string"`, `"boolean"`…):
64
-
65
- - AG Grid **ignores it** (logs only `warning #36 colDef.type 'x' does not correspond to defined gridOptions.columnTypes`) → raw text, **no** format/parse/filter.
66
- - f-sheet adds a `console.error` naming column + valid names (scans group `children` too) — **visible in `fui module simulate --render`**; AG Grid's warning isn't.
67
-
68
- Other types (currency, percent, datetime…) → declare `valueFormatter`/`valueParser`/`filter` on the colDef, never a new `type`.
69
-
70
- ### Built-in cell renderers
71
-
72
- Use `"cellRenderer": "name"` + `cellRendererParams`; nothing to declare in `script.js`:
73
-
74
- | Renderer | Purpose | `cellRendererParams` |
75
- | ---------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
76
- | `fsHtmlCellRenderer` | raw HTML `value` (like f-table `t-html`) | _(none)_ |
77
- | `fsLinkCellRenderer` | link | `urlField`/`textField` (row field for URL/label, default `value`), `icon` (mdi, optional), `target`: `'dialog'` → `openWindow({id: wid, title, url, onclose})`, else `<a>` target (default `_blank`) |
78
- | `fsButtonCellRenderer` | small button | `label`, `icon` (mdi, optional), `color` (default `#1976d2`); click: `url` → `openWindow({id: wid, title, url, onclose})`, no `url` → `tableActionEvent({action, item: data})` |
79
- | `fsMenuCellRenderer` | `⋮` per-row dropdown | `items`: array `{icon, text, color, action}` or `function(params)` returning it (per-row); `icon` for trigger (default `mdi-dots-vertical`). Click → `tableActionEvent({action: item.action, item: data})` |
80
-
81
- `openWindow`/`tableActionEvent` are FUI globals (as in f-table `t-link`/`t-button`/`t-menu`). **Own global functions** also work for `cellRenderer`/`cellEditor`/`cellEditorParams`/`cellRendererParams`: define in `script.js`, reference by string name; auto-registered (incl. group `children`).
82
-
83
- ### `"type": "number"` — auto-attached (only if colDef doesn't declare it)
84
-
85
- | Key | Behavior |
86
- | ---------------- | -------------------------------------------------------------------------------------------------------------------- |
87
- | `cellStyle` | right-aligned |
88
- | `valueFormatter` | `toLocaleString('en-US', { maximumFractionDigits: 10 })` → `1,234,567.89`; **display only**, editor shows raw number |
89
- | `valueParser` | empty/`null`/`undefined` → real `null` (not `""`); numeric string → `Number`; invalid → `null`. Also on Excel paste |
90
- | `filter` | `agNumberColumnFilter` |
91
-
92
- ### `"type": "date"` — auto-attached (only if colDef doesn't declare it)
93
-
94
- | Key | Behavior |
95
- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
96
- | `cellStyle` | centered |
97
- | `valueFormatter` | `dd/mm/yyyy`; non-date → **raw string** shown (junk stays visible) |
98
- | `valueParser` | accepts `dd/mm/yyyy`, `dd-mm-yyyy`, `yyyy-mm-dd`, ISO `2026-08-15T00:00:00` → stores `yyyy-mm-dd`; empty/invalid → `null`. Also on Excel paste |
99
- | `filter` | `agDateColumnFilter` |
100
-
101
- **Value contract (what the SP gets):** **`yyyy-mm-dd`** or `null` — matches `f-date` and tAPI `OPENJSON ... NgayX date '$.NgayX'`. Invalid (`31/02/2026`, `15/13/2026`) → `null`, no rollover. **Copy/Excel export use the raw value** (`2026-08-15`) — intentional, lossless round-trip.
102
-
103
- #### Date picker
104
-
105
- `type: "date"` adds **no editor** (text typing). For a picker:
106
-
107
- ```json
108
- { "field": "NgayThi", "headerName": "Ngày thi", "type": "date", "cellEditor": "agDateStringCellEditor" }
109
- ```
110
-
111
- AG Grid built-in. Reads the **raw value**: ISO datetime (`2026-08-15T00:00:00`) opens **empty** until edited once → SP returns `CONVERT(date, NgayThi)`. Not attached by default because Enter would write `null` over the date.
112
-
113
- > **`fsheetDate` was removed.** Old `"cellEditor": "fsheetDate"` → `"type": "date"` (+ `agDateStringCellEditor`). String `cellEditor`/`cellRenderer` resolves only if `window[name]` is a function; unknown names **silently** fall back to the text editor.
114
-
115
- ### `:height`
116
-
117
- | Value | Behavior |
118
- | --------- | ------------------------------------------------------------- |
119
- | _(empty)_ | **auto-grow** — `domLayout: 'autoHeight'`, no inner scrollbar |
120
- | `<= 0` | **fill to screen bottom, leaving `abs(height)` px** |
121
- | `> 0` | fixed px (e.g. `":height": 300`) |
122
-
123
- `<=0` = `window.innerHeight - getBoundingClientRect().top - abs(height)` (min 200px), measured after layout, updated on `window.resize` — only the bottom gap matters.
124
-
125
- - Main table: `":height": -16` (scrolls inside grid; lint D9 warns if `:height` missing).
126
- - Dialog: **positive** px.
127
- - Short/lookup tables: omit (auto-grow, exempt from D9). ⚠ Auto-grow renders every row, no virtualization — hundreds of rows → set `:height`.
128
- - ⚠ Negative ≠ "viewport minus N": old `-220` → `-16`, else ~200px short.
129
-
130
- ---
131
-
132
- ## API wiring — response contract
133
-
134
- Called as `ajaxCALL(api, { ...saveExtra, ...row })`. **SP returns one row:**
135
-
136
- ```
137
- SELECT Success = 1, Message = N'Đã lưu' -- hoặc Success = 0 khi lỗi nghiệp vụ
138
- ```
139
-
140
- Reads `res.data[0]`: `Message` → snackbar, `Success` → marks success. See [tapi-reference.md](tapi-reference.md).
141
-
142
- - **New rows**: temp **negative** key (−1, −2…), **light green**; on save key → `0` in payload → SP INSERTs.
143
- - **Edited cells**: **light yellow**; toolbar shows "N rows changed".
144
- - Save runs **per row, in parallel**; all OK → baseline refreshed, marks cleared, `saved` emitted.
145
- - Delete existing rows: with `delete-api` → `confirm`, then API per row; without → local, no prompt. New rows → always local, no prompt, no API.
146
-
147
- ---
148
-
149
- ## Built-in (don't code)
150
-
151
- - Inline edit + change tracking.
152
- - Range select: drag or Shift+click.
153
- - Fill handle: drag bottom-right square to repeat the source block (along the longer drag direction).
154
- - TSV Copy/Cut/Paste (Excel/Google Sheets): Ctrl+C/X/V in grid, or right-click (Cut/Copy/Copy with Headers/Paste).
155
- - Escape clears selection.
156
- - Toolbar: **Export Excel** (always, `jsonToExcel`) · **Add** (`allow-add`) · **Save** (when changes; `disabled` without `update-api`) · **Discard changes** (when changes) · **Delete selected** (`allow-delete`, multi-select, only with selection).
157
-
158
- ## Events & methods (`$refs`)
159
-
160
- - Emit: `change(data, field, value)` (each cell change) · `save(rows)` (Save clicked, before API) · `saved` (all saved).
161
- - Methods: `getChangedRows()` · `addRow()` · `deleteRow(row)` · `deleteSelected()` · `discardAll()` · `exportExcel()`.
162
-
163
- ---
164
-
165
- ## module.json example
166
-
167
- ```json
168
- {
169
- "el": "f-sheet",
170
- "w": 12,
171
- "attr": {
172
- "label": "Bảng điểm",
173
- ":items": "dsDiem",
174
- "row-key": "DiemID",
175
- ":columns": [
176
- { "field": "MaSV", "headerName": "Mã SV", "editable": false, "width": 120, "pinned": "left" },
177
- { "field": "HoTen", "headerName": "Họ tên", "editable": false },
178
- { "field": "Diem", "headerName": "Điểm", "type": "number" },
179
- { "field": "NgayThi", "headerName": "Ngày thi", "type": "date" },
180
- { "field": "GhiChu", "headerName": "Ghi chú" }
181
- ],
182
- ":allow-add": true,
183
- ":allow-delete": true,
184
- "update-api": "Diem_Save",
185
- "delete-api": "Diem_Delete",
186
- ":save-extra": { "LopID": "vueData.lopID" },
187
- ":default-item": { "Diem": 0 },
188
- ":height": -16
189
- }
190
- }
191
- ```
192
-
193
- - `dsDiem` loaded first by a `data[]` action (`API`/`OUT`).
194
- - `Diem_Save`/`Diem_Delete`: tAPI SPs returning `{ Success, Message }` per row.
195
- - Detail window from toolbar/column button/menu → `openWindow`: [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md) §5.
196
-
197
- ## Checklist
198
-
199
- 1. Both **AG Grid files** imported via `fui import new`, in order (`ag-grid-community.min.js` then `fsheet.js`).
200
- 2. `row-key` = real PK; `update-api`/`delete-api` SPs return `{ Success, Message }`.
201
- 3. AG Grid colDef; link/button/menu → 4 built-in renderers; `type: "number"`/`"date"` only (no manual `cellStyle`/`valueFormatter`/`valueParser`); other types → `valueFormatter`/`valueParser`.
202
- 4. `:height`: main `-16`, dialog positive, empty only for short tables.
203
- 5. Don't rebuild grid/CSS — config via `columns`; range select/fill/copy-paste are built in.
@@ -1,313 +0,0 @@
1
- # Full-Stack Module Workflow
2
-
3
- > Owns: **the 6-step UI-first fullstack module workflow (analyze → UI with hard-coded data → lock contract → SP → API wiring → static verification)** and **the Design Review Gate procedure** (SKILL.md §3 holds only the rule "must pass the gate"; the steps live here). tAPI conventions: [tapi-reference.md](tapi-reference.md); multi-module design: [system-design.md](system-design.md).
4
-
5
- **UI-first:** build UI (FUI) with hard-coded data, confirm visuals with `fui module simulate --render`, THEN lock the API (tAPI) contract and write SPs. Fixing data shape while it is a literal in `data[]` is far cheaper than after `IN`/`OUT`/`watch` depend on it. No step calls a real API/SP to "test" — verification = read + reason + mock ([verification.md](verification.md)). Read [tapi-reference.md](tapi-reference.md) before step 3.
6
-
7
- ---
8
-
9
- ## Step 1 — Analyze requirements
10
-
11
- **1.1 Business need:** list / search / filter? add / edit / delete? report / statistics? workflow (approve, reject, status)?
12
-
13
- **1.2 Data (initial estimate — locked in step 3):** main entity and linked entities; which fields are displayed, which are input.
14
-
15
- **1.3 Permissions and auth:**
16
-
17
- - Login required? (no → use `AUTH_` in the SP)
18
- - Rights by `sys_SystemRight`? → handle in the SP body
19
-
20
- **Output:** feature list + main entity names (types/column names not yet locked).
21
-
22
- ---
23
-
24
- ## Step 1b — Design Review Gate (mandatory, before the first line of code)
25
-
26
- SKILL.md §3 = the **rule** (every new system/page/function passes); this = the **procedure**, for fullstack and UI-only modules.
27
-
28
- 1. **Business efficiency** — steps to finish the task, data truly needed, redundant steps.
29
- 2. **Project patterns** — `fui module list` + `fui module get` on similar modules (layout, control naming, action placement, filter/table/dialog). Match existing pages; **never invent a new style when a convention exists**.
30
- 3. **Sketch the UI** in words/rough layout — controls, placement, dialog/form flow.
31
- 4. **Self-review** — friendly? step count? Add/Edit/Delete, filters, pagination where expected? consistent? Issues → **fix the sketch first**, never in code.
32
- 5. Only then write code (step 2+).
33
-
34
- **Choose the build approach:**
35
-
36
- | Approach | When |
37
- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
38
- | **A — plain module.json** | Simple page: data display, single-table CRUD, basic form. Fewer files, easy to maintain. |
39
- | **B — Vue component (`uc-*.vue`)** | Complex UI: multi-area dashboard, custom layout, UI logic reused in many places, or `controls` over ~200 lines ([component-design.md](component-design.md)). |
40
-
41
- > Unspecified → **propose** one with a short reason and **wait for confirmation**; never choose silently.
42
-
43
- **Component priority**: `f-*` first (`f-button` over `v-btn`, `f-table` over `v-data-table` for EVERY table, `f-echart` for every chart); `v-*` only when no FUI equivalent exists. → [component-quickref.md](component-quickref.md)
44
-
45
- **Design System gate** (parallel): decide App-mode vs Web-mode and check `{projectId}/DESIGN.md` **before** sketching — separate aesthetic rules; never mix. → [design-modes.md](design-modes.md)
46
-
47
- **Output:** self-reviewed sketch, approach A/B locked (user-confirmed if we proposed it), aesthetic mode decided.
48
-
49
- ---
50
-
51
- ## Step 2 — Build UI with hard-coded data
52
-
53
- Whole UI on **literal** data in `data[]`, no API yet. Structure: [module-json-anatomy.md](module-json-anatomy.md), [controls-patterns.md](controls-patterns.md).
54
-
55
- ```json
56
- "data": [
57
- { "keyword": "" },
58
- { "classId": "" },
59
- { "studentList": [
60
- { "StudentID": "SV001", "FullName": "Nguyễn Văn A", "ClassID": "C01", "ClassName": "Lớp 10A", "GPA": 8.5 },
61
- { "StudentID": "SV002", "FullName": "Trần Thị B", "ClassID": "C02", "ClassName": "Lớp 10B", "GPA": 7.2 }
62
- ] },
63
- { "form": {} },
64
- { "dialogOpen": false }
65
- ]
66
- ```
67
-
68
- `f-table`/`v-dialog`/form patterns: [component-table.md](component-table.md), [ui-templates.md](ui-templates.md).
69
-
70
- **Visual QA now:** `fui module simulate --render` (vueData override: `fui module simulate --help`). Read screenshot + DOM audit; fix display bugs (horizontal overflow, broken dialog, missing labels...) before moving on.
71
-
72
- **Output:** `module.json` with complete layout, running on hard-coded data, clean `--render` QA.
73
-
74
- ---
75
-
76
- ## Step 3 — Lock data shape + design API endpoints
77
-
78
- From the step-2 fields (visually confirmed before becoming an SP contract), lock exact field names + types (→ SQL column types) and the source entity/table of each. Map features to SPs as `spAPI_{Entity}{Action}`; full naming (`spAPI_*` / `spAPI_AUTH_*` / `spAPIFILE_*`, `@url1_`, response contract) is in [tapi-reference.md](tapi-reference.md).
79
-
80
- | Feature | SP name | Main params |
81
- | ----------------- | ---------------------------- | ---------------------- |
82
- | List | `spAPI_{Entity}List` | Filter params (custom) |
83
- | Detail | `spAPI_{Entity}Get` | `@url1_{EntityID}` |
84
- | Insert | `spAPI_{Entity}Insert` | Entity fields |
85
- | Update | `spAPI_{Entity}Update` | ID + fields to update |
86
- | Delete | `spAPI_{Entity}Delete` | `@url1_{EntityID}` |
87
- | Lookup / dropdown | `spAPI_AUTH_{Entity}Options` | No auth, returns list |
88
-
89
- **Example — "Student management"** (matches step-2 fields `StudentID`, `FullName`, `ClassID`, `ClassName`, `GPA`):
90
-
91
- ```
92
- spAPI_StudentList -- danh sách, lọc theo ClassID + Keyword — trả đúng các cột đã hiển thị ở bước 2
93
- spAPI_StudentGet -- chi tiết theo url1=StudentID
94
- spAPI_StudentInsert -- thêm mới
95
- spAPI_StudentUpdate -- cập nhật
96
- spAPI_StudentDelete -- xoá theo url1=StudentID
97
- spAPI_AUTH_ClassOptions -- dropdown danh sách lớp (không cần auth)
98
- ```
99
-
100
- **Output:** full SP list; names + params + response shape match the fields used in the UI.
101
-
102
- ---
103
-
104
- ## Step 4 — Write SQL stored procedures
105
-
106
- Per tAPI rules ([tapi-reference.md](tapi-reference.md)) and the step-3 contract.
107
-
108
- **SELECT (list):**
109
-
110
- ```sql
111
- CREATE PROCEDURE spAPI_StudentList
112
- @Keyword nvarchar(200) = NULL,
113
- @ClassID varchar(20) = NULL,
114
- @sys_UserID varchar(9)
115
- AS BEGIN
116
- SELECT s.StudentID, s.FullName, s.ClassID, c.ClassName, s.GPA
117
- FROM tblStudent s
118
- JOIN tblClass c ON s.ClassID = c.ClassID
119
- WHERE (@Keyword IS NULL OR s.FullName LIKE '%' + @Keyword + '%')
120
- AND (@ClassID IS NULL OR s.ClassID = @ClassID)
121
- ORDER BY s.FullName
122
- END
123
- ```
124
-
125
- **INSERT:**
126
-
127
- ```sql
128
- CREATE PROCEDURE spAPI_StudentInsert
129
- @StudentID varchar(9),
130
- @FullName nvarchar(100),
131
- @ClassID varchar(20),
132
- @sys_UserID varchar(9)
133
- AS BEGIN
134
- IF EXISTS (SELECT 1 FROM tblStudent WHERE StudentID = @StudentID)
135
- BEGIN
136
- RAISERROR(N'Mã sinh viên đã tồn tại', 16, 1)
137
- RETURN
138
- END
139
- INSERT INTO tblStudent (StudentID, FullName, ClassID, CreateUser, CreateTime)
140
- VALUES (@StudentID, @FullName, @ClassID, @sys_UserID, GETDATE())
141
- SELECT mess = N'Thêm thành công', id = @StudentID
142
- END
143
- ```
144
-
145
- **DELETE:**
146
-
147
- ```sql
148
- CREATE PROCEDURE spAPI_StudentDelete
149
- @url1_StudentID varchar(9),
150
- @sys_UserID varchar(9)
151
- AS BEGIN
152
- DELETE FROM tblStudent WHERE StudentID = @url1_StudentID
153
- SELECT mess = N'Đã xoá', id = @url1_StudentID
154
- END
155
- ```
156
-
157
- **GET (detail, convert_to_object):**
158
-
159
- ```sql
160
- CREATE PROCEDURE spAPI_StudentGet
161
- @url1_StudentID varchar(9),
162
- @sys_UserID varchar(9)
163
- AS BEGIN
164
- SELECT convert_to_object = '',
165
- s.StudentID, s.FullName, s.ClassID, c.ClassName, s.GPA, s.Email
166
- FROM tblStudent s
167
- JOIN tblClass c ON s.ClassID = c.ClassID
168
- WHERE s.StudentID = @url1_StudentID
169
- END
170
- ```
171
-
172
- > `convert_to_object = ''` when the SP returns exactly one record → response is an object instead of `data[0][0]`.
173
-
174
- Deploy with `fui sp save` + `fui sp deploy` (never call the endpoint to "try it" — see step 6).
175
-
176
- **NEVER put `GRANT EXECUTE` in the .sql file.** A `CREATE PROCEDURE` body runs **to end of batch**; `_sql_execute_NonQuery` sends one batch and **does not support `GO`** (client directive, not T-SQL). A `GRANT` after `END` is **swallowed into the SP body** — runs on every call, never grants at deploy. No error; the endpoint just returns a permission error while the SP looks right.
177
-
178
- `fui sp deploy` **auto-grants** new procedures (not FUNCTIONs — functions need no EXECUTE for `[public]`), then **re-checks `sys.database_permissions`** and reports:
179
-
180
- - `✓ [public] đã có quyền EXECUTE trên "..."` — done.
181
- - `❗ [public] CHƯA có quyền EXECUTE ...` — the output prints a runnable command; copy verbatim:
182
- `fui exec "GRANT EXECUTE ON [dbo].[spAPI_StudentList] TO [public] AS [dbo]" --db ...`
183
- - Grant failed but deploy succeeded → **the SP is already on the server**; run the GRANT above, **do not redeploy**.
184
-
185
- Deploying via `fui exec` with `CREATE PROCEDURE` → **no auto-grant, no tAPI param-cache clear**; fui warns and names the two commands to run next.
186
-
187
- **Output:** all step-3 SPs deployed.
188
-
189
- ---
190
-
191
- ## Step 5 — Replace hard-coded data with API bindings
192
-
193
- Keep the step-2 layout; replace `data[]` literals with named `IN`/`OUT`/`CALLBACK` actions on the deployed SPs.
194
-
195
- **5.1 Actions replacing hard-coded data**
196
-
197
- ```json
198
- "data": [
199
- { "keyword": "" },
200
- { "classId": "" },
201
- { "studentList": [] },
202
-
203
- {
204
- "fetchStudents": {
205
- "API": "/{apiName}/StudentList",
206
- "IN": { "Keyword": "vueData.keyword", "ClassID": "vueData.classId" },
207
- "OUT": "studentList"
208
- }
209
- },
210
- {
211
- "saveStudent": {
212
- "API": "/{apiName}/StudentInsert",
213
- "IN": {
214
- "StudentID": "vueData.form.StudentID",
215
- "FullName": "vueData.form.FullName",
216
- "ClassID": "vueData.form.ClassID"
217
- },
218
- "CALLBACK": [
219
- { "CALL": "vueData.fetchStudents" },
220
- { "MESS": "Thêm thành công" },
221
- { "EXE": "vueData.dialogOpen = false" }
222
- ]
223
- }
224
- },
225
- {
226
- "deleteStudent": {
227
- "API": "/{apiName}/StudentDelete/`{{vueData.selectedID}}",
228
- "CONFIRM": "Bạn có chắc muốn xoá sinh viên này?",
229
- "CALLBACK": { "CALL": "vueData.fetchStudents" }
230
- }
231
- }
232
- ]
233
- ```
234
-
235
- **5.2 `watch` to reload when filters change**
236
-
237
- ```json
238
- "watch": {
239
- "keyword": { "CALL": "vueData.fetchStudents" },
240
- "classId": { "CALL": "vueData.fetchStudents" }
241
- }
242
- ```
243
-
244
- **5.3** Append `{ "CALL": "vueData.fetchStudents" }` at the end of `data[]` to auto-load on open.
245
-
246
- **Output:** `module.json` fully wired, layout unchanged from step 2.
247
-
248
- ---
249
-
250
- ## Step 6 — Static verification and finish
251
-
252
- Pre-publish checklist — **no step calls a real API/SP/DML**:
253
-
254
- **API/SP** — full checklist (naming, params, security, `GRANT EXECUTE`, deploy workflow): [verification.md](verification.md). Here:
255
-
256
- - [ ] `AUTH_` on endpoints that need no login
257
- - [ ] `fui sp verify` per new SP — reads the real body, infers response shape/error contract, checks params, emits a `--mocks` snippet (no endpoint call)
258
- - [ ] **Write** APIs: read the body and reason (WHERE, transaction, RAISERROR) — no trial run
259
-
260
- **module.json:**
261
-
262
- - [ ] Each action's `API` path matches `/{apiName}/{FunctionName}`
263
- - [ ] `IN` params match SP param names exactly (case-sensitive)
264
- - [ ] Route params (url1, url2) go via the URL, not `IN`
265
- - [ ] `OUT` receives the right type (array vs object per SP) — check against the shape inferred by `fui sp verify`
266
- - [ ] `watch` declared for auto-reload filters
267
- - [ ] `data[]` declares the initial fetch action and ends with a startup `{ "CALL": ... }`
268
-
269
- **Final verification, in order:** `fui module validate` (syntax/rule errors) → `fui module outline` (text review of UI structure) → `fui module simulate --mocks '{...}'` using the `fui sp verify` snippet (action/watch/dialog flow) → if display is still in doubt, a last `fui module simulate --render` with the same `--mocks`. (`fui module check` runs validate + outline + simulate in one step.)
270
-
271
- **Output:** module works end-to-end, UI ↔ API connected, zero real calls during verification.
272
-
273
- ---
274
-
275
- ## Complete output example
276
-
277
- ### SPs (step 4)
278
-
279
- Each SP is **its own file** via `fui sp save` → `fui sp deploy`. No GRANT in the file, no `GO` (step 4); tAPI params **must not have default values** — `fui sp deploy` hard-blocks them.
280
-
281
- ```sql
282
- -- 1. Danh sách (file spAPI_StudentList.sql)
283
- CREATE PROCEDURE spAPI_StudentList @Keyword nvarchar(200), @ClassID varchar(20), @sys_UserID varchar(9)
284
- AS BEGIN
285
- SELECT s.StudentID, s.FullName, s.ClassID, s.GPA
286
- FROM tblStudent s WHERE (@Keyword = '' OR s.FullName LIKE '%'+@Keyword+'%') AND (@ClassID = '' OR s.ClassID = @ClassID)
287
- END
288
- ```
289
-
290
- ```sql
291
- -- 2. Thêm mới (file spAPI_StudentInsert.sql)
292
- CREATE PROCEDURE spAPI_StudentInsert @StudentID varchar(9), @FullName nvarchar(100), @ClassID varchar(20), @sys_UserID varchar(9)
293
- AS BEGIN
294
- INSERT INTO tblStudent(StudentID, FullName, ClassID, CreateUser, CreateTime) VALUES(@StudentID, @FullName, @ClassID, @sys_UserID, GETDATE())
295
- SELECT mess = N'Thêm thành công'
296
- END
297
- ```
298
-
299
- ### module.json actions (step 5)
300
-
301
- ```json
302
- "fetchStudents": { "API": "/me/StudentList", "IN": { "Keyword": "vueData.keyword" }, "OUT": "studentList" },
303
- "addStudent": { "API": "/me/StudentInsert", "IN": { "StudentID": "vueData.form.StudentID", "FullName": "vueData.form.FullName", "ClassID": "vueData.form.ClassID" }, "CALLBACK": [ { "CALL": "vueData.fetchStudents" }, { "MESS": "Đã thêm" } ] }
304
- ```
305
-
306
- ---
307
-
308
- ## Key notes
309
-
310
- - **`{apiName}`** in the API path is the app's tAPI apiName (e.g. `me`, `lms`, `hr`). Ask the user if unknown. **A project may use several apiNames** (one database each) — each alias needs its own `fui db add`, and the alias in the path is the `--db` argument of DB commands ([db-workflow.md](db-workflow.md) §4).
311
- - **Route params** (`@url1_`) go via the URL, not `IN`, using the backtick prefix: `"API": "/me/StudentDelete/\`{{vueData.selectedID}}"`
312
- - **SP with multiple SELECTs** → `data[0]`, `data[1]`... — declare explicitly in `OUT` or split with `EXE`.
313
- - **`convert_to_object = ''`** → flat object response: read `vueData.studentDetail.FullName`, not `vueData.studentDetail[0].FullName`.
@@ -1,117 +0,0 @@
1
- # module.json `data[]` — State declaration & value resolution
2
-
3
- > Owns: **`data[]` declaration patterns: three element kinds, order state → named action → auto-startup**. Value resolution: [controls-patterns.md](controls-patterns.md) §0; nested key syntax (dot/bracket/`#PARENT`): [controls-patterns.md](controls-patterns.md) §1; cross-window: [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md).
4
-
5
- FUI processes `data[]` **sequentially top to bottom**; each object assigns/runs based on how its value is written. Examples come from `ex/ex-v3-1SetData` + `ex-v3-2CauTruc` (official FUI demo modules).
6
-
7
- > Overview of the 4 blocks: [module-json-anatomy.md](module-json-anatomy.md). Engine: [platform-architecture.md](platform-architecture.md) §5.
8
-
9
- ---
10
-
11
- ## 1. Three element kinds in `data[]`
12
-
13
- ```json
14
- "data": [
15
- { "keyword": "", "dsUser": [], "dlOpen": false }, // (a) khởi tạo state
16
- { "getUser": { "API": "...", "IN": {...}, "OUT": "dsUser" } }, // (b) named action
17
- { "API": "...", "OUT": "dsInit" }, // (c) action tự chạy khi load
18
- { "CALL": "getUser" } // (c) auto-startup
19
- ]
20
- ```
21
-
22
- - (a) no action key → creates reactive var in `vueData`.
23
- - (b) key is a **function name** → reusable action (invoke via `CALL`).
24
- - (c) object is itself an action (`API`/`CALL`/`FUN`…) → **runs on page load**, in order.
25
-
26
- ---
27
-
28
- ## 2. Value resolution — HOW you write it decides the VALUE (common mistake)
29
-
30
- Full rules (`getVueData`: backtick / `{{ }}` / string with spaces / string without spaces → JS eval) live in [controls-patterns.md §0](controls-patterns.md#0-value-resolution-rules-fundamental) — Tier A, always loaded; not repeated here.
31
-
32
- > **Consequence for `data[]`:** to assign a **fixed string without spaces** (id, url, code) → **wrap in backticks** (``"`winUser"``) or single quotes (`"'winUser'"`), otherwise FUI treats it as a variable. This is the most common `data`/`IN` bug.
33
-
34
- ---
35
-
36
- ## 3. Keys as nested paths (deep set)
37
-
38
- The left-hand key is also resolved (dot / bracket / dynamic index / `#PARENT`) — full syntax in [controls-patterns.md §1](controls-patterns.md#1-data-keys-and-assignment) _(Tier A, already loaded)_.
39
-
40
- ---
41
-
42
- ## 4. Auto-run action: inline `IN`/`OUT`/`API` on load
43
-
44
- A standalone action object in `data[]` runs on load; no name needed:
45
-
46
- ```json
47
- { "API": "https://tapi.example.vn/me/auth/ApplicationList", "OUT": "Mang" },
48
- { "IN": { "ABC": "`gdsgdsg", "DEF": "val5", "CBV": 1233 }, "OUT": "DoiTuong" }
49
- ```
50
-
51
- → loads the page's initial data (catalogs, dropdowns, table data).
52
-
53
- ---
54
-
55
- ## 5. Named action + CALLBACK (reusable)
56
-
57
- ```json
58
- {
59
- "cmdSETData": [{ "MESS": "Thông báo" }, { "#Win1.GiaTriSetTuCHA": "Giá trị set từ CHA" }],
60
- "apiChart": {
61
- "API": "/ts/TS_Report_ForChart/{{ab}}",
62
- "IN": { "sachID": "apiLoadFBUser", "modeY": 12345 },
63
- "OUT": "DSDoiQuaTang",
64
- "CALLBACK": { "AAA": "val2" }
65
- }
66
- }
67
- ```
68
-
69
- Invoke with `{ "CALL": "cmdSETData" }` from `controls`, `watch` or another action. `CALLBACK` runs after the API completes (see [ui-patterns.md](ui-patterns.md) §7).
70
-
71
- ---
72
-
73
- ## 6. `watch` with `v_old` — previous value
74
-
75
- In `watch`, `v_old.<field>` gives the value **before the change**:
76
-
77
- ```json
78
- "watch": {
79
- "val4": {
80
- "API": "ts/auth/QuanHuyen_Select",
81
- "IN": { "MaTinhTP": "tinhID" },
82
- "OUT": "DS_QUAN_HUYEN",
83
- "CALLBACK": { "BAC": "v_old.abcdeID" }
84
- },
85
- "taoID": { "dungthu": "v_old.taoID" }
86
- }
87
- ```
88
-
89
- `v_old`, `deep-watch` details: [watcher-patterns.md](watcher-patterns.md).
90
-
91
- ---
92
-
93
- ## 7. Cross-window exchange inside `data[]`
94
-
95
- `data[]` can read/write other windows via `#PARENT.*` / `#WinID.*` keys/values — place **before** fetch CALLs:
96
-
97
- ```json
98
- "data": [
99
- {
100
- "LayDuLieuWinCha": "#PARENT.Win1", // đọc data cha
101
- "#PARENT.abcdef": 1234567890, // ghi xuống cha
102
- "#Win1": { "CALL": "cmdTHONGBao" } // gọi lệnh ở Win1
103
- }
104
- ]
105
- ```
106
-
107
- Full: [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md).
108
-
109
- ---
110
-
111
- ## 8. `data[]` checklist
112
-
113
- 1. Initialize **every** state var in the first object (including `[]`, `false`, `null`) so reactivity works.
114
- 2. Named actions in the middle; auto-startup (`CALL`/`API`) **last**.
115
- 3. Fixed space-free strings → **backticks**; never let FUI read them as variables.
116
- 4. `#PARENT.*` / URL vars before fetch CALLs.
117
- 5. No complex logic in `data` — move to `script.js` (functions) or sparing `EXE`.