feat(promocodes): состав промокода чекбоксами — баланс / дни / промогруппа

Форма создания промокода переведена с жёсткого выбора одного типа на режим
«Набор бонусов»: галочками выбирается, что входит в промокод — бонус на
баланс, дни подписки, промогруппа — в любой комбинации. Тип для бэкенда
выводится из выбора (balance / subscription_days / balance_and_days /
promo_group), промогруппа едет через promo_group_id при любом типе (бэкенд
назначает её при активации независимо от типа).

Триал и процентная скидка остаются отдельными режимами: скидка
переиспользует те же колонки с другой семантикой (процент/часы) и не
комбинируется без миграции.

- список и статистика: лейбл/цвет нового типа balance_and_days, у составных
  кодов видны оба значения (+руб и +дни)
- edit-режим восстанавливает чекбоксы из типа и promo_group_id
- унифицирована валидация формы (isValid = список ошибок пуст; для скидки
  0 часов = «до первой покупки» — разрешено, как и было в isValid)
- локали ru/en/fa/zh

Парный PR бэкенда: remnawave-bedolaga-telegram-bot (тип BALANCE_AND_DAYS,
порядок эффектов дни→баланс, валидации, бот-визард).
This commit is contained in:
Case211
2026-07-14 08:52:06 +05:00
parent d7ea3c0635
commit 9ce838bade
8 changed files with 162 additions and 56 deletions

View File

@@ -3133,7 +3133,8 @@
"subscriptionDays": "Subscription days",
"trialSubscription": "Trial subscription",
"promoGroup": "Discount group",
"discount": "Discount %"
"discount": "Discount %",
"balanceAndDays": "Balance + days"
},
"modal": {
"editPromocode": "Edit promo code",
@@ -3151,6 +3152,11 @@
"typeTrialSubscription": "Trial subscription",
"typePromoGroup": "Discount group",
"typeDiscount": "Percentage discount",
"typeBonusSet": "Bonus set (balance / days / promo group)",
"bonusComposition": "Promo code contents",
"includeBalance": "Balance bonus",
"includeDays": "Subscription days",
"includePromoGroup": "Promo group",
"bonusAmount": "Bonus amount",
"rub": "RUB",
"daysCount": "Number of days",
@@ -3242,6 +3248,7 @@
},
"validation": {
"codeRequired": "Enter promo code",
"bonusSetEmpty": "Select at least one bonus for the promo code",
"balanceRequired": "Bonus amount must be greater than 0",
"daysRequired": "Number of days must be greater than 0",
"groupRequired": "Select a discount group",

View File

@@ -2669,7 +2669,8 @@
"subscriptionDays": "روزهای اشتراک",
"trialSubscription": "اشتراک آزمایشی",
"promoGroup": "گروه تخفیف",
"discount": "تخفیف %"
"discount": "تخفیف %",
"balanceAndDays": "موجودی + روزها"
},
"modal": {
"editPromocode": "ویرایش کد تخفیف",
@@ -2687,6 +2688,11 @@
"typeTrialSubscription": "اشتراک آزمایشی",
"typePromoGroup": "گروه تخفیف",
"typeDiscount": "تخفیف درصدی",
"typeBonusSet": "مجموعه پاداش (موجودی / روزها / گروه تخفیف)",
"bonusComposition": "محتوای کد تخفیف",
"includeBalance": "پاداش موجودی",
"includeDays": "روزهای اشتراک",
"includePromoGroup": "گروه تخفیف",
"bonusAmount": "مبلغ پاداش",
"rub": "روبل",
"daysCount": "تعداد روزها",
@@ -2776,6 +2782,7 @@
},
"validation": {
"codeRequired": "کد تخفیف را وارد کنید",
"bonusSetEmpty": "حداقل یک پاداش برای کد تخفیف انتخاب کنید",
"balanceRequired": "مبلغ پاداش باید بیشتر از 0 باشد",
"daysRequired": "تعداد روزها باید بیشتر از 0 باشد",
"groupRequired": "یک گروه تخفیف انتخاب کنید",

View File

@@ -3537,7 +3537,8 @@
"subscriptionDays": "Дни подписки",
"trialSubscription": "Тестовая подписка",
"promoGroup": "Группа скидок",
"discount": "Скидка %"
"discount": "Скидка %",
"balanceAndDays": "Баланс + дни"
},
"modal": {
"editPromocode": "Редактирование промокода",
@@ -3555,6 +3556,11 @@
"typeTrialSubscription": "Тестовая подписка",
"typePromoGroup": "Группа скидок",
"typeDiscount": "Процентная скидка",
"typeBonusSet": "Набор бонусов (баланс / дни / промогруппа)",
"bonusComposition": "Состав промокода",
"includeBalance": "Бонус на баланс",
"includeDays": "Дни подписки",
"includePromoGroup": "Промогруппа",
"bonusAmount": "Сумма бонуса",
"rub": "руб.",
"daysCount": "Количество дней",
@@ -3648,6 +3654,7 @@
},
"validation": {
"codeRequired": "Введите код промокода",
"bonusSetEmpty": "Выберите хотя бы один бонус в составе промокода",
"balanceRequired": "Сумма бонуса должна быть больше 0",
"daysRequired": "Количество дней должно быть больше 0",
"groupRequired": "Выберите группу скидок",

View File

@@ -2668,7 +2668,8 @@
"subscriptionDays": "订阅天数",
"trialSubscription": "试用订阅",
"promoGroup": "折扣组",
"discount": "折扣 %"
"discount": "折扣 %",
"balanceAndDays": "余额 + 天数"
},
"modal": {
"editPromocode": "编辑促销码",
@@ -2686,6 +2687,11 @@
"typeTrialSubscription": "试用订阅",
"typePromoGroup": "折扣组",
"typeDiscount": "百分比折扣",
"typeBonusSet": "奖励组合(余额 / 天数 / 优惠组)",
"bonusComposition": "促销码内容",
"includeBalance": "余额奖励",
"includeDays": "订阅天数",
"includePromoGroup": "优惠组",
"bonusAmount": "奖金金额",
"rub": "卢布",
"daysCount": "天数",
@@ -2775,6 +2781,7 @@
},
"validation": {
"codeRequired": "请输入促销码",
"bonusSetEmpty": "请为促销码至少选择一项奖励",
"balanceRequired": "奖金金额必须大于0",
"daysRequired": "天数必须大于0",
"groupRequired": "请选择折扣组",