cabloy 5.1.174 → 5.1.175

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 (76) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +4 -0
  3. package/CHANGELOG.md +18 -0
  4. package/package.json +2 -2
  5. package/repo-docs/.vitepress/config.mjs +7 -0
  6. package/repo-docs/frontend/a-model-under-the-hood.md +11 -0
  7. package/repo-docs/frontend/behavior-guide.md +73 -0
  8. package/repo-docs/frontend/column-configuration-guide.md +254 -0
  9. package/repo-docs/frontend/component-guide.md +2 -29
  10. package/repo-docs/frontend/controller-boundary-guide.md +229 -0
  11. package/repo-docs/frontend/introduction.md +4 -0
  12. package/repo-docs/frontend/page-route-guide.md +6 -0
  13. package/repo-docs/frontend/resource-picker-guide.md +223 -0
  14. package/repo-docs/frontend/rest-resource-source-reading-map.md +37 -21
  15. package/repo-docs/frontend/rest-resource-under-the-hood.md +50 -30
  16. package/repo-docs/frontend/routed-dialog-guide.md +471 -0
  17. package/repo-docs/frontend/router-stack-guide.md +4 -2
  18. package/repo-docs/frontend/router-view-hosts-guide.md +3 -2
  19. package/repo-docs/frontend/ssr-client-only.md +6 -0
  20. package/repo-docs/frontend/table-guide.md +2 -1
  21. package/repo-docs/frontend/table-resource-crud-cookbook.md +1 -0
  22. package/repo-docs/frontend/use-state-data-best-practices.md +26 -0
  23. package/repo-e2e/specs/cabloy-basic.spec.ts +11 -0
  24. package/repo-e2e/specs/controller-boundary.spec.ts +1 -2
  25. package/test-results/.last-run.json +1 -1
  26. package/vona/packages-cli/cli/package.json +1 -1
  27. package/vona/packages-cli/cli-set-api/package.json +1 -1
  28. package/vona/packages-vona/vona/package.json +1 -1
  29. package/vona/packages-vona/vona-core/package.json +1 -1
  30. package/vona/packages-vona/vona-core/src/lib/utils/util.ts +1 -0
  31. package/vona/packages-vona/vona-mock/package.json +1 -1
  32. package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +1 -1
  33. package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/package.json +1 -1
  34. package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/test/layoutprofile.test.ts +2 -3
  35. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  36. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  37. package/vona/src/suite-vendor/a-vona/modules/a-executor/package.json +1 -1
  38. package/vona/src/suite-vendor/a-vona/modules/a-executor/src/service/executor.ts +6 -1
  39. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  40. package/zova/package.original.json +1 -1
  41. package/zova/packages-zova/zova/package.json +2 -2
  42. package/zova/src/suite/a-commerce/modules/commerce-catalog/src/page/product/controller.tsx +4 -3
  43. package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +4 -3
  44. package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionAdjustStock/controller.tsx +3 -2
  45. package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +16 -20
  46. package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionShip/controller.tsx +3 -2
  47. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +6 -5
  48. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +5 -4
  49. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +4 -4
  50. package/zova/src/suite/a-demo/modules/demo-basic/src/page/controllerBoundary/controller.tsx +2 -2
  51. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionDeleteForce.tsx +4 -4
  52. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionSummary.tsx +4 -4
  53. package/zova/src/suite/cabloy-basic/modules/basic-app/src/monkey.ts +11 -0
  54. package/zova/src/suite/cabloy-basic/modules/basic-app/src/types/appModal.ts +1 -0
  55. package/zova/src/suite/cabloy-basic/modules/basic-app/test/lib/routedDialogContext.test.ts +60 -0
  56. package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionUpdate.tsx +4 -4
  57. package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/actionCreate/controller.tsx +4 -4
  58. package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/page/dashboard/controller.tsx +8 -2
  59. package/zova/src/suite/cabloy-basic/modules/basic-table/src/bean/tableCell.actionUpdate.tsx +4 -4
  60. package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/actionCreate/controller.tsx +4 -4
  61. package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/actionDeleteBulk/controller.tsx +4 -4
  62. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  63. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +6 -5
  64. package/zova/src/suite-vendor/a-pay/package.json +2 -2
  65. package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
  66. package/zova/src/suite-vendor/a-zova/modules/a-router/src/monkey.ts +17 -3
  67. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +1 -2
  68. package/zova/src/suite-vendor/a-zova/package.json +3 -4
  69. package/zova/pnpm-lock.yaml +0 -15375
  70. package/zova/src/suite-vendor/a-zova/modules/a-boundary/LICENSE +0 -21
  71. package/zova/src/suite-vendor/a-zova/modules/a-boundary/package.json +0 -52
  72. package/zova/src/suite-vendor/a-zova/modules/a-boundary/src/.metadata/index.ts +0 -26
  73. package/zova/src/suite-vendor/a-zova/modules/a-boundary/src/.metadata/this.ts +0 -2
  74. package/zova/src/suite-vendor/a-zova/modules/a-boundary/src/index.ts +0 -1
  75. package/zova/src/suite-vendor/a-zova/modules/a-boundary/tsconfig.build.json +0 -13
  76. package/zova/src/suite-vendor/a-zova/modules/a-boundary/tsconfig.json +0 -5
