@atlashub/smartstack-cli 3.23.0 → 3.24.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 (32) hide show
  1. package/dist/mcp-entry.mjs +56 -15
  2. package/dist/mcp-entry.mjs.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/mcp-scaffolding/component.tsx.hbs +21 -1
  5. package/templates/skills/apex/references/smartstack-api.md +31 -5
  6. package/templates/skills/apex/references/smartstack-frontend.md +1081 -0
  7. package/templates/skills/apex/references/smartstack-layers.md +81 -5
  8. package/templates/skills/apex/steps/step-01-analyze.md +27 -3
  9. package/templates/skills/apex/steps/step-02-plan.md +5 -1
  10. package/templates/skills/apex/steps/step-03-execute.md +43 -3
  11. package/templates/skills/apex/steps/step-04-validate.md +159 -0
  12. package/templates/skills/apex/steps/step-05-examine.md +7 -0
  13. package/templates/skills/apex/steps/step-07-tests.md +19 -0
  14. package/templates/skills/business-analyse/_shared.md +6 -6
  15. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +1 -1
  16. package/templates/skills/business-analyse/questionnaire/07-ui.md +3 -3
  17. package/templates/skills/business-analyse/references/cadrage-pre-analysis.md +1 -1
  18. package/templates/skills/business-analyse/references/entity-architecture-decision.md +3 -3
  19. package/templates/skills/business-analyse/references/handoff-file-templates.md +13 -5
  20. package/templates/skills/business-analyse/references/spec-auto-inference.md +14 -14
  21. package/templates/skills/business-analyse/steps/step-01-cadrage.md +2 -2
  22. package/templates/skills/business-analyse/steps/step-02-decomposition.md +1 -1
  23. package/templates/skills/business-analyse/steps/step-03a1-setup.md +2 -2
  24. package/templates/skills/business-analyse/steps/step-03b-ui.md +2 -1
  25. package/templates/skills/business-analyse/steps/step-05a-handoff.md +15 -4
  26. package/templates/skills/business-analyse/templates/tpl-frd.md +2 -2
  27. package/templates/skills/business-analyse/templates-frd.md +2 -2
  28. package/templates/skills/ralph-loop/references/category-rules.md +45 -7
  29. package/templates/skills/ralph-loop/references/compact-loop.md +2 -2
  30. package/templates/skills/ralph-loop/references/core-seed-data.md +10 -0
  31. package/templates/skills/ralph-loop/steps/step-02-execute.md +110 -1
  32. package/templates/skills/validate-feature/steps/step-05-db-validation.md +86 -1
@@ -192,14 +192,16 @@ var sectionRoute = $"{moduleRoute}/{ToKebabCase(sectionCode)}";
192
192
 
193
193
  ## Layer 2 — Frontend (parallel with I18n)
194
194
 
195
+ > **Detailed patterns:** See `references/smartstack-frontend.md` for complete code templates.
196
+
195
197
  **Pages:** `src/pages/{ContextPascal}/{AppPascal}/{Module}/`
196
198
  **Components:** `src/components/{Module}/`
197
199
 
198
200
  | Action | Tool |
199
201
  |--------|------|
200
202
  | API client | MCP `scaffold_api_client` |
201
- | Routes | MCP `scaffold_routes` |
202
- | Complex pages | /ui-components skill |
203
+ | Routes | MCP `scaffold_routes` (outputFormat: `clientRoutes`) |
204
+ | Complex pages | /ui-components skill + `smartstack-frontend.md` patterns |
203
205
  | Validate routes | MCP `validate_frontend_routes` |
204
206
 
205
207
  **Layout mapping:**
@@ -210,21 +212,95 @@ var sectionRoute = $"{moduleRoute}/{ToKebabCase(sectionCode)}";
210
212
  | `business.*` | `BusinessLayout` | `/business` |
211
213
  | `personal.*` | `UserLayout` | `/personal/myspace` |
212
214
 
215
+ ### Lazy Loading (MANDATORY)
216
+
217
+ ALL page components MUST be lazy-loaded using `React.lazy()`:
218
+
219
+ ```tsx
220
+ const EmployeesPage = lazy(() =>
221
+ import('@/pages/Business/HumanResources/Employees/EmployeesPage')
222
+ .then(m => ({ default: m.EmployeesPage }))
223
+ );
224
+
225
+ // Route element — ALWAYS wrap with Suspense
226
+ element: <Suspense fallback={<PageLoader />}><EmployeesPage /></Suspense>
227
+ ```
228
+
229
+ ### Page Structure Pattern
230
+
231
+ All pages MUST follow: hooks → useEffect(load) → loading state → error state → content.
232
+
233
+ ```tsx
234
+ // 1. Hooks at top
235
+ const { t } = useTranslation(['{module}']);
236
+ // 2. State
237
+ const [loading, setLoading] = useState(true);
238
+ // 3. useCallback + useEffect for data loading
239
+ // 4. if (loading) return <Loader2 spinner />
240
+ // 5. if (error) return <error UI with retry>
241
+ // 6. return <content with CSS variables>
242
+ ```
243
+
244
+ ### Components & CSS
245
+
213
246
  **Components:** SmartTable, SmartFilter, EntityCard, SmartForm, StatCard (NEVER raw HTML)
