@pisell/private-materials 6.12.160 → 6.12.162

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 (50) 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/preview.js +1 -1
  6. package/build/lowcode/render/default/view.js +18 -18
  7. package/build/lowcode/view.js +18 -18
  8. package/es/components/bigSale/components/ProductList/index.js +30 -19
  9. package/es/components/bigSale/index.js +3 -3
  10. package/es/plus/saleDetail/components/CartItems/Editable/index.js +1 -0
  11. package/es/plus/salesSdk/context/SalesSdkCartContext.js +25 -12
  12. package/es/plus/salesSdk/context/SalesSdkProvider.js +9 -5
  13. package/es/plus/salesSdk/hosts/BookingEditHostRenderer.js +12 -7
  14. package/es/plus/salesSdk/utils/resolvePreviousService.js +28 -0
  15. package/es/plus/salesSdk/utils/runAddProductFlow.js +8 -4
  16. package/es/plus/skuOptionsSelection/components/NormalCard/index.js +18 -12
  17. package/es/plus/skuOptionsSelection/docs/skuOptionsSelection.$tab-dev.md +66 -2
  18. package/es/plus/skuOptionsSelection/types.d.ts +50 -1
  19. package/es/plus/skuOptionsSelection/utils/normalizers.js +17 -0
  20. package/es/plus/skuOptionsSelection/utils/transformOptionGroup.js +17 -7
  21. package/es/plus/skuOptionsSelection/utils/transformSelectorValueToValue.js +89 -15
  22. package/es/plus/skuOptionsSelection/utils/transformValueToSelectorValue.js +31 -19
  23. package/es/pro/Selector/Selector.js +11 -6
  24. package/es/pro/Selector/hooks/useSelectionController.js +54 -61
  25. package/es/pro/Selector/locales.js +10 -0
  26. package/es/pro/Selector/types.d.ts +5 -0
  27. package/es/pro/skuDetailModal/index.js +4 -4
  28. package/es/pro/skuDetailModal/types.d.ts +1 -0
  29. package/lib/components/bigSale/components/ProductList/index.js +30 -19
  30. package/lib/components/bigSale/index.js +3 -3
  31. package/lib/plus/saleDetail/components/CartItems/Editable/index.js +1 -0
  32. package/lib/plus/salesSdk/context/SalesSdkCartContext.js +25 -12
  33. package/lib/plus/salesSdk/context/SalesSdkProvider.js +9 -5
  34. package/lib/plus/salesSdk/hosts/BookingEditHostRenderer.js +12 -7
  35. package/lib/plus/salesSdk/utils/resolvePreviousService.js +30 -0
  36. package/lib/plus/salesSdk/utils/runAddProductFlow.js +8 -4
  37. package/lib/plus/skuOptionsSelection/components/NormalCard/index.js +18 -12
  38. package/lib/plus/skuOptionsSelection/docs/skuOptionsSelection.$tab-dev.md +66 -2
  39. package/lib/plus/skuOptionsSelection/types.d.ts +50 -1
  40. package/lib/plus/skuOptionsSelection/utils/normalizers.js +19 -0
  41. package/lib/plus/skuOptionsSelection/utils/transformOptionGroup.js +17 -7
  42. package/lib/plus/skuOptionsSelection/utils/transformSelectorValueToValue.js +89 -15
  43. package/lib/plus/skuOptionsSelection/utils/transformValueToSelectorValue.js +31 -19
  44. package/lib/pro/Selector/Selector.js +11 -6
  45. package/lib/pro/Selector/hooks/useSelectionController.js +54 -61
  46. package/lib/pro/Selector/locales.js +10 -0
  47. package/lib/pro/Selector/types.d.ts +5 -0
  48. package/lib/pro/skuDetailModal/index.js +4 -4
  49. package/lib/pro/skuDetailModal/types.d.ts +1 -0
  50. package/package.json +3 -3
