@escapenavigator/hooks 1.10.130 → 1.10.132

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.
@@ -80,6 +80,17 @@ export type PlayersAndVariationPickerProps = {
80
80
  * excludes the current booking from occupancy calculations.
81
81
  */
82
82
  orderId?: number;
83
+ /**
84
+ * Fires whenever the internal `usePlayersAndVariationPicker` loading
85
+ * flag changes. Hosts can use it to show a full-screen busy overlay
86
+ * over their form (the player section + dependent UI like upsellings)
87
+ * while `/slots/details` is in flight on initial mount — without
88
+ * having to duplicate the hook call themselves.
89
+ *
90
+ * Starts `true` and flips to `false` once the first `/slots/details`
91
+ * resolves (success or error).
92
+ */
93
+ onLoadingChange?: (loading: boolean) => void;
83
94
  };
84
95
  /**
85
96
  * Single shared component that renders the "Variation + Players" picker used
@@ -62,9 +62,9 @@ const use_players_and_variation_picker_1 = require("../use-players-and-variation
62
62
  * buttons, kids row, price hint, save button) was reimplemented in three
63
63
  * places with subtle drift. This component is the single source of truth.
64
64
  */
65
- const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loadSlotDetails, onVariationChange, onVariationResolved, onDetailsLoaded, players = 0, child = 0, tariff: tariffOverride, currency, minPlayers, maxPlayersLimit, slotDiscount, error, t, onChange, onSave, orderId, }) => {
65
+ const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loadSlotDetails, onVariationChange, onVariationResolved, onDetailsLoaded, players = 0, child = 0, tariff: tariffOverride, currency, minPlayers, maxPlayersLimit, slotDiscount, error, t, onChange, onSave, orderId, onLoadingChange, }) => {
66
66
  var _a, _b;
67
- const { options: pickerOptions, selectedOptionId, selectedVariation, buildVariationChange, selectVariation, details, } = (0, use_players_and_variation_picker_1.usePlayersAndVariationPicker)({
67
+ const { loading: pickerLoading, options: pickerOptions, selectedOptionId, selectedVariation, buildVariationChange, selectVariation, details, } = (0, use_players_and_variation_picker_1.usePlayersAndVariationPicker)({
68
68
  slot,
69
69
  currentTariffId,
70
70
  currentDuration,
@@ -72,6 +72,17 @@ const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loa
72
72
  t: t,
73
73
  orderId,
74
74
  });
75
+ /**
76
+ * Прокидываем loading-сигнал наверх через ref'нутый колбэк, чтобы
77
+ * не зависеть от референциальной стабильности `onLoadingChange`
78
+ * со стороны хоста.
79
+ */
80
+ const onLoadingChangeRef = (0, react_1.useRef)(onLoadingChange);
81
+ onLoadingChangeRef.current = onLoadingChange;
82
+ (0, react_1.useEffect)(() => {
83
+ var _a;
84
+ (_a = onLoadingChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onLoadingChangeRef, pickerLoading);
85
+ }, [pickerLoading]);
75
86
  const onDetailsLoadedRef = (0, react_1.useRef)(onDetailsLoaded);
76
87
  const onVariationResolvedRef = (0, react_1.useRef)(onVariationResolved);
77
88
  onDetailsLoadedRef.current = onDetailsLoaded;
@@ -17,6 +17,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.usePlayersAndVariationPicker = void 0;
19
19
  const use_api_method_1 = require("@escapenavigator/services/dist/hooks/use-api-method");
20
+ const format_flex_duration_label_1 = require("@escapenavigator/utils/dist/format-flex-duration-label");
20
21
  const react_1 = require("react");
21
22
  __exportStar(require("./types"), exports);
22
23
  const variationKey = (variation) => { var _a; return (_a = variation.id) !== null && _a !== void 0 ? _a : `${variation.tariffId}-${variation.duration}`; };
@@ -35,8 +36,23 @@ const variationKey = (variation) => { var _a; return (_a = variation.id) !== nul
35
36
  const usePlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loadSlotDetails, t, orderId, }) => {
36
37
  const api = (0, use_api_method_1.useApiMethod)({ api: loadSlotDetails });
37
38
  const data = api[`${loadSlotDetails.displayName}Data`];
38
- const loading = !!api[`${loadSlotDetails.displayName}Loading`];
39
+ const apiLoading = !!api[`${loadSlotDetails.displayName}Loading`];
39
40
  const fetch = api[`${loadSlotDetails.displayName}Fetch`];
41
+ /**
42
+ * Guard against the one-frame visual flicker when the picker first
43
+ * mounts: `useApiMethod` initialises `loading` to `false`, so the
44
+ * very first render returns `loading=false` with empty `variations`
45
+ * — the host then briefly paints a "no options" / default-price
46
+ * row before our useEffect below fires the `/slots/details` request
47
+ * and `loading` flips to `true`.
48
+ *
49
+ * `firstFetchSettled` starts as `false` and only becomes `true` once
50
+ * the first `/slots/details` request actually resolves. Until then
51
+ * we report `loading=true` to the caller — the picker can render a
52
+ * skeleton/spinner for that entire window, with no empty-state
53
+ * flash.
54
+ */
55
+ const [firstFetchSettled, setFirstFetchSettled] = (0, react_1.useState)(false);
40
56
  (0, react_1.useEffect)(() => {
41
57
  if (!(slot === null || slot === void 0 ? void 0 : slot.id) || !slot.questroomId || !slot.date || !slot.start)
42
58
  return;
@@ -46,11 +62,16 @@ const usePlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration,
46
62
  date: slot.date,
47
63
  start: slot.start,
48
64
  orderId,
49
- });
65
+ })
66
+ .then(() => setFirstFetchSettled(true))
67
+ .catch(() => setFirstFetchSettled(true));
50
68
  // Only primitive slot fields — callers often pass inline `{ id, date, ... }`
51
69
  // objects that get a new reference every render; including `slot` here
52
70
  // re-triggers `/slots/details` on every parent render (ERR_INSUFFICIENT_RESOURCES).
53
71
  }, [slot === null || slot === void 0 ? void 0 : slot.id, slot === null || slot === void 0 ? void 0 : slot.questroomId, slot === null || slot === void 0 ? void 0 : slot.date, slot === null || slot === void 0 ? void 0 : slot.start, orderId, fetch]);