214
- **CSS:** Variables only → `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`
247
+ **CSS:** Variables only → `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`, `border-[var(--border-color)]`
248
+ **Loader:** `Loader2` from `lucide-react` for spinners, `PageLoader` for Suspense fallback
249
+
250
+ ### FK Fields in Forms (CRITICAL)
251
+
252
+ Any entity property that is a FK Guid (e.g., `EmployeeId`, `DepartmentId`) MUST use the `EntityLookup` component — NEVER a plain text input. Users cannot type GUIDs manually.
253
+
254
+ ```tsx
255
+ // CORRECT — EntityLookup for FK field
256
+ <EntityLookup
257
+ apiEndpoint="/api/business/human-resources/employees"
258
+ value={formData.employeeId}
259
+ onChange={(id) => handleChange('employeeId', id)}
260
+ label={t('module:form.employee', 'Employee')}
261
+ mapOption={(item) => ({ id: item.id, label: item.name, sublabel: item.code })}
262
+ required
263
+ />
264
+
265
+ // WRONG — plain text input for FK GUID
266
+ <input type="text" value={formData.employeeId} placeholder="Enter employee ID" />
267
+ ```
268
+
269
+ **Backend requirement:** Each target entity's GetAll endpoint MUST accept `?search=` parameter. See `smartstack-api.md` Service Pattern.
270
+
271
+ See `references/smartstack-frontend.md` section 6 for the full `EntityLookup` component definition and usage patterns.
215
272
 
216
273
  **FORBIDDEN:**
217
274
  - `src/pages/{Module}/` (flat, missing Context/App)
218
275
  - `import axios` → use `@/services/api/apiClient`
219
276
  - `<table>` → use SmartTable
220
- - Hardcoded colors → use CSS variables
277
+ - `<input type="text">` for FK Guid fields → use `EntityLookup`
278
+ - Placeholder "Enter ID" or "Enter GUID" → use `EntityLookup`
279
+ - Hardcoded Tailwind colors (`bg-white`, `text-gray-900`) → use CSS variables
280
+ - Static page imports in route files → use `React.lazy()`
281
+ - `<Suspense>` without `fallback` prop
221
282
  - Only fr/en → MUST have 4 languages
283
+ - `t('key')` without namespace prefix → use `t('ns:key', 'fallback')`
284
+ - Hardcoded user-facing strings in JSX → use `t()`
222
285
 
223
286
  ---
224
287
 
225
288
  ## Layer 2 — I18n
226
289
 
227
- 4 JSON files: fr, en, it, de identical key structures.
290
+ > **Template:** See `references/smartstack-frontend.md` Section 2 for complete JSON template.
291
+
292
+ 4 JSON files per module namespace: fr, en, it, de — identical key structures.
293
+
294
+ **Location:** `src/i18n/locales/{lang}/{module}.json`
295
+
296
+ **Required keys:** `title`, `description`, `actions`, `labels`, `columns`, `form`, `errors`, `validation`, `messages`, `empty`
297
+
298
+ **Usage pattern:**
299
+ ```tsx
300
+ const { t } = useTranslation(['{module}']);
301
+ t('{module}:title', 'Module Title') // ALWAYS with fallback
302
+ t('{module}:actions.create', 'Create entity') // ALWAYS with namespace prefix
303
+ ```
228
304
 
229
305
  ---
230
306
 
@@ -136,7 +136,31 @@ Identify:
136
136
 
137
137
  ---
138
138
 
139
- ## 4. Gap Analysis
139
+ ## 4. FK Field Detection
140
+
141
+ For each entity found (or to be created), identify FK relationships:
142
+
143
+ ```
144
+ For each entity in analysis results:
145
+ - List properties ending in "Id" with a matching navigation property (e.g., EmployeeId + Employee)
146
+ - Record: { fkProperty, targetEntity, isRequired }
147
+
148
+ Output:
149
+ fkFields: [
150
+ { entity: "TimeEntry", fkProperty: "EmployeeId", targetEntity: "Employee", isRequired: true },
151
+ { entity: "TimeEntry", fkProperty: "ProjectId", targetEntity: "Project", isRequired: true }
152
+ ]
153
+ ```
154
+
155
+ **Why:** FK fields drive two critical downstream requirements:
156
+ 1. **Frontend:** Each FK field MUST use `EntityLookup` component (NEVER plain text input for GUIDs)
157
+ 2. **Backend:** Each target entity's GetAll endpoint MUST support `?search=` query parameter
158
+
159
+ These are propagated to step-02 (plan) and step-03 (execute).
160
+
161
+ ---
162
+
163
+ ## 5. Gap Analysis
140
164
 
141
165
  Compare what EXISTS vs what the TASK REQUIRES:
142
166
 
@@ -149,7 +173,7 @@ For each element in the task description:
149
173
 
150
174
  ---
151
175
 
152
- ## 5. Analysis Summary
176
+ ## 6. Analysis Summary
153
177
 
