@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.
Files changed (29) hide show
  1. package/build/lowcode/assets-daily.json +11 -11
  2. package/build/lowcode/assets-dev.json +2 -2
  3. package/build/lowcode/assets-prod.json +11 -11
  4. package/build/lowcode/meta.js +1 -1
  5. package/build/lowcode/render/default/view.css +1 -1
  6. package/build/lowcode/render/default/view.js +10 -10
  7. package/build/lowcode/view.css +1 -1
  8. package/build/lowcode/view.js +10 -10
  9. package/es/components/HDLKiosk/index.js +9 -6
  10. package/es/components/HDLKiosk/locales.js +15 -0
  11. package/es/components/HDLKiosk/runtime/entryPreparation.js +48 -0
  12. package/es/components/HDLKiosk/runtime/session/createSession.js +46 -2
  13. package/es/components/HDLKiosk/slices/customization/Screen/index.js +25 -2
  14. package/es/components/HDLKiosk/ui/controllers/useHDLKioskSessionController.js +50 -9
  15. package/es/components/HDLKiosk/ui/entry/Screen/index.js +5 -2
  16. package/es/components/HDLKiosk/ui/entry/Screen/index.less +10 -0
  17. package/es/components/bigSale/templates/HDLKiosk/index.js +3 -1
  18. package/es/components/bigSale/templates/HDLKiosk/payment/HDLKioskPaymentRuntime.js +163 -25
  19. package/lib/components/HDLKiosk/index.js +9 -6
  20. package/lib/components/HDLKiosk/locales.js +15 -0
  21. package/lib/components/HDLKiosk/runtime/entryPreparation.js +48 -0
  22. package/lib/components/HDLKiosk/runtime/session/createSession.js +46 -2
  23. package/lib/components/HDLKiosk/slices/customization/Screen/index.js +25 -2
  24. package/lib/components/HDLKiosk/ui/controllers/useHDLKioskSessionController.js +49 -8
  25. package/lib/components/HDLKiosk/ui/entry/Screen/index.js +5 -2
  26. package/lib/components/HDLKiosk/ui/entry/Screen/index.less +10 -0
  27. package/lib/components/bigSale/templates/HDLKiosk/index.js +3 -1
  28. package/lib/components/bigSale/templates/HDLKiosk/payment/HDLKioskPaymentRuntime.js +162 -24
  29. package/package.json +3 -3
@@ -1,3 +1,4 @@
1
+ import { _objectSpread2 } from "../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
1
2
  import { _asyncToGenerator } from "../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js";
2
3
  import useEngineContext from "../../../../../hooks/useEngineContext.js";
3
4
  import { CHECKOUT_VISIBLE_PAYMENT_CODES } from "../../../../checkout/constants.js";
@@ -9,12 +10,15 @@ import { executeEftposPayment } from "./eftposPayment.js";
9
10
  import { createStaffPaymentAuthorizationStore, isStaffPaymentAuthorizationCancelled, resolveStaffPaymentResult } from "./staffPaymentAuthorization.js";
10
11
  import { createHDLKioskPaymentResult } from "./paymentResult.js";
11
12
  import { completeBigSaleNoPaymentOrder } from "./noPaymentOrder.js";
12
- import React, { useEffect, useLayoutEffect, useMemo, useRef } from "react";
13
+ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from "react";
13
14
  //#region src/components/bigSale/templates/HDLKiosk/payment/HDLKioskPaymentRuntime.tsx
14
15
  /**
15
16
  * BigSale 模板内的 Kiosk 支付桥。它持有需要 React/设备 UI 的 PaymentMethodRuntime,
16
17
  * 再把窄命令交给独立 HDLKiosk Host;Core 不感知 BigSale、PiPayment 或 window.app。
17
18
  */
