@escapenavigator/hooks 2.0.46 → 2.0.47
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/dist/lead-form/lead-field.js +6 -4
- package/dist/lead-form/lead-form.d.ts +2 -0
- package/dist/lead-form/lead-form.js +12 -12
- package/dist/players-and-variation-picker/index.d.ts +19 -0
- package/dist/players-and-variation-picker/index.js +80 -12
- package/dist/use-players-and-variation-picker/types.d.ts +12 -0
- package/package.json +5 -5
|
@@ -53,13 +53,15 @@ const LeadField = ({ field, value, error, language, profileCountry, questrooms,
|
|
|
53
53
|
case lead_field_type_enum_1.LeadFieldTypeEnum.DATE:
|
|
54
54
|
return withHint(react_1.default.createElement(ui_1.CalendarInput, { block: true, label: label, lang: language, value: String(value || ''), error: error, onChange: (_e, payload) => onChange(payload.value) }));
|
|
55
55
|
case lead_field_type_enum_1.LeadFieldTypeEnum.TIME:
|
|
56
|
-
return withHint(react_1.default.createElement(ui_1.Select, { block: true, optionsListWidth: 'field', label: label, options:
|
|
56
|
+
return withHint(react_1.default.createElement(ui_1.Select, { block: true, optionsListWidth: 'field', label: label, options: value && !TIME_OPTIONS.some((option) => option.key === String(value))
|
|
57
|
+
? [{ key: String(value), content: String(value) }, ...TIME_OPTIONS]
|
|
58
|
+
: TIME_OPTIONS, selected: String(value || ''), error: error, onChange: ({ selected }) => onChange(selected === null || selected === void 0 ? void 0 : selected.key) }));
|
|
57
59
|
case lead_field_type_enum_1.LeadFieldTypeEnum.GUESTS: {
|
|
58
60
|
const min = (_a = field.min) !== null && _a !== void 0 ? _a : 1;
|
|
59
61
|
const max = (_b = field.max) !== null && _b !== void 0 ? _b : 30;
|
|
60
62
|
const current = typeof value === 'number' ? value : min;
|
|
61
63
|
return (react_1.default.createElement(ui_1.FlexColumns, { columns: 1, gr: 8 },
|
|
62
|
-
react_1.default.createElement(ui_1.Typography.Text, { view: 'primary-small', weight: 'medium' }, label),
|
|
64
|
+
react_1.default.createElement(ui_1.Typography.Text, { view: 'primary-small', weight: 'medium', color: 'primary' }, label),
|
|
63
65
|
react_1.default.createElement("div", { className: index_module_css_1.default.stepper },
|
|
64
66
|
react_1.default.createElement("button", { type: 'button', className: index_module_css_1.default.stepperButton, "aria-label": t('lead.guestsMinus'), disabled: current <= min, onClick: () => onChange(Math.max(min, current - 1)) },
|
|
65
67
|
react_1.default.createElement(MinusM_1.default, null)),
|
|
@@ -77,14 +79,14 @@ const LeadField = ({ field, value, error, language, profileCountry, questrooms,
|
|
|
77
79
|
})), selected: String(value || ''), error: error, onChange: ({ selected }) => onChange(selected === null || selected === void 0 ? void 0 : selected.key) }));
|
|
78
80
|
case lead_field_type_enum_1.LeadFieldTypeEnum.RADIO:
|
|
79
81
|
return (react_1.default.createElement(ui_1.FlexColumns, { columns: 1, gr: 8 },
|
|
80
|
-
react_1.default.createElement(ui_1.Typography.Text, { view: 'primary-small', weight: 'medium' }, label),
|
|
82
|
+
react_1.default.createElement(ui_1.Typography.Text, { view: 'primary-small', weight: 'medium', color: 'primary' }, label),
|
|
81
83
|
activeOptions.map((option) => (react_1.default.createElement(ui_1.Radio, { key: option.id, label: (0, lib_1.optionLabel)(option, language, option.id), checked: value === option.id, onChange: () => onChange(option.id) }))),
|
|
82
84
|
hint,
|
|
83
85
|
!!error && (react_1.default.createElement(ui_1.Typography.Text, { view: 'caps', color: 'negative' }, error))));
|
|
84
86
|
case lead_field_type_enum_1.LeadFieldTypeEnum.CHECKBOX: {
|
|
85
87
|
const selected = Array.isArray(value) ? value : [];
|
|
86
88
|
return (react_1.default.createElement(ui_1.FlexColumns, { columns: 1, gr: 8 },
|
|
87
|
-
react_1.default.createElement(ui_1.Typography.Text, { view: 'primary-small', weight: 'medium' }, label),
|
|
89
|
+
react_1.default.createElement(ui_1.Typography.Text, { view: 'primary-small', weight: 'medium', color: 'primary' }, label),
|
|
88
90
|
activeOptions.map((option) => (react_1.default.createElement(ui_1.Checkbox, { key: option.id, label: (0, lib_1.optionLabel)(option, language, option.id), checked: selected.includes(option.id), onChange: () => onChange(selected.includes(option.id)
|
|
89
91
|
? selected.filter((item) => item !== option.id)
|
|
90
92
|
: [...selected, option.id]) }))),
|
|
@@ -39,6 +39,8 @@ type Props = {
|
|
|
39
39
|
/** Что делать после успеха: модалка закрывается, страница остаётся. */
|
|
40
40
|
onDone?: () => void;
|
|
41
41
|
doneLabel?: string;
|
|
42
|
+
/** Контейнер прячет свою шапку, чтобы экран успеха не конкурировал с названием. */
|
|
43
|
+
onSuccess?: () => void;
|
|
42
44
|
};
|
|
43
45
|
export declare const LeadForm: React.FC<Props>;
|
|
44
46
|
export {};
|
|
@@ -47,14 +47,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
47
47
|
};
|
|
48
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
49
|
exports.LeadForm = void 0;
|
|
50
|
-
const CheckmarkCircleFillM_1 = __importDefault(require("@alphakits/icons/dist/CheckmarkCircleFillM"));
|
|
51
50
|
const ui_1 = require("@alphakits/ui");
|
|
52
51
|
const react_1 = __importStar(require("react"));
|
|
53
52
|
const index_module_css_1 = __importDefault(require("./index.module.css"));
|
|
54
53
|
const lead_field_1 = require("./lead-field");
|
|
55
54
|
const lib_1 = require("./lib");
|
|
56
55
|
const use_lead_t_1 = require("./use-lead-t");
|
|
57
|
-
const LeadForm = ({ form, source, language, profileCountry, submit, prefill, contextNote, withHeader = true, onDone, doneLabel, }) => {
|
|
56
|
+
const LeadForm = ({ form, source, language, profileCountry, submit, prefill, contextNote, withHeader = true, onDone, doneLabel, onSuccess, }) => {
|
|
58
57
|
const t = (0, use_lead_t_1.useLeadT)();
|
|
59
58
|
const [values, setValues] = (0, react_1.useState)(() => (0, lib_1.buildInitialValues)(form, prefill));
|
|
60
59
|
const [errors, setErrors] = (0, react_1.useState)({});
|
|
@@ -95,23 +94,24 @@ const LeadForm = ({ form, source, language, profileCountry, submit, prefill, con
|
|
|
95
94
|
return;
|
|
96
95
|
}
|
|
97
96
|
setSuccess(response.data);
|
|
97
|
+
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess();
|
|
98
98
|
});
|
|
99
99
|
if (success) {
|
|
100
|
-
return (react_1.default.createElement("div", { className: index_module_css_1.default.success },
|
|
101
|
-
react_1.default.createElement(
|
|
102
|
-
react_1.default.createElement("
|
|
103
|
-
react_1.default.createElement(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
return (react_1.default.createElement("div", { className: index_module_css_1.default.success, role: 'status' },
|
|
101
|
+
react_1.default.createElement("div", { className: index_module_css_1.default.successIcon, "aria-hidden": 'true' },
|
|
102
|
+
react_1.default.createElement("svg", { width: '28', height: '28', viewBox: '0 0 24 24', fill: 'none', "aria-hidden": 'true' },
|
|
103
|
+
react_1.default.createElement("path", { d: 'M5 12.5l4.2 4.2L19 7', stroke: 'currentColor', strokeWidth: '2.4', strokeLinecap: 'round', strokeLinejoin: 'round' }))),
|
|
104
|
+
react_1.default.createElement("div", { className: index_module_css_1.default.successCopy },
|
|
105
|
+
react_1.default.createElement(ui_1.Typography.Title, { tag: 'h1', view: 'small', weight: 'bold' }, (0, lib_1.localized)(success.successTitle, language) || t('lead.successTitle')),
|
|
106
|
+
react_1.default.createElement(ui_1.Typography.Text, { view: 'primary-medium', color: 'secondary' }, (0, lib_1.localized)(success.successDescription, language) || t('lead.successText'))),
|
|
107
|
+
!!onDone && (react_1.default.createElement("div", { className: index_module_css_1.default.successActions },
|
|
108
|
+
react_1.default.createElement(ui_1.Button, { view: 'primary', size: 's', block: true, onClick: onDone }, doneLabel || t('lead.close'))))));
|
|
109
109
|
}
|
|
110
110
|
const title = (0, lib_1.localized)(form.clientTitle, language) || t('lead.title');
|
|
111
111
|
const description = (0, lib_1.localized)(form.description, language);
|
|
112
112
|
return (react_1.default.createElement(ui_1.FlexColumns, { columns: 1, gr: 16 },
|
|
113
113
|
withHeader && (react_1.default.createElement(ui_1.FlexColumns, { columns: 1, gr: 4 },
|
|
114
|
-
react_1.default.createElement(ui_1.Typography.Title, { tag: '
|
|
114
|
+
react_1.default.createElement(ui_1.Typography.Title, { tag: 'h1', view: 'small', weight: 'bold' }, title),
|
|
115
115
|
!!description && (react_1.default.createElement(ui_1.Typography.Text, { view: 'primary-small', color: 'secondary' }, description)))),
|
|
116
116
|
!!formError && react_1.default.createElement(ui_1.ToastPlate, { view: 'negative' }, formError),
|
|
117
117
|
form.fields
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ProfileCurrencyEnum } from '@escapenavigator/types/dist/profile/enum/profile-currency';
|
|
2
2
|
import { SlotDiscountTypeEnum } from '@escapenavigator/types/dist/slot/enum/slot-discount-type.enum';
|
|
3
|
+
import { TariffReducedRateModeEnum } from '@escapenavigator/types/dist/tariff/enum/tariff-reduced-rate-mode.enum';
|
|
3
4
|
import React from 'react';
|
|
4
5
|
import { usePlayersAndVariationPicker } from '../use-players-and-variation-picker';
|
|
5
6
|
import { PickerSlotDetails, PickerSlotInfo, PickerSlotVariation, PickerVariationChange } from '../use-players-and-variation-picker/types';
|
|
@@ -39,6 +40,23 @@ export type PlayersAndVariationPickerProps = {
|
|
|
39
40
|
players: number;
|
|
40
41
|
/** Children form field value (defaults to 0). */
|
|
41
42
|
child?: number;
|
|
43
|
+
/** Сколько мест выбрано по льготной ставке (школьники и т.п.). */
|
|
44
|
+
reducedPlayers?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Льготная ставка и минимальная стоимость игры. По умолчанию берутся из
|
|
47
|
+
* выбранной вариации (`/slots/details`). Явно передают там, где условия
|
|
48
|
+
* нужно взять со снимка на заказе, а не с текущего тарифа — карточка уже
|
|
49
|
+
* оформленной брони.
|
|
50
|
+
*/
|
|
51
|
+
reduced?: {
|
|
52
|
+
minGameAmount?: number | null;
|
|
53
|
+
reducedRateMode?: TariffReducedRateModeEnum | null;
|
|
54
|
+
reducedRate?: number | null;
|
|
55
|
+
reducedTitle?: string | null;
|
|
56
|
+
reducedPriceDescription?: string | null;
|
|
57
|
+
};
|
|
58
|
+
/** Fires when the guest changes the number of reduced seats. */
|
|
59
|
+
onReducedChange?: (value: number) => void;
|
|
42
60
|
/**
|
|
43
61
|
* Override for the price map used to render player buttons. Defaults to
|
|
44
62
|
* the selected variation's tariff; pass an explicit value when the host
|
|
@@ -78,6 +96,7 @@ export type PlayersAndVariationPickerProps = {
|
|
|
78
96
|
onSave?: (data: {
|
|
79
97
|
players: number;
|
|
80
98
|
children: number;
|
|
99
|
+
reducedPlayers: number;
|
|
81
100
|
}) => void;
|
|
82
101
|
/**
|
|
83
102
|
* When editing an existing order, pass the order id here so the backend
|
|
@@ -52,6 +52,7 @@ const format_amount_1 = require("@escapenavigator/utils/dist/format-amount");
|
|
|
52
52
|
const get_players_price_1 = require("@escapenavigator/utils/dist/get-players-price");
|
|
53
53
|
const get_prices_1 = require("@escapenavigator/utils/dist/get-prices");
|
|
54
54
|
const get_slot_discount_amount_1 = require("@escapenavigator/utils/dist/get-slot-discount-amount");
|
|
55
|
+
const reduced_price_1 = require("@escapenavigator/utils/dist/reduced-price");
|
|
55
56
|
const react_1 = __importStar(require("react"));
|
|
56
57
|
const react_i18next_1 = require("react-i18next");
|
|
57
58
|
const use_players_and_variation_picker_1 = require("../use-players-and-variation-picker");
|
|
@@ -64,8 +65,8 @@ const use_players_and_variation_picker_1 = require("../use-players-and-variation
|
|
|
64
65
|
* buttons, kids row, price hint, save button) was reimplemented in three
|
|
65
66
|
* places with subtle drift. This component is the single source of truth.
|
|
66
67
|
*/
|
|
67
|
-
const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loadSlotDetails, onVariationChange, onVariationResolved, onDetailsLoaded, players = 0, child = 0, tariff: tariffOverride, currency, minPlayers, maxPlayersLimit, slotDiscount, slotDiscountType, error, t, onChange, onSave, orderId, onLoadingChange, hidePriceHints = false, flexPlayersOption, }) => {
|
|
68
|
-
var _a, _b;
|
|
68
|
+
const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loadSlotDetails, onVariationChange, onVariationResolved, onDetailsLoaded, players = 0, child = 0, reducedPlayers = 0, reduced: reducedOverride, onReducedChange, tariff: tariffOverride, currency, minPlayers, maxPlayersLimit, slotDiscount, slotDiscountType, error, t, onChange, onSave, orderId, onLoadingChange, hidePriceHints = false, flexPlayersOption, }) => {
|
|
69
|
+
var _a, _b, _c, _d, _e, _f;
|
|
69
70
|
/**
|
|
70
71
|
* Страховка от «голых ключей» перевода. Пикер рендерит `prices:*` /
|
|
71
72
|
* `common:*` через переданный хостом `t`, но сам хост часто берёт
|
|
@@ -132,12 +133,16 @@ const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loa
|
|
|
132
133
|
]);
|
|
133
134
|
const [_players, setPlayers] = (0, react_1.useState)(players);
|
|
134
135
|
const [_children, setChildren] = (0, react_1.useState)(child);
|
|
136
|
+
const [_reduced, setReduced] = (0, react_1.useState)(reducedPlayers);
|
|
135
137
|
(0, react_1.useEffect)(() => {
|
|
136
138
|
setPlayers(players);
|
|
137
139
|
}, [players]);
|
|
138
140
|
(0, react_1.useEffect)(() => {
|
|
139
141
|
setChildren(child);
|
|
140
142
|
}, [child]);
|
|
143
|
+
(0, react_1.useEffect)(() => {
|
|
144
|
+
setReduced(reducedPlayers);
|
|
145
|
+
}, [reducedPlayers]);
|
|
141
146
|
const flexSelected = !!(flexPlayersOption === null || flexPlayersOption === void 0 ? void 0 : flexPlayersOption.selected);
|
|
142
147
|
const handleChangePlayers = (value) => {
|
|
143
148
|
setPlayers(value);
|
|
@@ -215,11 +220,46 @@ const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loa
|
|
|
215
220
|
const maxChildren = maxFromTariff + 1;
|
|
216
221
|
return Array.from({ length: maxChildren }, (_, i) => i);
|
|
217
222
|
}, [tariff, maxFromTariff]);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
+
// Условия льготной ставки: снимок с брони (если хост его передал) важнее
|
|
224
|
+
// текущего тарифа — иначе повышение цен переоценило бы открытую карточку.
|
|
225
|
+
const reducedConfig = (0, react_1.useMemo)(() => {
|
|
226
|
+
var _a, _b, _c, _d, _e, _f;
|
|
227
|
+
return ({
|
|
228
|
+
minGameAmount: (_b = (_a = reducedOverride === null || reducedOverride === void 0 ? void 0 : reducedOverride.minGameAmount) !== null && _a !== void 0 ? _a : selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.minGameAmount) !== null && _b !== void 0 ? _b : 0,
|
|
229
|
+
reducedRateMode: (_d = (_c = reducedOverride === null || reducedOverride === void 0 ? void 0 : reducedOverride.reducedRateMode) !== null && _c !== void 0 ? _c : selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.reducedRateMode) !== null && _d !== void 0 ? _d : null,
|
|
230
|
+
reducedRate: (_f = (_e = reducedOverride === null || reducedOverride === void 0 ? void 0 : reducedOverride.reducedRate) !== null && _e !== void 0 ? _e : selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.reducedRate) !== null && _f !== void 0 ? _f : 0,
|
|
231
|
+
});
|
|
232
|
+
}, [
|
|
233
|
+
reducedOverride === null || reducedOverride === void 0 ? void 0 : reducedOverride.minGameAmount,
|
|
234
|
+
reducedOverride === null || reducedOverride === void 0 ? void 0 : reducedOverride.reducedRateMode,
|
|
235
|
+
reducedOverride === null || reducedOverride === void 0 ? void 0 : reducedOverride.reducedRate,
|
|
236
|
+
selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.minGameAmount,
|
|
237
|
+
selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.reducedRateMode,
|
|
238
|
+
selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.reducedRate,
|
|
239
|
+
]);
|
|
240
|
+
const reducedTitle = (_b = (_a = reducedOverride === null || reducedOverride === void 0 ? void 0 : reducedOverride.reducedTitle) !== null && _a !== void 0 ? _a : selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.reducedTitle) !== null && _b !== void 0 ? _b : '';
|
|
241
|
+
const reducedDescription = ((_d = (_c = reducedOverride === null || reducedOverride === void 0 ? void 0 : reducedOverride.reducedPriceDescription) !== null && _c !== void 0 ? _c : selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.reducedPriceDescription) !== null && _d !== void 0 ? _d : '').trim();
|
|
242
|
+
const hasReducedRate = !!reducedConfig.reducedRateMode && !!reducedConfig.reducedRate;
|
|
243
|
+
// Лимит льготных мест не приходит с бэка числом: он выводится из
|
|
244
|
+
// минимальной стоимости игры той же формулой, которой бэк посчитает цену.
|
|
245
|
+
const maxReduced = (0, react_1.useMemo)(() => hasReducedRate
|
|
246
|
+
? (0, reduced_price_1.getMaxReducedPlayers)(Object.assign({ price: tariff, players: _players }, reducedConfig))
|
|
247
|
+
: 0, [hasReducedRate, tariff, _players, reducedConfig]);
|
|
248
|
+
const reducedCounts = (0, react_1.useMemo)(() => Array.from({ length: maxReduced + 1 }, (_, index) => index), [maxReduced]);
|
|
249
|
+
const handleChangeReduced = (value) => {
|
|
250
|
+
setReduced(value);
|
|
251
|
+
onReducedChange === null || onReducedChange === void 0 ? void 0 : onReducedChange(value);
|
|
252
|
+
};
|
|
253
|
+
// Состав уменьшился (или сменилась вариация) — вчерашние 3 льготных места
|
|
254
|
+
// могут больше не влезать в минимум. Подтягиваем вниз, иначе бэк отдал бы
|
|
255
|
+
// `reducedPlayersLimitExceeded` уже на сохранении.
|
|
256
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: односторонний clamp
|
|
257
|
+
(0, react_1.useEffect)(() => {
|
|
258
|
+
if (_reduced > maxReduced)
|
|
259
|
+
handleChangeReduced(maxReduced);
|
|
260
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
261
|
+
}, [maxReduced, _reduced]);
|
|
262
|
+
const teamPriceBase = (0, get_players_price_1.getPlayersPrice)(Object.assign({ players: _players, children: _children, price: tariff, reducedPlayers: Math.min(_reduced, maxReduced) }, reducedConfig));
|
|
223
263
|
const teamPrice = teamPriceBase -
|
|
224
264
|
(0, get_slot_discount_amount_1.getSlotDiscountAmount)({
|
|
225
265
|
price: teamPriceBase,
|
|
@@ -231,13 +271,13 @@ const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loa
|
|
|
231
271
|
const personPrice = totalHeads > 0 ? Math.round(teamPrice / totalHeads / 100) * 100 : 0;
|
|
232
272
|
// Описание цены за детей показываем только когда дети реально выбраны.
|
|
233
273
|
const childDescription = _children
|
|
234
|
-
? (
|
|
274
|
+
? (_e = selectedVariation === null || selectedVariation === void 0 ? void 0 : selectedVariation.childPriceDescription) === null || _e === void 0 ? void 0 : _e.trim()
|
|
235
275
|
: undefined;
|
|
236
276
|
const showVariations = pickerOptions.length > 1;
|
|
237
277
|
// Клиентское описание показываем только для вариаций — у стандартного
|
|
238
278
|
// (default) тарифа описание не выводим.
|
|
239
279
|
const variationDescription = selectedVariation && !selectedVariation.isDefault
|
|
240
|
-
? ((
|
|
280
|
+
? ((_f = selectedVariation.description) !== null && _f !== void 0 ? _f : null)
|
|
241
281
|
: null;
|
|
242
282
|
const handleVariationClick = (optionId, isFree) => {
|
|
243
283
|
if (!isFree)
|
|
@@ -301,13 +341,35 @@ const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loa
|
|
|
301
341
|
const isActive = _children === count;
|
|
302
342
|
return (react_1.default.createElement(tag_1.Tag, { key: `child-${count}`, text: count === 0 ? t('prices:noKids') : `${count}`, onClick: () => handleChangeChildren(isActive ? 0 : count), view: isActive ? 'primary-inverted' : 'primary' }));
|
|
303
343
|
})))),
|
|
344
|
+
!flexSelected && hasReducedRate && !!_players && (react_1.default.createElement(flex_columns_1.FlexColumns, { columns: 1, gr: 8 },
|
|
345
|
+
react_1.default.createElement(flex_1.Flex, { gap: 'sm', align: 'center', justify: 'between' },
|
|
346
|
+
react_1.default.createElement(typography_1.Typography.Text, { view: 'title', color: 'secondary' }, reducedTitle ||
|
|
347
|
+
t('prices:reducedPlayers', { defaultValue: 'Льготные места' })),
|
|
348
|
+
!!_reduced && (react_1.default.createElement(typography_1.Typography.Text, { view: 'title', color: 'secondary' }, `− ${(0, format_amount_1.formatAmount)(Math.min(_reduced, maxReduced) *
|
|
349
|
+
(0, reduced_price_1.getReducedSeatDiscount)(Object.assign({ price: tariff, players: _players }, reducedConfig)), currency)}`))),
|
|
350
|
+
maxReduced > 0 ? (react_1.default.createElement(flex_1.Flex, { gap: 'xs', wrap: true, justify: 'start' }, reducedCounts.map((count) => {
|
|
351
|
+
const isActive = Math.min(_reduced, maxReduced) === count;
|
|
352
|
+
return (react_1.default.createElement(tag_1.Tag, { key: `reduced-${count}`, text: count === 0
|
|
353
|
+
? t('prices:noReducedPlayers', {
|
|
354
|
+
defaultValue: 'Нет',
|
|
355
|
+
})
|
|
356
|
+
: `${count}`, onClick: () => handleChangeReduced(isActive ? 0 : count), view: isActive ? 'primary-inverted' : 'primary' }));
|
|
357
|
+
}))) : (react_1.default.createElement(typography_1.Typography.Text, { view: 'caps', color: 'secondary' }, t('prices:reducedNotAvailable', {
|
|
358
|
+
players: _players,
|
|
359
|
+
minAmount: (0, format_amount_1.formatAmount)(reducedConfig.minGameAmount || 0, currency),
|
|
360
|
+
defaultValue: 'На такой состав льготных мест нет: игра не может стоить меньше {{minAmount}}.',
|
|
361
|
+
}))),
|
|
362
|
+
!hidePriceHints && !!reducedDescription && !!_reduced && (react_1.default.createElement("div", { style: { whiteSpace: 'pre-line' } },
|
|
363
|
+
react_1.default.createElement(typography_1.Typography.Text, { view: 'caps', color: 'secondary' }, reducedDescription))))),
|
|
304
364
|
!hidePriceHints && !flexSelected && !!totalHeads && (react_1.default.createElement(flex_1.Flex, { justify: 'start', gap: 'xs' },
|
|
305
365
|
react_1.default.createElement(InfoMarkS_1.default, { style: { fill: 'var(--color-text-secondary)' } }),
|
|
306
366
|
react_1.default.createElement(typography_1.Typography.Text, { view: 'caps', color: 'secondary' }, t('prices:totalPriceDescription', {
|
|
307
367
|
teamPrice: (0, format_amount_1.formatAmount)(teamPrice, currency),
|
|
308
368
|
personPrice: (0, format_amount_1.formatAmount)(personPrice, currency),
|
|
309
369
|
})))),
|
|
310
|
-
hidePriceHints &&
|
|
370
|
+
hidePriceHints &&
|
|
371
|
+
!flexSelected &&
|
|
372
|
+
(!!totalHeads || !!childDescription || (!!_reduced && !!reducedDescription)) && (react_1.default.createElement("div", { style: {
|
|
311
373
|
display: 'flex',
|
|
312
374
|
flexDirection: 'column',
|
|
313
375
|
gap: 6,
|
|
@@ -323,8 +385,14 @@ const PlayersAndVariationPicker = ({ slot, currentTariffId, currentDuration, loa
|
|
|
323
385
|
personPrice: (0, format_amount_1.formatAmount)(personPrice, currency),
|
|
324
386
|
})))),
|
|
325
387
|
!!childDescription && (react_1.default.createElement("div", { style: { whiteSpace: 'pre-line' } },
|
|
326
|
-
react_1.default.createElement(typography_1.Typography.Text, { view: 'caps', color: 'secondary' }, childDescription)))
|
|
388
|
+
react_1.default.createElement(typography_1.Typography.Text, { view: 'caps', color: 'secondary' }, childDescription))),
|
|
389
|
+
!!_reduced && !!reducedDescription && (react_1.default.createElement("div", { style: { whiteSpace: 'pre-line' } },
|
|
390
|
+
react_1.default.createElement(typography_1.Typography.Text, { view: 'caps', color: 'secondary' }, reducedDescription))))),
|
|
327
391
|
!!error && (react_1.default.createElement(toast_plate_1.ToastPlate, { view: 'negative' }, error || t('prices:playersError', { minPlayers, maxPlayers: effectiveMax }))),
|
|
328
|
-
!!onSave && (react_1.default.createElement(button_1.Button, { onClick: () => onSave({
|
|
392
|
+
!!onSave && (react_1.default.createElement(button_1.Button, { onClick: () => onSave({
|
|
393
|
+
players: _players,
|
|
394
|
+
children: _children,
|
|
395
|
+
reducedPlayers: Math.min(_reduced, maxReduced),
|
|
396
|
+
}), view: 'primary', size: 'xs' }, t('common:save')))));
|
|
329
397
|
};
|
|
330
398
|
exports.PlayersAndVariationPicker = PlayersAndVariationPicker;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* (`OpenapiSlotRO`/`OpenapiSlotEnrichedRO`) contracts so a single picker can
|
|
6
6
|
* be plugged into either context.
|
|
7
7
|
*/
|
|
8
|
+
import { TariffReducedRateModeEnum } from '@escapenavigator/types/dist/tariff/enum/tariff-reduced-rate-mode.enum';
|
|
8
9
|
export type PickerSlotVariation = {
|
|
9
10
|
/**
|
|
10
11
|
* Stable picker key returned by the backend (`${tariffId}-${duration}`).
|
|
@@ -23,6 +24,17 @@ export type PickerSlotVariation = {
|
|
|
23
24
|
[k: string]: number;
|
|
24
25
|
};
|
|
25
26
|
childPriceDescription?: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Минимальная стоимость игры и льготная ставка тарифа. Лимит льготных мест
|
|
29
|
+
* пикер считает сам (`getMaxReducedPlayers`) — той же формулой, которой бэк
|
|
30
|
+
* посчитает цену, поэтому «сколько мест влезает» не может разойтись между
|
|
31
|
+
* UI и броней.
|
|
32
|
+
*/
|
|
33
|
+
minGameAmount?: number;
|
|
34
|
+
reducedRateMode?: TariffReducedRateModeEnum | null;
|
|
35
|
+
reducedRate?: number;
|
|
36
|
+
reducedTitle?: string | null;
|
|
37
|
+
reducedPriceDescription?: string | null;
|
|
26
38
|
minPlayers: number;
|
|
27
39
|
maxPlayers: number;
|
|
28
40
|
numSeatsAvailable: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@escapenavigator/hooks",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.47",
|
|
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": "^2.0.
|
|
18
|
-
"@escapenavigator/types": "^2.0.
|
|
19
|
-
"@escapenavigator/utils": "^2.0.
|
|
17
|
+
"@escapenavigator/services": "^2.0.47",
|
|
18
|
+
"@escapenavigator/types": "^2.0.45",
|
|
19
|
+
"@escapenavigator/utils": "^2.0.47",
|
|
20
20
|
"libphonenumber-js": "^1.12.24",
|
|
21
21
|
"react": "19.2.6",
|
|
22
22
|
"react-phone-input-2": "^2.15.1"
|
|
@@ -32,5 +32,5 @@
|
|
|
32
32
|
"ts-jest": "^29.1.1",
|
|
33
33
|
"typescript": "^5.6"
|
|
34
34
|
},
|
|
35
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "cd6ca16767c8a03b4deded5c6abc8943b02ce1ee"
|
|
36
36
|
}
|