@pisell/private-materials 6.12.200 → 6.12.201
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/build/lowcode/assets-daily.json +11 -11
- package/build/lowcode/assets-dev.json +2 -2
- package/build/lowcode/assets-prod.json +11 -11
- package/build/lowcode/meta.js +1 -1
- package/build/lowcode/render/default/view.css +1 -1
- package/build/lowcode/render/default/view.js +10 -10
- package/build/lowcode/view.css +1 -1
- package/build/lowcode/view.js +10 -10
- package/es/components/HDLKiosk/index.js +9 -6
- package/es/components/HDLKiosk/locales.js +15 -0
- package/es/components/HDLKiosk/runtime/entryPreparation.js +48 -0
- package/es/components/HDLKiosk/runtime/session/createSession.js +46 -2
- package/es/components/HDLKiosk/slices/customization/Screen/index.js +25 -2
- package/es/components/HDLKiosk/ui/controllers/useHDLKioskSessionController.js +50 -9
- package/es/components/HDLKiosk/ui/entry/Screen/index.js +5 -2
- package/es/components/HDLKiosk/ui/entry/Screen/index.less +10 -0
- package/es/components/bigSale/templates/HDLKiosk/index.js +3 -1
- package/es/components/bigSale/templates/HDLKiosk/payment/HDLKioskPaymentRuntime.js +163 -25
- package/lib/components/HDLKiosk/index.js +9 -6
- package/lib/components/HDLKiosk/locales.js +15 -0
- package/lib/components/HDLKiosk/runtime/entryPreparation.js +48 -0
- package/lib/components/HDLKiosk/runtime/session/createSession.js +46 -2
- package/lib/components/HDLKiosk/slices/customization/Screen/index.js +25 -2
- package/lib/components/HDLKiosk/ui/controllers/useHDLKioskSessionController.js +49 -8
- package/lib/components/HDLKiosk/ui/entry/Screen/index.js +5 -2
- package/lib/components/HDLKiosk/ui/entry/Screen/index.less +10 -0
- package/lib/components/bigSale/templates/HDLKiosk/index.js +3 -1
- package/lib/components/bigSale/templates/HDLKiosk/payment/HDLKioskPaymentRuntime.js +162 -24
- package/package.json +3 -3
|
@@ -57,10 +57,11 @@ function HDLKioskContent({ host, config, isActive = true, entryDataPreparing = f
|
|
|
57
57
|
setActiveLocale(nextLocale);
|
|
58
58
|
onLocaleChange === null || onLocaleChange === void 0 || onLocaleChange(nextLocale);
|
|
59
59
|
}, [onLocaleChange]);
|
|
60
|
-
const { session, starting, startErrorReference, exiting, exitError, pendingExit, start, back, requestExit, resolveExit, completeOrder, recover } = useHDLKioskSessionController({
|
|
60
|
+
const { session, starting, preparingEntry, entryPreparationErrorReference, startErrorReference, exiting, exitError, pendingExit, start, back, requestExit, resolveExit, completeOrder, recover, retryEntryPreparation } = useHDLKioskSessionController({
|
|
61
61
|
host,
|
|
62
62
|
config,
|
|
63
|
-
isActive
|
|
63
|
+
isActive,
|
|
64
|
+
entryPreparationBlocked: entryDataPreparing || sales.hostStatus === "loading"
|
|
64
65
|
});
|
|
65
66
|
const observeExit = useCallback((result) => {
|
|
66
67
|
if (result.status === "exited") onPaymentResultReset === null || onPaymentResultReset === void 0 || onPaymentResultReset();
|
|
@@ -167,10 +168,12 @@ function HDLKioskContent({ host, config, isActive = true, entryDataPreparing = f
|
|
|
167
168
|
start({ entry });
|
|
168
169
|
}
|
|
169
170
|
},
|
|
170
|
-
busy: starting,
|
|
171
|
-
errorReference: startErrorReference,
|
|
172
|
-
errorMessage: locales.getText("pisell2.hdl-kiosk.session-start-failed")
|
|
173
|
-
|
|
171
|
+
busy: starting || preparingEntry || Boolean(entryPreparationErrorReference),
|
|
172
|
+
errorReference: entryPreparationErrorReference || startErrorReference,
|
|
173
|
+
errorMessage: locales.getText(entryPreparationErrorReference ? "pisell2.hdl-kiosk.reset-failed-description" : "pisell2.hdl-kiosk.session-start-failed"),
|
|
174
|
+
retryLabel: entryPreparationErrorReference ? locales.getText("pisell2.hdl-kiosk.retry-entry-preparation") : void 0,
|
|
175
|
+
onRetry: entryPreparationErrorReference ? retryEntryPreparation : void 0
|
|
176
|
+
}), starting || preparingEntry ? /* @__PURE__ */ React.createElement("div", {
|
|
174
177
|
className: "hdl-operation-overlay",
|
|
175
178
|
role: "status"
|
|
176
179
|
}, /* @__PURE__ */ React.createElement("span", { className: "hdl-operation-overlay__spinner" }), /* @__PURE__ */ React.createElement("strong", null, locales.getText("pisell2.hdl-kiosk.working"))) : null));
|
|
@@ -45,6 +45,7 @@ const localeTexts = {
|
|
|
45
45
|
"pisell2.hdl-kiosk.payment-conflict-title": "Payment needs attention",
|
|
46
46
|
"pisell2.hdl-kiosk.payment-conflict-description": "Please do not pay again. Ask a staff member to check your payment.",
|
|
47
47
|
"pisell2.hdl-kiosk.resetting-order": "Preparing next order…",
|
|
48
|
+
"pisell2.hdl-kiosk.retry-entry-preparation": "Retry preparation",
|
|
48
49
|
"pisell2.hdl-kiosk.order-needs-attention": "The previous action did not complete. Review the order before continuing.",
|
|
49
50
|
"pisell2.hdl-kiosk.fulfillment-unavailable": "This device cannot save the dining option yet. Please ask a staff member for help.",
|
|
50
51
|
"pisell2.hdl-kiosk.keep-ordering": "Keep ordering",
|
|
@@ -86,6 +87,8 @@ const localeTexts = {
|
|
|
86
87
|
"pisell2.hdl-kiosk.customization-unit-price": "Unit price",
|
|
87
88
|
"pisell2.hdl-kiosk.customization-per100g": "/100g",
|
|
88
89
|
"pisell2.hdl-kiosk.customization-next": "Next",
|
|
90
|
+
"pisell2.hdl-kiosk.customization-required-options": "Please select an option for every required group.",
|
|
91
|
+
"pisell2.hdl-kiosk.customization-required-group": (groupName) => `Please select an option in “${groupName}”.`,
|
|
89
92
|
"pisell2.hdl-kiosk.primary-product-prompt-eyebrow": "Before Checkout",
|
|
90
93
|
"pisell2.hdl-kiosk.primary-product-prompt-title": "Would you like to add a Malatang bowl?",
|
|
91
94
|
"pisell2.hdl-kiosk.primary-product-prompt-accept": "Yes",
|
|
@@ -202,6 +205,7 @@ const localeTexts = {
|
|
|
202
205
|
"pisell2.hdl-kiosk.payment-conflict-title": "请确认支付状态",
|
|
203
206
|
"pisell2.hdl-kiosk.payment-conflict-description": "请勿重复付款,并联系工作人员确认。",
|
|
204
207
|
"pisell2.hdl-kiosk.resetting-order": "正在准备下一单…",
|
|
208
|
+
"pisell2.hdl-kiosk.retry-entry-preparation": "重新准备",
|
|
205
209
|
"pisell2.hdl-kiosk.order-needs-attention": "上一步未完成,请核对订单后再继续。",
|
|
206
210
|
"pisell2.hdl-kiosk.fulfillment-unavailable": "当前设备尚未接入用餐方式写入能力,请联系工作人员处理。",
|
|
207
211
|
"pisell2.hdl-kiosk.keep-ordering": "继续点餐",
|
|
@@ -243,6 +247,8 @@ const localeTexts = {
|
|
|
243
247
|
"pisell2.hdl-kiosk.customization-unit-price": "单价",
|
|
244
248
|
"pisell2.hdl-kiosk.customization-per100g": "/100克",
|
|
245
249
|
"pisell2.hdl-kiosk.customization-next": "下一步",
|
|
250
|
+
"pisell2.hdl-kiosk.customization-required-options": "请完成所有必选规格的选择。",
|
|
251
|
+
"pisell2.hdl-kiosk.customization-required-group": (groupName) => `请选择「${groupName}」中的必选项。`,
|
|
246
252
|
"pisell2.hdl-kiosk.primary-product-prompt-eyebrow": "结账前确认",
|
|
247
253
|
"pisell2.hdl-kiosk.primary-product-prompt-title": "需要添加一份麻辣烫吗?",
|
|
248
254
|
"pisell2.hdl-kiosk.primary-product-prompt-accept": "需要",
|
|
@@ -359,6 +365,7 @@ const localeTexts = {
|
|
|
359
365
|
"pisell2.hdl-kiosk.payment-conflict-title": "請確認付款狀態",
|
|
360
366
|
"pisell2.hdl-kiosk.payment-conflict-description": "請勿重複付款,並聯絡工作人員確認。",
|
|
361
367
|
"pisell2.hdl-kiosk.resetting-order": "正在準備下一單…",
|
|
368
|
+
"pisell2.hdl-kiosk.retry-entry-preparation": "重新準備",
|
|
362
369
|
"pisell2.hdl-kiosk.order-needs-attention": "上一步未完成,請核對訂單後再繼續。",
|
|
363
370
|
"pisell2.hdl-kiosk.fulfillment-unavailable": "當前設備尚未接入用餐方式寫入能力,請聯絡工作人員處理。",
|
|
364
371
|
"pisell2.hdl-kiosk.keep-ordering": "繼續點餐",
|
|
@@ -400,6 +407,8 @@ const localeTexts = {
|
|
|
400
407
|
"pisell2.hdl-kiosk.customization-unit-price": "單價",
|
|
401
408
|
"pisell2.hdl-kiosk.customization-per100g": "/100克",
|
|
402
409
|
"pisell2.hdl-kiosk.customization-next": "下一步",
|
|
410
|
+
"pisell2.hdl-kiosk.customization-required-options": "請完成所有必選規格的選擇。",
|
|
411
|
+
"pisell2.hdl-kiosk.customization-required-group": (groupName) => `請選擇「${groupName}」中的必選項。`,
|
|
403
412
|
"pisell2.hdl-kiosk.primary-product-prompt-eyebrow": "結帳前確認",
|
|
404
413
|
"pisell2.hdl-kiosk.primary-product-prompt-title": "需要加入一份麻辣燙嗎?",
|
|
405
414
|
"pisell2.hdl-kiosk.primary-product-prompt-accept": "需要",
|
|
@@ -516,6 +525,7 @@ const localeTexts = {
|
|
|
516
525
|
"pisell2.hdl-kiosk.payment-conflict-title": "お支払い状況をご確認ください",
|
|
517
526
|
"pisell2.hdl-kiosk.payment-conflict-description": "再度支払わず、スタッフにお声がけください。",
|
|
518
527
|
"pisell2.hdl-kiosk.resetting-order": "次の注文を準備中…",
|
|
528
|
+
"pisell2.hdl-kiosk.retry-entry-preparation": "もう一度準備する",
|
|
519
529
|
"pisell2.hdl-kiosk.order-needs-attention": "前の操作が完了していません。注文を確認してから続けてください。",
|
|
520
530
|
"pisell2.hdl-kiosk.fulfillment-unavailable": "この端末では現在、飲食方法を保存できません。スタッフにお声がけください。",
|
|
521
531
|
"pisell2.hdl-kiosk.keep-ordering": "注文を続ける",
|
|
@@ -557,6 +567,8 @@ const localeTexts = {
|
|
|
557
567
|
"pisell2.hdl-kiosk.customization-unit-price": "単価",
|
|
558
568
|
"pisell2.hdl-kiosk.customization-per100g": "/100g",
|
|
559
569
|
"pisell2.hdl-kiosk.customization-next": "次へ",
|
|
570
|
+
"pisell2.hdl-kiosk.customization-required-options": "必須項目をすべて選択してください。",
|
|
571
|
+
"pisell2.hdl-kiosk.customization-required-group": (groupName) => `「${groupName}」の必須項目を選択してください。`,
|
|
560
572
|
"pisell2.hdl-kiosk.primary-product-prompt-eyebrow": "お会計前の確認",
|
|
561
573
|
"pisell2.hdl-kiosk.primary-product-prompt-title": "麻辣湯を1杯追加しますか?",
|
|
562
574
|
"pisell2.hdl-kiosk.primary-product-prompt-accept": "はい",
|
|
@@ -673,6 +685,7 @@ const localeTexts = {
|
|
|
673
685
|
"pisell2.hdl-kiosk.payment-conflict-title": "Confirme o estado do pagamento",
|
|
674
686
|
"pisell2.hdl-kiosk.payment-conflict-description": "Não pague novamente. Peça ajuda a um funcionário.",
|
|
675
687
|
"pisell2.hdl-kiosk.resetting-order": "A preparar o próximo pedido…",
|
|
688
|
+
"pisell2.hdl-kiosk.retry-entry-preparation": "Tentar preparar novamente",
|
|
676
689
|
"pisell2.hdl-kiosk.order-needs-attention": "A ação anterior não foi concluída. Verifique o pedido antes de continuar.",
|
|
677
690
|
"pisell2.hdl-kiosk.fulfillment-unavailable": "Este dispositivo ainda não consegue guardar a opção de consumo. Peça ajuda a um funcionário.",
|
|
678
691
|
"pisell2.hdl-kiosk.keep-ordering": "Continuar a pedir",
|
|
@@ -714,6 +727,8 @@ const localeTexts = {
|
|
|
714
727
|
"pisell2.hdl-kiosk.customization-unit-price": "Preço unitário",
|
|
715
728
|
"pisell2.hdl-kiosk.customization-per100g": "/100 g",
|
|
716
729
|
"pisell2.hdl-kiosk.customization-next": "Seguinte",
|
|
730
|
+
"pisell2.hdl-kiosk.customization-required-options": "Selecione uma opção em cada grupo obrigatório.",
|
|
731
|
+
"pisell2.hdl-kiosk.customization-required-group": (groupName) => `Selecione uma opção obrigatória em “${groupName}”.`,
|
|
717
732
|
"pisell2.hdl-kiosk.primary-product-prompt-eyebrow": "Antes de finalizar",
|
|
718
733
|
"pisell2.hdl-kiosk.primary-product-prompt-title": "Deseja adicionar uma tigela de Malatang?",
|
|
719
734
|
"pisell2.hdl-kiosk.primary-product-prompt-accept": "Sim",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { _asyncToGenerator } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js";
|
|
2
|
+
import { createOperationError, operationUnknown } from "./operations/errors.js";
|
|
3
|
+
//#region src/components/HDLKiosk/runtime/entryPreparation.ts
|
|
4
|
+
function forwardFailure(result) {
|
|
5
|
+
return result;
|
|
6
|
+
}
|
|
7
|
+
/** Entry 首次展示或重新激活时,必须先证明 Host 已不存在上一笔购物车。 */
|
|
8
|
+
function prepareHDLKioskEntry(_x, _x2) {
|
|
9
|
+
return _prepareHDLKioskEntry.apply(this, arguments);
|
|
10
|
+
}
|
|
11
|
+
function _prepareHDLKioskEntry() {
|
|
12
|
+
_prepareHDLKioskEntry = _asyncToGenerator(function* (host, signal) {
|
|
13
|
+
let result;
|
|
14
|
+
try {
|
|
15
|
+
const payment = host.sales.readTransactionState().payment;
|
|
16
|
+
if (payment.status === "pending" || payment.status === "unknown") result = operationUnknown("entryPreparationBlockedByPaymentState");
|
|
17
|
+
else if (signal.aborted) result = {
|
|
18
|
+
status: "cancelled",
|
|
19
|
+
reason: "entryPreparationAborted"
|
|
20
|
+
};
|
|
21
|
+
else {
|
|
22
|
+
const reset = yield host.sales.clearAndReset(signal);
|
|
23
|
+
if (reset.status !== "succeeded") result = forwardFailure(reset);
|
|
24
|
+
else if (!reset.data.cartEmpty) result = operationUnknown("entryPreparationHostResultNotEmpty");
|
|
25
|
+
else if (signal.aborted) result = {
|
|
26
|
+
status: "cancelled",
|
|
27
|
+
reason: "entryPreparationAborted"
|
|
28
|
+
};
|
|
29
|
+
else {
|
|
30
|
+
const consistency = yield host.sales.waitForCartConsistency(signal);
|
|
31
|
+
result = consistency.status === "succeeded" ? {
|
|
32
|
+
status: "succeeded",
|
|
33
|
+
data: void 0
|
|
34
|
+
} : forwardFailure(consistency);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (cause) {
|
|
38
|
+
result = {
|
|
39
|
+
status: "failed",
|
|
40
|
+
error: createOperationError("entryPreparationUnhandled", "staff", cause)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
});
|
|
45
|
+
return _prepareHDLKioskEntry.apply(this, arguments);
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { prepareHDLKioskEntry };
|
|
@@ -61,9 +61,30 @@ function createHDLKioskWorkflowSession(entry, host, options = {}) {
|
|
|
61
61
|
let staffPaymentPending = false;
|
|
62
62
|
let staffPaymentConfirmed = false;
|
|
63
63
|
let staffPaymentRejected = false;
|
|
64
|
+
let staffPaymentObserved = false;
|
|
65
|
+
let staffPaymentAdvanceDispatched = false;
|
|
66
|
+
let lastStaffPaymentAdvanceDiagnostic = "";
|
|
64
67
|
let completedOrderFinalized = false;
|
|
65
68
|
let completedOrderFinalizePromise;
|
|
66
69
|
const staffPaymentReceipt = Object.freeze({ paymentConfirmed: true });
|
|
70
|
+
const emitStaffPaymentWorkflow = (phase, authorizationStatus, reason) => {
|
|
71
|
+
const snapshot = workflowActor.getSnapshot();
|
|
72
|
+
try {
|
|
73
|
+
host.telemetry.emit({
|
|
74
|
+
name: "hdlKiosk.staffPayment.workflow",
|
|
75
|
+
traceId: `${snapshot.workflowId}:${snapshot.sessionEpoch}:staff-payment`,
|
|
76
|
+
occurredAt: Date.now(),
|
|
77
|
+
attributes: _objectSpread2({
|
|
78
|
+
phase,
|
|
79
|
+
authorizationStatus,
|
|
80
|
+
workflowId: snapshot.workflowId,
|
|
81
|
+
sessionEpoch: snapshot.sessionEpoch,
|
|
82
|
+
nodeId: snapshot.nodeId,
|
|
83
|
+
workflowStatus: snapshot.status
|
|
84
|
+
}, reason ? { reason } : {})
|
|
85
|
+
});
|
|
86
|
+
} catch (_unused) {}
|
|
87
|
+
};
|
|
67
88
|
const dispatchConfirmedPayment = (receipt, intentId) => {
|
|
68
89
|
var _definition$nodes$sna;
|
|
69
90
|
const snapshot = workflowActor.getSnapshot();
|
|
@@ -79,8 +100,26 @@ function createHDLKioskWorkflowSession(entry, host, options = {}) {
|
|
|
79
100
|
};
|
|
80
101
|
const advanceConfirmedStaffPayment = () => {
|
|
81
102
|
var _definition$nodes$sna2;
|
|
103
|
+
if (!staffPaymentObserved || staffPaymentPending || staffPaymentAdvanceDispatched) return;
|
|
82
104
|
const snapshot = workflowActor.getSnapshot();
|
|
83
|
-
if (
|
|
105
|
+
if (staffPaymentRejected || !staffPaymentConfirmed) return;
|
|
106
|
+
const skipReason = snapshot.status !== "idle" ? "workflow-not-idle" : ((_definition$nodes$sna2 = definition.nodes[snapshot.nodeId]) === null || _definition$nodes$sna2 === void 0 ? void 0 : _definition$nodes$sna2.activity) !== "paymentMethod" ? "not-payment-method" : void 0;
|
|
107
|
+
if (skipReason) {
|
|
108
|
+
const diagnostic = [
|
|
109
|
+
snapshot.sessionEpoch,
|
|
110
|
+
snapshot.nodeId,
|
|
111
|
+
snapshot.status,
|
|
112
|
+
skipReason
|
|
113
|
+
].join(":");
|
|
114
|
+
if (diagnostic !== lastStaffPaymentAdvanceDiagnostic) {
|
|
115
|
+
lastStaffPaymentAdvanceDiagnostic = diagnostic;
|
|
116
|
+
emitStaffPaymentWorkflow("advanceSkipped", "succeeded", skipReason);
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
staffPaymentAdvanceDispatched = true;
|
|
121
|
+
lastStaffPaymentAdvanceDiagnostic = `${snapshot.sessionEpoch}:advanced`;
|
|
122
|
+
emitStaffPaymentWorkflow("advanced", "succeeded");
|
|
84
123
|
dispatchConfirmedPayment(staffPaymentReceipt, `staff-payment:${snapshot.sessionEpoch}`);
|
|
85
124
|
};
|
|
86
125
|
const finalizeCompletedOrder = (intentId) => {
|
|
@@ -115,15 +154,20 @@ function createHDLKioskWorkflowSession(entry, host, options = {}) {
|
|
|
115
154
|
try {
|
|
116
155
|
unsubscribePaymentWorkflow = workflowActor.subscribe(handleWorkflowUpdate);
|
|
117
156
|
unsubscribeStaffPayment = host.sales.subscribeStaffPaymentAuthorizationResult((result) => {
|
|
157
|
+
staffPaymentObserved = true;
|
|
118
158
|
if (result.status === "pending") {
|
|
119
159
|
staffPaymentPending = true;
|
|
120
160
|
staffPaymentConfirmed = false;
|
|
121
161
|
staffPaymentRejected = false;
|
|
162
|
+
staffPaymentAdvanceDispatched = false;
|
|
163
|
+
lastStaffPaymentAdvanceDiagnostic = "";
|
|
164
|
+
emitStaffPaymentWorkflow("pending", result.status);
|
|
122
165
|
return;
|
|
123
166
|
}
|
|
124
167
|
staffPaymentPending = false;
|
|
125
168
|
staffPaymentConfirmed = result.status === "succeeded";
|
|
126
169
|
staffPaymentRejected = !staffPaymentConfirmed;
|
|
170
|
+
emitStaffPaymentWorkflow(staffPaymentConfirmed ? "confirmed" : "rejected", result.status);
|
|
127
171
|
if (staffPaymentConfirmed) coordinator.confirmPaymentSucceeded();
|
|
128
172
|
advanceConfirmedStaffPayment();
|
|
129
173
|
});
|
|
@@ -153,7 +197,7 @@ function createHDLKioskWorkflowSession(entry, host, options = {}) {
|
|
|
153
197
|
sessionEpoch: snapshot.sessionEpoch
|
|
154
198
|
}
|
|
155
199
|
});
|
|
156
|
-
} catch (
|
|
200
|
+
} catch (_unused2) {}
|
|
157
201
|
const disposeSession = () => {
|
|
158
202
|
if (disposed) return;
|
|
159
203
|
disposed = true;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { _objectSpread2 } from "../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
|
|
2
2
|
import { _asyncToGenerator } from "../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js";
|
|
3
|
+
import { resolveLocalizedText } from "../../../../../plus/salesSdk/utils/resolveLocalizedText.js";
|
|
3
4
|
import { useHDLKioskBottomBar, useHDLKioskBottomBarController } from "../../../components/shell/HDLKioskBottomBarHost/index.js";
|
|
4
5
|
import { HDLKioskOrderSummary } from "../../../components/shell/HDLKioskOrderSummary/index.js";
|
|
5
6
|
import { createHDLKioskSkuValue } from "../../../components/common/HDLKioskSkuOptionsSelection/model.js";
|
|
@@ -9,6 +10,7 @@ import { useHDLScaleRuntime, useHDLScaleSnapshot } from "../../../ui/scale.js";
|
|
|
9
10
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
10
11
|
import { STANDARD_WEIGHT_UNIT, formatAmount, getWeightUnitSymbol, locales } from "@pisell/utils";
|
|
11
12
|
import Decimal from "decimal.js";
|
|
13
|
+
import { useToast } from "@pisell/materials";
|
|
12
14
|
//#region src/components/HDLKiosk/slices/customization/Screen/index.tsx
|
|
13
15
|
/**
|
|
14
16
|
* 文件角色:呈现主称重商品的本地重量与标准 SkuValue 草稿。
|
|
@@ -47,6 +49,22 @@ function formatMoney(value, currencySymbol) {
|
|
|
47
49
|
const amount = value.toDecimalPlaces(2).toNumber();
|
|
48
50
|
return String(formatAmount(amount, 2, currencySymbol || ""));
|
|
49
51
|
}
|
|
52
|
+
function resolveInvalidSkuGroupName(error, product) {
|
|
53
|
+
var _group$name;
|
|
54
|
+
if (!error || typeof error !== "object" || Array.isArray(error) || !product) return;
|
|
55
|
+
const firstInvalidGroupId = Object.keys(error)[0];
|
|
56
|
+
if (!firstInvalidGroupId) return void 0;
|
|
57
|
+
const source = product;
|
|
58
|
+
const group = [
|
|
59
|
+
"variant_group",
|
|
60
|
+
"option_group",
|
|
61
|
+
"bundle_group"
|
|
62
|
+
].flatMap((key) => Array.isArray(source[key]) ? source[key] : []).find((candidate) => {
|
|
63
|
+
var _candidate$id;
|
|
64
|
+
return String((_candidate$id = candidate === null || candidate === void 0 ? void 0 : candidate.id) !== null && _candidate$id !== void 0 ? _candidate$id : "") === firstInvalidGroupId;
|
|
65
|
+
});
|
|
66
|
+
return resolveLocalizedText((_group$name = group === null || group === void 0 ? void 0 : group.name) !== null && _group$name !== void 0 ? _group$name : group === null || group === void 0 ? void 0 : group.title) || void 0;
|
|
67
|
+
}
|
|
50
68
|
function resolveScaleMeasurement(scale, productFacts, minimumWeightKg) {
|
|
51
69
|
var _scale$grossWeightKg, _productFacts$fixedTa, _productFacts$fixedTa2;
|
|
52
70
|
const gross = new Decimal((_scale$grossWeightKg = scale.grossWeightKg) !== null && _scale$grossWeightKg !== void 0 ? _scale$grossWeightKg : NaN);
|
|
@@ -148,6 +166,7 @@ function ProductFallback() {
|
|
|
148
166
|
}
|
|
149
167
|
function CustomizationScreen({ product, loading, errorReference, errorMessage, busy, syncing, canGoBack, minimumWeightKg, orderBar, send }) {
|
|
150
168
|
var _productFacts$product, _orderBar$primaryLine, _orderBar$primaryLine2, _orderBar$primaryLine3, _orderBar$primaryLine4, _orderBar$primaryLine7, _productFacts$unitPri, _orderBar$primaryLine9, _orderBar$primaryLine11;
|
|
169
|
+
const Toast = useToast();
|
|
151
170
|
const scaleRuntime = useHDLScaleRuntime();
|
|
152
171
|
const scale = useHDLScaleSnapshot();
|
|
153
172
|
const skuOptionsRef = useRef(null);
|
|
@@ -187,7 +206,9 @@ function CustomizationScreen({ product, loading, errorReference, errorMessage, b
|
|
|
187
206
|
try {
|
|
188
207
|
var _skuOptionsRef$curren;
|
|
189
208
|
yield (_skuOptionsRef$curren = skuOptionsRef.current) === null || _skuOptionsRef$curren === void 0 ? void 0 : _skuOptionsRef$curren.validate();
|
|
190
|
-
} catch (
|
|
209
|
+
} catch (error) {
|
|
210
|
+
const invalidGroupName = resolveInvalidSkuGroupName(error, product);
|
|
211
|
+
Toast.fail(invalidGroupName ? locales.getText("pisell2.hdl-kiosk.customization-required-group")(invalidGroupName) : locales.getText("pisell2.hdl-kiosk.customization-required-options"));
|
|
191
212
|
return;
|
|
192
213
|
}
|
|
193
214
|
const submittedSkuValue = (_skuOptionsRef$curren2 = (_skuOptionsRef$curren3 = skuOptionsRef.current) === null || _skuOptionsRef$curren3 === void 0 ? void 0 : _skuOptionsRef$curren3.getTransformValues()) !== null && _skuOptionsRef$curren2 !== void 0 ? _skuOptionsRef$curren2 : skuValue;
|
|
@@ -213,11 +234,13 @@ function CustomizationScreen({ product, loading, errorReference, errorMessage, b
|
|
|
213
234
|
busy,
|
|
214
235
|
minimumWeightKg,
|
|
215
236
|
(_orderBar$primaryLine9 = orderBar.primaryLine) === null || _orderBar$primaryLine9 === void 0 ? void 0 : _orderBar$primaryLine9.lineId,
|
|
237
|
+
product,
|
|
216
238
|
productFacts,
|
|
217
239
|
scaleRuntime,
|
|
218
240
|
skuValue,
|
|
219
241
|
send,
|
|
220
|
-
syncing
|
|
242
|
+
syncing,
|
|
243
|
+
Toast
|
|
221
244
|
]);
|
|
222
245
|
const omitPrimaryProduct = useCallback(() => {
|
|
223
246
|
var _orderBar$primaryLine10;
|
|
@@ -1,43 +1,83 @@
|
|
|
1
1
|
import { _asyncToGenerator } from "../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js";
|
|
2
2
|
import "../../runtime/admissionPolicy.js";
|
|
3
3
|
import { hdlKioskApplication } from "../../runtime/application.js";
|
|
4
|
-
import {
|
|
4
|
+
import { prepareHDLKioskEntry } from "../../runtime/entryPreparation.js";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
5
6
|
//#region src/components/HDLKiosk/ui/controllers/useHDLKioskSessionController.ts
|
|
6
7
|
/**
|
|
7
8
|
* 文件角色:把 React Entry 状态适配到无头 Application Runtime。
|
|
8
|
-
* 运行机制:Controller
|
|
9
|
-
*
|
|
9
|
+
* 运行机制:Controller 先建立 Entry Host 清理屏障,再激活 Trigger;Session 创建、ensure、
|
|
10
|
+
* 单会话准入与销毁均由 Runtime 持有。
|
|
10
11
|
* 边界:不直接创建 Actor,也不消费具体 Screen 的局部交互。
|
|
11
12
|
*/
|
|
12
|
-
function useHDLKioskSessionController({ host, config, isActive }) {
|
|
13
|
+
function useHDLKioskSessionController({ host, config, isActive, entryPreparationBlocked = false }) {
|
|
13
14
|
const applicationRuntime = useMemo(() => hdlKioskApplication.bind({
|
|
14
15
|
host,
|
|
15
16
|
config
|
|
16
17
|
}), [config, host]);
|
|
17
18
|
const snapshot = useSyncExternalStore(applicationRuntime.subscribe, applicationRuntime.getSnapshot, applicationRuntime.getSnapshot);
|
|
19
|
+
const [entryPreparationAttempt, setEntryPreparationAttempt] = useState(0);
|
|
20
|
+
const [entryPreparation, setEntryPreparation] = useState(() => ({ status: isActive ? "preparing" : "inactive" }));
|
|
18
21
|
useEffect(() => {
|
|
19
22
|
return () => applicationRuntime.dispose();
|
|
20
23
|
}, [applicationRuntime]);
|
|
21
24
|
useEffect(() => {
|
|
22
25
|
if (!isActive) {
|
|
23
26
|
applicationRuntime.deactivate();
|
|
27
|
+
setEntryPreparation({ status: "inactive" });
|
|
24
28
|
return;
|
|
25
29
|
}
|
|
26
|
-
applicationRuntime.
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
if (applicationRuntime.getSnapshot().session) {
|
|
31
|
+
applicationRuntime.activate();
|
|
32
|
+
setEntryPreparation({ status: "ready" });
|
|
33
|
+
return () => applicationRuntime.deactivate();
|
|
34
|
+
}
|
|
35
|
+
if (entryPreparationBlocked) {
|
|
36
|
+
applicationRuntime.deactivate();
|
|
37
|
+
setEntryPreparation({ status: "preparing" });
|
|
38
|
+
return () => applicationRuntime.deactivate();
|
|
39
|
+
}
|
|
40
|
+
applicationRuntime.deactivate();
|
|
41
|
+
setEntryPreparation({ status: "preparing" });
|
|
42
|
+
const abortController = new AbortController();
|
|
43
|
+
prepareHDLKioskEntry(host, abortController.signal).then((result) => {
|
|
44
|
+
if (abortController.signal.aborted) return;
|
|
45
|
+
if (result.status === "succeeded") {
|
|
46
|
+
setEntryPreparation({ status: "ready" });
|
|
47
|
+
applicationRuntime.activate();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
setEntryPreparation({
|
|
51
|
+
status: "failed",
|
|
52
|
+
errorReference: result.status === "failed" || result.status === "partialFailure" || result.status === "unknown" ? result.error.traceId : result.reason || "entryPreparationCancelled"
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
return () => {
|
|
56
|
+
abortController.abort();
|
|
57
|
+
applicationRuntime.deactivate();
|
|
58
|
+
};
|
|
59
|
+
}, [
|
|
60
|
+
applicationRuntime,
|
|
61
|
+
entryPreparationBlocked,
|
|
62
|
+
entryPreparationAttempt,
|
|
63
|
+
host,
|
|
64
|
+
isActive
|
|
65
|
+
]);
|
|
29
66
|
const start = useCallback(function() {
|
|
30
67
|
var _ref = _asyncToGenerator(function* ({ entry }) {
|
|
68
|
+
if (entryPreparation.status !== "ready") return void 0;
|
|
31
69
|
const result = yield applicationRuntime.start(entry);
|
|
32
70
|
return result.status === "started" || result.status === "duplicate" ? result.session : void 0;
|
|
33
71
|
});
|
|
34
72
|
return function(_x) {
|
|
35
73
|
return _ref.apply(this, arguments);
|
|
36
74
|
};
|
|
37
|
-
}(), [applicationRuntime]);
|
|
75
|
+
}(), [applicationRuntime, entryPreparation.status]);
|
|
38
76
|
return {
|
|
39
77
|
session: snapshot.session,
|
|
40
78
|
starting: snapshot.status === "starting",
|
|
79
|
+
preparingEntry: entryPreparation.status === "preparing",
|
|
80
|
+
entryPreparationErrorReference: entryPreparation.status === "failed" ? entryPreparation.errorReference : void 0,
|
|
41
81
|
startErrorReference: snapshot.startFailure ? typeof snapshot.startFailure.error === "object" && snapshot.startFailure.error !== null && "traceId" in snapshot.startFailure.error && typeof snapshot.startFailure.error.traceId === "string" ? snapshot.startFailure.error.traceId : snapshot.startFailure.reason : void 0,
|
|
42
82
|
exiting: snapshot.status === "exiting",
|
|
43
83
|
exitError: snapshot.exitError,
|
|
@@ -47,7 +87,8 @@ function useHDLKioskSessionController({ host, config, isActive }) {
|
|
|
47
87
|
requestExit: applicationRuntime.requestExit,
|
|
48
88
|
resolveExit: applicationRuntime.resolveExit,
|
|
49
89
|
completeOrder: applicationRuntime.completeOrder,
|
|
50
|
-
recover: applicationRuntime.recover
|
|
90
|
+
recover: applicationRuntime.recover,
|
|
91
|
+
retryEntryPreparation: () => setEntryPreparationAttempt((attempt) => attempt + 1)
|
|
51
92
|
};
|
|
52
93
|
}
|
|
53
94
|
//#endregion
|
|
@@ -177,7 +177,7 @@ function EntryGraphic({ entry }) {
|
|
|
177
177
|
if (entry === "barcode") return /* @__PURE__ */ React.createElement(BarcodeGraphic, null);
|
|
178
178
|
return /* @__PURE__ */ React.createElement(ScaleGraphic, null);
|
|
179
179
|
}
|
|
180
|
-
function EntryScreen({ locale, model, onLocaleChange, onTechnicalSupportClick, onSelect, busy = false, errorReference, errorMessage = "Unable to start a safe order session." }) {
|
|
180
|
+
function EntryScreen({ locale, model, onLocaleChange, onTechnicalSupportClick, onSelect, busy = false, errorReference, errorMessage = "Unable to start a safe order session.", retryLabel, onRetry }) {
|
|
181
181
|
const [reachMode, setReachMode] = useState(false);
|
|
182
182
|
const entryLocale = locale || "en";
|
|
183
183
|
return /* @__PURE__ */ React.createElement("section", {
|
|
@@ -236,7 +236,10 @@ function EntryScreen({ locale, model, onLocaleChange, onTechnicalSupportClick, o
|
|
|
236
236
|
}, model.assistanceMessage) : null, errorReference ? /* @__PURE__ */ React.createElement("div", {
|
|
237
237
|
className: "hdl-inline-error hdl-entry-screen__error",
|
|
238
238
|
role: "alert"
|
|
239
|
-
}, /* @__PURE__ */ React.createElement("strong", null, errorMessage), /* @__PURE__ */ React.createElement("small", null, errorReference)
|
|
239
|
+
}, /* @__PURE__ */ React.createElement("strong", null, errorMessage), /* @__PURE__ */ React.createElement("small", null, errorReference), onRetry && retryLabel ? /* @__PURE__ */ React.createElement("button", {
|
|
240
|
+
type: "button",
|
|
241
|
+
onClick: onRetry
|
|
242
|
+
}, retryLabel) : null) : null), /* @__PURE__ */ React.createElement("footer", { className: "hdl-entry-screen__utilities" }, /* @__PURE__ */ React.createElement("button", {
|
|
240
243
|
className: `hdl-entry-reach${reachMode ? " is-active" : ""}`,
|
|
241
244
|
type: "button",
|
|
242
245
|
"aria-pressed": reachMode,
|
|
@@ -745,6 +745,16 @@
|
|
|
745
745
|
color: rgba(255, 255, 255, 0.68);
|
|
746
746
|
}
|
|
747
747
|
|
|
748
|
+
.hdl-entry-screen__error button {
|
|
749
|
+
align-self: flex-start;
|
|
750
|
+
border: 1px solid rgba(255, 255, 255, 0.45);
|
|
751
|
+
border-radius: 999px;
|
|
752
|
+
padding: 7px 14px;
|
|
753
|
+
background: rgba(255, 255, 255, 0.12);
|
|
754
|
+
color: #fff;
|
|
755
|
+
cursor: pointer;
|
|
756
|
+
}
|
|
757
|
+
|
|
748
758
|
.hdl-entry-screen__utilities {
|
|
749
759
|
position: absolute;
|
|
750
760
|
right: 52px;
|
|
@@ -156,9 +156,11 @@ function HDLKioskBigSaleTemplate({ isActive = true, context, businessCode, local
|
|
|
156
156
|
renderCartContent,
|
|
157
157
|
cartOverlayActions
|
|
158
158
|
}), /* @__PURE__ */ React.createElement(HDLKioskPaymentRuntime, {
|
|
159
|
+
active: isActive,
|
|
159
160
|
context,
|
|
160
161
|
controllerRef: paymentControllerRef,
|
|
161
|
-
onPaymentResultChange: handlePaymentResultChange
|
|
162
|
+
onPaymentResultChange: handlePaymentResultChange,
|
|
163
|
+
telemetry: host.telemetry
|
|
162
164
|
}), orderNoteModal);
|
|
163
165
|
}
|
|
164
166
|
//#endregion
|