mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
feat(coupons): раздел оптовых купонов + публичная страница активации
UI для купонов из BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot#3042 (API: PR #3044 бота): - /admin/coupons — список партий с погашениями и offset-пагинацией; - /admin/coupons/create — мастер партии (тариф, дни, количество, учётная оптовая цена, срок), после создания — экран со ссылками (копирование всех, скачивание .txt); - /admin/coupons/:id — карточка со статистикой, экспорт актуальных ссылок, отзыв непогашенных за PermissionGate coupons:edit с подтверждением и тостом; - /coupon/:token — публичная страница купона: тариф/срок, кнопка активации в боте, для залогиненных — активация прямо в кабинете с маппингом структурированных кодов ошибок {code, message}; - пункт меню в разделе тарифов (гейт coupons:read), лениво загружаемые роуты, локали ru/en (zh/fa падают на ru-фолбэк).
This commit is contained in:
@@ -1241,6 +1241,7 @@
|
||||
"campaigns": "Campaigns",
|
||||
"promoOffers": "Promo Offers",
|
||||
"promocodes": "Promo Codes",
|
||||
"coupons": "Coupons",
|
||||
"promoGroups": "Discount Groups",
|
||||
"trafficUsage": "Traffic Usage",
|
||||
"updates": "Updates",
|
||||
@@ -3031,6 +3032,84 @@
|
||||
"description": "This will remove partner privileges. The partner will no longer earn commissions from referrals."
|
||||
}
|
||||
},
|
||||
"coupons": {
|
||||
"title": "Coupons",
|
||||
"subtitle": "Wholesale batches of one-time subscription links",
|
||||
"addBatch": "Create batch",
|
||||
"noBatches": "No batches yet",
|
||||
"days": "d.",
|
||||
"stats": {
|
||||
"batches": "Batches",
|
||||
"active": "Active",
|
||||
"redeemed": "Redeemed",
|
||||
"revoked": "Revoked"
|
||||
},
|
||||
"list": {
|
||||
"revoked": "Revoked",
|
||||
"until": "Until",
|
||||
"price": "Wholesale"
|
||||
},
|
||||
"pagination": {
|
||||
"prev": "Previous",
|
||||
"next": "Next",
|
||||
"page": "Page {{current}} of {{total}}"
|
||||
},
|
||||
"form": {
|
||||
"title": "New coupon batch",
|
||||
"subtitle": "Every coupon is a one-time subscription link",
|
||||
"name": "Batch name",
|
||||
"namePlaceholder": "E.g. the partner name",
|
||||
"tariff": "Tariff",
|
||||
"selectTariff": "Select a tariff",
|
||||
"periodDays": "Subscription days per coupon",
|
||||
"couponsCount": "Number of coupons (1–500)",
|
||||
"price": "Wholesale price per coupon, ₽",
|
||||
"priceHint": "Bookkeeping only, 0 — not set",
|
||||
"validDays": "Coupon lifetime, days",
|
||||
"validDaysHint": "0 or empty — perpetual",
|
||||
"create": "Create batch",
|
||||
"creating": "Creating…",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"validation": {
|
||||
"nameRequired": "Enter the batch name",
|
||||
"tariffRequired": "Select a tariff",
|
||||
"periodInvalid": "Subscription days: 1 to 3650",
|
||||
"countInvalid": "Number of coupons: 1 to 500"
|
||||
},
|
||||
"created": {
|
||||
"title": "Batch created",
|
||||
"linksLabel": "Links ({{count}})",
|
||||
"copyAll": "Copy all",
|
||||
"copied": "Copied",
|
||||
"download": "Download .txt",
|
||||
"toList": "Back to batches",
|
||||
"toBatch": "Open batch"
|
||||
},
|
||||
"detail": {
|
||||
"title": "Batch #{{id}}",
|
||||
"total": "Total coupons",
|
||||
"price": "Wholesale price",
|
||||
"priceTotal": "total",
|
||||
"validUntil": "Valid until",
|
||||
"perpetual": "Perpetual",
|
||||
"createdAt": "Created",
|
||||
"linksTitle": "Active links ({{count}})"
|
||||
},
|
||||
"revoke": {
|
||||
"button": "Revoke unredeemed ({{count}})",
|
||||
"confirmTitle": "Revoke the batch?",
|
||||
"confirmText": "{{count}} unredeemed coupons will be revoked. Their links will stop working. This cannot be undone.",
|
||||
"confirm": "Revoke",
|
||||
"cancel": "Cancel",
|
||||
"success": "Coupons revoked: {{count}}"
|
||||
},
|
||||
"errors": {
|
||||
"loadFailed": "Batch not found",
|
||||
"createFailed": "Failed to create the batch",
|
||||
"revokeFailed": "Failed to revoke the batch"
|
||||
}
|
||||
},
|
||||
"promocodes": {
|
||||
"title": "Promo Codes",
|
||||
"subtitle": "Manage promo codes and discount groups",
|
||||
@@ -5086,6 +5165,34 @@
|
||||
"nMonths": "{{count}} months"
|
||||
}
|
||||
},
|
||||
"coupon": {
|
||||
"title": "Subscription coupon",
|
||||
"subtitle": "A one-time coupon — redeem it to get or extend a subscription",
|
||||
"tariff": "Tariff",
|
||||
"period": "Period",
|
||||
"daysSuffix": "d.",
|
||||
"validUntil": "Redeem before",
|
||||
"openBot": "Redeem in the bot",
|
||||
"redeemCabinet": "Redeem in the cabinet",
|
||||
"redeeming": "Redeeming…",
|
||||
"needAuth": "Sign in to redeem the coupon in the cabinet — or open the link in the bot",
|
||||
"notFound": {
|
||||
"title": "Coupon not found",
|
||||
"text": "The coupon does not exist, was already used or has expired"
|
||||
},
|
||||
"success": {
|
||||
"activated": "Subscription activated!",
|
||||
"renewed": "Subscription extended!",
|
||||
"until": "Valid until"
|
||||
},
|
||||
"errors": {
|
||||
"invalid": "Coupon not found or already used",
|
||||
"expired": "The coupon has expired",
|
||||
"already_redeemed_by_you": "You have already redeemed this coupon",
|
||||
"internal": "Server error, try again later",
|
||||
"generic": "Failed to redeem the coupon"
|
||||
}
|
||||
},
|
||||
"gift": {
|
||||
"title": "Gift Subscription",
|
||||
"subtitle": "Send a VPN subscription as a gift",
|
||||
|
||||
@@ -1263,6 +1263,7 @@
|
||||
"campaigns": "Кампании",
|
||||
"promoOffers": "Промопредложения",
|
||||
"promocodes": "Промокоды",
|
||||
"coupons": "Купоны",
|
||||
"promoGroups": "Группы скидок",
|
||||
"trafficUsage": "Расход трафика",
|
||||
"updates": "Обновления",
|
||||
@@ -3433,6 +3434,84 @@
|
||||
"description": "Это лишит партнёра привилегий. Партнёр больше не будет получать комиссию от рефералов."
|
||||
}
|
||||
},
|
||||
"coupons": {
|
||||
"title": "Купоны",
|
||||
"subtitle": "Оптовые партии одноразовых ссылок на подписку",
|
||||
"addBatch": "Создать партию",
|
||||
"noBatches": "Партий пока нет",
|
||||
"days": "дн.",
|
||||
"stats": {
|
||||
"batches": "Партий",
|
||||
"active": "Активных",
|
||||
"redeemed": "Погашено",
|
||||
"revoked": "Отозвано"
|
||||
},
|
||||
"list": {
|
||||
"revoked": "Отозвана",
|
||||
"until": "До",
|
||||
"price": "Опт"
|
||||
},
|
||||
"pagination": {
|
||||
"prev": "Назад",
|
||||
"next": "Вперёд",
|
||||
"page": "Стр. {{current}} из {{total}}"
|
||||
},
|
||||
"form": {
|
||||
"title": "Новая партия купонов",
|
||||
"subtitle": "Каждый купон — одноразовая ссылка на подписку",
|
||||
"name": "Название партии",
|
||||
"namePlaceholder": "Например, имя партнёра",
|
||||
"tariff": "Тариф",
|
||||
"selectTariff": "Выберите тариф",
|
||||
"periodDays": "Дней подписки на купон",
|
||||
"couponsCount": "Количество купонов (1–500)",
|
||||
"price": "Оптовая цена за купон, ₽",
|
||||
"priceHint": "Только для учёта, 0 — не указывать",
|
||||
"validDays": "Срок действия купонов, дней",
|
||||
"validDaysHint": "0 или пусто — бессрочно",
|
||||
"create": "Создать партию",
|
||||
"creating": "Создание…",
|
||||
"cancel": "Отмена"
|
||||
},
|
||||
"validation": {
|
||||
"nameRequired": "Укажите название партии",
|
||||
"tariffRequired": "Выберите тариф",
|
||||
"periodInvalid": "Дней подписки: от 1 до 3650",
|
||||
"countInvalid": "Количество купонов: от 1 до 500"
|
||||
},
|
||||
"created": {
|
||||
"title": "Партия создана",
|
||||
"linksLabel": "Ссылки ({{count}} шт.)",
|
||||
"copyAll": "Скопировать все",
|
||||
"copied": "Скопировано",
|
||||
"download": "Скачать .txt",
|
||||
"toList": "К списку партий",
|
||||
"toBatch": "К партии"
|
||||
},
|
||||
"detail": {
|
||||
"title": "Партия #{{id}}",
|
||||
"total": "Всего купонов",
|
||||
"price": "Оптовая цена",
|
||||
"priceTotal": "итого",
|
||||
"validUntil": "Действует до",
|
||||
"perpetual": "Бессрочно",
|
||||
"createdAt": "Создана",
|
||||
"linksTitle": "Активные ссылки ({{count}} шт.)"
|
||||
},
|
||||
"revoke": {
|
||||
"button": "Отозвать непогашенные ({{count}})",
|
||||
"confirmTitle": "Отозвать партию?",
|
||||
"confirmText": "Будет отозвано {{count}} непогашенных купонов. Их ссылки перестанут работать. Действие необратимо.",
|
||||
"confirm": "Отозвать",
|
||||
"cancel": "Отмена",
|
||||
"success": "Отозвано купонов: {{count}}"
|
||||
},
|
||||
"errors": {
|
||||
"loadFailed": "Партия не найдена",
|
||||
"createFailed": "Не удалось создать партию",
|
||||
"revokeFailed": "Не удалось отозвать партию"
|
||||
}
|
||||
},
|
||||
"promocodes": {
|
||||
"title": "Промокоды",
|
||||
"subtitle": "Управление промокодами и группами скидок",
|
||||
@@ -5643,6 +5722,34 @@
|
||||
"nMonths": "{{count}} мес"
|
||||
}
|
||||
},
|
||||
"coupon": {
|
||||
"title": "Купон на подписку",
|
||||
"subtitle": "Одноразовый купон — активируйте, чтобы получить или продлить подписку",
|
||||
"tariff": "Тариф",
|
||||
"period": "Срок",
|
||||
"daysSuffix": "дн.",
|
||||
"validUntil": "Активировать до",
|
||||
"openBot": "Активировать в боте",
|
||||
"redeemCabinet": "Активировать в кабинете",
|
||||
"redeeming": "Активация…",
|
||||
"needAuth": "Чтобы активировать купон в кабинете, войдите в аккаунт — или откройте ссылку в боте",
|
||||
"notFound": {
|
||||
"title": "Купон не найден",
|
||||
"text": "Купон не существует, уже использован или истёк"
|
||||
},
|
||||
"success": {
|
||||
"activated": "Подписка активирована!",
|
||||
"renewed": "Подписка продлена!",
|
||||
"until": "Действует до"
|
||||
},
|
||||
"errors": {
|
||||
"invalid": "Купон не найден или уже использован",
|
||||
"expired": "Срок действия купона истёк",
|
||||
"already_redeemed_by_you": "Вы уже активировали этот купон",
|
||||
"internal": "Ошибка сервера, попробуйте позже",
|
||||
"generic": "Не удалось активировать купон"
|
||||
}
|
||||
},
|
||||
"gift": {
|
||||
"title": "Подарить подписку",
|
||||
"subtitle": "Отправьте VPN-подписку в подарок",
|
||||
|
||||
Reference in New Issue
Block a user