72
+ // До первого resolved-фетча — считаем `loading=true`, иначе host
73
+ // отрисует пустой picker в первый кадр.
74
+ const loading = !firstFetchSettled || apiLoading;
54
75
  const variations = (0, react_1.useMemo)(() => (data === null || data === void 0 ? void 0 : data.variations) || [], [data === null || data === void 0 ? void 0 : data.variations]);
55
76
  const isFlex = !!(data === null || data === void 0 ? void 0 : data.isFlex);
56
77
  const findCurrentVariation = (rows) => {
@@ -111,7 +132,7 @@ const usePlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration,
111
132
  const id = variationKey(variation);
112
133
  const isCurrent = id === selectedOptionId;
113
134
  const label = isFlex
114
- ? `${variation.duration} ${t('common:минут')}`
135
+ ? (0, format_flex_duration_label_1.formatFlexDurationLabel)(variation.duration, t)
115
136
  : variation.isDefault
116
137
  ? 'Default'
117
138
  : variation.title || `Tariff ${variation.tariffId}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@escapenavigator/hooks",
3
- "version": "1.10.130",
3
+ "version": "1.10.132",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -14,9 +14,9 @@
14
14
  "test": "jest"
15
15
  },
16
16
  "dependencies": {
17
- "@escapenavigator/services": "^1.10.138",
18
- "@escapenavigator/types": "^1.10.129",
19
- "@escapenavigator/utils": "^1.10.133",
17
+ "@escapenavigator/services": "^1.10.140",
18
+ "@escapenavigator/types": "^1.10.131",
19
+ "@escapenavigator/utils": "^1.10.135",
20
20
  "react": "19.2.6"
21
21
  },
22
22
  "peerDependencies": {
@@ -30,5 +30,5 @@
30
30
  "ts-jest": "^29.1.1",
31
31
  "typescript": "^5.6"
32
32
  },
33
- "gitHead": "25c61a2bd7e70a03406358540c8b2b7b2d76bd3a"
33
+ "gitHead": "cb2f3ab38ef5b8b2a38ab5b2c485e484a0cf9414"
34
34
  }