19
+ function diagnosticText(value, fallback = "unknown") {
20
+ return (String(value !== null && value !== void 0 ? value : "").trim() || fallback).slice(0, 200);
21
+ }
18
22
  function asKioskMethod(method) {
19
23
  var _raw$channel_applicat;
20
24
  const raw = method;
@@ -35,7 +39,7 @@ function currentAmount(context) {
35
39
  const amount = resolveBigSaleCurrentAmountDue(context);
36
40
  return typeof amount === "number" && Number.isFinite(amount) && amount > 0 ? amount : 0;
37
41
  }
38
- function HDLKioskPaymentRuntime({ context, controllerRef, onPaymentResultChange }) {
42
+ function HDLKioskPaymentRuntime({ active, context, controllerRef, onPaymentResultChange, telemetry }) {
39
43
  var _context$sales$tempOr, _engineContext$appHel5;
40
44
  const engineContext = useEngineContext();
41
45
  const runtimeRef = useRef(null);
@@ -43,11 +47,31 @@ function HDLKioskPaymentRuntime({ context, controllerRef, onPaymentResultChange
43
47
  const rawMethodsRef = useRef(/* @__PURE__ */ new Map());
44
48
  const paymentResultContextRef = useRef(context);
45
49
  const paymentResultChangeRef = useRef(onPaymentResultChange);
50
+ const activeRef = useRef(active);
51
+ const staffPaymentAttemptRef = useRef();
46
52
  paymentResultContextRef.current = context;
47
53
  paymentResultChangeRef.current = onPaymentResultChange;
54
+ activeRef.current = active;
48
55
  const authorizationStore = useMemo(() => createStaffPaymentAuthorizationStore(), []);
49
56
  const baseSales = context.sales.bookingTicket;
50
57
  const currencySymbol = ((_context$sales$tempOr = context.sales.tempOrder) === null || _context$sales$tempOr === void 0 ? void 0 : _context$sales$tempOr.currency_symbol) || "$";
58
+ const emitDiagnostic = useCallback((name, traceId, attributes) => {
59
+ try {
60
+ telemetry.emit({
61
+ name,
62
+ traceId,
63
+ occurredAt: Date.now(),
64
+ attributes
65
+ });
66
+ } catch (_unused) {}
67
+ }, [telemetry]);
68
+ useEffect(() => {
69
+ var _window$location;
70
+ emitDiagnostic("hdlKiosk.runtime.lifecycle", `hdl-payment-runtime:${Date.now()}:${active ? "active" : "inactive"}`, {
71
+ phase: active ? "activated" : "deactivated",
72
+ pathname: diagnosticText((_window$location = window.location) === null || _window$location === void 0 ? void 0 : _window$location.pathname, "/")
73
+ });
74
+ }, [active, emitDiagnostic]);
51
75
  const controller = useMemo(() => ({
52
76
  loadPaymentMethods: function() {
53
77
  var _ref = _asyncToGenerator(function* () {
@@ -126,6 +150,19 @@ function HDLKioskPaymentRuntime({ context, controllerRef, onPaymentResultChange
126
150
  if (typeof (globalApp === null || globalApp === void 0 ? void 0 : globalApp.runFunction) !== "function") return failed("staffPaymentAuthorizationUnavailable", "staff");
127
151
  const authorization = authorizationStore.begin();
128
152
  if (!authorization) return failed("staffPaymentAuthorizationInProgress", "retry");
153
+ const startedAt = Date.now();
154
+ const attempt = {
155
+ attemptId: `hdl-staff-payment:${startedAt}:${authorization.token}`,
156
+ authorization,
157
+ methodCode: method.code,
158
+ startedAt
159
+ };
160
+ staffPaymentAttemptRef.current = attempt;
161
+ emitDiagnostic("hdlKiosk.staffPayment.authorization", attempt.attemptId, {
162
+ phase: "started",
163
+ authorizationToken: authorization.token,
164
+ methodCode: method.code
165
+ });
129
166
  try {
130
167
  var _baseSales$getOtherPa;
131
168
  const otherParams = (yield baseSales === null || baseSales === void 0 || (_baseSales$getOtherPa = baseSales.getOtherParams) === null || _baseSales$getOtherPa === void 0 ? void 0 : _baseSales$getOtherPa.call(baseSales)) || (baseSales === null || baseSales === void 0 ? void 0 : baseSales.otherParams) || {};
@@ -137,30 +174,82 @@ function HDLKioskPaymentRuntime({ context, controllerRef, onPaymentResultChange
137
174
  paymentMethodType: method.type,
138
175
  amount
139
176
  },
140
- onClose: () => authorizationStore.cancel(authorization)
177
+ onClose: () => {
178
+ var _staffPaymentAttemptR;
179
+ const cancelled = authorizationStore.cancel(authorization);
180
+ emitDiagnostic("hdlKiosk.staffPayment.authorization", attempt.attemptId, {
181
+ phase: cancelled ? "closed" : "closeIgnored",
182
+ authorizationToken: authorization.token,
183
+ elapsedMs: Date.now() - attempt.startedAt
184
+ });
185
+ if (cancelled && ((_staffPaymentAttemptR = staffPaymentAttemptRef.current) === null || _staffPaymentAttemptR === void 0 ? void 0 : _staffPaymentAttemptR.authorization) === authorization) staffPaymentAttemptRef.current = void 0;
186
+ }
141
187
  });
142
- if (authorizationStore.isSettling(authorization)) return {
143
- status: "succeeded",
144
- data: void 0
145
- };
188
+ if (authorizationStore.isSettling(authorization)) {
189
+ emitDiagnostic("hdlKiosk.staffPayment.authorization", attempt.attemptId, {
190
+ phase: "dispatchSucceededDuringSettlement",
191
+ authorizationToken: authorization.token,
192
+ elapsedMs: Date.now() - attempt.startedAt
193
+ });
194
+ return {
195
+ status: "succeeded",
196
+ data: void 0
197
+ };
198
+ }
146
199
  if (isStaffPaymentAuthorizationCancelled(result)) {
200
+ var _staffPaymentAttemptR2;
147
201
  authorizationStore.cancel(authorization);
202
+ emitDiagnostic("hdlKiosk.staffPayment.authorization", attempt.attemptId, {
203
+ phase: "dispatchCancelled",
204
+ authorizationToken: authorization.token,
205
+ elapsedMs: Date.now() - attempt.startedAt
206
+ });
207
+ if (((_staffPaymentAttemptR2 = staffPaymentAttemptRef.current) === null || _staffPaymentAttemptR2 === void 0 ? void 0 : _staffPaymentAttemptR2.authorization) === authorization) staffPaymentAttemptRef.current = void 0;
148
208
  return failed("staffPaymentAuthorizationCancelled", "retry");
149
209
  }
150
210
  if ((result === null || result === void 0 ? void 0 : result.status) !== "success") {
211
+ var _staffPaymentAttemptR3;
151
212
  authorizationStore.fail(authorization);
213
+ emitDiagnostic("hdlKiosk.staffPayment.authorization", attempt.attemptId, {
214
+ phase: "dispatchFailed",
215
+ authorizationToken: authorization.token,
216
+ resultStatus: diagnosticText(result === null || result === void 0 ? void 0 : result.status),
217
+ elapsedMs: Date.now() - attempt.startedAt
218
+ });
219
+ if (((_staffPaymentAttemptR3 = staffPaymentAttemptRef.current) === null || _staffPaymentAttemptR3 === void 0 ? void 0 : _staffPaymentAttemptR3.authorization) === authorization) staffPaymentAttemptRef.current = void 0;
152
220
  return failed("staffPaymentAuthorizationDispatchFailed", "retry", result.msg);
153
221
  }
222
+ emitDiagnostic("hdlKiosk.staffPayment.authorization", attempt.attemptId, {
223
+ phase: "dispatchSucceeded",
224
+ authorizationToken: authorization.token,
225
+ elapsedMs: Date.now() - attempt.startedAt
226
+ });
154
227
  return {
155
228
  status: "succeeded",
156
229
  data: void 0
157
230
  };
158
231
  } catch (error) {
159
- if (authorizationStore.isSettling(authorization)) return {
160
- status: "succeeded",
161
- data: void 0
162
- };
232
+ var _staffPaymentAttemptR4;
233
+ if (authorizationStore.isSettling(authorization)) {
234
+ emitDiagnostic("hdlKiosk.staffPayment.authorization", attempt.attemptId, {
235
+ phase: "dispatchErroredDuringSettlement",
236
+ authorizationToken: authorization.token,
237
+ message: diagnosticText(error instanceof Error ? error.message : error),
238
+ elapsedMs: Date.now() - attempt.startedAt
239
+ });
240
+ return {
241
+ status: "succeeded",
242
+ data: void 0
243
+ };
244
+ }
163
245
  authorizationStore.fail(authorization);
246
+ emitDiagnostic("hdlKiosk.staffPayment.authorization", attempt.attemptId, {
247
+ phase: "dispatchFailed",
248
+ authorizationToken: authorization.token,
249
+ message: diagnosticText(error instanceof Error ? error.message : error),
250
+ elapsedMs: Date.now() - attempt.startedAt
251
+ });
252
+ if (((_staffPaymentAttemptR4 = staffPaymentAttemptRef.current) === null || _staffPaymentAttemptR4 === void 0 ? void 0 : _staffPaymentAttemptR4.authorization) === authorization) staffPaymentAttemptRef.current = void 0;
164
253
  return failed("staffPaymentAuthorizationDispatchFailed", "retry", error);
165
254
  }
166
255
  });
@@ -175,26 +264,48 @@ function HDLKioskPaymentRuntime({ context, controllerRef, onPaymentResultChange
175
264
  context,
176
265
  currencySymbol,
177
266
  (_engineContext$appHel5 = engineContext.appHelper) === null || _engineContext$appHel5 === void 0 ? void 0 : _engineContext$appHel5.utils,
267
+ emitDiagnostic,
178
268
  onPaymentResultChange
179
269
  ]);
180
270
  useEffect(() => {
181
271
  let cleanup;
182
272
  let globalApp;
183
273
  try {
184
- var _window2, _app$applicationManag, _globalApp$runFunctio;
274
+ var _window2, _app$applicationManag;
185
275
  const app = (_window2 = window) === null || _window2 === void 0 ? void 0 : _window2.app;
186
276
  globalApp = app === null || app === void 0 || (_app$applicationManag = app.applicationManager) === null || _app$applicationManag === void 0 ? void 0 : _app$applicationManag.get("global");
187
- cleanup = globalApp === null || globalApp === void 0 || (_globalApp$runFunctio = globalApp.runFunction) === null || _globalApp$runFunctio === void 0 ? void 0 : _globalApp$runFunctio.call(globalApp, "listenExternalCommand", {
277
+ if (typeof (globalApp === null || globalApp === void 0 ? void 0 : globalApp.runFunction) !== "function") throw new Error("global application is unavailable");
278
+ cleanup = globalApp.runFunction("listenExternalCommand", {
188
279
  app,
189
280
  handler: (command) => {
281
+ var _staffPaymentAttemptR5;
190
282
  if (command.command !== "payment_result") return;
283
+ const params = command.params || {};
284
+ const currentAttempt = staffPaymentAttemptRef.current;
285
+ const hostRequestId = diagnosticText(params.requestId, "missing");
286
+ const traceId = (currentAttempt === null || currentAttempt === void 0 ? void 0 : currentAttempt.attemptId) || `hdl-staff-payment:orphan:${hostRequestId}:${Date.now()}`;
287
+ emitDiagnostic("hdlKiosk.staffPayment.result", traceId, _objectSpread2({
288
+ phase: "received",
289
+ hostRequestId,
290
+ resultStatus: diagnosticText(params.status),
291
+ ok: params.ok === true,
292
+ hasActiveAttempt: Boolean(currentAttempt)
293
+ }, currentAttempt ? { methodCode: currentAttempt.methodCode } : {}));
191
294
  const authorization = authorizationStore.beginSettlement();
192
- if (!authorization) return;
295
+ if (!authorization) {
296
+ emitDiagnostic("hdlKiosk.staffPayment.result", traceId, {
297
+ phase: "ignored",
298
+ reason: "no-active-authorization",
299
+ hostRequestId,
300
+ resultStatus: diagnosticText(params.status)
301
+ });
302
+ return;
303
+ }
193
304
  try {
194
- var _globalApp$runFunctio2;
195
- globalApp === null || globalApp === void 0 || (_globalApp$runFunctio2 = globalApp.runFunction) === null || _globalApp$runFunctio2 === void 0 || _globalApp$runFunctio2.call(globalApp, "closeAuthorization");
196
- } catch (_unused) {}
197
- const result = resolveStaffPaymentResult(command.params || {});
305
+ var _globalApp$runFunctio;
306
+ globalApp === null || globalApp === void 0 || (_globalApp$runFunctio = globalApp.runFunction) === null || _globalApp$runFunctio === void 0 || _globalApp$runFunctio.call(globalApp, "closeAuthorization");
307
+ } catch (_unused2) {}
308
+ const result = resolveStaffPaymentResult(params);
198
309
  if (result.status === "succeeded") {
199
310
  var _currentContext$sales;
200
311
  const currentContext = paymentResultContextRef.current;
@@ -202,21 +313,48 @@ function HDLKioskPaymentRuntime({ context, controllerRef, onPaymentResultChange
202
313
  source: "staff",
203
314
  fallbackAmount: currentAmount(currentContext),
204
315
  currencySymbol: (_currentContext$sales = currentContext.sales.tempOrder) === null || _currentContext$sales === void 0 ? void 0 : _currentContext$sales.currency_symbol,
205
- rawResult: command.params
316
+ rawResult: params
206
317
  }));
318
+ emitDiagnostic("hdlKiosk.staffPayment.result", traceId, {
319
+ phase: "projected",
320
+ hostRequestId,
321
+ resultStatus: result.status
322
+ });
207
323
  }
208
- authorizationStore.complete(authorization, result);
324
+ const published = authorizationStore.complete(authorization, result);
325
+ emitDiagnostic("hdlKiosk.staffPayment.result", traceId, {
326
+ phase: published ? "published" : "publishIgnored",
327
+ hostRequestId,
328
+ resultStatus: result.status
329
+ });
330
+ if (published && ((_staffPaymentAttemptR5 = staffPaymentAttemptRef.current) === null || _staffPaymentAttemptR5 === void 0 ? void 0 : _staffPaymentAttemptR5.authorization) === authorization) staffPaymentAttemptRef.current = void 0;
209
331
  }
210
332
  });
211
- } catch (_unused2) {}
333
+ emitDiagnostic("hdlKiosk.staffPayment.result", `hdl-staff-payment-listener:${Date.now()}`, { phase: "listenerRegistered" });
334
+ } catch (error) {
335
+ emitDiagnostic("hdlKiosk.staffPayment.result", `hdl-staff-payment-listener:${Date.now()}`, {
336
+ phase: "listenerRegistrationFailed",
337
+ message: diagnosticText(error instanceof Error ? error.message : error)
338
+ });
339
+ }
212
340
  return () => {
341
+ var _window$location2;
213
342
  if (typeof cleanup === "function") cleanup();
214
- if (authorizationStore.dispose()) try {
215
- var _globalApp$runFunctio3;
216
- globalApp === null || globalApp === void 0 || (_globalApp$runFunctio3 = globalApp.runFunction) === null || _globalApp$runFunctio3 === void 0 || _globalApp$runFunctio3.call(globalApp, "closeAuthorization");
343
+ const hadActiveAuthorization = authorizationStore.dispose();
344
+ const currentAttempt = staffPaymentAttemptRef.current;
345
+ emitDiagnostic("hdlKiosk.runtime.lifecycle", (currentAttempt === null || currentAttempt === void 0 ? void 0 : currentAttempt.attemptId) || `hdl-payment-runtime:${Date.now()}:unmounted`, {
346
+ phase: "unmounted",
347
+ active: activeRef.current,
348
+ hadActiveAuthorization,
349
+ pathname: diagnosticText((_window$location2 = window.location) === null || _window$location2 === void 0 ? void 0 : _window$location2.pathname, "/")
350
+ });
351
+ staffPaymentAttemptRef.current = void 0;
352
+ if (hadActiveAuthorization) try {
353
+ var _globalApp$runFunctio2;
354
+ globalApp === null || globalApp === void 0 || (_globalApp$runFunctio2 = globalApp.runFunction) === null || _globalApp$runFunctio2 === void 0 || _globalApp$runFunctio2.call(globalApp, "closeAuthorization");
217
355
  } catch (_unused3) {}
218
356
  };
219
- }, [authorizationStore]);
357
+ }, [authorizationStore, emitDiagnostic]);
220
358
  useLayoutEffect(() => {
221
359
  controllerRef.current = controller;
222
360
  return () => {
@@ -59,10 +59,11 @@ function HDLKioskContent({ host, config, isActive = true, entryDataPreparing = f
59
59
  setActiveLocale(nextLocale);
60
60
  onLocaleChange === null || onLocaleChange === void 0 || onLocaleChange(nextLocale);
61
61
  }, [onLocaleChange]);
62
- const { session, starting, startErrorReference, exiting, exitError, pendingExit, start, back, requestExit, resolveExit, completeOrder, recover } = require_useHDLKioskSessionController.useHDLKioskSessionController({
62
+ const { session, starting, preparingEntry, entryPreparationErrorReference, startErrorReference, exiting, exitError, pendingExit, start, back, requestExit, resolveExit, completeOrder, recover, retryEntryPreparation } = require_useHDLKioskSessionController.useHDLKioskSessionController({
63
63
  host,
64
64
  config,
65
- isActive
65
+ isActive,
66
+ entryPreparationBlocked: entryDataPreparing || sales.hostStatus === "loading"
66
67
  });
67
68
  const observeExit = (0, react.useCallback)((result) => {
68
69
  if (result.status === "exited") onPaymentResultReset === null || onPaymentResultReset === void 0 || onPaymentResultReset();
@@ -169,10 +170,12 @@ function HDLKioskContent({ host, config, isActive = true, entryDataPreparing = f
169
170
  start({ entry });
170
171
  }
171
172
  },
172
- busy: starting,
173
- errorReference: startErrorReference,
174
- errorMessage: _pisell_utils.locales.getText("pisell2.hdl-kiosk.session-start-failed")
175
- }), starting ? /* @__PURE__ */ react.default.createElement("div", {
173
+ busy: starting || preparingEntry || Boolean(entryPreparationErrorReference),
174
+ errorReference: entryPreparationErrorReference || startErrorReference,
175
+ errorMessage: _pisell_utils.locales.getText(entryPreparationErrorReference ? "pisell2.hdl-kiosk.reset-failed-description" : "pisell2.hdl-kiosk.session-start-failed"),
176
+ retryLabel: entryPreparationErrorReference ? _pisell_utils.locales.getText("pisell2.hdl-kiosk.retry-entry-preparation") : void 0,
177
+ onRetry: entryPreparationErrorReference ? retryEntryPreparation : void 0
178
+ }), starting || preparingEntry ? /* @__PURE__ */ react.default.createElement("div", {
176
179
  className: "hdl-operation-overlay",
177
180
  role: "status"
178
181
  }, /* @__PURE__ */ react.default.createElement("span", { className: "hdl-operation-overlay__spinner" }), /* @__PURE__ */ react.default.createElement("strong", null, _pisell_utils.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
+ const require_asyncToGenerator = require("../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js");
2
+ const require_errors = require("./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 = require_asyncToGenerator._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 = require_errors.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 = require_errors.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: require_errors.createOperationError("entryPreparationUnhandled", "staff", cause)
41
+ };
42
+ }
43
+ return result;
44
+ });
45
+ return _prepareHDLKioskEntry.apply(this, arguments);
46
+ }
47
+ //#endregion
48
+ exports.prepareHDLKioskEntry = prepareHDLKioskEntry;
@@ -62,9 +62,30 @@ function createHDLKioskWorkflowSession(entry, host, options = {}) {
62
62
  let staffPaymentPending = false;
63
63
  let staffPaymentConfirmed = false;
64
64
  let staffPaymentRejected = false;
65
+ let staffPaymentObserved = false;
66
+ let staffPaymentAdvanceDispatched = false;
67
+ let lastStaffPaymentAdvanceDiagnostic = "";
65
68
  let completedOrderFinalized = false;
66
69
  let completedOrderFinalizePromise;
67
70
  const staffPaymentReceipt = Object.freeze({ paymentConfirmed: true });
71
+ const emitStaffPaymentWorkflow = (phase, authorizationStatus, reason) => {
72
+ const snapshot = workflowActor.getSnapshot();
73
+ try {
74
+ host.telemetry.emit({
75
+ name: "hdlKiosk.staffPayment.workflow",
76
+ traceId: `${snapshot.workflowId}:${snapshot.sessionEpoch}:staff-payment`,
77
+ occurredAt: Date.now(),
78
+ attributes: require_objectSpread2._objectSpread2({
79
+ phase,
80
+ authorizationStatus,
81
+ workflowId: snapshot.workflowId,
82
+ sessionEpoch: snapshot.sessionEpoch,
83
+ nodeId: snapshot.nodeId,
84
+ workflowStatus: snapshot.status
85
+ }, reason ? { reason } : {})
86
+ });
87
+ } catch (_unused) {}
88
+ };
68
89
  const dispatchConfirmedPayment = (receipt, intentId) => {
69
90
  var _definition$nodes$sna;
70
91
  const snapshot = workflowActor.getSnapshot();
@@ -80,8 +101,26 @@ function createHDLKioskWorkflowSession(entry, host, options = {}) {
80
101
  };
81
102
  const advanceConfirmedStaffPayment = () => {
82
103
  var _definition$nodes$sna2;
104
+ if (!staffPaymentObserved || staffPaymentPending || staffPaymentAdvanceDispatched) return;
83
105
  const snapshot = workflowActor.getSnapshot();
84
- if (snapshot.status !== "idle" || ((_definition$nodes$sna2 = definition.nodes[snapshot.nodeId]) === null || _definition$nodes$sna2 === void 0 ? void 0 : _definition$nodes$sna2.activity) !== "paymentMethod" || staffPaymentRejected || staffPaymentPending && !staffPaymentConfirmed || !staffPaymentConfirmed) return;
106
+ if (staffPaymentRejected || !staffPaymentConfirmed) return;
107
+ 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;
108
+ if (skipReason) {
109
+ const diagnostic = [
110
+ snapshot.sessionEpoch,
111
+ snapshot.nodeId,
112
+ snapshot.status,
113
+ skipReason
114
+ ].join(":");
115
+ if (diagnostic !== lastStaffPaymentAdvanceDiagnostic) {
116
+ lastStaffPaymentAdvanceDiagnostic = diagnostic;
117
+ emitStaffPaymentWorkflow("advanceSkipped", "succeeded", skipReason);
118
+ }
119
+ return;
120
+ }
121
+ staffPaymentAdvanceDispatched = true;
122
+ lastStaffPaymentAdvanceDiagnostic = `${snapshot.sessionEpoch}:advanced`;
123
+ emitStaffPaymentWorkflow("advanced", "succeeded");
85
124
  dispatchConfirmedPayment(staffPaymentReceipt, `staff-payment:${snapshot.sessionEpoch}`);
86
125
  };
87
126
  const finalizeCompletedOrder = (intentId) => {
@@ -116,15 +155,20 @@ function createHDLKioskWorkflowSession(entry, host, options = {}) {
116
155
  try {
117
156
  unsubscribePaymentWorkflow = workflowActor.subscribe(handleWorkflowUpdate);
118
157
  unsubscribeStaffPayment = host.sales.subscribeStaffPaymentAuthorizationResult((result) => {
158
+ staffPaymentObserved = true;
119
159
  if (result.status === "pending") {
120
160
  staffPaymentPending = true;
121
161
  staffPaymentConfirmed = false;
122
162
  staffPaymentRejected = false;
163
+ staffPaymentAdvanceDispatched = false;
164
+ lastStaffPaymentAdvanceDiagnostic = "";
165
+ emitStaffPaymentWorkflow("pending", result.status);
123
166
  return;
124
167
  }
125
168
  staffPaymentPending = false;
126
169
  staffPaymentConfirmed = result.status === "succeeded";
127
170
  staffPaymentRejected = !staffPaymentConfirmed;
171
+ emitStaffPaymentWorkflow(staffPaymentConfirmed ? "confirmed" : "rejected", result.status);
128
172
  if (staffPaymentConfirmed) coordinator.confirmPaymentSucceeded();
129
173
  advanceConfirmedStaffPayment();
130
174
  });
@@ -154,7 +198,7 @@ function createHDLKioskWorkflowSession(entry, host, options = {}) {
154
198
  sessionEpoch: snapshot.sessionEpoch
155
199
  }
156
200
  });
157
- } catch (_unused) {}
201
+ } catch (_unused2) {}
158
202
  const disposeSession = () => {
159
203
  if (disposed) return;
160
204
  disposed = true;
@@ -1,6 +1,7 @@
1
1
  const require_runtime = require("../../../../../_virtual/_rolldown/runtime.js");
2
2
  const require_objectSpread2 = require("../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js");
3
3
  const require_asyncToGenerator = require("../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js");
4
+ const require_resolveLocalizedText = require("../../../../../plus/salesSdk/utils/resolveLocalizedText.js");
4
5
  const require_index = require("../../../components/shell/HDLKioskBottomBarHost/index.js");
5
6
  const require_index$1 = require("../../../components/shell/HDLKioskOrderSummary/index.js");
6
7
  const require_model = require("../../../components/common/HDLKioskSkuOptionsSelection/model.js");
@@ -12,6 +13,7 @@ react = require_runtime.__toESM(react);
12
13
  let _pisell_utils = require("@pisell/utils");
13
14
  let decimal_js = require("decimal.js");
14
15
  decimal_js = require_runtime.__toESM(decimal_js);
16
+ let _pisell_materials = require("@pisell/materials");
15
17
  //#region src/components/HDLKiosk/slices/customization/Screen/index.tsx
16
18
  /**
17
19
  * 文件角色:呈现主称重商品的本地重量与标准 SkuValue 草稿。
@@ -50,6 +52,22 @@ function formatMoney(value, currencySymbol) {
50
52
  const amount = value.toDecimalPlaces(2).toNumber();
51
53
  return String((0, _pisell_utils.formatAmount)(amount, 2, currencySymbol || ""));
52
54
  }
55
+ function resolveInvalidSkuGroupName(error, product) {
56
+ var _group$name;
57
+ if (!error || typeof error !== "object" || Array.isArray(error) || !product) return;
58
+ const firstInvalidGroupId = Object.keys(error)[0];
59
+ if (!firstInvalidGroupId) return void 0;
60
+ const source = product;
61
+ const group = [
62
+ "variant_group",
63
+ "option_group",
64
+ "bundle_group"
65
+ ].flatMap((key) => Array.isArray(source[key]) ? source[key] : []).find((candidate) => {
66
+ var _candidate$id;
67
+ return String((_candidate$id = candidate === null || candidate === void 0 ? void 0 : candidate.id) !== null && _candidate$id !== void 0 ? _candidate$id : "") === firstInvalidGroupId;
68
+ });
69
+ return require_resolveLocalizedText.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;
70
+ }
53
71
  function resolveScaleMeasurement(scale, productFacts, minimumWeightKg) {
54
72
  var _scale$grossWeightKg, _productFacts$fixedTa, _productFacts$fixedTa2;
55
73
  const gross = new decimal_js.default((_scale$grossWeightKg = scale.grossWeightKg) !== null && _scale$grossWeightKg !== void 0 ? _scale$grossWeightKg : NaN);
@@ -151,6 +169,7 @@ function ProductFallback() {
151
169
  }
152
170
  function CustomizationScreen({ product, loading, errorReference, errorMessage, busy, syncing, canGoBack, minimumWeightKg, orderBar, send }) {
153
171
  var _productFacts$product, _orderBar$primaryLine, _orderBar$primaryLine2, _orderBar$primaryLine3, _orderBar$primaryLine4, _orderBar$primaryLine7, _productFacts$unitPri, _orderBar$primaryLine9, _orderBar$primaryLine11;
172
+ const Toast = (0, _pisell_materials.useToast)();
154
173
  const scaleRuntime = require_scale.useHDLScaleRuntime();
155
174
  const scale = require_scale.useHDLScaleSnapshot();
156
175
  const skuOptionsRef = (0, react.useRef)(null);
@@ -190,7 +209,9 @@ function CustomizationScreen({ product, loading, errorReference, errorMessage, b
190
209
  try {
191
210
  var _skuOptionsRef$curren;
192
211
  yield (_skuOptionsRef$curren = skuOptionsRef.current) === null || _skuOptionsRef$curren === void 0 ? void 0 : _skuOptionsRef$curren.validate();
193
- } catch (_unused) {
212
+ } catch (error) {
213
+ const invalidGroupName = resolveInvalidSkuGroupName(error, product);
214
+ Toast.fail(invalidGroupName ? _pisell_utils.locales.getText("pisell2.hdl-kiosk.customization-required-group")(invalidGroupName) : _pisell_utils.locales.getText("pisell2.hdl-kiosk.customization-required-options"));
194
215
  return;
195
216
  }
196
217
  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;
@@ -216,11 +237,13 @@ function CustomizationScreen({ product, loading, errorReference, errorMessage, b
216
237
  busy,
217
238
  minimumWeightKg,
218
239
  (_orderBar$primaryLine9 = orderBar.primaryLine) === null || _orderBar$primaryLine9 === void 0 ? void 0 : _orderBar$primaryLine9.lineId,
240
+ product,
219
241
  productFacts,
220
242
  scaleRuntime,
221
243
  skuValue,
222
244
  send,
223
- syncing
245
+ syncing,
246
+ Toast
224
247
  ]);
225
248
  const omitPrimaryProduct = (0, react.useCallback)(() => {
226
249
  var _orderBar$primaryLine10;