154
178
  ```
155
179
  **APEX SmartStack - Analysis Complete**
@@ -176,7 +200,7 @@ For each element in the task description:
176
200
 
177
201
  ---
178
202
 
179
- ## 6. Save Output (if save_mode)
203
+ ## 7. Save Output (if save_mode)
180
204
 
181
205
  Write to `{output_dir}/01-analyze.md` with analysis results.
182
206
 
@@ -63,9 +63,13 @@ For EACH file in the plan, specify HOW it will be created/modified:
63
63
  | # | File | Action | Tool |
64
64
  |---|------|--------|------|
65
65
  | 10 | src/pages/{Ctx}/{App}/{Mod}/ListPage.tsx | create | /ui-components skill |
66
+ | 10b | src/pages/{Ctx}/{App}/{Mod}/CreatePage.tsx | create | /ui-components skill (EntityLookup for FK fields) |
67
+ | 10c | src/pages/{Ctx}/{App}/{Mod}/EditPage.tsx | create | /ui-components skill (EntityLookup for FK fields) |
66
68
  | 11 | src/services/api/{module}Api.ts | create | MCP scaffold_api_client |
67
69
  | 12 | src/routes/{module}.tsx | create | MCP scaffold_routes |
68
- | 13 | src/locales/fr/{module}.json | create | manual (4 languages) |
70
+ | 13 | src/i18n/locales/{lang}/{module}.json | create | ref smartstack-frontend.md (4 languages: fr, en, it, de) |
71
+
72
+ **FK Field Guidance:** If step-01 identified `fkFields[]`, every Create/Edit page MUST use `EntityLookup` for those fields (see `smartstack-frontend.md` section 6). The corresponding backend GetAll endpoints (Layer 1) MUST support `?search=` parameter.
69
73
 
70
74
  ### Layer 3 — Tests (sequential, if -t)
71
75
 
@@ -14,6 +14,7 @@ All code goes through skills (/controller, /application, /ui-components, /efcore
14
14
  ## LOAD CONDITIONALLY
15
15
 
16
16
  - **ALWAYS** read `references/smartstack-api.md` — BaseEntity API, entity/config/controller patterns
17
+ - **ALWAYS** read `references/smartstack-frontend.md` — lazy loading, i18n, page structure, CSS variables
17
18
  - Read `references/smartstack-layers.md` for execution rules per layer
18
19
  - If NOT `{economy_mode}` AND Layer 1 has parallel work: read `references/agent-teams-protocol.md`
19
20
 
@@ -66,6 +67,18 @@ dotnet build
66
67
 
67
68
  Execute each item from the plan sequentially using skills and MCP.
68
69
 
70
+ **For frontend tasks (economy_mode):** Follow the same rules as exec-frontend above:
71
+ - `MCP scaffold_api_client` → API client + types + React Query hook
72
+ - `MCP scaffold_routes` with `outputFormat: 'clientRoutes'` for lazy imports
73
+ - Pages: use `/ui-components` skill — MANDATORY for entity lists, grids, tables, dashboards
74
+ - **Form pages: Create `EntityCreatePage.tsx` (route: `/create`) and `EntityEditPage.tsx` (route: `/:id/edit`)**
75
+ - **FK FIELDS (CRITICAL):** Any Guid FK property (e.g., EmployeeId, DepartmentId) MUST use `EntityLookup` component — NEVER a plain text input. See `smartstack-frontend.md` section 6.
76
+ - **ZERO modals/popups/drawers for forms — ALL forms are full pages with their own URL**
77
+ - **Form tests: Generate `EntityCreatePage.test.tsx` and `EntityEditPage.test.tsx` (co-located)**
78
+ - Read `references/smartstack-frontend.md` for mandatory patterns (sections 3b + 8)
79
+ - Generate i18n JSON files for all 4 languages (fr, en, it, de) — `src/i18n/locales/{lang}/{module}.json`
80
+ - All pages must follow loading → error → content pattern with CSS variables
81
+
69
82
  ### If NOT economy_mode: Agent Teams (parallel)
70
83
 
71
84
  > **Protocol:** See `references/agent-teams-protocol.md`
@@ -82,6 +95,7 @@ Spawn 2 teammates (Opus, full tools):
82
95
  For each task:
83
96
  - Application services/DTOs: MCP scaffold_extension
84
97
  - Controllers: use /controller skill for complex, MCP scaffold_extension for simple
98
+ - IMPORTANT: ALL GetAll endpoints MUST support ?search= query parameter (enables EntityLookup on frontend)
85
99
  - Seed data: MCP generate_permissions, then follow smartstack-layers.md templates
86
100
 
87
101
  After ALL tasks done:
@@ -91,11 +105,37 @@ Spawn 2 teammates (Opus, full tools):
91
105
  "Execute these Layer 1 frontend tasks for module {module_code}:
92
106
  {list of frontend + i18n tasks from plan}
93
107
 
108
+ **MANDATORY: Read references/smartstack-frontend.md FIRST** — contains all required patterns.
109
+
94
110
  For each task:
95
111
  - API client: MCP scaffold_api_client
96
- - Routes: MCP scaffold_routes
97
- - Pages: use /ui-components skill
98
- - I18n: 4 languages (fr, en, it, de)
112
+ - Routes: MCP scaffold_routes (outputFormat: 'clientRoutes') → generates lazy imports + Suspense
113
+ - Pages: use /ui-components skill — MUST follow smartstack-frontend.md patterns:
114
+ React.lazy() for all page imports (named export wrapping)
115
+ → <Suspense fallback={<PageLoader />}> around all lazy components
116
+ → Page structure: hooks → useEffect(load) → loading → error → content
117
+ → CSS variables only: bg-[var(--bg-card)], text-[var(--text-primary)]
118
+ → SmartTable/SmartFilter/EntityCard (NEVER raw HTML)
119
+ - **FORM PAGES (CRITICAL):** Create/Edit forms are FULL PAGES with own routes:
120
+ → EntityCreatePage.tsx with route /{module}/create
121
+ → EntityEditPage.tsx with route /{module}/:id/edit
122
+ → ZERO modals/popups/drawers/dialogs for forms
123
+ → Back button with navigate(-1) on every form page
124
+ → See smartstack-frontend.md section 3b for templates
125
+ - **FK FIELDS (CRITICAL):** Foreign key Guid fields (e.g., EmployeeId) MUST use EntityLookup:
126
+ → NEVER render FK fields as plain text inputs — users cannot type GUIDs
127
+ → Use EntityLookup from @/components/ui/EntityLookup for searchable selection
128
+ → Each FK field needs: apiEndpoint, mapOption (display name), search support on backend
129
+ → See smartstack-frontend.md section 6 for the full EntityLookup pattern
130
+ - **FORM TESTS (MANDATORY):**
131
+ → EntityCreatePage.test.tsx (co-located next to page)
132
+ → EntityEditPage.test.tsx (co-located next to page)
133
+ → Cover: rendering, validation, submit, pre-fill, navigation, errors
134
+ → See smartstack-frontend.md section 8 for test templates
135
+ - I18n: Generate 4 JSON files per module namespace (fr, en, it, de)
136
+ → Follow JSON template from smartstack-frontend.md
137
+ → Keys: actions, labels, errors, validation, columns, form, messages, empty
138
+ → ALL t() calls MUST use namespace prefix + fallback: t('ns:key', 'Default text')
99
139
  - MUST use src/pages/{Context}/{App}/{Module}/ hierarchy (NOT flat)
100
140
 
101
141
  After ALL tasks done:
@@ -222,6 +222,157 @@ if [ -n "$SERVICE_FILES" ]; then
222
222
  fi
223
223
  ```
