cabloy 5.1.109 → 5.1.111
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.
- package/.cabloy-version +1 -1
- package/.gitignore +5 -2
- package/CHANGELOG.md +32 -0
- package/cabloy-docs/.vitepress/config.mjs +1 -0
- package/cabloy-docs/ai/playbook-module-removal.md +1 -1
- package/cabloy-docs/ai/virtual-decorator-guidance.md +9 -9
- package/cabloy-docs/backend/websocket-call-flow.md +1 -1
- package/cabloy-docs/backend/websocket-guide.md +1 -1
- package/cabloy-docs/frontend/fetch-interceptor-guide.md +8 -8
- package/cabloy-docs/frontend/form-guide.md +3 -1
- package/cabloy-docs/frontend/form-layout-guide.md +322 -0
- package/cabloy-docs/frontend/introduction.md +1 -0
- package/cabloy-docs/frontend/resource-entry-page-deep-dive.md +2 -0
- package/cabloy-docs/frontend/router-tabs-layout-integration.md +2 -1
- package/cabloy-docs/frontend/scripts.md +56 -1
- package/cabloy-docs/frontend/ssr-build-deploy-guide.md +1 -1
- package/cabloy-docs/frontend/ssr-overview.md +6 -0
- package/cabloy-docs/frontend/ssr-review-checklist.md +1 -1
- package/cabloy-docs/frontend/ssr-troubleshooting-guide.md +9 -9
- package/cabloy-docs/frontend/table-resource-crud-cookbook.md +4 -4
- package/cabloy-docs/frontend/zova-form-source-reading-map.md +36 -4
- package/cabloy-docs/frontend/zova-form-under-the-hood.md +13 -0
- package/cabloy-docs/fullstack/framework-performance.md +3 -3
- package/cabloy-docs/fullstack/quickstart.md +14 -0
- package/cabloy-docs/reference/repo-scripts.md +79 -0
- package/e2e/config/playwright.basic.config.ts +3 -0
- package/e2e/config/playwright.commerce.config.ts +3 -0
- package/e2e/config/playwright.shared.config.ts +38 -0
- package/e2e/scripts/e2e.ts +33 -0
- package/e2e/scripts/startE2eVona.ts +38 -0
- package/e2e/scripts/testE2eClean.ts +56 -0
- package/e2e/specs/a-basic/basic.spec.ts +271 -0
- package/e2e/specs/a-commerce/commerce.spec.ts +61 -0
- package/package.json +17 -1
- package/scripts/upgrade.ts +182 -7
- package/test-results/.last-run.json +4 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +383 -40
- package/vona/src/suite/a-commerce/modules/commerce-catalog/package.json +52 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/.metadata/index.ts +29 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/.metadata/this.ts +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/tsconfig.build.json +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/tsconfig.json +7 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/package.json +52 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/.metadata/index.ts +29 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/.metadata/this.ts +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/tsconfig.build.json +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/tsconfig.json +7 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/package.json +52 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/.metadata/index.ts +29 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/.metadata/this.ts +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/tsconfig.build.json +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/tsconfig.json +7 -0
- package/vona/src/suite/a-commerce/modules/commerce-promotion/package.json +52 -0
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/.metadata/index.ts +29 -0
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/.metadata/this.ts +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-promotion/tsconfig.build.json +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-promotion/tsconfig.json +7 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/package.json +53 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/.metadata/index.ts +81 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/.metadata/this.ts +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/bean/ssrMenu.home.ts +21 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/bean/ssrSite.commerceAdmin.ts +34 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/tsconfig.build.json +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/tsconfig.json +7 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/package.json +53 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/.metadata/index.ts +81 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/.metadata/this.ts +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/bean/ssrMenu.home.ts +20 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/bean/ssrSite.commerce.ts +34 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/tsconfig.build.json +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/tsconfig.json +7 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/package.json +52 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +29 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/this.ts +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/tsconfig.build.json +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/tsconfig.json +7 -0
- package/vona/src/suite/a-commerce/package.json +18 -0
- package/vona/src/suite/a-commerce/tsconfig.base.json +4 -0
- package/vona/src/suite/a-commerce/tsconfig.json +28 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +3 -3
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +4 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/bean/bean.database.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/bean/schedule.softDeletionPrune.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/bean.model/bean.model_meta.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoAggregate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoCreate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoGroup.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoSelectAndCount.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoUpdate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/modelCacheBase.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relations.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsDynamic.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsStatic.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/database.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/databaseDialectBase_.ts +10 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/db_.ts +1 -1
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/env/.env.cabloyCommerce +24 -0
- package/zova/env/.env.cabloyCommerceAdmin +19 -0
- package/zova/package.original.json +10 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +134 -45
- package/zova/src/front/config/config/config.cabloyCommerce.ts +16 -0
- package/zova/src/front/config/config/config.cabloyCommerceAdmin.ts +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-catalog/package.json +52 -0
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/.metadata/index.ts +26 -0
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/.metadata/this.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/index.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-catalog/tsconfig.build.json +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-catalog/tsconfig.json +5 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/package.json +52 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/src/.metadata/index.ts +26 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/src/.metadata/this.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/src/index.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/tsconfig.build.json +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/tsconfig.json +5 -0
- package/zova/src/suite/a-commerce/modules/commerce-payment/package.json +52 -0
- package/zova/src/suite/a-commerce/modules/commerce-payment/src/.metadata/index.ts +26 -0
- package/zova/src/suite/a-commerce/modules/commerce-payment/src/.metadata/this.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-payment/src/index.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-payment/tsconfig.build.json +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-payment/tsconfig.json +5 -0
- package/zova/src/suite/a-commerce/modules/commerce-promotion/package.json +52 -0
- package/zova/src/suite/a-commerce/modules/commerce-promotion/src/.metadata/index.ts +26 -0
- package/zova/src/suite/a-commerce/modules/commerce-promotion/src/.metadata/this.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-promotion/src/index.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-promotion/tsconfig.build.json +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-promotion/tsconfig.json +5 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/package.json +52 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/.metadata/index.ts +26 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/.metadata/this.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/index.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/tsconfig.build.json +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteadmin/tsconfig.json +5 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteweb/package.json +52 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteweb/src/.metadata/index.ts +26 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteweb/src/.metadata/this.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteweb/src/index.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteweb/tsconfig.build.json +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-siteweb/tsconfig.json +5 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/package.json +52 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +26 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/this.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/index.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/tsconfig.build.json +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/tsconfig.json +5 -0
- package/zova/src/suite/a-commerce/package.json +18 -0
- package/zova/src/suite/a-commerce/tsconfig.base.json +4 -0
- package/zova/src/suite/a-commerce/tsconfig.json +4 -0
- package/zova/src/suite/cabloy-basic/modules/basic-form/src/component/blockFormLayout/controller.tsx +38 -15
- package/zova/src/suite-vendor/a-zova/modules/a-app/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-app/src/component/app/controller.tsx +6 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/formLayout.ts +5 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/resource/formLayout.ts +3 -0
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssr.ts +18 -0
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/monkey.ts +5 -0
- package/zova/src/suite-vendor/a-zova/package.json +5 -5
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.111
|
package/.gitignore
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
node_modules
|
|
2
2
|
miniprogram_npm
|
|
3
3
|
.assets/
|
|
4
|
+
|
|
5
|
+
test-results/
|
|
6
|
+
playwright-report/
|
|
7
|
+
blob-report/
|
|
4
8
|
**/vite.config.ts.timestamp-*
|
|
5
9
|
**/coverage
|
|
6
10
|
**/dist/
|
|
@@ -72,5 +76,4 @@ vona/docker-compose.yml
|
|
|
72
76
|
/vona/src/backend/play
|
|
73
77
|
!/zova/packages-utils/quasar-app-extension-zova/dist
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
vona/src/suite/cabloy-basic/modules/basic-siteweb/assets/site
|
|
79
|
+
**/assets/site
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.111
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Update the application functionality.
|
|
8
|
+
|
|
9
|
+
## 5.1.110
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- Add form layout sections for flow forms.
|
|
14
|
+
- Add a Commerce development E2E workflow.
|
|
15
|
+
- Establish the A-Commerce Phase 20 foundation.
|
|
16
|
+
- Upgrade the Basic E2E baseline.
|
|
17
|
+
- Add a Basic SSR E2E test skeleton.
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
- Complete the Zova SSR hydration lifecycle.
|
|
22
|
+
- Clarify E2E upgrade repair behavior.
|
|
23
|
+
|
|
24
|
+
### Improvements
|
|
25
|
+
|
|
26
|
+
- Rename and reorganize the E2E runner and harness files.
|
|
27
|
+
- Standardize E2E test selection.
|
|
28
|
+
- Normalize Commerce script commands.
|
|
29
|
+
- Add coverage for the Student flow filter layout.
|
|
30
|
+
- Update Student selection item and controller components.
|
|
31
|
+
- Update framework performance documentation.
|
|
32
|
+
- Add an A-Commerce planning baseline.
|
|
33
|
+
- Add a Form Layout guide.
|
|
34
|
+
|
|
3
35
|
## 5.1.109
|
|
4
36
|
|
|
5
37
|
### Features
|
|
@@ -429,6 +429,7 @@ export default defineConfig({
|
|
|
429
429
|
items: [
|
|
430
430
|
{ text: 'Component Guide', link: '/frontend/component-guide' },
|
|
431
431
|
{ text: 'Form Guide', link: '/frontend/form-guide' },
|
|
432
|
+
{ text: 'Form Layout Guide', link: '/frontend/form-layout-guide' },
|
|
432
433
|
{ text: 'Image Guide', link: '/frontend/image-guide' },
|
|
433
434
|
{ text: 'File Guide', link: '/frontend/file-guide' },
|
|
434
435
|
{
|
|
@@ -153,15 +153,15 @@ If a class does not need container-managed behavior, it usually belongs in `src/
|
|
|
153
153
|
|
|
154
154
|
## Decision table
|
|
155
155
|
|
|
156
|
-
| Scenario
|
|
157
|
-
|
|
|
158
|
-
| independent business service
|
|
159
|
-
| runtime-anchor base with container behavior
|
|
160
|
-
| bean-scene facade that intentionally stays on shorthand surface | Yes, if it is a bridge node | `src/bean`
|
|
161
|
-
| compatibility / dialect / variant node built on a parent bean
|
|
162
|
-
| pure helper or superclass convenience class
|
|
163
|
-
| class kept in `src/bean` only to expose shortcut lookup
|
|
164
|
-
| misplaced bean-scene class that should not be globally visible
|
|
156
|
+
| Scenario | Use `@Virtual()` | Recommended placement | Reason |
|
|
157
|
+
| --------------------------------------------------------------- | --------------------------- | --------------------------------------- | ----------------------------------------------------------------------- |
|
|
158
|
+
| independent business service | No | `src/service` | it should own its own runtime resource boundary |
|
|
159
|
+
| runtime-anchor base with container behavior | Yes | `src/service` or `src/service/*_.ts` | it is container-managed but belongs logically to the parent bean system |
|
|
160
|
+
| bean-scene facade that intentionally stays on shorthand surface | Yes, if it is a bridge node | `src/bean` | it participates in shorthand lookup but is not a new ownership center |
|
|
161
|
+
| compatibility / dialect / variant node built on a parent bean | Yes | same scene as the concrete variant type | runtime ownership should inherit from the parent chain |
|
|
162
|
+
| pure helper or superclass convenience class | No | `src/lib` | no bean ownership semantics are needed |
|
|
163
|
+
| class kept in `src/bean` only to expose shortcut lookup | Usually no | re-evaluate placement | `@Virtual()` is not a shorthand filter |
|
|
164
|
+
| misplaced bean-scene class that should not be globally visible | No | move to `src/lib` or `src/service` | fix placement, not metadata |
|
|
165
165
|
|
|
166
166
|
## Worked examples
|
|
167
167
|
|
|
@@ -432,4 +432,4 @@ If you need the broader context next, read:
|
|
|
432
432
|
- [Web Socket Guide](/backend/websocket-guide) for the architecture and extension model
|
|
433
433
|
- [Web Socket Usage Guide](/backend/websocket-usage-guide) for server-side authoring patterns
|
|
434
434
|
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide) for the client-visible wire format
|
|
435
|
-
- [Broadcast Guide](/backend/broadcast-guide) and [Worker Guide](/backend/worker-guide) for cross-worker delivery context
|
|
435
|
+
- [Broadcast Guide](/backend/broadcast-guide) and [Worker Guide](/backend/worker-guide) for cross-worker delivery context
|
|
@@ -452,4 +452,4 @@ When extending or reviewing Web Socket behavior, ask:
|
|
|
452
452
|
4. should delivery be point-to-point or namespace-wide?
|
|
453
453
|
5. is the behavior local to one worker, or should it propagate through broadcast?
|
|
454
454
|
|
|
455
|
-
That helps backend extensions stay aligned with the existing `a-socket` architecture instead of bypassing the framework transport model.
|
|
455
|
+
That helps backend extensions stay aligned with the existing `a-socket` architecture instead of bypassing the framework transport model.
|
|
@@ -258,13 +258,13 @@ That distinction matters because the order is part of the onion dependency model
|
|
|
258
258
|
|
|
259
259
|
A source-aligned hook matrix for the current built-ins is:
|
|
260
260
|
|
|
261
|
-
| Interceptor
|
|
262
|
-
|
|
|
263
|
-
| `mock`
|
|
264
|
-
| `headers`
|
|
265
|
-
| `jwt`
|
|
266
|
-
| `performAction` | yes
|
|
267
|
-
| `body`
|
|
261
|
+
| Interceptor | onRequest | onRequestError | onResponse | onResponseError |
|
|
262
|
+
| --------------- | --------- | -------------- | ---------- | --------------- |
|
|
263
|
+
| `mock` | - | - | - | yes |
|
|
264
|
+
| `headers` | yes | - | - | - |
|
|
265
|
+
| `jwt` | yes | - | - | - |
|
|
266
|
+
| `performAction` | yes | - | - | - |
|
|
267
|
+
| `body` | - | - | yes | yes |
|
|
268
268
|
|
|
269
269
|
This table is only a reading shortcut. The authoritative source is still the concrete bean implementation under `a-interceptor/src/bean/`.
|
|
270
270
|
|
|
@@ -437,4 +437,4 @@ When you use this page to reason about current behavior, verify:
|
|
|
437
437
|
3. `ServiceComposer` still merges `config.interceptors[...]` into the final onion options
|
|
438
438
|
4. the built-in bean roles still match their current source files
|
|
439
439
|
|
|
440
|
-
That keeps the explanation tied to current source rather than to stale assumptions.
|
|
440
|
+
That keeps the explanation tied to current source rather than to stale assumptions.
|
|
@@ -413,7 +413,9 @@ Use `layout` for concerns such as:
|
|
|
413
413
|
- label text
|
|
414
414
|
- icon prefix/suffix
|
|
415
415
|
- field wrapper presentation
|
|
416
|
-
- row-level
|
|
416
|
+
- one field's row-level presentation intent
|
|
417
|
+
|
|
418
|
+
`layout` is a field-level surface. When the requirement is to arrange multiple schema fields into responsive sections, groups, or tabs, use DTO-driven `formLayout` with `basic-form:blockFormLayout`; see [Form Layout Guide](/frontend/form-layout-guide).
|
|
417
419
|
|
|
418
420
|
### Use `options` for renderer-specific input props
|
|
419
421
|
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# Form Layout Guide
|
|
2
|
+
|
|
3
|
+
Use Form Layout when a schema-driven resource form needs a deliberate structure: field order, Grid or flow sections, groups, or tabs.
|
|
4
|
+
|
|
5
|
+
This guide covers the **structural** layout contract authored in DTO metadata and rendered through `basic-form:blockFormLayout`. It does not replace [Form Guide](/frontend/form-guide), which explains `ZForm`, field rendering, validation, and manual or mixed forms.
|
|
6
|
+
|
|
7
|
+
> [!TIP]
|
|
8
|
+
> Cabloy Basic implements the renderer described here with DaisyUI and Tailwind CSS. The `formLayout` contract and its resolver are shared Zova surfaces, but group, Grid, flow, and tab presentation are Basic-specific. Do not assume Cabloy Start uses identical markup or styling.
|
|
9
|
+
|
|
10
|
+
## The layout layers are different
|
|
11
|
+
|
|
12
|
+
Several APIs contain the word “layout,” but they own different concerns:
|
|
13
|
+
|
|
14
|
+
| Surface | Owns | Does not own |
|
|
15
|
+
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
16
|
+
| `formLayout` | Field placement, Grid/flow sections, groups, responsive spans, and tabs | Field renderer selection, validation rules, submit policy |
|
|
17
|
+
| `basic-form:blockFormLayout` | Resolving and rendering a structural `formLayout` tree in Cabloy Basic | Page-entry or filter actions |
|
|
18
|
+
| `layout`, `formFieldLayout`, `FormFieldLayout` | One field's label and wrapper presentation: inline/block mode, icons, borders, header/footer, class, and style | Sections, Grid/flow placement, groups, or tabs |
|
|
19
|
+
|
|
20
|
+
For example, `formFieldLayout: { inline: false }` makes each field use a block-style wrapper. It does not create a grid. Pair it with `basic-form:blockFormLayout` when the fields also need structural placement.
|
|
21
|
+
|
|
22
|
+
Read [Form Guide](/frontend/form-guide) for field wrapper and provider customization, and [Behavior Guide](/frontend/behavior-guide) for the `FormFieldLayout` behavior pipeline.
|
|
23
|
+
|
|
24
|
+
## Compose the layout through resource blocks
|
|
25
|
+
|
|
26
|
+
For resource forms, author structural layout in backend DTO metadata with `ZovaRender.block(...)`. The helper creates contract metadata; it does not render the form itself. Zova later consumes the generated schema metadata and renders the registered blocks.
|
|
27
|
+
|
|
28
|
+
### Entry form composition
|
|
29
|
+
|
|
30
|
+
A resource entry DTO normally nests the layout block inside the page-entry form block:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
@Dto({
|
|
34
|
+
blocks: [
|
|
35
|
+
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
36
|
+
blocks: [
|
|
37
|
+
ZovaRender.block('basic-pageentry:blockForm', {
|
|
38
|
+
blocks: [
|
|
39
|
+
ZovaRender.block('basic-form:blockFormLayout', {
|
|
40
|
+
formLayout: {
|
|
41
|
+
children: [/* fields, groups, sections, or tabs */],
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
}),
|
|
46
|
+
ZovaRender.block('basic-pageentry:blockToolbarRow', {
|
|
47
|
+
actions: [/* Submit, Back, and other page-entry actions */],
|
|
48
|
+
}),
|
|
49
|
+
],
|
|
50
|
+
}),
|
|
51
|
+
],
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The responsibilities stay separate:
|
|
56
|
+
|
|
57
|
+
- the DTO supplies the structural contract metadata
|
|
58
|
+
- `basic-pageentry:blockForm` bridges page-entry form state, schema, and scene into `ZForm`
|
|
59
|
+
- `basic-form:blockFormLayout` places schema fields
|
|
60
|
+
- `basic-pageentry:blockToolbarRow` owns page-entry actions and their scene/permission rules
|
|
61
|
+
|
|
62
|
+
Form Layout changes neither readonly behavior nor actions. Create, update, and view scenes can reuse the same structure while the normal form and page-entry pipelines decide field state and available actions. See [Resource Entry Page Deep Dive](/frontend/resource-entry-page-deep-dive) for the wider entry-page runtime.
|
|
63
|
+
|
|
64
|
+
### Filter form composition
|
|
65
|
+
|
|
66
|
+
A filter uses the filter block as its host and keeps filter actions as an explicit sibling block:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
ZovaRender.block('basic-page:blockFilter', {
|
|
70
|
+
formFieldLayout: { inline: false },
|
|
71
|
+
blocks: [
|
|
72
|
+
ZovaRender.block('basic-form:blockFormLayout', {
|
|
73
|
+
formLayout: {
|
|
74
|
+
children: [
|
|
75
|
+
/* structural nodes */
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
ZovaRender.block('basic-page:blockFilterActions'),
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
A nonempty `blocks` list replaces `ZForm`'s automatic body and footer. Therefore a structured filter must explicitly include `basic-page:blockFilterActions`; it owns Search and Reset and keeps the filter's existing normalization and page-query handoff. See [Table + Resource CRUD Cookbook](/frontend/table-resource-crud-cookbook#use-blocks-for-a-structural-filter-layout) for the filter ownership model.
|
|
85
|
+
|
|
86
|
+
## Layout node grammar
|
|
87
|
+
|
|
88
|
+
`formLayout` has a root `children` array. The node types deliberately have limited nesting rather than arbitrary recursion:
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
formLayout
|
|
92
|
+
├─ field
|
|
93
|
+
├─ section
|
|
94
|
+
│ └─ field
|
|
95
|
+
├─ group
|
|
96
|
+
│ ├─ field
|
|
97
|
+
│ ├─ group
|
|
98
|
+
│ └─ section
|
|
99
|
+
└─ tabs
|
|
100
|
+
└─ tab
|
|
101
|
+
├─ field
|
|
102
|
+
├─ group
|
|
103
|
+
└─ section
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Node | Key properties | Allowed children | Use it for |
|
|
107
|
+
| --------- | ---------------------------------------------------------- | ------------------------ | ----------------------------------------- |
|
|
108
|
+
| `field` | required `name`; optional `span` | none | Place one resolved schema field |
|
|
109
|
+
| `section` | optional `id`, `title`, `description`, `layout`, `columns` | fields only | A Grid or wrapping flow field layout |
|
|
110
|
+
| `group` | optional `id`, `title`, `description` | fields, groups, sections | A semantic, bordered fieldset-style group |
|
|
111
|
+
| `tabs` | optional `id` | tabs only | One tab container |
|
|
112
|
+
| `tab` | optional `id`; required `title` | fields, groups, sections | One tab panel |
|
|
113
|
+
|
|
114
|
+
A section is a layout boundary. It uses the Grid strategy by default; set `layout: 'flow'` for compact, left-packed fields that wrap at their intrinsic widths. Use a group when the fields need a semantic or visual boundary, and place a section inside that group when it also needs Grid columns or flow placement. There is no separate `row` node: Grid and flow placement create rows automatically.
|
|
115
|
+
|
|
116
|
+
Nested tabs are not part of the current contract. Likewise, a section cannot contain a group or another section.
|
|
117
|
+
|
|
118
|
+
## Grid sections and field spans
|
|
119
|
+
|
|
120
|
+
Grid is the default section strategy: omit `layout` or set `layout: 'grid'`. Both `section.columns` and `field.span` use the same responsive shape:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
{
|
|
124
|
+
default?: 1 | 2 | 3 | 4,
|
|
125
|
+
md?: 1 | 2 | 3 | 4,
|
|
126
|
+
lg?: 1 | 2 | 3 | 4,
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- `columns` chooses how many columns a section has at each breakpoint.
|
|
131
|
+
- `span` chooses how many of those columns a field occupies.
|
|
132
|
+
- In Cabloy Basic, these become Tailwind classes such as `grid-cols-2`, `md:grid-cols-2`, `col-span-2`, and `md:col-span-2`.
|
|
133
|
+
- A section with no `columns.default` renders as one column.
|
|
134
|
+
- A field with no `span` takes its normal grid cell.
|
|
135
|
+
|
|
136
|
+
The following compact filter structure becomes one column by default, two columns at `md`, and makes `createdAt` use both medium columns:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
formLayout: {
|
|
140
|
+
children: [
|
|
141
|
+
{
|
|
142
|
+
type: 'section',
|
|
143
|
+
columns: { default: 1, md: 2 },
|
|
144
|
+
children: [
|
|
145
|
+
{ type: 'field', name: 'name' },
|
|
146
|
+
{ type: 'field', name: 'level' },
|
|
147
|
+
{ type: 'field', name: 'createdAt', span: { default: 1, md: 2 } },
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Flow sections
|
|
155
|
+
|
|
156
|
+
Set `layout: 'flow'` when compact fields should appear from left to right without being distributed across equal Grid columns. Cabloy Basic renders a flow section as a wrapping flex layout, so each field keeps its renderer-defined width and moves to the next line only when space runs out.
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
{
|
|
160
|
+
type: 'section',
|
|
161
|
+
layout: 'flow',
|
|
162
|
+
children: [
|
|
163
|
+
{ type: 'field', name: 'name' },
|
|
164
|
+
{ type: 'field', name: 'level' },
|
|
165
|
+
{ type: 'field', name: 'createdAt' },
|
|
166
|
+
],
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`columns` and `span` are Grid-only settings. Omit them from flow sections; they do not determine flex widths or offsets. Use `formFieldLayout.inline` independently when the fields themselves should use compact inline wrappers.
|
|
171
|
+
|
|
172
|
+
## How the resolver handles the declared tree
|
|
173
|
+
|
|
174
|
+
Before rendering, `resolveFormLayout(...)` reconciles `formLayout` with the current scene's resolved schema properties. This makes the declaration a **placement overlay**, not an allow-list.
|
|
175
|
+
|
|
176
|
+
### Eligible and omitted fields
|
|
177
|
+
|
|
178
|
+
Only schema properties with `rest.visible !== false` are eligible. When an eligible visible field is absent from `formLayout`, the resolver appends it as a root-level field after the declared nodes, in schema-property order.
|
|
179
|
+
|
|
180
|
+
If a field must not render, make it invisible in schema metadata. Leaving it out of `formLayout.children` is not enough.
|
|
181
|
+
|
|
182
|
+
### Invalid declarations and diagnostics
|
|
183
|
+
|
|
184
|
+
The resolver keeps the first occurrence of each field and removes later duplicates. It also removes field names that are not eligible in the current schema scene.
|
|
185
|
+
|
|
186
|
+
| Situation | Resolver result | Diagnostic |
|
|
187
|
+
| ----------------------------------------- | --------------------------------------------- | ---------------- |
|
|
188
|
+
| Unknown or invisible field name | Field is removed | `unknownField` |
|
|
189
|
+
| Repeated field name | Later field is removed | `duplicateField` |
|
|
190
|
+
| Repeated structural ID | Later group, section, tabs, or tab is removed | `duplicateId` |
|
|
191
|
+
| Structural node has no surviving children | Node is removed | none |
|
|
192
|
+
|
|
193
|
+
Groups, sections, tab containers, and tabs receive an ID even when the DTO omits one. The resolver derives it from the node type and index path, for example `tabs-0` or `section-0-1-0`. Omit IDs for simple static layouts; provide explicit IDs when external state, diagnostics, tests, or future extensions need a stable structural reference.
|
|
194
|
+
|
|
195
|
+
> [!WARNING]
|
|
196
|
+
> Diagnostics are returned in the resolved layout plan, but the current `basic-form:blockFormLayout` renderer does not display or log them. Treat field names and explicit IDs as metadata that must be reviewed and tested, rather than expecting a visible authoring error at runtime.
|
|
197
|
+
|
|
198
|
+
## Tabs and validation feedback
|
|
199
|
+
|
|
200
|
+
The Basic renderer keeps active-tab state locally for each `tabs` node. If no saved active tab remains valid, it falls back to the first surviving tab.
|
|
201
|
+
|
|
202
|
+
It renders native tab buttons and panels with `role="tablist"`, `role="tab"`, `role="tabpanel"`, `aria-selected`, `aria-controls`, and `aria-labelledby` relationships. A tab receives an error badge when fields below it have validation errors. The badge counts fields with errors, not individual error messages.
|
|
203
|
+
|
|
204
|
+
Field error messages remain the responsibility of the field-layout behavior. The structural layout only summarizes their presence on tabs.
|
|
205
|
+
|
|
206
|
+
Current behavior boundaries:
|
|
207
|
+
|
|
208
|
+
- an error badge does not automatically activate or focus the tab containing the invalid field
|
|
209
|
+
- tab state belongs to the `basic-form:blockFormLayout` component, not the form globally
|
|
210
|
+
- the contract does not support nested tabs
|
|
211
|
+
- Form Layout does not select field renderers, change field visibility, or alter validation and submit policies
|
|
212
|
+
|
|
213
|
+
## Complete entry-form example
|
|
214
|
+
|
|
215
|
+
The Student create DTO is the canonical complete example. It uses optional structural IDs, two tabs, a titled group, a responsive profile section, and a nested-details field:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
ZovaRender.block('basic-pageentry:blockForm', {
|
|
219
|
+
blocks: [
|
|
220
|
+
ZovaRender.block('basic-form:blockFormLayout', {
|
|
221
|
+
formLayout: {
|
|
222
|
+
children: [
|
|
223
|
+
{
|
|
224
|
+
type: 'tabs',
|
|
225
|
+
children: [
|
|
226
|
+
{
|
|
227
|
+
type: 'tab',
|
|
228
|
+
title: $locale('BasicInformation'),
|
|
229
|
+
children: [
|
|
230
|
+
{
|
|
231
|
+
type: 'group',
|
|
232
|
+
title: $locale('StudentProfile'),
|
|
233
|
+
children: [
|
|
234
|
+
{
|
|
235
|
+
type: 'section',
|
|
236
|
+
columns: { default: 1, md: 2 },
|
|
237
|
+
children: [
|
|
238
|
+
{ type: 'field', name: 'name' },
|
|
239
|
+
{ type: 'field', name: 'mobile' },
|
|
240
|
+
{ type: 'field', name: 'imageId' },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: 'tab',
|
|
249
|
+
title: $locale('TrainingRecords'),
|
|
250
|
+
children: [
|
|
251
|
+
{ type: 'field', name: 'level' },
|
|
252
|
+
{
|
|
253
|
+
type: 'section',
|
|
254
|
+
children: [{ type: 'field', name: 'trainingRecords' }],
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
}),
|
|
263
|
+
],
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`trainingRecords` is one field in the structural tree. Its `basic-details:formFieldDetails` renderer owns the nested details UI; Form Layout does not recursively arrange the properties inside each detail record.
|
|
268
|
+
|
|
269
|
+
Student update and view DTOs use the same structural shape. The normal form scene controls readonly behavior, while the page-entry toolbar decides whether Submit, Back, or other actions are available.
|
|
270
|
+
|
|
271
|
+
## Complete filter-form example
|
|
272
|
+
|
|
273
|
+
The Student list filter combines field-wrapper and structural layout concerns:
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
ZovaRender.block('basic-page:blockFilter', {
|
|
277
|
+
formFieldLayout: { inline: true },
|
|
278
|
+
blocks: [
|
|
279
|
+
ZovaRender.block('basic-form:blockFormLayout', {
|
|
280
|
+
formLayout: {
|
|
281
|
+
children: [
|
|
282
|
+
{
|
|
283
|
+
type: 'section',
|
|
284
|
+
layout: 'flow',
|
|
285
|
+
children: [
|
|
286
|
+
{ type: 'field', name: 'name' },
|
|
287
|
+
{ type: 'field', name: 'level' },
|
|
288
|
+
{ type: 'field', name: 'createdAt' },
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
},
|
|
293
|
+
}),
|
|
294
|
+
ZovaRender.block('basic-page:blockFilterActions'),
|
|
295
|
+
],
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Here `formFieldLayout.inline: true` controls how each field wrapper is presented. The flow section keeps those compact wrappers left-packed and wraps them when necessary. `basic-page:blockFilterActions` remains required because the custom blocks replace automatic filter body/footer content.
|
|
300
|
+
|
|
301
|
+
## Authoring checklist
|
|
302
|
+
|
|
303
|
+
1. Start with DTO or resource metadata; do not hand-patch generated `.zova-rest` artifacts.
|
|
304
|
+
2. Use `formLayout` when the requirement is field placement, Grid or flow structure, groups, or tabs.
|
|
305
|
+
3. Use `layout`, `formFieldLayout`, `options`, or provider behaviors when the requirement is one field's wrapper or renderer.
|
|
306
|
+
4. Keep entry actions in page-entry toolbar blocks and filter actions in `basic-page:blockFilterActions`.
|
|
307
|
+
5. Review field names against the scene-specific schema. Unlisted visible fields are appended; unknown and duplicate declarations are silently pruned from the rendered plan.
|
|
308
|
+
6. Use the smallest layout that communicates the form structure; reserve tabs for genuinely separate field groups.
|
|
309
|
+
|
|
310
|
+
## Source-reading and verification path
|
|
311
|
+
|
|
312
|
+
For source-level investigation, follow this order:
|
|
313
|
+
|
|
314
|
+
1. `vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx` or `studentSelectResItem.tsx`
|
|
315
|
+
2. `zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/resource/formLayout.ts`
|
|
316
|
+
3. `zova/src/suite-vendor/a-zova/modules/a-form/src/lib/formLayout.ts`
|
|
317
|
+
4. `zova/src/suite/cabloy-basic/modules/basic-form/src/component/blockFormLayout/controller.tsx`
|
|
318
|
+
5. `vona/src/suite/a-training/modules/training-student/test/student.test.ts`
|
|
319
|
+
|
|
320
|
+
The Student test verifies that entry and filter DTO metadata preserves the current block nesting, optional IDs, Grid columns/spans, flow layout selection, and field order through OpenAPI generation. It is a contract-metadata test, not a browser end-to-end assertion for tabs, layout behavior, or error badges.
|
|
321
|
+
|
|
322
|
+
For the broader form runtime, continue with [Zova Form Under the Hood](/frontend/zova-form-under-the-hood) and [Zova Form Source Reading Map](/frontend/zova-form-source-reading-map).
|
|
@@ -80,6 +80,7 @@ Use this path when the task is about UI composition, component contracts, form o
|
|
|
80
80
|
|
|
81
81
|
- [Component Guide](/frontend/component-guide)
|
|
82
82
|
- [Form Guide](/frontend/form-guide)
|
|
83
|
+
- [Form Layout Guide](/frontend/form-layout-guide)
|
|
83
84
|
- [Image Guide](/frontend/image-guide)
|
|
84
85
|
- [Zova Form Under the Hood](/frontend/zova-form-under-the-hood)
|
|
85
86
|
- [Zova Form Source Reading Map](/frontend/zova-form-source-reading-map)
|
|
@@ -187,6 +187,8 @@ Instead, it consumes `$$pageEntry` from the host render context and passes the c
|
|
|
187
187
|
|
|
188
188
|
This is the clearest source-confirmed proof that `blockForm` is a bridge, not the main owner of entry-page orchestration.
|
|
189
189
|
|
|
190
|
+
A resource DTO can provide a nested `basic-form:blockFormLayout` block here to arrange the schema fields into sections, groups, or tabs. That block only owns structural placement; read [Form Layout Guide](/frontend/form-layout-guide) for the DTO grammar and keep this page focused on the entry runtime handoff.
|
|
191
|
+
|
|
190
192
|
### 7. `blockToolbarRow` renders the action row
|
|
191
193
|
|
|
192
194
|
The action row lives in:
|
|
@@ -15,6 +15,7 @@ Read this page together with:
|
|
|
15
15
|
|
|
16
16
|
> [!TIP]
|
|
17
17
|
> **Router ecosystem docs path**
|
|
18
|
+
>
|
|
18
19
|
> 1. **[Page Route Guide](/frontend/page-route-guide)** — learn the public route-record and layout surface
|
|
19
20
|
> 2. **[Zova Router Under the Hood](/frontend/zova-router-under-the-hood)** — learn how the core router runtime cooperates
|
|
20
21
|
> 3. **[Router View Hosts Guide](/frontend/router-view-hosts-guide)** — learn how routed pages are actually hosted
|
|
@@ -364,4 +365,4 @@ That means the integration story here is accurate for the current Basic Admin an
|
|
|
364
365
|
|
|
365
366
|
If [Router Tabs Mechanism](/frontend/router-tabs-mechanism) explains how the shared tabs model works, this page explains how the real Basic layouts turn that shared model into visible shell behavior.
|
|
366
367
|
|
|
367
|
-
That is the missing bridge between framework-level tabs semantics and the actual layout code contributors edit.
|
|
368
|
+
That is the missing bridge between framework-level tabs semantics and the actual layout code contributors edit.
|
|
@@ -28,7 +28,62 @@ npm run dev:zova:web
|
|
|
28
28
|
npm run build:zova
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
These map to Basic-specific Zova flavors in this repository.
|
|
31
|
+
These map to Basic-specific Zova flavors in this repository. Commerce follows the same aggregate-and-surface pattern:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Customer Web or Operator Admin development server
|
|
35
|
+
npm run dev:zova:commerce:web
|
|
36
|
+
npm run dev:zova:commerce:admin
|
|
37
|
+
|
|
38
|
+
# Both Commerce SSR/REST artifact pairs, or one surface only
|
|
39
|
+
npm run build:zova:commerce
|
|
40
|
+
npm run build:zova:commerce:web
|
|
41
|
+
npm run build:zova:commerce:admin
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Basic SSR browser acceptance
|
|
45
|
+
|
|
46
|
+
The default Basic Web and Admin sites have browser smoke commands that exercise Vona SSR dispatch at `7102`, not a standalone Zova development-server port.
|
|
47
|
+
|
|
48
|
+
Prepare current artifacts explicitly when the relevant frontend SSR output changed:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Web only
|
|
52
|
+
npm run build:zova:web
|
|
53
|
+
|
|
54
|
+
# Admin only
|
|
55
|
+
npm run build:zova:admin
|
|
56
|
+
|
|
57
|
+
# Both default Basic sites
|
|
58
|
+
npm run build:zova
|
|
59
|
+
npm run deps:vona
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then use the managed clean local acceptance command:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run test:e2e:basic:clean
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
It resets Vona-managed test data and the local Redis namespace, starts one development Vona worker, and runs the complete suite by default. The E2E commands consume already-built artifacts; they do not rebuild them.
|
|
69
|
+
|
|
70
|
+
The suite command family is consistent across Basic and Commerce:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
test:e2e:<suite> complete suite
|
|
74
|
+
test:e2e:<suite>:web all @web surface scenarios
|
|
75
|
+
test:e2e:<suite>:admin all @admin surface scenarios
|
|
76
|
+
test:e2e:<suite>:clean managed clean local suite run
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use Playwright tags after npm's argument delimiter for feature/category selection instead of adding one root script per scenario:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm run test:e2e:basic:clean -- --grep @flow
|
|
83
|
+
npm run test:e2e:basic -- --grep ATP-BASIC-FLOW-01
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For the complete tag vocabulary, managed-runner argument boundaries, and externally managed-target examples, see [Repo Scripts](/reference/repo-scripts#ssr-browser-checks). For a separately managed Basic target, set `BASIC_E2E_BASE_URL`; the equivalent Commerce commands use `COMMERCE_E2E_BASE_URL`. The caller owns external-target data, cache, and artifact freshness.
|
|
32
87
|
|
|
33
88
|
## Zova script model
|
|
34
89
|
|
|
@@ -305,4 +305,4 @@ Before finalizing an SSR build/deploy change, ask:
|
|
|
305
305
|
4. if the change crosses the frontend/backend SSR boundary, did I verify the aligned fullstack build path?
|
|
306
306
|
5. did I verify built behavior rather than relying only on dev behavior?
|
|
307
307
|
|
|
308
|
-
That keeps SSR deployment guidance aligned with the real Cabloy monorepo workflow rather than with generic frontend-only assumptions.
|
|
308
|
+
That keeps SSR deployment guidance aligned with the real Cabloy monorepo workflow rather than with generic frontend-only assumptions.
|
|
@@ -28,6 +28,12 @@ For admin systems, SSR still needs to cooperate with client-facing behavior such
|
|
|
28
28
|
|
|
29
29
|
A central SSR capability is preparing initial data on the server, synchronizing it to the client, and completing hydration naturally.
|
|
30
30
|
|
|
31
|
+
### Initial hydration completion
|
|
32
|
+
|
|
33
|
+
For browser work that must wait for the initial SSR handoff, register `this.$ssr.onHydrated(...)`. The callback runs once only after the SSR root and every currently tracked Zova nested hydration boundary complete, including deferred client updates. Root mount alone is not sufficient when an async component or controller is still hydrating.
|
|
34
|
+
|
|
35
|
+
This lifecycle applies only to the initial client hydration of SSR HTML. It is not an SPA-startup or later client-navigation readiness signal. Do not render a client-ready marker into server HTML; add it from an `onHydrated(...)` callback when browser-visible evidence is required.
|
|
36
|
+
|
|
31
37
|
### SEO meta
|
|
32
38
|
|
|
33
39
|
SSR also supports flexible SEO metadata handling.
|
|
@@ -181,4 +181,4 @@ Before accepting an SSR-related change, ask:
|
|
|
181
181
|
3. did the review verify edition/theme assumptions when the change was SSR-UI-sensitive?
|
|
182
182
|
4. did the review use the smallest correct checklist rather than relying on intuition alone?
|
|
183
183
|
|
|
184
|
-
That keeps SSR reviews consistent across human contributors and AI-assisted workflows.
|
|
184
|
+
That keeps SSR reviews consistent across human contributors and AI-assisted workflows.
|
|
@@ -51,15 +51,15 @@ If you identify the failing layer first, most SSR debugging becomes much faster.
|
|
|
51
51
|
|
|
52
52
|
Use this quick map before diving deeper.
|
|
53
53
|
|
|
54
|
-
| Symptom
|
|
55
|
-
|
|
|
56
|
-
| request does not hit the expected SSR page
|
|
57
|
-
| dev works but prod fails
|
|
58
|
-
| HTML appears but hydration is wrong
|
|
54
|
+
| Symptom | Start here |
|
|
55
|
+
| --------------------------------------------------------- | --------------------------------------- |
|
|
56
|
+
| request does not hit the expected SSR page | Vona SSR orchestration |
|
|
57
|
+
| dev works but prod fails | frontend build output + SSR env/runtime |
|
|
58
|
+
| HTML appears but hydration is wrong | client hydration |
|
|
59
59
|
| server-rendered data is missing or refetched unexpectedly | Zova SSR server render + init data flow |
|
|
60
|
-
| SEO/meta output is missing
|
|
61
|
-
| first paint theme is wrong
|
|
62
|
-
| static assets 404 after SSR deploy
|
|
60
|
+
| SEO/meta output is missing | Zova SSR server render + meta flow |
|
|
61
|
+
| first paint theme is wrong | SSR env + theme rules |
|
|
62
|
+
| static assets 404 after SSR deploy | frontend build output |
|
|
63
63
|
|
|
64
64
|
## Symptom 1: the request does not hit the expected SSR page
|
|
65
65
|
|
|
@@ -298,4 +298,4 @@ Before finalizing an SSR fix, ask:
|
|
|
298
298
|
3. am I reusing the intended framework abstraction instead of adding a parallel workaround?
|
|
299
299
|
4. does the fix accidentally assume an edition-specific UI or theme rule is universal?
|
|
300
300
|
|
|
301
|
-
That keeps SSR debugging aligned with the Cabloy fullstack model and reduces one-off fixes that solve only the symptom.
|
|
301
|
+
That keeps SSR debugging aligned with the Cabloy fullstack model and reduces one-off fixes that solve only the symptom.
|