@@ -91,23 +91,28 @@ const Selector = forwardRef((props, ref) => {
91
91
  if (props.showStepper === false) return null;
92
92
  if ((props.showStepper === "auto" || props.showStepper === void 0) && !showQuantity) return null;
93
93
  return /* @__PURE__ */ React.createElement(NumericStepper, _objectSpread2(_objectSpread2({}, props.stepperProps || {}), {}, {
94
- min: option.effectiveMin,
94
+ min: 0,
95
95
  max: option.effectiveMax,
96
96
  value: option.quantity,
97
97
  disabled: disabled || option.disabled,
98
- onChange: (value) => {
99
- actions.update(option._key, { quantity: value });
98
+ onChange: (value, meta) => {
99
+ const initialQuantity = Math.max(1, Number((option === null || option === void 0 ? void 0 : option.effectiveMin) || 0));
100
+ let nextQuantity = value;
101
+ if ((meta === null || meta === void 0 ? void 0 : meta.action) === "increase" && option.quantity <= 0) nextQuantity = initialQuantity;
102
+ else if ((meta === null || meta === void 0 ? void 0 : meta.action) === "decrease" && initialQuantity > 1 && option.quantity === initialQuantity && value < initialQuantity) nextQuantity = 0;
103
+ actions.update(option._key, { quantity: nextQuantity });
100
104
  }
101
105
  }));
102
106
  };
103
107
  const renderOption = (option) => {
104
- var _option$ruleConfig$mi, _option$ruleConfig, _ruleConfig$min, _option$ruleConfig$ma, _option$ruleConfig2;
108
+ var _option$ruleConfig$mi, _option$ruleConfig, _ruleConfig$min, _option$ruleConfig2;
105
109
  const optionItemValue = Array.isArray(state.values) ? state.values.find((item) => (item === null || item === void 0 ? void 0 : item.value) === option._key) : [state.values].find((item) => (item === null || item === void 0 ? void 0 : item.value) === option._key);
106
110
  if (React.isValidElement(renderItem)) return renderItem;
107
111
  const configMin = (_option$ruleConfig$mi = option === null || option === void 0 || (_option$ruleConfig = option.ruleConfig) === null || _option$ruleConfig === void 0 ? void 0 : _option$ruleConfig.min) !== null && _option$ruleConfig$mi !== void 0 ? _option$ruleConfig$mi : 0;
108
112
  const groupMin = (_ruleConfig$min = ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.min) !== null && _ruleConfig$min !== void 0 ? _ruleConfig$min : 0;
109
113
  const effectiveMin = options.length < 2 && groupMin > 0 ? groupMin : configMin;
110
- const configMax = (_option$ruleConfig$ma = option === null || option === void 0 || (_option$ruleConfig2 = option.ruleConfig) === null || _option$ruleConfig2 === void 0 ? void 0 : _option$ruleConfig2.max) !== null && _option$ruleConfig$ma !== void 0 ? _option$ruleConfig$ma : Number.MAX_SAFE_INTEGER;
114
+ const configuredMax = option === null || option === void 0 || (_option$ruleConfig2 = option.ruleConfig) === null || _option$ruleConfig2 === void 0 ? void 0 : _option$ruleConfig2.max;
115
+ const configMax = typeof configuredMax === "number" && configuredMax > 0 ? configuredMax : Number.MAX_SAFE_INTEGER;
111
116
  const effectiveMax = remainingCount !== void 0 ? Math.min(remainingCount + (option.quantity || 0), configMax) : configMax;
112
117
  option.effectiveMin = effectiveMin;
113
118
  option.effectiveMax = effectiveMax;
@@ -159,7 +164,7 @@ const Selector = forwardRef((props, ref) => {
159
164
  const { required = false } = ruleConfig || {};
160
165
  if (!required) return null;
161
166
  const style = { borderRadius: 16 };
162
- if (errors.find((item) => item.type === "required" || item.type === "min")) return /* @__PURE__ */ React.createElement(ATag, {
167
+ if (errors.find((item) => item.type === "required" || item.type === "min" || item.type === "minTypes")) return /* @__PURE__ */ React.createElement(ATag, {
163
168
  className: "pisell-pro-selector_required-tag",
164
169
  icon: /* @__PURE__ */ React.createElement(ExclamationCircleOutlined, null),
165
170
  color: "error",
@@ -4,6 +4,24 @@ import { useCallback, useMemo, useRef, useState } from "react";
4
4
  import { locales } from "@pisell/utils";
5
5
  import { useControllableValue } from "ahooks";
6
6
  //#region src/pro/Selector/hooks/useSelectionController.ts
7
+ const normalizeSelectionQuantity = (quantity) => {
8
+ if (quantity === null || quantity === void 0) return 1;
9
+ const normalizedQuantity = Number(quantity);
10
+ return Number.isFinite(normalizedQuantity) ? normalizedQuantity : 1;
11
+ };
12
+ const normalizeSelectionValues = (values) => {
13
+ if (values === null || values === void 0) return [];
14
+ return (Array.isArray(values) ? values : [values]).map((item) => {
15
+ if (typeof item === "object" && item !== null) return _objectSpread2(_objectSpread2({}, item), {}, {
16
+ value: item.value,
17
+ quantity: normalizeSelectionQuantity(item.quantity)
18
+ });
19
+ return {
20
+ value: item,
21
+ quantity: 1
22
+ };
23
+ });
24
+ };
7
25
  function useSelectionController(props) {
8
26
  const { dataSource = [], ruleConfig, fieldNames, mode = "single", valueType = "primitive" } = props || {};
9
27
  const [values, setValues] = useControllableValue(props);
@@ -38,48 +56,34 @@ function useSelectionController(props) {
38
56
  return map;
39
57
  }, [mutexGroups]);
40
58
  const normalizedValues = useMemo(() => {
41
- if (!values) return [];
42
- if (Array.isArray(values)) return values.map((item) => {
43
- if (typeof item === "object" && item !== null) return _objectSpread2(_objectSpread2({}, item), {}, {
44
- value: item.value,
45
- quantity: item.quantity || 1
46
- });
47
- return {
48
- value: item,
49
- quantity: 1
50
- };
51
- });
52
- if (typeof values === "object" && values !== null) {
53
- const objValue = values;
54
- return [_objectSpread2(_objectSpread2({}, objValue), {}, {
55
- value: objValue.value,
56
- quantity: objValue.quantity || 1
57
- })];
58
- }
59
- return [{
60
- value: values,
61
- quantity: 1
62
- }];
59
+ return normalizeSelectionValues(values);
63
60
  }, [values]);
64
61
  const quantityInfo = useMemo(() => {
65
- const selectedCount = normalizedValues.reduce((sum, item) => sum + (item.quantity || 0), 0);
62
+ const positiveValues = normalizedValues.filter((item) => Number(item.quantity) > 0);
63
+ const selectedCount = positiveValues.reduce((sum, item) => sum + Number(item.quantity), 0);
64
+ const selectedTypeCount = new Set(positiveValues.map((item) => item.value)).size;
66
65
  const showQuantity = (Array.isArray(dataSource) ? dataSource : dataSource || []).some((opt) => {
67
66
  var _opt$ruleConfig, _opt$ruleConfig2;
68
- return typeof (opt === null || opt === void 0 || (_opt$ruleConfig = opt.ruleConfig) === null || _opt$ruleConfig === void 0 ? void 0 : _opt$ruleConfig.max) === "number" && opt.ruleConfig.max > 1 || typeof (opt === null || opt === void 0 || (_opt$ruleConfig2 = opt.ruleConfig) === null || _opt$ruleConfig2 === void 0 ? void 0 : _opt$ruleConfig2.min) === "number" && opt.ruleConfig.min > 1;
67
+ return typeof (opt === null || opt === void 0 || (_opt$ruleConfig = opt.ruleConfig) === null || _opt$ruleConfig === void 0 ? void 0 : _opt$ruleConfig.max) === "number" && (opt.ruleConfig.max === 0 || opt.ruleConfig.max > 1) || typeof (opt === null || opt === void 0 || (_opt$ruleConfig2 = opt.ruleConfig) === null || _opt$ruleConfig2 === void 0 ? void 0 : _opt$ruleConfig2.min) === "number" && opt.ruleConfig.min > 1;
69
68
  });
70
69
  return {
71
70
  maxReached: typeof (ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.max) === "number" && ruleConfig.max > 0 && selectedCount >= ruleConfig.max,
72
71
  showQuantity,
73
72
  remainingCount: typeof (ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.max) === "number" && ruleConfig.max > 0 ? Math.max(0, ruleConfig.max - selectedCount) : Infinity,
74
- selectedCount
73
+ selectedCount,
74
+ selectedTypeCount,
75
+ typeMaxReached: mode === "multiple" && typeof (ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.maxTypes) === "number" && ruleConfig.maxTypes > 0 && selectedTypeCount >= ruleConfig.maxTypes,
76
+ remainingTypeCount: typeof (ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.maxTypes) === "number" && ruleConfig.maxTypes > 0 ? Math.max(0, ruleConfig.maxTypes - selectedTypeCount) : Infinity
75
77
  };
76
78
  }, [
77
79
  normalizedValues,
78
80
  dataSource,
79
- ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.max
81
+ mode,
82
+ ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.max,
83
+ ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.maxTypes
80
84
  ]);
81
85
  const getOptionState = useCallback((optionKey) => {
82
- const found = normalizedValues.find((item) => item.value === optionKey);
86
+ const found = normalizedValues.find((item) => item.value === optionKey && Number(item.quantity) > 0);
83
87
  return {
84
88
  selected: !!found,
85
89
  quantity: (found === null || found === void 0 ? void 0 : found.quantity) || 0
@@ -88,59 +92,51 @@ function useSelectionController(props) {
88
92
  const options = useMemo(() => {
89
93
  return normalizedOptions.map((option) => {
90
94
  const state = getOptionState(option._key);
91
- const disabled = quantityInfo.maxReached && !(mode === "single" && (ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.max) === 1) && !state.selected;
95
+ const disabledByTotalLimit = quantityInfo.maxReached && !(mode === "single" && (ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.max) === 1) && !state.selected;
96
+ const disabledByTypeLimit = mode === "multiple" && quantityInfo.typeMaxReached && !state.selected;
92
97
  return _objectSpread2(_objectSpread2({}, option), {}, {
93
98
  selected: state.selected,
94
99
  quantity: state.quantity,
95
- disabled: option.disabled || disabled
100
+ disabled: option.disabled || disabledByTotalLimit || disabledByTypeLimit
96
101
  });
97
102
  });
98
103
  }, [
99
104
  normalizedOptions,
100
105
  getOptionState,
101
106
  quantityInfo.maxReached,
107
+ quantityInfo.typeMaxReached,
102
108
  mode,
103
109
  ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.max
104
110
  ]);
105
111
  const validate = function() {
106
112
  var _ref = _asyncToGenerator(function* () {
107
113
  const errors = [];
108
- const { required, min, max, customValidator } = ruleConfig || {};
114
+ const { required, min, max, minTypes, maxTypes, customValidator } = ruleConfig || {};
109
115
  const values = valuesRef.current;
110
- let currentNormalizedValues = [];
111
- if (values) if (Array.isArray(values)) currentNormalizedValues = values.map((item) => {
112
- if (typeof item === "object" && item !== null) return _objectSpread2(_objectSpread2({}, item), {}, {
113
- value: item.value,
114
- quantity: item.quantity || 1
115
- });
116
- return {
117
- value: item,
118
- quantity: 1
119
- };
120
- });
121
- else if (typeof values === "object" && values !== null) {
122
- const objValue = values;
123
- currentNormalizedValues = [_objectSpread2(_objectSpread2({}, objValue), {}, {
124
- value: objValue.value,
125
- quantity: objValue.quantity || 1
126
- })];
127
- } else currentNormalizedValues = [{
128
- value: values,
129
- quantity: 1
130
- }];
131
- const totalQuantity = currentNormalizedValues.reduce((sum, item) => sum + (item.quantity || 0), 0);
116
+ const currentNormalizedValues = normalizeSelectionValues(values);
117
+ const positiveValues = currentNormalizedValues.filter((item) => Number(item.quantity) > 0);
118
+ const totalQuantity = positiveValues.reduce((sum, item) => sum + Number(item.quantity), 0);
119
+ const selectedTypeCount = new Set(positiveValues.map((item) => item.value)).size;
132
120
  if (required && totalQuantity < 1) errors.push({
133
121
  type: "required",
134
122
  message: locales.getText("pisell2.pro.selector.validate.required-msg")
135
123
  });
136
- if (typeof min === "number" && totalQuantity < min) errors.push({
124
+ if (typeof min === "number" && min > 0 && totalQuantity < min) errors.push({
137
125
  type: "min",
138
126
  message: locales.getText("pisell2.pro.selector.validate.min-selection")(min)
139
127
  });
140
- if (typeof max === "number" && totalQuantity > max) errors.push({
128
+ if (typeof max === "number" && max > 0 && totalQuantity > max) errors.push({
141
129
  type: "max",
142
130
  message: locales.getText("pisell2.pro.selector.validate.max-selection")(max)
143
131
  });
132
+ if (typeof minTypes === "number" && minTypes > 0 && selectedTypeCount < minTypes) errors.push({
133
+ type: "minTypes",
134
+ message: locales.getText("pisell2.pro.selector.validate.min-types")(minTypes)
135
+ });
136
+ if (typeof maxTypes === "number" && maxTypes > 0 && selectedTypeCount > maxTypes) errors.push({
137
+ type: "maxTypes",
138
+ message: locales.getText("pisell2.pro.selector.validate.max-types")(maxTypes)
139
+ });
144
140
  options.forEach((option) => {
145
141
  var _option$ruleConfig, _option$ruleConfig2;
146
142
  const _optionVal = currentNormalizedValues.find((item) => item.value === option._key);
@@ -148,19 +144,19 @@ function useSelectionController(props) {
148
144
  const { _label } = option;
149
145
  const _min = option === null || option === void 0 || (_option$ruleConfig = option.ruleConfig) === null || _option$ruleConfig === void 0 ? void 0 : _option$ruleConfig.min;
150
146
  const _max = option === null || option === void 0 || (_option$ruleConfig2 = option.ruleConfig) === null || _option$ruleConfig2 === void 0 ? void 0 : _option$ruleConfig2.max;
151
- if (typeof _min === "number" && quantity < _min) errors.push({
147
+ if (quantity > 0 && typeof _min === "number" && _min > 0 && quantity < _min) errors.push({
152
148
  type: "optionMin",
153
149
  message: locales.getText("pisell2.pro.selector.validate.min-selection")(_min, _label),
154
150
  _key: option._key
155
151
  });
156
- if (typeof _max === "number" && quantity > _max) errors.push({
152
+ if (typeof _max === "number" && _max > 0 && quantity > _max) errors.push({
157
153
  type: "optionMax",
158
154
  message: locales.getText("pisell2.pro.selector.validate.max-selection")(_max, _label),
159
155
  _key: option._key
160
156
  });
161
157
  });
162
158
  if (mutexGroups.length > 0) {
163
- const selectedKeys = (Array.isArray(values) ? values : values ? [values] : []).map((v) => typeof v === "object" && v !== null ? v.value : v);
159
+ const selectedKeys = positiveValues.map((item) => item.value);
164
160
  mutexGroups.forEach((group) => {
165
161
  if (selectedKeys.filter((k) => group.includes(k)).length > 1) errors.push({
166
162
  type: "mutex",
@@ -210,10 +206,7 @@ function useSelectionController(props) {
210
206
  } else {
211
207
  if (existingIndex >= 0) newNormalizedValues[existingIndex] = _objectSpread2(_objectSpread2({}, newNormalizedValues[existingIndex]), data);
212
208
  else {
213
- const newItem = _objectSpread2({
214
- value: optionKey,
215
- quantity: data.quantity || 1
216
- }, data);
209
+ const newItem = _objectSpread2(_objectSpread2({ value: optionKey }, data), {}, { quantity: normalizeSelectionQuantity(data === null || data === void 0 ? void 0 : data.quantity) });
217
210
  if (mode === "single") newNormalizedValues = [newItem];
218
211
  else newNormalizedValues.push(newItem);
219
212
  }
@@ -8,6 +8,8 @@ var locales_default = {
8
8
  "pisell2.pro.selector.required-tag": "Required",
9
9
  "pisell2.pro.selector.validate.min-selection": (min, label) => `Select at least ${min}${label ? " " + label : ""}`,
10
10
  "pisell2.pro.selector.validate.max-selection": (max, label) => `Select at most ${max}${label ? " " + label : ""}`,
11
+ "pisell2.pro.selector.validate.min-types": (min) => `Select at least ${min} option types`,
12
+ "pisell2.pro.selector.validate.max-types": (max) => `Select at most ${max} option types`,
11
13
  "pisell2.pro.selector.tip.require-one-option": "Choose 1",
12
14
  "pisell2.pro.selector.tip.min-max-selection": (min, max) => {
13
15
  if (min === max && min > 0) return `Choose ${min}`;
@@ -25,6 +27,8 @@ var locales_default = {
25
27
  "pisell2.pro.selector.required-tag": "必选",
26
28
  "pisell2.pro.selector.validate.min-selection": (min, label) => `至少选 ${min} 个${label ? " " + label : ""}`,
27
29
  "pisell2.pro.selector.validate.max-selection": (max, label) => `最多选 ${max} 个${label ? " " + label : ""}`,
30
+ "pisell2.pro.selector.validate.min-types": (min) => `至少选择 ${min} 种选项`,
31
+ "pisell2.pro.selector.validate.max-types": (max) => `最多选择 ${max} 种选项`,
28
32
  "pisell2.pro.selector.tip.require-one-option": "需选 1 项",
29
33
  "pisell2.pro.selector.tip.min-max-selection": (min, max) => {
30
34
  if (min === max && min > 0) return `需选 ${min} 个`;
@@ -42,6 +46,8 @@ var locales_default = {
42
46
  "pisell2.pro.selector.required-tag": "必選",
43
47
  "pisell2.pro.selector.validate.min-selection": (min, label) => `至少選 ${min} 個${label ? " " + label : ""}`,
44
48
  "pisell2.pro.selector.validate.max-selection": (max, label) => `最多選 ${max} 個${label ? " " + label : ""}`,
49
+ "pisell2.pro.selector.validate.min-types": (min) => `至少選擇 ${min} 種選項`,
50
+ "pisell2.pro.selector.validate.max-types": (max) => `最多選擇 ${max} 種選項`,
45
51
  "pisell2.pro.selector.tip.require-one-option": "需選 1 項",
46
52
  "pisell2.pro.selector.tip.min-max-selection": (min, max) => {
47
53
  if (min === max && min > 0) return `需選 ${min} 個`;
@@ -59,6 +65,8 @@ var locales_default = {
59
65
  "pisell2.pro.selector.required-tag": "必須",
60
66
  "pisell2.pro.selector.validate.min-selection": (min, label) => `少なくとも${min}${label ? " " + label : ""}を選択`,
61
67
  "pisell2.pro.selector.validate.max-selection": (max, label) => `最大${max}${label ? " " + label : ""}を選択`,
68
+ "pisell2.pro.selector.validate.min-types": (min) => `少なくとも${min}種類のオプションを選択してください`,
69
+ "pisell2.pro.selector.validate.max-types": (max) => `選択できるオプションは最大${max}種類です`,
62
70
  "pisell2.pro.selector.tip.require-one-option": "1つ選択",
63
71
  "pisell2.pro.selector.tip.min-max-selection": (min, max) => {
64
72
  if (min === max && min > 0) return `${min}つ選択`;
@@ -76,6 +84,8 @@ var locales_default = {
76
84
  "pisell2.pro.selector.required-tag": "Obrigatório",
77
85
  "pisell2.pro.selector.validate.min-selection": (min, label) => `Selecione pelo menos ${min}${label ? " " + label : ""}`,
78
86
  "pisell2.pro.selector.validate.max-selection": (max, label) => `Selecione no máximo ${max}${label ? " " + label : ""}`,
87
+ "pisell2.pro.selector.validate.min-types": (min) => `Selecione pelo menos ${min} tipos de opção`,
88
+ "pisell2.pro.selector.validate.max-types": (max) => `Selecione no máximo ${max} tipos de opção`,
79
89
  "pisell2.pro.selector.tip.require-one-option": "Escolha 1",
80
90
  "pisell2.pro.selector.tip.min-max-selection": (min, max) => {
81
91
  if (min === max && min > 0) return `Escolha ${min}`;
@@ -8,6 +8,8 @@ interface RuleConfig {
8
8
  required?: 0 | 1;
9
9
  min?: number;
10
10
  max?: number;
11
+ minTypes?: number;
12
+ maxTypes?: number;
11
13
  mutex?: Array<Array<OptionKey>>;
12
14
  customValidator?: (value: SelectionValue) => Promise<void>;
13
15
  }
@@ -63,6 +65,9 @@ type RenderItemProps = {
63
65
  maxReached: boolean;
64
66
  remainingCount: number;
65
67
  selectedCount: number;
68
+ selectedTypeCount: number;
69
+ typeMaxReached: boolean;
70
+ remainingTypeCount: number;
66
71
  };
67
72
  };
68
73
  type ItemProps = {
@@ -1673,7 +1673,7 @@ const SkuDetailModal = ({ onConfirm, onRemove, onClose, variant }, ref) => {
1673
1673
  }, isMobile && (skuDetailConfig === null || skuDetailConfig === void 0 ? void 0 : skuDetailConfig.isShowProductSummary) ? showProductDetailsCollapsible ? /* @__PURE__ */ React.createElement(SkuDetailCollapsibleSection, {
1674
1674
  key: `details-${selectionFlowInstanceKey}`,
1675
1675
  title: locales.getText("pisell2.text.sku.detail.modal.section.details"),
1676
- defaultExpanded: false
1676
+ defaultExpanded: isKioskPlatform
1677
1677
  }, renderProductDetails()) : renderProductSummary() : null, shouldRenderMobileWeighing ? /* @__PURE__ */ React.createElement("div", { className: "sku-detail-mobile-weighing" }, renderWeighingSection()) : null, /* @__PURE__ */ React.createElement(PisellSelectionFlow, {
1678
1678
  key: selectionFlowInstanceKey,
1679
1679
  ref: selectionFlowRef,
@@ -1758,7 +1758,7 @@ const SkuDetailModal = ({ onConfirm, onRemove, onClose, variant }, ref) => {
1758
1758
  key: `details-${selectionFlowInstanceKey}`,
1759
1759
  className: "sku-detail-collapsible-section--product-details",
1760
1760
  title: locales.getText("pisell2.text.sku.detail.modal.section.details"),
1761
- defaultExpanded: false
1761
+ defaultExpanded: isKioskPlatform
1762
1762
  }, renderProductDetails()) : mobileProductDetails, shouldRenderMobileWeighing ? /* @__PURE__ */ React.createElement("div", { className: "sku-detail-mobile-weighing" }, renderWeighingSection()) : null, shouldRenderSkuSelection ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SKUOptionsSelection, {
1763
1763
  ref: skuOptionsSelectionRef,
1764
1764
  dataSource: productData,
@@ -1821,8 +1821,8 @@ const SkuDetailModal = ({ onConfirm, onRemove, onClose, variant }, ref) => {
1821
1821
  footer: variant === "phone" ? leftContent({ showTop: false }) : null,
1822
1822
  closable: false,
1823
1823
  destroyOnClose: true,
1824
- style: { maxHeight: variant === "phone" ? "99dvh" : void 0 },
1825
- height: variant === "phone" ? "auto" : void 0,
1824
+ style: { maxHeight: isKioskPlatform ? "100%" : variant === "phone" ? "99dvh" : void 0 },
1825
+ height: isKioskPlatform ? "100%" : variant === "phone" ? "auto" : void 0,
1826
1826
  styles: isKioskPlatform ? { content: { height: "100%" } } : void 0,
1827
1827
  width: variant === "phone" ? "100%" : "80%",
1828
1828
  className: classNames({
@@ -315,6 +315,7 @@ interface ProductData {
315
315
  over_sold: number;
316
316
  is_gst: number;
317
317
  code: string;
318
+ production_code?: string;
318
319
  barcode: string;
319
320
  is_delivery: number;
320
321
  weight_unit: WeightUnitName;
@@ -7,6 +7,7 @@ const require_useInitLocalesOnce = require("../../../../hooks/useInitLocalesOnce
7
7
  const require_index = require("../../../../plus/productSelect/ProductCard/index.js");
8
8
  const require_ProductCardV2 = require("../../../../plus/productSelect/ProductCard/ProductCardV2.js");
9
9
  const require_useLocale = require("../../../../hooks/useLocale.js");
10
+ const require_useLogger = require("../../../../hooks/useLogger.js");
10
11
  const require_List = require("../../../list/List.js");
11
12
  const require_utils$1 = require("../../../ticketBooking/components/ProductDisplayAdapter/utils.js");
12
13
  const require_showAddToCartToast = require("../../../../plus/salesSdk/components/showAddToCartToast.js");
@@ -136,6 +137,7 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
136
137
  addToCartToastOptions === null || addToCartToastOptions === void 0 ? void 0 : addToCartToastOptions.duration
137
138
  ]);
138
139
  const locale = require_useLocale.default();
140
+ const logger = require_useLogger.default();
139
141
  const { sales, products, cart } = context;
140
142
  const { load } = products;
141
143
  const [keyword, setKeyword] = (0, react.useState)("");
@@ -303,17 +305,26 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
303
305
  * handleSelectProduct(productItem);
304
306
  */
305
307
  const handleSelectProduct = (0, ahooks.useMemoizedFn)(function() {
306
- var _ref4 = require_asyncToGenerator._asyncToGenerator(function* (product) {
308
+ var _ref5 = require_asyncToGenerator._asyncToGenerator(function* (product) {
307
309
  var _ref3, _product$id;
308
310
  const handlesKioskToastAfterResult = productCardVersion === "v2" && !usesV2ProductCard;
309
311
  const productId = String((_ref3 = (_product$id = product === null || product === void 0 ? void 0 : product.id) !== null && _product$id !== void 0 ? _product$id : product === null || product === void 0 ? void 0 : product.product_id) !== null && _ref3 !== void 0 ? _ref3 : "");
310
312
  const baselineQuantity = phoneCartProductEntries.filter((entry) => resolvePhoneCartProductId(entry.value) === productId).reduce((total, entry) => total + entry.quantity, 0);
311
313
  try {
312
- var _sales$tempOrder3;
314
+ var _sales$tempOrder3, _logger$addLog, _ref4, _sales$tempOrder$orde, _sales$tempOrder4;
313
315
  if (!sales.isReady) return;
314
316
  if (requiredOrderId !== void 0 && Number((_sales$tempOrder3 = sales.tempOrder) === null || _sales$tempOrder3 === void 0 ? void 0 : _sales$tempOrder3.order_id) !== Number(requiredOrderId)) return;
315
317
  const latestProduct = currentProducts.find((currentProduct) => currentProduct.id === product.id);
316
318
  latestProduct && require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, product), {}, { cartDetailValue: latestProduct.cartDetailValue });
319
+ logger === null || logger === void 0 || (_logger$addLog = logger.addLog) === null || _logger$addLog === void 0 || _logger$addLog.call(logger, {
320
+ type: "info",
321
+ title: "[BigSaleProductList] 用户点击商品卡片加入购物车",
322
+ metadata: {
323
+ product_id: productId,
324
+ product_title: resolvePhoneProductTitle(product),
325
+ order_id: (_ref4 = (_sales$tempOrder$orde = (_sales$tempOrder4 = sales.tempOrder) === null || _sales$tempOrder4 === void 0 ? void 0 : _sales$tempOrder4.order_id) !== null && _sales$tempOrder$orde !== void 0 ? _sales$tempOrder$orde : requiredOrderId) !== null && _ref4 !== void 0 ? _ref4 : null
326
+ }
327
+ });
317
328
  const result = yield cart.addProductWithFlow(product, {
318
329
  quantity: 1,
319
330
  notShowToast: handlesKioskToastAfterResult
@@ -325,7 +336,7 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
325
336
  }
326
337
  });
327
338
  return function(_x) {
328
- return _ref4.apply(this, arguments);
339
+ return _ref5.apply(this, arguments);
329
340
  };
330
341
  }());
331
342
  const phoneCartProductEntries = (0, react.useMemo)(() => {
@@ -339,8 +350,8 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
339
350
  const productUid = resolvePhoneCartLineUid(booking === null || booking === void 0 || (_booking$_extend = booking._extend) === null || _booking$_extend === void 0 ? void 0 : _booking$_extend.product);
340
351
  if (productUid) bookingProductUids.add(productUid);
341
352
  ((booking === null || booking === void 0 || (_booking$_extend2 = booking._extend) === null || _booking$_extend2 === void 0 ? void 0 : _booking$_extend2.mergedFrom) || []).forEach((source) => {
342
- var _ref5, _source$booking$produ, _source$booking, _source$booking2;
343
- const sourceUid = (_ref5 = (_source$booking$produ = source === null || source === void 0 || (_source$booking = source.booking) === null || _source$booking === void 0 ? void 0 : _source$booking.product_uid) !== null && _source$booking$produ !== void 0 ? _source$booking$produ : source === null || source === void 0 || (_source$booking2 = source.booking) === null || _source$booking2 === void 0 || (_source$booking2 = _source$booking2.metadata) === null || _source$booking2 === void 0 ? void 0 : _source$booking2.product_uid) !== null && _ref5 !== void 0 ? _ref5 : resolvePhoneCartLineUid(source === null || source === void 0 ? void 0 : source.product);
353
+ var _ref6, _source$booking$produ, _source$booking, _source$booking2;
354
+ const sourceUid = (_ref6 = (_source$booking$produ = source === null || source === void 0 || (_source$booking = source.booking) === null || _source$booking === void 0 ? void 0 : _source$booking.product_uid) !== null && _source$booking$produ !== void 0 ? _source$booking$produ : source === null || source === void 0 || (_source$booking2 = source.booking) === null || _source$booking2 === void 0 || (_source$booking2 = _source$booking2.metadata) === null || _source$booking2 === void 0 ? void 0 : _source$booking2.product_uid) !== null && _ref6 !== void 0 ? _ref6 : resolvePhoneCartLineUid(source === null || source === void 0 ? void 0 : source.product);
344
355
  if (sourceUid !== void 0 && sourceUid !== null && sourceUid !== "") bookingProductUids.add(String(sourceUid));
345
356
  });
346
357
  });
@@ -354,10 +365,10 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
354
365
  quantity: resolvePhoneCartEntryQuantity(item)
355
366
  }));
356
367
  displayBookings.forEach((booking) => {
357
- var _booking$_extend3, _ref6, _booking$_extend$disp, _booking$_extend4, _product$_extend, _product$product_id;
368
+ var _booking$_extend3, _ref7, _booking$_extend$disp, _booking$_extend4, _product$_extend, _product$product_id;
358
369
  const product = booking === null || booking === void 0 || (_booking$_extend3 = booking._extend) === null || _booking$_extend3 === void 0 ? void 0 : _booking$_extend3.product;
359
370
  if (!product || (product === null || product === void 0 ? void 0 : product._isGiftPromotion)) return;
360
- const displayQuantity = (_ref6 = (_booking$_extend$disp = booking === null || booking === void 0 || (_booking$_extend4 = booking._extend) === null || _booking$_extend4 === void 0 ? void 0 : _booking$_extend4.displayQuantity) !== null && _booking$_extend$disp !== void 0 ? _booking$_extend$disp : product === null || product === void 0 || (_product$_extend = product._extend) === null || _product$_extend === void 0 ? void 0 : _product$_extend.displayQuantity) !== null && _ref6 !== void 0 ? _ref6 : product === null || product === void 0 ? void 0 : product.num;
371
+ const displayQuantity = (_ref7 = (_booking$_extend$disp = booking === null || booking === void 0 || (_booking$_extend4 = booking._extend) === null || _booking$_extend4 === void 0 ? void 0 : _booking$_extend4.displayQuantity) !== null && _booking$_extend$disp !== void 0 ? _booking$_extend$disp : product === null || product === void 0 || (_product$_extend = product._extend) === null || _product$_extend === void 0 ? void 0 : _product$_extend.displayQuantity) !== null && _ref7 !== void 0 ? _ref7 : product === null || product === void 0 ? void 0 : product.num;
361
372
  const displayValue = require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, product), {}, {
362
373
  product_id: (_product$product_id = product === null || product === void 0 ? void 0 : product.product_id) !== null && _product$product_id !== void 0 ? _product$product_id : product === null || product === void 0 ? void 0 : product.id,
363
374
  num: displayQuantity
@@ -401,9 +412,9 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
401
412
  show();
402
413
  });
403
414
  const addPhoneProductWithToast = (0, ahooks.useMemoizedFn)(function() {
404
- var _ref8 = require_asyncToGenerator._asyncToGenerator(function* (product, quantity, type = "select", delayUntilModalClosed = false) {
405
- var _ref7, _product$id2;
406
- const productId = String((_ref7 = (_product$id2 = product === null || product === void 0 ? void 0 : product.id) !== null && _product$id2 !== void 0 ? _product$id2 : product === null || product === void 0 ? void 0 : product.product_id) !== null && _ref7 !== void 0 ? _ref7 : "");
415
+ var _ref9 = require_asyncToGenerator._asyncToGenerator(function* (product, quantity, type = "select", delayUntilModalClosed = false) {
416
+ var _ref8, _product$id2;
417
+ const productId = String((_ref8 = (_product$id2 = product === null || product === void 0 ? void 0 : product.id) !== null && _product$id2 !== void 0 ? _product$id2 : product === null || product === void 0 ? void 0 : product.product_id) !== null && _ref8 !== void 0 ? _ref8 : "");
407
418
  const currentQuantity = phoneCartProductEntries.filter((entry) => resolvePhoneCartProductId(entry.value) === productId).reduce((total, entry) => total + entry.quantity, 0);
408
419
  const result = yield cart.addProductWithFlow(product, {
409
420
  type,
@@ -414,7 +425,7 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
414
425
  return result;
415
426
  });
416
427
  return function(_x2, _x3) {
417
- return _ref8.apply(this, arguments);
428
+ return _ref9.apply(this, arguments);
418
429
  };
419
430
  }());
420
431
  /**
@@ -425,10 +436,10 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
425
436
  * 这样不会为了“判断商品类型”预先渲染抽屉,同时保留异常商品数据的兜底能力。
426
437
  */
427
438
  const addPhoneQuantityProductWithToast = (0, ahooks.useMemoizedFn)(function() {
428
- var _ref10 = require_asyncToGenerator._asyncToGenerator(function* (product, quantity) {
429
- var _ref9, _product$id3;
439
+ var _ref11 = require_asyncToGenerator._asyncToGenerator(function* (product, quantity) {
440
+ var _ref10, _product$id3;
430
441
  if (!require_utils.isNormalProductByDurationSchedule(product)) return addPhoneProductWithToast(product, quantity);
431
- const productId = String((_ref9 = (_product$id3 = product === null || product === void 0 ? void 0 : product.id) !== null && _product$id3 !== void 0 ? _product$id3 : product === null || product === void 0 ? void 0 : product.product_id) !== null && _ref9 !== void 0 ? _ref9 : "");
442
+ const productId = String((_ref10 = (_product$id3 = product === null || product === void 0 ? void 0 : product.id) !== null && _product$id3 !== void 0 ? _product$id3 : product === null || product === void 0 ? void 0 : product.product_id) !== null && _ref10 !== void 0 ? _ref10 : "");
432
443
  const result = yield cart.addProduct(product, {
433
444
  type: "select",
434
445
  quantity
@@ -440,11 +451,11 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
440
451
  return addPhoneProductWithToast(product, quantity);
441
452
  });
442
453
  return function(_x4, _x5) {
443
- return _ref10.apply(this, arguments);
454
+ return _ref11.apply(this, arguments);
444
455
  };
445
456
  }());
446
457
  const handlePhoneProductCardChange = (0, ahooks.useMemoizedFn)(function() {
447
- var _ref11 = require_asyncToGenerator._asyncToGenerator(function* (nextValue, _intent, meta) {
458
+ var _ref12 = require_asyncToGenerator._asyncToGenerator(function* (nextValue, _intent, meta) {
448
459
  var _product$id4;
449
460
  const product = meta === null || meta === void 0 ? void 0 : meta.product;
450
461
  if (!product) return;
@@ -499,7 +510,7 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
499
510
  }
500
511
  });
501
512
  return function(_x6, _x7, _x8) {
502
- return _ref11.apply(this, arguments);
513
+ return _ref12.apply(this, arguments);
503
514
  };
504
515
  }());
505
516
  const { data: listData, listProps: configuredListProps, skuCardAllConfig } = productListConfig;
@@ -674,9 +685,9 @@ const BigSaleProductListInner = ({ isActive = true, osKey, productListPreference
674
685
  setActiveTab([]);
675
686
  });
676
687
  const handleKioskSearchClose = (0, ahooks.useMemoizedFn)(() => {
677
- var _ref12, _kioskSearchCategoryT;
688
+ var _ref13, _kioskSearchCategoryT;
678
689
  cancelPendingCategoryTransition();
679
- const categoryTabBeforeSearch = (_ref12 = (_kioskSearchCategoryT = kioskSearchCategoryTabRef.current) !== null && _kioskSearchCategoryT !== void 0 ? _kioskSearchCategoryT : lastCategoryTabRef.current) !== null && _ref12 !== void 0 ? _ref12 : defaultActiveTab;
690
+ const categoryTabBeforeSearch = (_ref13 = (_kioskSearchCategoryT = kioskSearchCategoryTabRef.current) !== null && _kioskSearchCategoryT !== void 0 ? _kioskSearchCategoryT : lastCategoryTabRef.current) !== null && _ref13 !== void 0 ? _ref13 : defaultActiveTab;
680
691
  kioskSearchCategoryTabRef.current = void 0;
681
692
  lastCategoryTabRef.current = categoryTabBeforeSearch;
682
693
  setKeyword("");
@@ -50,7 +50,7 @@ const BigSaleHostRuntimeBridge = ({ runtimeRef }) => {
50
50
  /**
51
51
  * BigSale 入口:只创建 SalesSdkProvider 运行环境,UI 由 ResponsiveBigSaleBase 承载。
52
52
  */
53
- const BigSale = ({ osKey = DEFAULT_OS_KEY, osModuleName, selectionFlowScene, lifecycleOwner, orderId, autoBootstrap, autoRestoreOnInit, restoreOrderAfterPaidCheckout, destroyOnUnmount, businessCode, bookingConfigParams, otherParams, rulesHooks, uiHosts, normalProductDetailConfig, mergeBookingLinesForDisplay, mode = "ticket", variant, tmpl, template, secondaryScreenDisplay, productLoadParams, productDisplayConfig, hideSelectFloorMapResourceAction, productCatalogSessionKey, productCatalogReady, saleDetailTemplate, saleDetailModuleConfig, autoCreate = true, enableTempOrderPersist, enableTempOrderSync = true, isActive = true, preserveProductCatalogOnInactiveCustomerClear, enableGlobalScanListener = false, promotionScanSuccessMessageKey, promotionItemIncreaseMessageKey, className, style, bootstrapSlot, saleDetailHeaderSlot, rightActionBarClose, overlaySlot, onOrderComplete, externalActionRequest, onExternalActionAccepted, onExternalPaymentSettled }) => {
53
+ const BigSale = ({ osKey = DEFAULT_OS_KEY, osModuleName, selectionFlowScene, lifecycleOwner, orderId, autoBootstrap, autoRestoreOnInit, restoreOrderAfterPaidCheckout, destroyOnUnmount, businessCode, bookingConfigParams, autoInitBookingContext, otherParams, rulesHooks, uiHosts, normalProductDetailConfig, mergeBookingLinesForDisplay, mode = "ticket", variant, tmpl, template, secondaryScreenDisplay, productLoadParams, productDisplayConfig, hideSelectFloorMapResourceAction, productCatalogSessionKey, productCatalogReady, saleDetailTemplate, saleDetailModuleConfig, autoCreate = true, enableTempOrderPersist, enableTempOrderSync = true, isActive = true, preserveProductCatalogOnInactiveCustomerClear, enableGlobalScanListener = false, promotionScanSuccessMessageKey, promotionItemIncreaseMessageKey, className, style, bootstrapSlot, saleDetailHeaderSlot, rightActionBarClose, overlaySlot, onOrderComplete, externalActionRequest, onExternalActionAccepted, onExternalPaymentSettled }) => {
54
54
  var _context$appHelper, _context$appHelper2, _context$appHelper3, _ref;
55
55
  const context = require_useEngineContext.default();
56
56
  const action = context === null || context === void 0 || (_context$appHelper = context.appHelper) === null || _context$appHelper === void 0 || (_context$appHelper = _context$appHelper.utils) === null || _context$appHelper === void 0 ? void 0 : _context$appHelper.action;
@@ -75,7 +75,7 @@ const BigSale = ({ osKey = DEFAULT_OS_KEY, osModuleName, selectionFlowScene, lif
75
75
  return resolvedTemplate === "kiosk" ? require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, selectionFlowConfig), {}, {
76
76
  initializeFullBookingContext: false,
77
77
  variant: "phone",
78
- selectorColumns: variant === "phone" ? 1 : selectionFlowConfig === null || selectionFlowConfig === void 0 ? void 0 : selectionFlowConfig.selectorColumns,
78
+ selectorColumns: (selectionFlowConfig === null || selectionFlowConfig === void 0 ? void 0 : selectionFlowConfig.presentationPlatform) === "kiosk" ? 2 : 1,
79
79
  isShowProductSummary: true,
80
80
  isShowCollapsibleSections: true,
81
81
  isShowMobileWeighing: true,
@@ -132,7 +132,7 @@ const BigSale = ({ osKey = DEFAULT_OS_KEY, osModuleName, selectionFlowScene, lif
132
132
  restoreOrderAfterPaidCheckout,
133
133
  businessCode,
134
134
  bookingConfigParams,
135
- autoInitBookingContext: false,
135
+ autoInitBookingContext,
136
136
  otherParams,
137
137
  shouldMergeProductLines,
138
138
  rulesHooks,
@@ -109,6 +109,7 @@ function useCartLineRowActions({ item, quantityLoading, cardAction, isShowRightA
109
109
  text: /* @__PURE__ */ react.default.createElement(require_index$2.default, {
110
110
  enablePriceAdjust: true,
111
111
  min: 1,
112
+ width: "max-content",
112
113
  className: "pisell-sale-detail-cart-items__quantity-selector",
113
114
  value: getBookingQuantity(item),
114
115
  disabled: quantityLoading,