224
224
 
225
+ ### POST-CHECK 6: Translation files must exist for all 4 languages (if frontend)
226
+
227
+ ```bash
228
+ # Find all i18n namespaces used in tsx files
229
+ TSX_FILES=$(find src/pages/ -name "*.tsx" 2>/dev/null)
230
+ if [ -n "$TSX_FILES" ]; then
231
+ NAMESPACES=$(grep -ohP "useTranslation\(\[?'([^']+)" $TSX_FILES | sed "s/.*'//" | sort -u)
232
+ for NS in $NAMESPACES; do
233
+ for LANG in fr en it de; do
234
+ if [ ! -f "src/i18n/locales/$LANG/$NS.json" ]; then
235
+ echo "BLOCKING: Missing translation file: src/i18n/locales/$LANG/$NS.json"
236
+ exit 1
237
+ fi
238
+ done
239
+ done
240
+ fi
241
+ ```
242
+
243
+ ### POST-CHECK 7: Pages must use lazy loading (no static page imports in routes)
244
+
245
+ ```bash
246
+ ROUTE_FILES=$(find src/routes/ -name "*.tsx" -o -name "*.ts" 2>/dev/null)
247
+ if [ -n "$ROUTE_FILES" ]; then
248
+ STATIC_PAGE_IMPORTS=$(grep -Pn "^import .+ from '@/pages/" $ROUTE_FILES 2>/dev/null)
249
+ if [ -n "$STATIC_PAGE_IMPORTS" ]; then
250
+ echo "BLOCKING: Route files must use React.lazy() for page imports, not static imports"
251
+ echo "$STATIC_PAGE_IMPORTS"
252
+ echo "Fix: const Page = lazy(() => import('@/pages/...').then(m => ({ default: m.Page })));"
253
+ exit 1
254
+ fi
255
+ fi
256
+ ```
257
+
258
+ ### POST-CHECK 8: Forms must be full pages with routes — ZERO modals/popups/drawers
259
+
260
+ ```bash
261
+ # Check for modal/dialog/drawer imports in page files (create/edit forms)
262
+ PAGE_FILES=$(find src/pages/ -name "*.tsx" 2>/dev/null)
263
+ if [ -n "$PAGE_FILES" ]; then
264
+ MODAL_IMPORTS=$(grep -Pn "import.*(?:Modal|Dialog|Drawer|Popup|Sheet)" $PAGE_FILES 2>/dev/null)
265
+ if [ -n "$MODAL_IMPORTS" ]; then
266
+ echo "BLOCKING: Form pages must NOT use Modal/Dialog/Drawer/Popup components"
267
+ echo "Create/Edit forms MUST be full pages with their own URL routes"
268
+ echo "Found modal imports in page files:"
269
+ echo "$MODAL_IMPORTS"
270
+ echo "Fix: Create EntityCreatePage.tsx with route /create and EntityEditPage.tsx with route /:id/edit"
271
+ exit 1
272
+ fi
273
+ fi
274
+ ```
275
+
276
+ ### POST-CHECK 9: Create/Edit pages must exist as separate route pages
277
+
278
+ ```bash
279
+ # For each module with a list page, verify create and edit pages exist
280
+ LIST_PAGES=$(find src/pages/ -name "*ListPage.tsx" -o -name "*sPage.tsx" 2>/dev/null | grep -v test)
281
+ if [ -n "$LIST_PAGES" ]; then
282
+ for LIST_PAGE in $LIST_PAGES; do
283
+ PAGE_DIR=$(dirname "$LIST_PAGE")
284
+ MODULE_NAME=$(basename "$PAGE_DIR")
285
+ # Check for create page
286
+ CREATE_PAGE=$(find "$PAGE_DIR" -name "*CreatePage.tsx" 2>/dev/null)
287
+ if [ -z "$CREATE_PAGE" ]; then
288
+ echo "WARNING: Module $MODULE_NAME has a list page but no CreatePage — expected EntityCreatePage.tsx"
289
+ fi
290
+ # Check for edit page
291
+ EDIT_PAGE=$(find "$PAGE_DIR" -name "*EditPage.tsx" 2>/dev/null)
292
+ if [ -z "$EDIT_PAGE" ]; then
293
+ echo "WARNING: Module $MODULE_NAME has a list page but no EditPage — expected EntityEditPage.tsx"
294
+ fi
295
+ done
296
+ fi
297
+ ```
298
+
299
+ ### POST-CHECK 10: Form pages must have companion test files
300
+
301
+ ```bash
302
+ # Every CreatePage and EditPage must have a .test.tsx file
303
+ FORM_PAGES=$(find src/pages/ -name "*CreatePage.tsx" -o -name "*EditPage.tsx" 2>/dev/null | grep -v test)
304
+ if [ -n "$FORM_PAGES" ]; then
305
+ for FORM_PAGE in $FORM_PAGES; do
306
+ TEST_FILE="${FORM_PAGE%.tsx}.test.tsx"
307
+ if [ ! -f "$TEST_FILE" ]; then
308
+ echo "BLOCKING: Form page missing test file: $FORM_PAGE"
309
+ echo "Expected: $TEST_FILE"
310
+ echo "All form pages MUST have companion test files (rendering, validation, submit, navigation)"
311
+ exit 1
312
+ fi
313
+ done
314
+ fi
315
+ ```
316
+
317
+ ### POST-CHECK 11: FK fields must NOT be plain text inputs (EntityLookup required)
318
+
319
+ ```bash
320
+ # Check for FK fields rendered as plain text inputs in form pages
321
+ FORM_PAGES=$(find src/pages/ -name "*CreatePage.tsx" -o -name "*EditPage.tsx" 2>/dev/null | grep -v test | grep -v node_modules)
322
+ if [ -n "$FORM_PAGES" ]; then
323
+ # Detect pattern: input with value containing "Id" (e.g., employeeId, departmentId)
324
+ FK_TEXT_INPUTS=$(grep -Pn 'type=["\x27]text["\x27].*[a-z]+Id|value=\{[^}]*\.[a-z]+Id\}.*type=["\x27]text["\x27]' $FORM_PAGES 2>/dev/null)
325
+ if [ -n "$FK_TEXT_INPUTS" ]; then
326
+ echo "BLOCKING: FK fields rendered as plain text inputs — MUST use EntityLookup component"
327
+ echo "Users cannot type GUIDs manually. Use <EntityLookup /> from @/components/ui/EntityLookup"
328
+ echo "See smartstack-frontend.md section 6 for the EntityLookup pattern"
329
+ echo "$FK_TEXT_INPUTS"
330
+ exit 1
331
+ fi
332
+
333
+ # Check for input placeholders mentioning "ID" or "id" or "Enter...Id"
334
+ FK_PLACEHOLDER=$(grep -Pn 'placeholder=["\x27].*[Ee]nter.*[Ii][Dd]|placeholder=["\x27].*[Gg][Uu][Ii][Dd]' $FORM_PAGES 2>/dev/null)
335
+ if [ -n "$FK_PLACEHOLDER" ]; then
336
+ echo "BLOCKING: Form has placeholder asking user to enter an ID/GUID — use EntityLookup instead"
337
+ echo "$FK_PLACEHOLDER"
338
+ exit 1
339
+ fi
340
+ fi
341
+ ```
342
+
343
+ ### POST-CHECK 12: Backend APIs must support search parameter for EntityLookup
344
+
345
+ ```bash
346
+ # Check that controller GetAll methods accept search parameter
347
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
348
+ if [ -n "$CTRL_FILES" ]; then
349
+ for f in $CTRL_FILES; do
350
+ # Check if controller has GetAll but no search parameter
351
+ if grep -q "\[HttpGet\]" "$f" && grep -q "GetAll" "$f"; then
352
+ if ! grep -q "search" "$f"; then
353
+ echo "WARNING: Controller missing search parameter on GetAll: $f"
354
+ echo "GetAll endpoints MUST accept ?search= to enable EntityLookup on frontend"
355
+ echo "Fix: Add [FromQuery] string? search parameter to GetAll action"
356
+ fi
357
+ fi
358
+ done
359
+ fi
360
+ ```
361
+
362
+ ### POST-CHECK 13: No hardcoded Tailwind colors in generated pages
363
+
364
+ ```bash
365
+ NEW_PAGES=$(git diff --name-only HEAD 2>/dev/null | grep "src/pages/.*\.tsx$")
366
+ if [ -n "$NEW_PAGES" ]; then
367
+ HARDCODED=$(grep -Pn '(bg|text|border)-(?!\[)(red|blue|green|gray|white|black|slate|zinc|neutral|stone)-' $NEW_PAGES 2>/dev/null)
368
+ if [ -n "$HARDCODED" ]; then
369
+ echo "WARNING: Pages should use CSS variables instead of hardcoded Tailwind colors"
370
+ echo "Fix: bg-[var(--bg-card)] instead of bg-white, text-[var(--text-primary)] instead of text-gray-900"
371
+ echo "$HARDCODED"
372
+ fi
373
+ fi
374
+ ```
375
+
225
376
  **If ANY POST-CHECK fails → fix in step-03, re-validate.**