@@ -183,6 +183,7 @@ export class AppUtil extends BeanSimple {
183
183
  createError(data, returnObject?: boolean) {
184
184
  const error = returnObject ? ({} as any) : new Error();
185
185
  error.code = data.code !== undefined ? data.code : 500;
186
+ if (data.status !== undefined) error.status = data.status;
186
187
  if (data.message && typeof data.message === 'object') {
187
188
  error.message = JSON.stringify(data.message, null, 2);
188
189
  } else {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-mock",
3
- "version": "6.1.38",
3
+ "version": "6.1.39",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona mock",
6
6
  "keywords": [
@@ -541,7 +541,7 @@ describe('student.test.ts', { concurrency: false }, () => {
541
541
  const deleted = await app.bean.executor.performAction(
542
542
  'post',
543
543
  '/training/student/bulk/delete',
544
- { body: { ids: [studentId, studentId, '__missing-student__'] } },
544
+ { body: { ids: [studentId, studentId, '-1'] } },
545
545
  );
546
546
  assert.equal(deleted, null);
547
547
  assert.equal(
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-layoutprofile",
3
- "version": "5.0.1",
3
+ "version": "5.0.2",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "Vona Module"
@@ -4,6 +4,7 @@ import { catchError } from '@cabloy/utils';
4
4
  import assert from 'node:assert';
5
5
  import { randomUUID } from 'node:crypto';
6
6
  import { describe, it } from 'node:test';
7
+ import { isDeepStrictEqual } from 'node:util';
7
8
  import { app } from 'vona-mock';
8
9
 
9
10
  import type { ILayoutProfile } from '../src/types/layoutprofile.ts';
@@ -200,9 +201,7 @@ describe('layoutprofile.test.ts', { concurrency: false }, () => {
200
201
  });
201
202
  });
202
203
  assert.equal(persisted.length, 1);
203
- assert.ok(
204
- profiles.some(profile => JSON.stringify(profile) === JSON.stringify(persisted[0].profile)),
205
- );
204
+ assert.ok(profiles.some(profile => isDeepStrictEqual(profile, persisted[0].profile)));
206
205
 
207
206
  const loaded = await app.bean.executor.mockCtx(async () => {
208
207
  return await performAs(fixture.token, 'get', `${actionPath}/load`, {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-cabloy",
3
- "version": "5.1.53",
3
+ "version": "5.1.54",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -8,7 +8,7 @@
8
8
  "type": "module",
9
9
  "dependencies": {
10
10
  "vona-module-a-cabloy": "workspace:^",
11
- "vona-module-a-layoutprofile": "^5.0.1",
11
+ "vona-module-a-layoutprofile": "^5.0.2",
12
12
  "vona-module-a-markdown": "^5.0.3",
13
13
  "vona-module-a-rbac": "^5.0.6",
14
14
  "vona-module-a-socket": "workspace:^",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-core",
3
- "version": "5.1.52",
3
+ "version": "5.1.53",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-executor",
3
- "version": "5.1.16",
3
+ "version": "5.1.17",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -64,10 +64,15 @@ export class ServiceExecutor extends BeanBase {
64
64
  throw app.util.createError(ctx.body);
65
65
  }
66
66
  if (ctx.body && typeof ctx.body === 'object') {
67
- throw app.util.createError(ctx.body);
67
+ throw app.util.createError({
68
+ ...ctx.body,
69
+ status: ctx.status,
70
+ code: (ctx.body as { code?: unknown }).code ?? ctx.status,
71
+ });
68
72
  }
69
73
  throw app.util.createError({
70
74
  code: ctx.status,
75
+ status: ctx.status,
71
76
  message: ctx.message,
72
77
  });
73
78
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-vona",
3
- "version": "5.1.86",
3
+ "version": "5.1.87",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -74,7 +74,7 @@
74
74
  "zova": "workspace:^"
75
75
  },
76
76
  "devDependencies": {
77
- "@cabloy/lint": "^5.1.33",
77
+ "@cabloy/lint": "^5.1.35",
78
78
  "@cabloy/openapi-typescript": "^7.9.2",
79
79
  "@quasar/app-vite": "npm:@cabloy/quasar-app-vite@^2.5.10",
80
80
  "@types/node": "^22.19.17",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova",
3
- "version": "5.1.163",
3
+ "version": "5.1.164",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A vue3 framework with ioc",
6
6
  "keywords": [
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "zova-core": "^5.1.90",
49
- "zova-suite-a-zova": "^5.1.162"
49
+ "zova-suite-a-zova": "^5.1.163"
50
50
  },
51
51
  "devDependencies": {
52
52
  "clean-package": "^2.2.0",
@@ -5,6 +5,7 @@ import { z } from 'zod';
5
5
  import { BeanControllerPageBase, Use } from 'zova';
6
6
  import { Controller } from 'zova-module-a-bean';
7
7
  import { $QueryEnsureLoaded } from 'zova-module-a-model';
8
+ import { ZButton } from 'zova-module-basic-button';
8
9
  import { ZMarkdownHtml } from 'zova-module-basic-markdown';
9
10
  import { ZPage } from 'zova-module-home-base';
10
11
 
@@ -88,12 +89,12 @@ export class ControllerPageProduct extends BeanControllerPageBase {
88
89
  <span class="badge badge-success">
89
90
  {this.scope.locale.AvailableCount(sku.available)}
90
91
  </span>
91
- <button
92
+ <ZButton
92
93
  class="btn btn-primary btn-sm"
93
- onClick={() => this.addItem(String(sku.id))}
94
+ onPerform={() => this.addItem(String(sku.id))}
94
95
  >
95
96
  {this.scope.locale.AddToCart()}
96
- </button>
97
+ </ZButton>
97
98
  </div>
98
99
  ))}
99
100
  </div>
@@ -7,6 +7,7 @@ import { BeanControllerPageBase, Use } from 'zova';
7
7
  import { Controller } from 'zova-module-a-bean';
8
8
  import { ZForm } from 'zova-module-a-form';
9
9
  import { $QueryEnsureLoaded } from 'zova-module-a-model';
10
+ import { ZButton } from 'zova-module-basic-button';
10
11
  import { ZPage } from 'zova-module-home-base';
11
12
 
12
13
  import type {
@@ -127,12 +128,12 @@ export class ControllerPageAddress extends BeanControllerPageBase {
127
128
  <button class="btn btn-outline btn-sm" onClick={() => this.edit(item)}>
128
129
  {this.scope.locale.EditAddress()}
129
130
  </button>
130
- <button
131
+ <ZButton
131
132
  class="btn btn-error btn-outline btn-sm"
132
- onClick={() => this.delete(item)}
133
+ onPerform={() => this.delete(item)}
133
134
  >
134
135
  {this.scope.locale.DeleteAddress()}
135
- </button>
136
+ </ZButton>
136
137
  </div>
137
138
  </div>
138
139
  <p>{item.addressLine1}</p>
@@ -7,6 +7,7 @@ import { BeanControllerBase, Use } from 'zova';
7
7
  import { Controller } from 'zova-module-a-bean';
8
8
  import { ZForm } from 'zova-module-a-form';
9
9
  import { $QueryEnsureLoaded } from 'zova-module-a-model';
10
+ import { ZButton } from 'zova-module-basic-button';
10
11
 
11
12
  import type { ApiApiCommerceTradeStockBalanceadjustStockRequestBody } from '../../api/commerceTradeStockBalance.js';
12
13
  import type { ITableCellOptionsActionAdjustStock } from '../../bean/tableCell.actionAdjustStock.js';
@@ -130,9 +131,9 @@ export class ControllerTableCellActionAdjustStock extends BeanControllerBase {
130
131
  protected render() {
131
132
  const props = this.$props as ControllerTableCellActionAdjustStockProps;
132
133
  return (
133
- <button class={props.class} type="button" onClick={() => this._openDialog()}>
134
+ <ZButton class={props.class} onPerform={() => this._openDialog()}>
134
135
  {this.scope.locale.AdjustStock()}
135
- </button>
136
+ </ZButton>
136
137
  );
137
138
  }
138
139
  }
@@ -4,6 +4,7 @@ import type { ModelPayMockPayment } from 'zova-module-pay-mock';
4
4
 
5
5
  import { BeanControllerBase, Use } from 'zova';
6
6
  import { Controller } from 'zova-module-a-bean';
7
+ import { ZButton } from 'zova-module-basic-button';
7
8
 
8
9
  import type {
9
10
  ApiSchemaCommerceTradeDtoOrderSelectResItem,
@@ -118,22 +119,20 @@ export class ControllerTableCellActionRefund extends BeanControllerBase {
118
119
  <button class="btn btn-ghost" type="button" onClick={dialog.close}>
119
120
  {this.scope.locale.Cancel()}
120
121
  </button>
121
- <button
122
+ <ZButton
122
123
  class="btn btn-error"
123
- type="button"
124
124
  disabled={disabled}
125
- onClick={() => this._submitDecision(false, dialog.close)}
125
+ onPerform={() => this._submitDecision(false, dialog.close)}
126
126
  >
127
127
  {this.scope.locale.RejectRefund()}
128
- </button>
129
- <button
128
+ </ZButton>
129
+ <ZButton
130
130
  class="btn btn-warning"
131
- type="button"
132
131
  disabled={disabled}
133
- onClick={() => this._submitDecision(true, dialog.close)}
132
+ onPerform={() => this._submitDecision(true, dialog.close)}
134
133
  >
135
134
  {this.scope.locale.ApproveRefund()}
136
- </button>
135
+ </ZButton>
137
136
  </>
138
137
  );
139
138
  },
@@ -397,14 +396,13 @@ export class ControllerTableCellActionRefund extends BeanControllerBase {
397
396
  )}
398
397
  {(recoveryData.recoveryDisposition === 'query_only' ||
399
398
  recoveryData.recoveryDisposition === 'reconcile_only') && (
400
- <button
399
+ <ZButton
401
400
  class="btn btn-outline btn-sm mt-2"
402
- type="button"
403
401
  disabled={this._recoveryActionPending}
404
- onClick={() => this._reconcile()}
402
+ onPerform={() => this._reconcile()}
405
403
  >
406
404
  {this.scope.locale.ReconcileProviderRefund()}
407
- </button>
405
+ </ZButton>
408
406
  )}
409
407
  {recoveryData.recoveryDisposition === 'retry_same_key' && (
410
408
  <>
@@ -419,14 +417,13 @@ export class ControllerTableCellActionRefund extends BeanControllerBase {
419
417
  {this.scope.locale.AcknowledgeRefundRetryRisk()}
420
418
  </span>
421
419
  </label>
422
- <button
420
+ <ZButton
423
421
  class="btn btn-warning btn-sm"
424
- type="button"
425
422
  disabled={this._recoveryActionPending}
426
- onClick={() => this._retry()}
423
+ onPerform={() => this._retry()}
427
424
  >
428
425
  {this.scope.locale.RetryRefund()}
429
- </button>
426
+ </ZButton>
430
427
  </>
431
428
  )}
432
429
  </>
@@ -444,14 +441,13 @@ export class ControllerTableCellActionRefund extends BeanControllerBase {
444
441
  <button class="btn btn-ghost" type="button" onClick={dialog.close}>
445
442
  {this.scope.locale.Cancel()}
446
443
  </button>
447
- <button
444
+ <ZButton
448
445
  class="btn btn-warning"
449
- type="button"
450
446
  disabled={disabled}
451
- onClick={() => this._execute(dialog.close)}
447
+ onPerform={() => this._execute(dialog.close)}
452
448
  >
453
449
  {this.scope.locale.ExecuteRefund()}
454
- </button>
450
+ </ZButton>
455
451
  </>
456
452
  );
457
453
  },
@@ -3,6 +3,7 @@ import type { IJsxRenderContextTableCell } from 'zova-module-a-table';
3
3
 
4
4
  import { BeanControllerBase, Use } from 'zova';
5
5
  import { Controller } from 'zova-module-a-bean';
6
+ import { ZButton } from 'zova-module-basic-button';
6
7
 
7
8
  import type { ITableCellOptionsActionShip } from '../../bean/tableCell.actionShip.js';
8
9
  import type { ModelOrder } from '../../model/order.js';
@@ -57,9 +58,9 @@ export class ControllerTableCellActionShip extends BeanControllerBase {
57
58
  />
58
59
  <span class="label-text text-xs">{this.scope.locale.Confirm()}</span>
59
60
  </label>
60
- <button class={props.class} type="button" onClick={() => this._ship()}>
61
+ <ZButton class={props.class} onPerform={() => this._ship()}>
61
62
  {this.scope.locale.ShipOrder()}
62
- </button>
63
+ </ZButton>
63
64
  </div>
64
65
  );
65
66
  }
@@ -3,6 +3,7 @@ import { z } from 'zod';
3
3
  import { BeanControllerPageBase, Use } from 'zova';
4
4
  import { Controller } from 'zova-module-a-bean';
5
5
  import { $QueryEnsureLoaded } from 'zova-module-a-model';
6
+ import { ZButton } from 'zova-module-basic-button';
6
7
  import { ZPage } from 'zova-module-home-base';
7
8
 
8
9
  import { ModelCart } from '../../model/cart.js';
@@ -62,9 +63,9 @@ export class ControllerPageCart extends BeanControllerPageBase {
62
63
  <RouterLink class="btn btn-primary btn-sm" to={this._getCheckoutPagePath()}>
63
64
  {this.scope.locale.Checkout()}
64
65
  </RouterLink>
65
- <button class="btn btn-outline btn-sm" onClick={() => this.clear()}>
66
+ <ZButton class="btn btn-outline btn-sm" onPerform={() => this.clear()}>
66
67
  {this.scope.locale.ClearCart()}
67
- </button>
68
+ </ZButton>
68
69
  </>
69
70
  )}
70
71
  </div>
@@ -93,12 +94,12 @@ export class ControllerPageCart extends BeanControllerPageBase {
93
94
  onChange={(event: Event) => this.updateQuantity(String(item.id), event)}
94
95
  />
95
96
  </label>
96
- <button
97
+ <ZButton
97
98
  class="btn btn-error btn-outline btn-sm"
98
- onClick={() => this.deleteItem(String(item.id))}
99
+ onPerform={() => this.deleteItem(String(item.id))}
99
100
  >
100
101
  {this.scope.locale.RemoveCartItem()}
101
- </button>
102
+ </ZButton>
102
103
  </div>
103
104
  </div>
104
105
  </article>
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import { BeanControllerPageBase, Use } from 'zova';
3
3
  import { Controller } from 'zova-module-a-bean';
4
4
  import { $QueriesEnsureLoaded } from 'zova-module-a-model';
5
+ import { ZButton } from 'zova-module-basic-button';
5
6
  import { ModelAddressMine } from 'zova-module-commerce-member';
6
7
  import { ModelCoupon } from 'zova-module-commerce-promotion';
7
8
  import { ZPage } from 'zova-module-home-base';
@@ -57,7 +58,7 @@ export class ControllerPageCheckout extends BeanControllerPageBase {
57
58
  correlationId: `${Date.now()}-${Math.random().toString(36).slice(2, 12)}`,
58
59
  providerCandidateKey: this.providerCandidateKey,
59
60
  });
60
- this.$router.push(
61
+ await this.$router.push(
61
62
  this.$router.getPagePath('/commerce/trade/payment/:paymentSessionId/:orderId', {
62
63
  params: {
63
64
  paymentSessionId: String(result.paymentSessionId),
@@ -139,13 +140,13 @@ export class ControllerPageCheckout extends BeanControllerPageBase {
139
140
  </label>
140
141
  ))}
141
142
  </fieldset>
142
- <button
143
+ <ZButton
143
144
  class="btn btn-primary"
144
145
  disabled={!this.addressId || !this.providerCandidateKey || this.submitting}
145
- onClick={() => this.checkout()}
146
+ onPerform={() => this.checkout()}
146
147
  >
147
148
  {this.scope.locale.CreateOrder()}
148
- </button>
149
+ </ZButton>
149
150
  </div>
150
151
  </section>
151
152
  </ZPage>
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import { BeanControllerPageBase, Use } from 'zova';
3
3
  import { Controller } from 'zova-module-a-bean';
4
4
  import { $QueryEnsureLoaded } from 'zova-module-a-model';
5
+ import { ZButton } from 'zova-module-basic-button';
5
6
  import { ZPage } from 'zova-module-home-base';
6
7
 
7
8
  import { ModelOrderMine } from '../../model/orderMine.js';
@@ -101,14 +102,13 @@ export class ControllerPageOrder extends BeanControllerPageBase {
101
102
  placeholder={this.scope.locale.RefundReason()}
102
103
  v-model={this.refundReason}
103
104
  />
104
- <button
105
+ <ZButton
105
106
  class="btn btn-outline btn-warning self-start"
106
- type="button"
107
107
  disabled={!this.refundReason.trim() || this.mutationRequestRefund.isPending}
108
- onClick={() => this._requestRefund()}
108
+ onPerform={() => this._requestRefund()}
109
109
  >
110
110
  {this.scope.locale.RequestRefund()}
111
- </button>
111
+ </ZButton>
112
112
  </div>
113
113
  </article>
114
114
  )}
@@ -74,12 +74,12 @@ export class ControllerPageControllerBoundary extends BeanControllerPageBase {
74
74
  }
75
75
 
76
76
  mountOverrideFailingProbe() {
77
- this.overrideProbe = { mode: 'failure', delayMs: 800 };
77
+ this.overrideProbe = { mode: 'failure', delayMs: 2_000 };
78
78
  this.overrideProbeKey++;
79
79
  }
80
80
 
81
81
  recoverOverrideWithDelayedSuccess() {
82
- this.overrideProbe = { mode: 'success', delayMs: 800 };
82
+ this.overrideProbe = { mode: 'success', delayMs: 2_000 };
83
83
  this.overrideProbeKey++;
84
84
  }
85
85
 
@@ -8,6 +8,7 @@ import type {
8
8
  import { TableIdentity } from 'table-identity';
9
9
  import { BeanBase } from 'zova';
10
10
  import { TableCell } from 'zova-module-a-table';
11
+ import { ZButton } from 'zova-module-basic-button';
11
12
 
12
13
  import type { ModelStudent } from '../model/student.js';
13
14
 
@@ -30,10 +31,9 @@ export class TableCellActionDeleteForce extends BeanBase implements ITableCellRe
30
31
  ) {
31
32
  const { $host, cellContext, ctx } = renderContext;
32
33
  return (
33
- <button
34
+ <ZButton
34
35
  class={options.class}
35
- type="button"
36
- onClick={async () => {
36
+ onPerform={async () => {
37
37
  const confirmed = await $host.$performCommand('basic-commands:confirm', {
38
38
  text: this.scope.locale.ForceDeleteConfirm(),
39
39
  });
@@ -47,7 +47,7 @@ export class TableCellActionDeleteForce extends BeanBase implements ITableCellRe
47
47
  }}
48
48
  >
49
49
  {this.scope.locale.ForceDelete()}
50
- </button>
50
+ </ZButton>
51
51
  );
52
52
  }
53
53
  }
@@ -9,6 +9,7 @@ import type { ModelStudent } from 'zova-module-training-student';
9
9
 
10
10
  import { BeanBase } from 'zova';
11
11
  import { TableCell } from 'zova-module-a-table';
12
+ import { ZButton } from 'zova-module-basic-button';
12
13
  import { ZMarkdownHtml } from 'zova-module-basic-markdown';
13
14
 
14
15
  declare module 'zova-module-a-openapi' {
@@ -30,10 +31,9 @@ export class TableCellActionSummary extends BeanBase implements ITableCellRender
30
31
  ) {
31
32
  const { $host, cellContext, ctx } = renderContext;
32
33
  return (
33
- <button
34
+ <ZButton
34
35
  class={options.class}
35
- type="button"
36
- onClick={async () => {
36
+ onPerform={async () => {
37
37
  const id = cellContext.row.id as TableIdentity;
38
38
  const modelStudent = (await ctx.bean._getBean(
39
39
  'training-student.model.student',
@@ -84,7 +84,7 @@ export class TableCellActionSummary extends BeanBase implements ITableCellRender
84
84
  }}
85
85
  >
86
86
  {this.scope.locale.Summary()}
87
- </button>
87
+ </ZButton>
88
88
  );
89
89
  }
90
90
  }
@@ -4,6 +4,9 @@ import type { BeanBase, BeanContainer, IMonkeyBeanInit, IMonkeyModule } from 'zo
4
4
  import { BeanSimple } from 'zova';
5
5
 
6
6
  import type { ServiceAppModal } from './service/appModal.js';
7
+ import type { IRoutedDialogContext } from './types/appModal.js';
8
+
9
+ import { routedDialogContextKey } from './types/appModal.js';
7
10
 
8
11
  export class Monkey extends BeanSimple implements IMonkeyModule, IMonkeyBeanInit {
9
12
  private _moduleSelf: IModule;
@@ -30,6 +33,14 @@ export class Monkey extends BeanSimple implements IMonkeyModule, IMonkeyBeanInit
30
33
  return self._serviceAppModal;
31
34
  },
32
35
  });
36
+ Object.defineProperty(beanInstance, '$routedDialog', {
37
+ enumerable: false,
38
+ configurable: true,
39
+ get: () =>
40
+ bean._getBeanFromHost<IRoutedDialogContext>({
41
+ name: routedDialogContextKey,
42
+ }),
43
+ });
33
44
  }
34
45
 
35
46
  private async _loadServiceAppModal() {
@@ -188,5 +188,6 @@ export type IModalItem =
188
188
  declare module 'zova' {
189
189
  export interface BeanBase {
190
190
  $appModal: ServiceAppModal;
191
+ $routedDialog: IRoutedDialogContext | undefined;
191
192
  }
192
193
  }
@@ -0,0 +1,60 @@
1
+ import type { IModule } from '@cabloy/module-info';
2
+ import type { BeanBase, BeanContainer } from 'zova';
3
+
4
+ import assert from 'node:assert/strict';
5
+ import test from 'node:test';
6
+
7
+ import type { IRoutedDialogContext } from '../../src/types/appModal.js';
8
+
9
+ import { Monkey } from '../../src/monkey.js';
10
+ import { routedDialogContextKey } from '../../src/types/appModal.js';
11
+
12
+ function createBean() {
13
+ const lookups: string[] = [];
14
+ let context: IRoutedDialogContext | undefined;
15
+ const beanContainer = {
16
+ defineProperty<T>(beanInstance: T, prop: string, attributes: PropertyDescriptor) {
17
+ return Object.defineProperty(beanInstance, prop, attributes);
18
+ },
19
+ _getBeanFromHost<T>({ name }: { name: string }) {
20
+ lookups.push(name);
21
+ return context as T | undefined;
22
+ },
23
+ } as unknown as BeanContainer;
24
+ const beanInstance = {} as BeanBase;
25
+ const monkey = new Monkey({} as IModule);
26
+
27
+ return {
28
+ beanInstance,
29
+ beanContainer,
30
+ lookups,
31
+ monkey,
32
+ setContext(value: IRoutedDialogContext | undefined) {
33
+ context = value;
34
+ },
35
+ };
36
+ }
37
+
38
+ test('routed dialog bean property always reads the current host context', async () => {
39
+ const { beanInstance, beanContainer, lookups, monkey, setContext } = createBean();
40
+ await monkey.beanInit(beanContainer, beanInstance);
41
+
42
+ assert.equal(beanInstance.$routedDialog, undefined);
43
+
44
+ const firstContext = {} as IRoutedDialogContext;
45
+ setContext(firstContext);
46
+ assert.equal(beanInstance.$routedDialog, firstContext);
47
+
48
+ const secondContext = {} as IRoutedDialogContext;
49
+ setContext(secondContext);
50
+ assert.equal(beanInstance.$routedDialog, secondContext);
51
+
52
+ setContext(undefined);
53
+ assert.equal(beanInstance.$routedDialog, undefined);
54
+ assert.deepEqual(lookups, [
55
+ routedDialogContextKey,
56
+ routedDialogContextKey,
57
+ routedDialogContextKey,
58
+ routedDialogContextKey,
59
+ ]);
60
+ });
@@ -10,6 +10,7 @@ import { BeanBase, deepExtend } from 'zova';
10
10
  import { TypeFormOnSubmitData } from 'zova-module-a-form';
11
11
  import { IIconRecord, ZIcon } from 'zova-module-a-icon';
12
12
  import { TableCell } from 'zova-module-a-table';
13
+ import { ZButton } from 'zova-module-basic-button';
13
14
 
14
15
  import { ServiceDetail } from '../service/detail.jsx';
15
16
  import { IDialogFormOptions } from '../types/dialogForm.js';
@@ -35,10 +36,9 @@ export class TableCellActionUpdate extends BeanBase implements ITableCellRender
35
36
  ) {
36
37
  const { ctx, $celScope, cellContext } = renderContext;
37
38
  return (
38
- <button
39
+ <ZButton
39
40
  class={options.class}
40
- type="button"
41
- onClick={async () => {
41
+ onPerform={async () => {
42
42
  const $$details = $celScope.$$details;
43
43
  if (!$$details) throw new Error('should provide $$details in cell scope');
44
44
  const detailItem = cellContext.row.original as Record<string, any>;
@@ -68,7 +68,7 @@ export class TableCellActionUpdate extends BeanBase implements ITableCellRender
68
68
  }}
69
69
  >
70
70
  <ZIcon name="::draft" width={24}></ZIcon>
71
- </button>
71
+ </ZButton>
72
72
  );
73
73
  }
74
74
  }