refactor: align tariff builder with bot functionality

- Remove unused fields: custom_days, custom_traffic (bot doesn't use them)
- Add promo groups selection to tariff form
- Add API endpoint for fetching available promo groups
- Make traffic topup packages dynamic (add/remove any number)
- Fix stuck 0 in number inputs when clearing
- Add validation messages explaining why save button is blocked
- Match bot validation: name 2-50 chars, device_limit >= 1, tier_level 1-10
- Add missing i18n translations for new fields and validation errors
This commit is contained in:
c0mrade
2026-02-02 04:27:52 +03:00
parent 8cd95b84fb
commit d6edb659a3
6 changed files with 430 additions and 297 deletions

View File

@@ -1251,6 +1251,7 @@
"nameLabel": "Tariff name",
"nameExamplePeriod": "E.g.: Standard",
"nameExampleDaily": "E.g.: Daily",
"nameHint": "2 to 50 characters",
"descriptionLabel": "Description",
"descriptionPlaceholder2": "Brief tariff description",
"trafficLimitLabel": "Traffic limit",
@@ -1267,8 +1268,12 @@
"addButton": "Add",
"noPeriodsHint": "No added periods. Add at least one period.",
"daysShort": "d.",
"serversTitle": "Servers",
"serversTabHint": "Select servers available on this tariff.",
"noServersAvailable": "No servers available",
"promoGroupsTitle": "Promo Groups",
"promoGroupsHint": "Select promo groups that have access to this tariff. If none selected — available to everyone.",
"noPromoGroups": "No promo groups",
"extraDeviceTitle": "Additional devices",
"devicePriceLabel": "Device price (30 days):",
"devicePriceHint": "0 = device purchase unavailable",
@@ -1278,6 +1283,8 @@
"trafficMaxLimitLabel": "Max. limit:",
"trafficLimitHint2": "(0 = no limits)",
"trafficPackagesLabel": "Traffic packages:",
"addPackageTitle": "Add package",
"noPackagesHint": "No packages added. Add at least one package.",
"gbPackageUnit": "GB:",
"customDaysTitle": "Custom number of days",
"customDaysDesc": "User selects subscription duration",
@@ -1309,7 +1316,18 @@
"dailyPriceLabel": "Price per day",
"currencyPerDay": "\u20bd/day",
"dailyDeductionDesc": "Charged daily from user's balance",
"dailyPriceRequired": "Enter a price per day greater than 0"
"dailyPriceRequired": "Enter a price per day greater than 0",
"cannotSave": "Cannot save tariff:",
"validation": {
"nameRequired": "Enter tariff name",
"nameLength": "Name must be 2 to 50 characters",
"deviceLimitRequired": "Specify number of devices (minimum 1)",
"tierLevelInvalid": "Tier level must be between 1 and 10",
"fieldsRequired": "Fill in required fields (devices, tier level)",
"periodsRequired": "Add at least one period",
"dailyPriceRequired": "Enter a price per day greater than 0",
"trafficPackagesRequired": "Add at least one traffic package (or disable traffic topup)"
}
},
"servers": {
"title": "Server Management",

View File

@@ -1021,6 +1021,7 @@
"nameLabel": "نام تعرفه",
"nameExamplePeriod": "مثال: استاندارد",
"nameExampleDaily": "مثال: روزانه",
"nameHint": "۲ تا ۵۰ کاراکتر",
"descriptionLabel": "توضیحات",
"descriptionPlaceholder2": "توضیح مختصر تعرفه",
"trafficLimitLabel": "محدودیت ترافیک",
@@ -1037,8 +1038,12 @@
"addButton": "افزودن",
"noPeriodsHint": "هیچ دوره‌ای اضافه نشده. حداقل یک دوره اضافه کنید.",
"daysShort": "روز",
"serversTitle": "سرورها",
"serversTabHint": "سرورهای موجود در این تعرفه را انتخاب کنید.",
"noServersAvailable": "هیچ سروری در دسترس نیست",
"promoGroupsTitle": "گروه‌های تبلیغاتی",
"promoGroupsHint": "گروه‌های تبلیغاتی که به این تعرفه دسترسی دارند را انتخاب کنید. اگر هیچ‌کدام انتخاب نشود، همه دسترسی دارند.",
"noPromoGroups": "هیچ گروه تبلیغاتی وجود ندارد",
"extraDeviceTitle": "دستگاه‌های اضافی",
"devicePriceLabel": "قیمت دستگاه (30 روز):",
"devicePriceHint": "0 = خرید دستگاه در دسترس نیست",
@@ -1048,6 +1053,8 @@
"trafficMaxLimitLabel": "حداکثر محدودیت:",
"trafficLimitHint2": "(0 = بدون محدودیت)",
"trafficPackagesLabel": "بسته‌های ترافیک:",
"addPackageTitle": "افزودن بسته",
"noPackagesHint": "هیچ بسته‌ای اضافه نشده است. حداقل یک بسته اضافه کنید.",
"gbPackageUnit": "GB:",
"customDaysTitle": "تعداد روزهای سفارشی",
"customDaysDesc": "کاربر مدت اشتراک را انتخاب می‌کند",
@@ -1079,7 +1086,18 @@
"dailyPriceLabel": "قیمت روزانه",
"currencyPerDay": "₽/روز",
"dailyDeductionDesc": "هر روز از موجودی کاربر کسر می‌شود",
"dailyPriceRequired": "قیمت روزانه بزرگتر از 0 را وارد کنید"
"dailyPriceRequired": "قیمت روزانه بزرگتر از 0 را وارد کنید",
"cannotSave": "امکان ذخیره تعرفه وجود ندارد:",
"validation": {
"nameRequired": "نام تعرفه را وارد کنید",
"nameLength": "نام باید ۲ تا ۵۰ کاراکتر باشد",
"deviceLimitRequired": "تعداد دستگاه‌ها را مشخص کنید (حداقل ۱)",
"tierLevelInvalid": "سطح تعرفه باید بین ۱ تا ۱۰ باشد",
"fieldsRequired": "فیلدهای اجباری را پر کنید (دستگاه‌ها، سطح)",
"periodsRequired": "حداقل یک دوره اضافه کنید",
"dailyPriceRequired": "قیمت روزانه بزرگتر از 0 را وارد کنید",
"trafficPackagesRequired": "حداقل یک بسته ترافیک اضافه کنید (یا خرید ترافیک را غیرفعال کنید)"
}
},
"servers": {
"title": "مدیریت سرورها",

View File

@@ -1762,6 +1762,7 @@
"nameLabel": "Название тарифа",
"nameExamplePeriod": "Например: Стандарт",
"nameExampleDaily": "Например: Суточный",
"nameHint": "От 2 до 50 символов",
"descriptionLabel": "Описание",
"descriptionPlaceholder": "Краткое описание тарифа",
"trafficLimitLabel": "Лимит трафика",
@@ -1778,8 +1779,12 @@
"addButton": "Добавить",
"noPeriodsHint": "Нет добавленных периодов. Добавьте хотя бы один период.",
"daysShort": "дн.",
"serversTitle": "Серверы",
"serversTabHint": "Выберите серверы, доступные на этом тарифе.",
"noServersAvailable": "Нет доступных серверов",
"promoGroupsTitle": "Промо-группы",
"promoGroupsHint": "Выберите промо-группы, которым доступен этот тариф. Если не выбрано ничего — доступен всем.",
"noPromoGroups": "Нет промо-групп",
"extraDeviceTitle": "Докупка устройств",
"devicePriceLabel": "Цена за устройство (30 дней):",
"devicePriceHint": "0 = докупка недоступна",
@@ -1789,6 +1794,8 @@
"trafficMaxLimitLabel": "Макс. лимит:",
"trafficLimitHint2": "(0 = без ограничений)",
"trafficPackagesLabel": "Пакеты трафика:",
"addPackageTitle": "Добавить пакет",
"noPackagesHint": "Нет добавленных пакетов. Добавьте хотя бы один пакет.",
"gbPackageUnit": "ГБ:",
"customDaysTitle": "Произвольное количество дней",
"customDaysDesc": "Пользователь сам выбирает срок подписки",
@@ -1820,7 +1827,18 @@
"dailyPriceLabel": "Цена за день",
"currencyPerDay": "₽/день",
"dailyDeductionDesc": "Списывается ежедневно с баланса пользователя",
"dailyPriceRequired": "Укажите цену за день больше 0"
"dailyPriceRequired": "Укажите цену за день больше 0",
"cannotSave": "Невозможно сохранить тариф:",
"validation": {
"nameRequired": "Введите название тарифа",
"nameLength": "Название должно быть от 2 до 50 символов",
"deviceLimitRequired": "Укажите количество устройств (минимум 1)",
"tierLevelInvalid": "Уровень тарифа должен быть от 1 до 10",
"fieldsRequired": "Заполните обязательные поля (устройства, уровень)",
"periodsRequired": "Добавьте хотя бы один период",
"dailyPriceRequired": "Укажите цену за день больше 0",
"trafficPackagesRequired": "Добавьте хотя бы один пакет трафика (или отключите докупку)"
}
},
"servers": {
"title": "Управление серверами",

View File

@@ -1059,6 +1059,7 @@
"nameLabel": "套餐名称",
"nameExamplePeriod": "例如:标准",
"nameExampleDaily": "例如:日套餐",
"nameHint": "2到50个字符",
"descriptionLabel": "描述",
"trafficLimitLabel": "流量限制",
"gbUnit": "GB",
@@ -1074,8 +1075,12 @@
"addButton": "添加",
"noPeriodsHint": "没有添加的周期。请至少添加一个周期。",
"daysShort": "天",
"serversTitle": "服务器",
"serversTabHint": "选择此套餐上可用的服务器。",
"noServersAvailable": "没有可用的服务器",
"promoGroupsTitle": "促销组",
"promoGroupsHint": "选择可以访问此套餐的促销组。如果未选择任何组,则所有人都可以访问。",
"noPromoGroups": "没有促销组",
"extraDeviceTitle": "额外设备",
"devicePriceLabel": "设备价格30天",
"devicePriceHint": "0 = 无法购买设备",
@@ -1085,6 +1090,8 @@
"trafficMaxLimitLabel": "最大限制:",
"trafficLimitHint2": "0 = 无限制)",
"trafficPackagesLabel": "流量包:",
"addPackageTitle": "添加流量包",
"noPackagesHint": "没有添加流量包。请至少添加一个流量包。",
"gbPackageUnit": "GB",
"customDaysTitle": "自定义天数",
"customDaysDesc": "用户自行选择订阅期限",
@@ -1116,7 +1123,18 @@
"dailyPriceLabel": "每天价格",
"currencyPerDay": "₽/天",
"dailyDeductionDesc": "每天从用户余额中扣除",
"dailyPriceRequired": "请输入大于0的每日价格"
"dailyPriceRequired": "请输入大于0的每日价格",
"cannotSave": "无法保存套餐:",
"validation": {
"nameRequired": "请输入套餐名称",
"nameLength": "名称必须为2到50个字符",
"deviceLimitRequired": "请指定设备数量至少1个",
"tierLevelInvalid": "等级必须在1到10之间",
"fieldsRequired": "请填写必填字段(设备数、等级)",
"periodsRequired": "请添加至少一个期限",
"dailyPriceRequired": "请输入大于0的每日价格",
"trafficPackagesRequired": "请添加至少一个流量包(或关闭流量购买)"
}
},
"servers": {
"title": "服务器管理",