226
377
 
227
378
  ---
@@ -255,6 +406,14 @@ AC2: {criterion} → PASS / FAIL (evidence: {file:line or test})
255
406
  | dotnet build | PASS |
256
407
  | npm typecheck | PASS / N/A |
257
408
  | Seed data | PASS / N/A |
409
+ | I18n: 4 languages per namespace | PASS / N/A |
410
+ | Lazy loading: no static page imports | PASS / N/A |
411
+ | Forms: full pages, zero modals | PASS / N/A |
412
+ | Forms: create/edit pages exist | PASS / N/A |
413
+ | Forms: test files exist | PASS / N/A |
414
+ | FK fields: EntityLookup, no plain text | PASS / N/A |
415
+ | APIs: search parameter on GetAll | PASS / N/A |
416
+ | CSS variables: no hardcoded colors | PASS / N/A |
258
417
  | Acceptance criteria | {X}/{Y} PASS |
259
418
  ```
260
419
 
@@ -60,6 +60,13 @@ For each changed file, check:
60
60
  - [ ] SmartTable/SmartForm (not raw HTML tables/forms)
61
61
  - [ ] Correct Layout wrapper per context
62
62
 
63
+ **FK Fields & Forms:**
64
+ - [ ] FK Guid fields use `EntityLookup` component (NEVER plain text `<input>`)
65
+ - [ ] No placeholder text asking user to "Enter ID" or "Enter GUID"
66
+ - [ ] Create/Edit forms are full pages with own routes (ZERO modals/dialogs)
67
+ - [ ] Backend GetAll endpoints support `?search=` param for EntityLookup
68
+ - [ ] Each EntityLookup has `apiEndpoint`, `mapOption`, `label` props
69
+
63
70
  ---
64
71
 
65
72
  ## 4. Produce Findings
@@ -100,6 +100,24 @@ Call: mcp__smartstack__scaffold_tests
100
100
  Tests: RequirePermission enforcement, multi-tenant isolation
101
101
  ```
