cabloy 5.1.141 → 5.1.142
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/.github/workflows/playwright-e2e.yml +2 -2
- package/CHANGELOG.md +21 -0
- package/cabloy-docs/frontend/form-guide.md +47 -0
- package/cabloy-docs/frontend/markdown-guide.md +50 -17
- package/cabloy-docs/frontend/route-alias-guide.md +20 -0
- package/cabloy-docs/frontend/scripts.md +9 -11
- package/cabloy-docs/frontend/zova-form-under-the-hood.md +25 -0
- package/cabloy-docs/fullstack/parallel-worktree-environment.md +1 -1
- package/cabloy-docs/fullstack/quickstart.md +5 -4
- package/cabloy-docs/reference/repo-scripts.md +29 -42
- package/e2e/config/playwright.config.ts +34 -0
- package/e2e/scripts/e2e.ts +2 -27
- package/e2e/scripts/runE2e.ts +62 -0
- package/e2e/scripts/runE2eArgs.test.ts +75 -0
- package/e2e/scripts/runE2eArgs.ts +158 -0
- package/e2e/specs/{a-commerce/commerce.spec.ts → a-commerce.spec.ts} +361 -30
- package/e2e/specs/{cabloy-basic/account.spec.ts → account.spec.ts} +1 -2
- package/oxlint.config.ts +3 -0
- package/package.json +3 -9
- package/scripts/upgrade.ts +63 -68
- package/vona/packages-utils/lint/package.json +1 -1
- package/vona/packages-utils/lint/src/oxc/lint.ts +1 -1
- package/vona/packages-utils/lint/src/oxc/lintVue.ts +1 -1
- package/vona/pnpm-lock.yaml +41 -41
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRecoveryView.tsx +7 -2
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +31 -15
- package/vona/src/suite/a-home/modules/home-user/package.json +1 -0
- package/vona/src/suite/a-home/modules/home-user/src/service/account.ts +15 -2
- package/vona/src/suite/a-home/modules/home-user/test/accountActivation.test.ts +34 -4
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/package.json +2 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/.metadata/index.ts +26 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/bean/imageScene.markdown.ts +13 -0
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +23 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +7 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/package.json +5 -5
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +1 -1
- package/zova/packages-zova/zova-core/src/bean/resource/locale/type.ts +1 -0
- package/zova/pnpm-lock.yaml +63 -18
- package/zova/src/front/config/config/config.cabloyBasicWeb.ts +3 -1
- package/zova/src/front/config/config/config.cabloyCommerce.ts +4 -2
- package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +64 -63
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +6 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +413 -134
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +76 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +67 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +1 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +7 -7
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +46 -9
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +50 -30
- package/zova/src/suite/cabloy-basic/modules/basic-captcha/src/component/formFieldCaptcha/controller.tsx +6 -2
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +9 -15
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/package.json +10 -2
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/index.ts +11 -2
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/locales.ts +7 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/controller.tsx +723 -5
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/render.tsx +461 -2
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/config/locale/en-us.ts +59 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/config/locale/zh-cn.ts +59 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/codeBlockLanguages.ts +42 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/richTextContentStyle.ts +20 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/test/lib/codeBlockLanguages.test.ts +25 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +1 -0
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
- package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +26 -12
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/cli/openapi.config.ts +1 -1
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/payMockMockPayment.ts +26 -0
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/apiSchema/payMockMockPayment.ts +8 -1
- package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/model/payMockPayment.ts +13 -1
- package/zova/src/suite-vendor/a-pay/package.json +3 -3
- 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/component/formField/controller.tsx +2 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/formField.ts +7 -6
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +3 -3
- package/e2e/config/playwright.basic.config.ts +0 -3
- package/e2e/config/playwright.commerce.config.ts +0 -3
- package/e2e/config/playwright.shared.config.ts +0 -38
- package/e2e/scripts/testE2eClean.ts +0 -56
- /package/e2e/specs/{cabloy-basic/basic.spec.ts → cabloy-basic.spec.ts} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TableIdentity } from 'table-identity';
|
|
2
2
|
import type { IJsxRenderContextTableCell } from 'zova-module-a-table';
|
|
3
|
+
import type { ModelPayMockPayment } from 'zova-module-pay-mock';
|
|
3
4
|
|
|
4
5
|
import { BeanControllerBase, Use } from 'zova';
|
|
5
6
|
import { Controller } from 'zova-module-a-bean';
|
|
@@ -23,10 +24,18 @@ export class ControllerTableCellActionRefund extends BeanControllerBase {
|
|
|
23
24
|
@Use({ beanFullName: 'commerce-trade.model.order' })
|
|
24
25
|
$$modelOrder: ModelOrder;
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
@Use({ beanFullName: 'pay-mock.model.payMockPayment' })
|
|
28
|
+
$$modelPayMockPayment: ModelPayMockPayment;
|
|
29
|
+
|
|
30
|
+
decisionReason = '';
|
|
31
|
+
decisionConfirmed = false;
|
|
32
|
+
executionConfirmed = false;
|
|
28
33
|
recoveryOpen = false;
|
|
29
34
|
recoveryLoaded = false;
|
|
35
|
+
recoveryUnavailable = false;
|
|
36
|
+
recoveryError?: unknown;
|
|
37
|
+
mockCompletionError?: unknown;
|
|
38
|
+
mockCompletionSucceeded = false;
|
|
30
39
|
recoveryReason = '';
|
|
31
40
|
retryRiskAcknowledged = false;
|
|
32
41
|
approveIdempotencyKey?: string;
|
|
@@ -34,185 +43,455 @@ export class ControllerTableCellActionRefund extends BeanControllerBase {
|
|
|
34
43
|
reconcileIdempotencyKey?: string;
|
|
35
44
|
retryIdempotencyKey?: string;
|
|
36
45
|
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
private get _orderId() {
|
|
47
|
+
return this.$$renderContext.cellContext.row.id as TableIdentity;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private get _decisionPending() {
|
|
51
|
+
return (
|
|
52
|
+
this.$$modelOrder.approveRefund(this._orderId).isPending ||
|
|
53
|
+
this.$$modelOrder.rejectRefund(this._orderId).isPending
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private get _decisionValid() {
|
|
58
|
+
return !!this.decisionReason.trim() && this.decisionConfirmed;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private async _submitDecision(approved: boolean, close: () => void) {
|
|
62
|
+
const reason = this.decisionReason.trim();
|
|
63
|
+
if (!reason || !this.decisionConfirmed || this._decisionPending) return;
|
|
64
|
+
const idempotencyKey = approved
|
|
65
|
+
? (this.approveIdempotencyKey ?? crypto.randomUUID())
|
|
66
|
+
: (this.rejectIdempotencyKey ?? crypto.randomUUID());
|
|
67
|
+
if (approved) {
|
|
68
|
+
this.approveIdempotencyKey = idempotencyKey;
|
|
69
|
+
} else {
|
|
70
|
+
this.rejectIdempotencyKey = idempotencyKey;
|
|
71
|
+
}
|
|
72
|
+
const mutation = approved
|
|
73
|
+
? this.$$modelOrder.approveRefund(this._orderId)
|
|
74
|
+
: this.$$modelOrder.rejectRefund(this._orderId);
|
|
75
|
+
await mutation.mutateAsync({ reason, idempotencyKey });
|
|
76
|
+
if (approved) {
|
|
77
|
+
this.approveIdempotencyKey = undefined;
|
|
78
|
+
} else {
|
|
79
|
+
this.rejectIdempotencyKey = undefined;
|
|
80
|
+
}
|
|
81
|
+
close();
|
|
45
82
|
}
|
|
46
83
|
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const idempotencyKey = this.rejectIdempotencyKey ?? crypto.randomUUID();
|
|
52
|
-
this.rejectIdempotencyKey = idempotencyKey;
|
|
53
|
-
await this.$$modelOrder.rejectRefund(orderId).mutateAsync({ reason, idempotencyKey });
|
|
84
|
+
private _openRefundDecisionDialog() {
|
|
85
|
+
this.decisionReason = '';
|
|
86
|
+
this.decisionConfirmed = false;
|
|
87
|
+
this.approveIdempotencyKey = undefined;
|
|
54
88
|
this.rejectIdempotencyKey = undefined;
|
|
89
|
+
this.$appModal.dialog(
|
|
90
|
+
{
|
|
91
|
+
title: this.scope.locale.RefundDecision(),
|
|
92
|
+
slotDefault: () => {
|
|
93
|
+
return (
|
|
94
|
+
<>
|
|
95
|
+
<textarea
|
|
96
|
+
class="textarea textarea-bordered w-full"
|
|
97
|
+
name="decisionReason"
|
|
98
|
+
placeholder={this.scope.locale.RefundDecisionReason()}
|
|
99
|
+
v-model={this.decisionReason}
|
|
100
|
+
/>
|
|
101
|
+
<label class="label cursor-pointer mt-2 justify-start gap-2">
|
|
102
|
+
<input
|
|
103
|
+
class="checkbox checkbox-sm"
|
|
104
|
+
name="decisionConfirmed"
|
|
105
|
+
type="checkbox"
|
|
106
|
+
v-model={this.decisionConfirmed}
|
|
107
|
+
/>
|
|
108
|
+
<span class="label-text text-xs">{this.scope.locale.Confirm()}</span>
|
|
109
|
+
</label>
|
|
110
|
+
</>
|
|
111
|
+
);
|
|
112
|
+
},
|
|
113
|
+
slotActions: dialog => {
|
|
114
|
+
const disabled = !this._decisionValid || this._decisionPending;
|
|
115
|
+
return (
|
|
116
|
+
<>
|
|
117
|
+
{this._decisionPending && <span class="loading loading-spinner text-primary"></span>}
|
|
118
|
+
<button class="btn btn-ghost" type="button" onClick={dialog.close}>
|
|
119
|
+
{this.scope.locale.Cancel()}
|
|
120
|
+
</button>
|
|
121
|
+
<button
|
|
122
|
+
class="btn btn-error"
|
|
123
|
+
type="button"
|
|
124
|
+
disabled={disabled}
|
|
125
|
+
onClick={() => this._submitDecision(false, dialog.close)}
|
|
126
|
+
>
|
|
127
|
+
{this.scope.locale.RejectRefund()}
|
|
128
|
+
</button>
|
|
129
|
+
<button
|
|
130
|
+
class="btn btn-warning"
|
|
131
|
+
type="button"
|
|
132
|
+
disabled={disabled}
|
|
133
|
+
onClick={() => this._submitDecision(true, dialog.close)}
|
|
134
|
+
>
|
|
135
|
+
{this.scope.locale.ApproveRefund()}
|
|
136
|
+
</button>
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
closeOnBackdrop: false,
|
|
143
|
+
closeOnEscape: false,
|
|
144
|
+
maxWidth: 480,
|
|
145
|
+
showCloseButton: true,
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private get _executionPending() {
|
|
151
|
+
return this.$$modelOrder.executeRefund(this._orderId).isPending;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private get _recoveryActionPending() {
|
|
155
|
+
return (
|
|
156
|
+
this.$$modelOrder.reconcileRefund(this._orderId).isPending ||
|
|
157
|
+
this.$$modelOrder.retryRefund(this._orderId).isPending
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private get _mockCompletion() {
|
|
162
|
+
const recovery = this._recovery().data as
|
|
163
|
+
| ApiSchemaCommerceTradeDtoRefundRecoveryView
|
|
164
|
+
| undefined;
|
|
165
|
+
if (!recovery || recovery.providerName !== 'pay-mock:mock') return undefined;
|
|
166
|
+
if (recovery.refundOperationState !== 'pending') return undefined;
|
|
167
|
+
return this.$$modelPayMockPayment.completeRefund(recovery.refundOperationId);
|
|
55
168
|
}
|
|
56
169
|
|
|
57
|
-
private
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
170
|
+
private get _mockCompletionAvailable() {
|
|
171
|
+
return (
|
|
172
|
+
this.sys.env.META_MODE !== 'production' &&
|
|
173
|
+
this.recoveryOpen &&
|
|
174
|
+
this.recoveryDisposition === 'await_webhook'
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private get recoveryDisposition() {
|
|
179
|
+
const recovery = this._recovery().data as
|
|
180
|
+
| ApiSchemaCommerceTradeDtoRefundRecoveryView
|
|
181
|
+
| undefined;
|
|
182
|
+
return recovery?.recoveryDisposition;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private async _completeMockRefund() {
|
|
186
|
+
const mutation = this._mockCompletion;
|
|
187
|
+
if (!mutation || mutation.isPending) return;
|
|
188
|
+
this.mockCompletionError = undefined;
|
|
189
|
+
try {
|
|
190
|
+
await mutation.mutateAsync({ outcome: 'succeeded' });
|
|
191
|
+
this.mockCompletionSucceeded = true;
|
|
192
|
+
this.recoveryError = undefined;
|
|
193
|
+
this.recoveryUnavailable = false;
|
|
194
|
+
this.recoveryLoaded = true;
|
|
195
|
+
await this.$$modelOrder.select()?.refetch();
|
|
196
|
+
} catch (error) {
|
|
197
|
+
this.mockCompletionError = error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private async _execute(close: () => void) {
|
|
202
|
+
if (!this.executionConfirmed || this._executionPending) return;
|
|
203
|
+
await this.$$modelOrder.executeRefund(this._orderId).mutateAsync(undefined);
|
|
204
|
+
close();
|
|
61
205
|
}
|
|
62
206
|
|
|
63
207
|
private _recovery() {
|
|
64
|
-
const orderId = this
|
|
208
|
+
const orderId = this._orderId;
|
|
65
209
|
return this.$$modelOrder.refundRecovery(orderId);
|
|
66
210
|
}
|
|
67
211
|
|
|
212
|
+
private _isRecoveryUnavailable(error: unknown) {
|
|
213
|
+
const candidate = error as { code?: unknown; message?: unknown; status?: unknown } | undefined;
|
|
214
|
+
const status = candidate?.code ?? candidate?.status;
|
|
215
|
+
return status === 404 && candidate?.message === 'refund recovery is not available';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private async _loadRecovery() {
|
|
219
|
+
this.recoveryError = undefined;
|
|
220
|
+
this.recoveryUnavailable = false;
|
|
221
|
+
const result = await this._recovery().refetch();
|
|
222
|
+
if (result.error) {
|
|
223
|
+
if (this._isRecoveryUnavailable(result.error)) {
|
|
224
|
+
this.recoveryUnavailable = true;
|
|
225
|
+
this.recoveryLoaded = true;
|
|
226
|
+
} else {
|
|
227
|
+
this.recoveryError = result.error;
|
|
228
|
+
this.recoveryLoaded = false;
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
this.recoveryLoaded = true;
|
|
233
|
+
}
|
|
234
|
+
|
|
68
235
|
private async _toggleRecovery() {
|
|
69
236
|
this.recoveryOpen = !this.recoveryOpen;
|
|
70
237
|
if (this.recoveryOpen && !this.recoveryLoaded) {
|
|
71
|
-
await this.
|
|
72
|
-
this.recoveryLoaded = true;
|
|
238
|
+
await this._loadRecovery();
|
|
73
239
|
}
|
|
74
240
|
}
|
|
75
241
|
|
|
76
242
|
private async _reconcile() {
|
|
77
243
|
const reason = this.recoveryReason.trim();
|
|
78
|
-
if (!reason) return;
|
|
79
|
-
const orderId = this
|
|
244
|
+
if (!reason || this._recoveryActionPending) return;
|
|
245
|
+
const orderId = this._orderId;
|
|
80
246
|
const actionIdempotencyKey = this.reconcileIdempotencyKey ?? crypto.randomUUID();
|
|
81
247
|
this.reconcileIdempotencyKey = actionIdempotencyKey;
|
|
82
248
|
await this.$$modelOrder
|
|
83
249
|
.reconcileRefund(orderId)
|
|
84
250
|
.mutateAsync({ reason, actionIdempotencyKey, acknowledgeRetryRisk: false });
|
|
85
251
|
this.reconcileIdempotencyKey = undefined;
|
|
86
|
-
await this.
|
|
252
|
+
await this._loadRecovery();
|
|
87
253
|
}
|
|
88
254
|
|
|
89
255
|
private async _retry() {
|
|
90
256
|
const reason = this.recoveryReason.trim();
|
|
91
|
-
if (!reason || !this.retryRiskAcknowledged) return;
|
|
92
|
-
const orderId = this
|
|
257
|
+
if (!reason || !this.retryRiskAcknowledged || this._recoveryActionPending) return;
|
|
258
|
+
const orderId = this._orderId;
|
|
93
259
|
const actionIdempotencyKey = this.retryIdempotencyKey ?? crypto.randomUUID();
|
|
94
260
|
this.retryIdempotencyKey = actionIdempotencyKey;
|
|
95
261
|
await this.$$modelOrder
|
|
96
262
|
.retryRefund(orderId)
|
|
97
263
|
.mutateAsync({ reason, actionIdempotencyKey, acknowledgeRetryRisk: true });
|
|
98
264
|
this.retryIdempotencyKey = undefined;
|
|
99
|
-
await this.
|
|
265
|
+
await this._loadRecovery();
|
|
100
266
|
}
|
|
101
267
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
type="button"
|
|
138
|
-
onClick={() => this._reject()}
|
|
139
|
-
>
|
|
140
|
-
{this.scope.locale.RejectRefund()}
|
|
141
|
-
</button>
|
|
142
|
-
</>
|
|
143
|
-
) : (
|
|
144
|
-
<>
|
|
145
|
-
{showExecute && (
|
|
146
|
-
<button class={props.class} type="button" onClick={() => this._execute()}>
|
|
147
|
-
{this.scope.locale.ExecuteRefund()}
|
|
148
|
-
</button>
|
|
149
|
-
)}
|
|
268
|
+
private _resetExecutionDialogState() {
|
|
269
|
+
this.executionConfirmed = false;
|
|
270
|
+
this.mockCompletionError = undefined;
|
|
271
|
+
this.mockCompletionSucceeded = false;
|
|
272
|
+
this.recoveryOpen = false;
|
|
273
|
+
this.recoveryLoaded = false;
|
|
274
|
+
this.recoveryUnavailable = false;
|
|
275
|
+
this.recoveryError = undefined;
|
|
276
|
+
this.recoveryReason = '';
|
|
277
|
+
this.retryRiskAcknowledged = false;
|
|
278
|
+
this.reconcileIdempotencyKey = undefined;
|
|
279
|
+
this.retryIdempotencyKey = undefined;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private _openRefundExecutionDialog() {
|
|
283
|
+
this._resetExecutionDialogState();
|
|
284
|
+
this.$appModal.dialog(
|
|
285
|
+
{
|
|
286
|
+
title: this.scope.locale.RefundExecution(),
|
|
287
|
+
slotDefault: () => {
|
|
288
|
+
const recovery = this.recoveryOpen ? this._recovery() : undefined;
|
|
289
|
+
const recoveryData = recovery?.data as
|
|
290
|
+
| ApiSchemaCommerceTradeDtoRefundRecoveryView
|
|
291
|
+
| undefined;
|
|
292
|
+
return (
|
|
293
|
+
<div class="flex flex-col items-start gap-3">
|
|
294
|
+
<label class="label m-0 cursor-pointer justify-start gap-2 p-0">
|
|
295
|
+
<input
|
|
296
|
+
class="checkbox checkbox-sm"
|
|
297
|
+
name="executionConfirmed"
|
|
298
|
+
type="checkbox"
|
|
299
|
+
v-model={this.executionConfirmed}
|
|
300
|
+
/>
|
|
301
|
+
<span class="label-text">{this.scope.locale.Confirm()}</span>
|
|
302
|
+
</label>
|
|
150
303
|
<button
|
|
151
|
-
class="btn btn-outline
|
|
304
|
+
class="btn btn-outline btn-sm"
|
|
152
305
|
type="button"
|
|
153
306
|
onClick={() => this._toggleRecovery()}
|
|
154
307
|
>
|
|
155
308
|
{this.scope.locale.InspectRefundRecovery()}
|
|
156
309
|
</button>
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
310
|
+
{this.recoveryOpen && (
|
|
311
|
+
<section class="w-full rounded border border-base-300 p-3 text-sm">
|
|
312
|
+
{recovery?.isFetching && !recoveryData && (
|
|
313
|
+
<div class="flex items-center gap-2">
|
|
314
|
+
<span class="loading loading-spinner text-primary"></span>
|
|
315
|
+
<span>{this.scope.locale.LoadingRefundRecovery()}</span>
|
|
316
|
+
</div>
|
|
317
|
+
)}
|
|
318
|
+
{this.recoveryUnavailable && (
|
|
319
|
+
<div role="alert" class="alert alert-info">
|
|
320
|
+
<span>{this.scope.locale.RefundRecoveryUnavailable()}</span>
|
|
321
|
+
</div>
|
|
322
|
+
)}
|
|
323
|
+
{this.mockCompletionSucceeded && (
|
|
324
|
+
<div role="status" class="alert alert-success">
|
|
325
|
+
<span>{this.scope.locale.MockRefundCompleted()}</span>
|
|
326
|
+
</div>
|
|
327
|
+
)}
|
|
328
|
+
{this.recoveryError && !this.mockCompletionSucceeded && (
|
|
329
|
+
<div role="alert" class="alert alert-error">
|
|
330
|
+
<span>{this.scope.locale.RefundRecoveryLoadFailed()}</span>
|
|
331
|
+
<button
|
|
332
|
+
class="btn btn-outline btn-sm"
|
|
333
|
+
type="button"
|
|
334
|
+
onClick={() => this._loadRecovery()}
|
|
335
|
+
>
|
|
336
|
+
{this.scope.locale.RetryInspectRefundRecovery()}
|
|
337
|
+
</button>
|
|
338
|
+
</div>
|
|
339
|
+
)}
|
|
340
|
+
{recoveryData &&
|
|
341
|
+
!this.mockCompletionSucceeded &&
|
|
342
|
+
!this.recoveryUnavailable &&
|
|
343
|
+
!this.recoveryError && (
|
|
344
|
+
<>
|
|
345
|
+
<p>
|
|
346
|
+
{this.scope.locale.RefundRecoveryState({
|
|
347
|
+
state: recoveryData.providerOperationState,
|
|
348
|
+
attemptCount: recoveryData.attemptCount,
|
|
349
|
+
})}
|
|
350
|
+
</p>
|
|
351
|
+
{recoveryData.providerRefundId && (
|
|
352
|
+
<p>
|
|
353
|
+
{this.scope.locale.RefundRecoveryProviderRefundId({
|
|
354
|
+
providerRefundId: recoveryData.providerRefundId,
|
|
355
|
+
})}
|
|
356
|
+
</p>
|
|
357
|
+
)}
|
|
358
|
+
{recoveryData.errorSummary && <p>{recoveryData.errorSummary}</p>}
|
|
359
|
+
<p class="text-base-content/70">{recoveryData.recoveryMessage}</p>
|
|
360
|
+
{recoveryData.recoveryDisposition === 'await_webhook' && (
|
|
361
|
+
<div class="mt-2 w-full rounded border border-info/40 bg-info/10 p-3">
|
|
362
|
+
<p>{this.scope.locale.RefundAwaitingProviderConfirmation()}</p>
|
|
363
|
+
{recoveryData.providerName === 'pay-mock:mock' &&
|
|
364
|
+
this._mockCompletionAvailable && (
|
|
365
|
+
<>
|
|
366
|
+
<p class="mt-1 text-base-content/70">
|
|
367
|
+
{this.scope.locale.MockRefundSimulatorHelp()}
|
|
368
|
+
</p>
|
|
369
|
+
<button
|
|
370
|
+
class="btn btn-info btn-sm mt-2"
|
|
371
|
+
type="button"
|
|
372
|
+
disabled={this._mockCompletion?.isPending}
|
|
373
|
+
onClick={() => this._completeMockRefund()}
|
|
374
|
+
>
|
|
375
|
+
{this._mockCompletion?.isPending && (
|
|
376
|
+
<span class="loading loading-spinner loading-xs"></span>
|
|
377
|
+
)}
|
|
378
|
+
{this.scope.locale.CompleteMockRefund()}
|
|
379
|
+
</button>
|
|
380
|
+
{this.mockCompletionError && (
|
|
381
|
+
<p role="alert" class="mt-2 text-error">
|
|
382
|
+
{this.scope.locale.MockRefundCompletionFailed()}
|
|
383
|
+
</p>
|
|
384
|
+
)}
|
|
385
|
+
</>
|
|
386
|
+
)}
|
|
387
|
+
</div>
|
|
388
|
+
)}
|
|
389
|
+
{recoveryData.recoveryDisposition !== 'none' &&
|
|
390
|
+
recoveryData.recoveryDisposition !== 'await_webhook' && (
|
|
391
|
+
<input
|
|
392
|
+
class="input input-bordered input-sm mt-2 w-full"
|
|
393
|
+
name="recoveryReason"
|
|
394
|
+
placeholder={this.scope.locale.RefundRecoveryReason()}
|
|
395
|
+
v-model={this.recoveryReason}
|
|
396
|
+
/>
|
|
397
|
+
)}
|
|
398
|
+
{(recoveryData.recoveryDisposition === 'query_only' ||
|
|
399
|
+
recoveryData.recoveryDisposition === 'reconcile_only') && (
|
|
400
|
+
<button
|
|
401
|
+
class="btn btn-outline btn-sm mt-2"
|
|
402
|
+
type="button"
|
|
403
|
+
disabled={this._recoveryActionPending}
|
|
404
|
+
onClick={() => this._reconcile()}
|
|
405
|
+
>
|
|
406
|
+
{this.scope.locale.ReconcileProviderRefund()}
|
|
407
|
+
</button>
|
|
408
|
+
)}
|
|
409
|
+
{recoveryData.recoveryDisposition === 'retry_same_key' && (
|
|
410
|
+
<>
|
|
411
|
+
<label class="label cursor-pointer mt-2 justify-start gap-2">
|
|
412
|
+
<input
|
|
413
|
+
class="checkbox checkbox-sm"
|
|
414
|
+
name="retryRiskAcknowledged"
|
|
415
|
+
type="checkbox"
|
|
416
|
+
v-model={this.retryRiskAcknowledged}
|
|
417
|
+
/>
|
|
418
|
+
<span class="label-text text-xs">
|
|
419
|
+
{this.scope.locale.AcknowledgeRefundRetryRisk()}
|
|
420
|
+
</span>
|
|
421
|
+
</label>
|
|
422
|
+
<button
|
|
423
|
+
class="btn btn-warning btn-sm"
|
|
424
|
+
type="button"
|
|
425
|
+
disabled={this._recoveryActionPending}
|
|
426
|
+
onClick={() => this._retry()}
|
|
427
|
+
>
|
|
428
|
+
{this.scope.locale.RetryRefund()}
|
|
429
|
+
</button>
|
|
430
|
+
</>
|
|
431
|
+
)}
|
|
432
|
+
</>
|
|
433
|
+
)}
|
|
434
|
+
</section>
|
|
435
|
+
)}
|
|
436
|
+
</div>
|
|
437
|
+
);
|
|
438
|
+
},
|
|
439
|
+
slotActions: dialog => {
|
|
440
|
+
const disabled = !this.executionConfirmed || this._executionPending;
|
|
441
|
+
return (
|
|
442
|
+
<>
|
|
443
|
+
{this._executionPending && <span class="loading loading-spinner text-primary"></span>}
|
|
444
|
+
<button class="btn btn-ghost" type="button" onClick={dialog.close}>
|
|
445
|
+
{this.scope.locale.Cancel()}
|
|
446
|
+
</button>
|
|
187
447
|
<button
|
|
188
|
-
class="btn btn-
|
|
448
|
+
class="btn btn-warning"
|
|
189
449
|
type="button"
|
|
190
|
-
|
|
450
|
+
disabled={disabled}
|
|
451
|
+
onClick={() => this._execute(dialog.close)}
|
|
191
452
|
>
|
|
192
|
-
{this.scope.locale.
|
|
453
|
+
{this.scope.locale.ExecuteRefund()}
|
|
193
454
|
</button>
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
455
|
+
</>
|
|
456
|
+
);
|
|
457
|
+
},
|
|
458
|
+
onClose: () => this._resetExecutionDialogState(),
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
closeOnBackdrop: false,
|
|
462
|
+
closeOnEscape: false,
|
|
463
|
+
maxWidth: 560,
|
|
464
|
+
showCloseButton: true,
|
|
465
|
+
},
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
protected render() {
|
|
470
|
+
const props = this.$props as ControllerTableCellActionRefundProps;
|
|
471
|
+
const order = this.$$renderContext.cellContext.row
|
|
472
|
+
.original as ApiSchemaCommerceTradeDtoOrderSelectResItem;
|
|
473
|
+
if (order.state === 'refund_requested') {
|
|
474
|
+
return (
|
|
475
|
+
<span
|
|
476
|
+
class={props.class}
|
|
477
|
+
role="button"
|
|
478
|
+
tabindex={0}
|
|
479
|
+
onClick={() => this._openRefundDecisionDialog()}
|
|
480
|
+
onKeydown={event => {
|
|
481
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
482
|
+
event.preventDefault();
|
|
483
|
+
this._openRefundDecisionDialog();
|
|
484
|
+
}
|
|
485
|
+
}}
|
|
486
|
+
>
|
|
487
|
+
{this.scope.locale.ReviewRefund()}
|
|
488
|
+
</span>
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
return (
|
|
492
|
+
<button class={props.class} type="button" onClick={() => this._openRefundExecutionDialog()}>
|
|
493
|
+
{this.scope.locale.ExecuteRefund()}
|
|
494
|
+
</button>
|
|
216
495
|
);
|
|
217
496
|
}
|
|
218
497
|
}
|