102
102
 
103
+ ### Frontend Form Tests
104
+ ```
105
+ For each module with create/edit pages:
106
+ → Generate EntityCreatePage.test.tsx and EntityEditPage.test.tsx
107
+ → Co-located next to the page component (NOT in __tests__/ folder)
108
+
109
+ Required test coverage:
110
+ - Rendering: form renders with all expected fields
111
+ - Validation: required fields show errors on empty submit
112
+ - Submission: successful submit calls API and navigates back
113
+ - Pre-fill (edit only): entity data loaded into fields
114
+ - Navigation: back/cancel button calls navigate(-1)
115
+ - Error handling: API error displays error message
116
+
117
+ Tools: Vitest + React Testing Library + @testing-library/user-event
118
+ Mock API: vi.mock() or MSW — NEVER real API calls
119
+ ```
120
+
103
121
  ---
104
122
 
105
123
  ## 3. Suggest Additional Scenarios
@@ -136,6 +154,7 @@ If under 80%: identify uncovered paths, scaffold additional tests
136
154
  | Application | {count} | {%} |
137
155
  | API | {count} | {%} |
138
156
  | Security | {count} | {%} |
157
+ | Frontend Forms | {count} | {%} |
139
158
 
140
159
  **Total tests:** {count}
141
160
  **Estimated coverage:** {%}
@@ -98,13 +98,13 @@ Level 1: Context (business)
98
98
  ### Standard Sections per Module
99
99
 
100
100
  > **RULE: Sections are functional zones, NOT CRUD operations.**
101
- > `create` and `edit` are ACTIONS within the `list` page (modal/drawer).
101
+ > `create` and `edit` are SEPARATE PAGES with their own URL routes (`/create` and `/:id/edit`).
102
102
  > `detail` is a page with tabs accessible by clicking a row in `list` (not a standalone nav section).
103
103
 
104
104
  | Section | Purpose | Typical Components |
105
105
  |---------|---------|-------------------|
106
- | `list` | **Main page**: entity grid + create action (modal/drawer) + click to detail | DataGrid, FilterBar, Pagination, CreateForm |
107
- | `detail` | **Reached from list** (hidden nav, route `:id`): tabbed view of entity details + edit capability | BackButton, DetailHeader, StatusBadge, TabPanel, DetailCard, SmartForm (edit), SmartTable (relations), Timeline (history) |
106
+ | `list` | **Main page**: entity grid + create button (navigates to `/create` page) + click to detail | DataGrid, FilterBar, Pagination, CreateButton |
107
+ | `detail` | **Reached from list** (hidden nav, route `:id`): tabbed view of entity details + edit button (navigates to `/:id/edit` page) | BackButton, DetailHeader, StatusBadge, TabPanel, DetailCard, SmartTable (relations), Timeline (history) |
108
108
  | `dashboard` | Module overview/KPIs (if analytics needed) | Charts, StatCards, RecentActivity |
109
109
  | `approve` | Approval workflow queue (if validation workflow) | ApprovalPanel, StatusTimeline |
110
110
  | `import` | Bulk data import (if volume > 100) | FileUpload, MappingTable, Preview |
@@ -115,9 +115,9 @@ Level 1: Context (business)
115
115
 
116
116
  | NOT a sidebar section | What it really is |
117
117
  |---------------|-------------------|
118
- | `create` | Action button in `list` page → opens Form in modal/drawer |
119
- | `edit` | Action in detail page → switches fields to edit mode |
120
- | `delete` | Action with confirmation dialog in `list` or detail |
118
+ | `create` | Action button in `list` page → navigates to `/create` page with form |
119
+ | `edit` | Action in list/detail page → navigates to `/:id/edit` page with pre-filled form |
120
+ | `delete` | Action with inline confirmation in `list` or detail |
121
121
  | `search` | FilterBar component integrated in the `list` section |
122
122
 
123
123
  > **Note on `detail`:** The detail page IS a `specification.sections[]` entry (with `navigation: "hidden"`) so it gets a wireframe, resources, and route. But it is NOT shown in the sidebar — it is reached by clicking a row in `list`. Every module with a `list` section MUST have a companion `detail` section.
@@ -489,7 +489,7 @@ Auto-added:
489
489
  | Produits/Products | Commerce, Inventaire | Catalogue avec variantes, tarification, stock |
490
490
 
491
491
  **Section pattern for module dedie:**
492
- - Section `list` (TOUJOURS) : grille + create en modale + clic vers detail
492
+ - Section `list` (TOUJOURS) : grille + bouton create (navigue vers page `/create`) + clic vers detail
493
493
  - Page detail avec onglets : Infos + onglets relationnels
494
494
  - Sections additionnelles UNIQUEMENT si : dashboard, approve, import, rapport
495
495
 
@@ -39,8 +39,8 @@
39
39
  | Need | Component | Détail |
40
40
  |------|-----------|--------|
41
41
  | List with pagination | DataTable | Tri, pagination, sélection, actions inline |
42
- | Creation form | Form + Modal | Validation FluentValidation, i18n des erreurs |
43
- | Edit form | Form + Modal | Pré-remplissage, dirty check, optimistic locking |
42
+ | Creation form | Form page (`/create` route) | Validation FluentValidation, i18n des erreurs |
43
+ | Edit form | Form page (`/:id/edit` route) | Pré-remplissage, dirty check, optimistic locking |
44
44
  | Filters | FilterBar | Filtres combinables, persistance URL params |
45
45
  | Export | ExportButton | CSV/Excel, respecte les filtres actifs |
46
46
  | Cards/Grid | CardGrid | Affichage visuel alternatif au tableau |
@@ -58,7 +58,7 @@
58
58
  | Question | If answer is vague/insufficient | Probe |
59
59
  |----------|-------------------------------|-------|
60
60
  | Q7.1 (devices) | "Desktop uniquement" | "Aucun accès mobile ? Même pas en consultation ? Tablette en atelier/terrain ?" |
61
- | Q7.5 (screens) | "Un écran de liste" | "Avec création/édition inline ou en modal ? Détail en side panel ou page séparée ?" |
61
+ | Q7.5 (screens) | "Un écran de liste" | "Avec pages dédiées pour création et édition ? Détail en page avec onglets ?" |
62
62
  | Q7.5 (screens) | Pas de mention dashboard | "L'utilisateur a-t-il besoin d'un tableau de bord avec des KPIs avant de plonger dans les données ?" |
63
63
  | Q7.7 (key info) | "Toutes les colonnes" | "Sur mobile/petit écran, quelles 3-4 colonnes sont indispensables ? Le reste = détail secondaire" |
64
64
  | Q7.8 (actions) | "CRUD classique" | "Actions métier spécifiques ? (valider, dupliquer, archiver, changer statut, assigner)" |
@@ -84,7 +84,7 @@ For each detected module, anticipate sections (Level 4) and resources (Level 5)
84
84
  | integration | list | sync-status-grid, config-form, log-viewer | Infos, Config, Logs |
85
85
 
86
86
  > **RULE:** Sections = functional zones only (`list`, `dashboard`, `approve`, `import`, `rapport`, `planning`).
87
- > `create`/`edit` = actions (modal/drawer) within the `list` page. `detail` = tabbed page reached by clicking a row in `list`.
87
+ > `create`/`edit` = separate pages with own URL routes (`/create` and `/:id/edit`). `detail` = tabbed page reached by clicking a row in `list`.
88
88
 
89
89
  **Always check:** Does the user's description imply additional functional sections? (e.g., "reports" → `dashboard`, "calendar view" → `planning`, "approval workflow" → `approve`)
90
90
 
@@ -121,7 +121,7 @@ L'entite {E} est-elle CORE pour cette application ?
121
121
  Module
122
122
  ├── Section: list
123
123
  │ └── Page principale avec grille/table
124
- │ └── Action "Creer" (ouvre formulaire en modale/drawer DANS cette page)
124
+ │ └── Action "Creer" (navigue vers la page /create avec formulaire)
125
125
  │ └── Clic sur une ligne → navigation vers la page detail
126
126
  │ └── Resources : {entity}-grid, {entity}-filters, {entity}-form
127
127
 
@@ -152,8 +152,8 @@ Module
152
152
 
153
153
  | PAS une section | C'est quoi en realite |
154
154
  |-----------------|----------------------|
155
- | `create` | Action (bouton) dans la page `list`, ouvre un formulaire en modale/drawer |
156
- | `edit` | Action dans la page detail, bascule les champs en mode edition |
155
+ | `create` | Action (bouton) dans la page `list`, navigue vers la page `/create` avec formulaire |
156
+ | `edit` | Action dans la page list/detail, navigue vers la page `/:id/edit` avec formulaire pre-rempli |
157
157
  | `detail` | Page de detail accessible par navigation depuis `list` (pas une section menu) |
158
158
  | `delete` | Action avec confirmation dans la page `list` ou `detail` |
159
159
  | `search` | Composant de filtrage integre dans la section `list` |
@@ -70,14 +70,22 @@ From `specification.uiWireframes[]`, `specification.dashboards[]` and `analysis.
70
70
 
71
71
  ```json
72
72
  "frontend": [
73
- { "path": "src/pages/{ModuleName}/{PageName}Page.tsx", "type": "Page", "linkedUCs": [], "linkedWireframes": ["{module}-list"], "module": "{moduleCode}", "wireframeAcceptanceCriteria": "Layout MUST match wireframe..." },
74
- { "path": "src/pages/{ModuleName}/{PageName}DetailPage.tsx", "type": "Page", "linkedUCs": [], "linkedWireframes": ["{module}-detail"], "module": "{moduleCode}" },
75
- { "path": "src/pages/{ModuleName}/{DashboardName}DashboardPage.tsx", "type": "DashboardPage", "linkedUCs": [], "linkedWireframes": ["{module}-dashboard"], "module": "{moduleCode}", "dashboardRef": "{module}-dashboard", "instructions": "Use Recharts library..." },
76
- { "path": "src/components/{ModuleName}/{ComponentName}.tsx", "type": "Component", "linkedUCs": [], "linkedWireframes": [], "module": "{moduleCode}" },
77
- { "path": "src/hooks/use{ModuleName}{Hook}.ts", "type": "Hook", "linkedUCs": [], "module": "{moduleCode}" }
73
+ { "path": "src/pages/{ContextPascal}/{ApplicationName}/{ModuleName}/{PageName}Page.tsx", "type": "Page", "linkedUCs": [], "linkedWireframes": ["{module}-list"], "module": "{moduleCode}", "wireframeAcceptanceCriteria": "Layout MUST match wireframe...", "skill": "/ui-components" },
74
+ { "path": "src/pages/{ContextPascal}/{ApplicationName}/{ModuleName}/{PageName}DetailPage.tsx", "type": "Page", "linkedUCs": [], "linkedWireframes": ["{module}-detail"], "module": "{moduleCode}", "skill": "/ui-components" },
75
+ { "path": "src/pages/{ContextPascal}/{ApplicationName}/{ModuleName}/{DashboardName}DashboardPage.tsx", "type": "DashboardPage", "linkedUCs": [], "linkedWireframes": ["{module}-dashboard"], "module": "{moduleCode}", "dashboardRef": "{module}-dashboard", "instructions": "Use Recharts library...", "skill": "/ui-components" },
76
+ { "path": "src/components/{ModuleName}/{ComponentName}.tsx", "type": "Component", "linkedUCs": [], "linkedWireframes": [], "module": "{moduleCode}", "skill": "/ui-components" },
77
+ { "path": "src/hooks/use{ModuleName}{Hook}.ts", "type": "Hook", "linkedUCs": [], "module": "{moduleCode}" },
78
+ { "path": "src/i18n/locales/fr/{moduleLower}.json", "type": "I18n", "language": "fr", "module": "{moduleCode}" },
79
+ { "path": "src/i18n/locales/en/{moduleLower}.json", "type": "I18n", "language": "en", "module": "{moduleCode}" },
80
+ { "path": "src/i18n/locales/it/{moduleLower}.json", "type": "I18n", "language": "it", "module": "{moduleCode}" },
81
+ { "path": "src/i18n/locales/de/{moduleLower}.json", "type": "I18n", "language": "de", "module": "{moduleCode}" }
78
82
  ]
79
83
  ```
80
84
 
85
+ **Page generation:** ALL pages and components MUST be generated via `/ui-components` skill (entity lists, grids, tables, dashboards, charts). The `"skill"` field indicates which skill to invoke.
86
+
87
+ **I18n generation:** 4 JSON files per module (fr, en, it, de) with identical key structures. `{moduleLower}` = lowercase module code. See `smartstack-frontend.md` for JSON template (keys: actions, labels, columns, form, errors, validation, messages, empty).
88
+
81
89
  **Dashboard acceptance criteria:** Chart library (Recharts), chart types matching spec, KPI cards, filters, CSS variables, responsive layout, wireframe-matching positions.
82
90
 
83
91
  ## 4.6 SeedData Files