Commit Graph

374 Commits

Author SHA1 Message Date
Fringg
a28f1167f7 feat(lava): покупка привязкой, продукт тарифа в админке, CTA
Фронтовые хвосты автопродления Lava (бэкенд: de3518b7).

- Поле «Продукт Lava» в форме создания/редактирования тарифа: раньше
  lava_product_id можно было задать только через API, из-за чего фичу нельзя
  было включить из интерфейса. Пустая строка отвязывает тариф от продукта.
- CTA «Оформить с автооплатой Lava» в форме покупки тарифа — рядом с
  СБП-кнопкой, в обеих ветках рендера; флаг lava_recurrent_enabled из
  purchase-options наконец получил потребителя.
- API-метод purchaseWithLavaRecurring + типы lava_product_id в списке,
  детали и запросах тарифа.
- Локали ru/en/zh/fa: подписи покупки и поля продукта (плейсхолдеры сверены).
2026-07-29 20:23:35 +03:00
Fringg
2dce539da2 feat(lava): автопродление Lava на странице подписки
Парная фронтовая часть к эндпоинтам /cabinet/subscription/lava-recurrent
(бот: 0dea2182). Блок — сиблинг Platega-блока с той же семантикой состояний
(off/pending/active/past_due), поллинг раз в 8с пока привязка PENDING.

Отличие от Platega в подписи: у Lava период задан продуктом в кабинете
провайдера и приезжает числом дней, поэтому канонические значения (30/90/365
и т.д.) показываются словами, а произвольные — как «раз в N дн.».

utils/lavaRecurring.ts: строгое распознавание 403 'Lava recurrent disabled'
(другие 403-guard'ы прятать нельзя), деградация неизвестного статуса в off.
Локали ru/en/zh/fa (+25 строк каждая, плейсхолдеры сверены). 7 тестов утилиты.
2026-07-29 16:34:43 +03:00
Egor
2f04f29b68 Merge PR #526: охват сегментов и прогресс доставки на странице отправки 2026-07-29 12:29:29 +03:00
kewldan
c6d8c0afe0 feat(promo-offers): охват сегментов и прогресс доставки на странице отправки
Сегмент выбирался вслепую — в списке не было числа пользователей, а после
отправки экран показывал только количество созданных офферов: сколько
человек реально получило предложение и сколько заблокировало бота, узнать
было негде.

В селекте сегментов теперь стоит охват каждого (GET /promo-offers/segments),
под ним — сколько уйдёт по выбранному. После отправки экран результата
показывает прогресс доставки с карточками Всего/Отправлено/Заблокировали/
Ошибки и опрашивает запись рассылки раз в 3 секунды, пока она не завершится.

Прогресс-блок и бейдж статуса вынесены из AdminBroadcastDetail в
BroadcastDeliveryStats и переиспользуются обеими страницами, а условие
«доставка ещё идёт» — в утилиту broadcastStatus с тестами.
2026-07-28 22:24:58 +03:00
kewldan
fb1f5cf3a7 fix(i18n): починка плейсхолдеров и переводов в zh/fa/en локалях кабинета
Аудит локалей кабинета с русским в качестве эталона.

Битые {{плейсхолдеры}} в zh/fa (рендерились как литеральные скобки,
теряли значение или дублировали то, что и так выводится в JSX):
- gift.sentTo / gift.activatedBy: неверное имя переменной
  ({{name}} → {{recipient}} / {{username}})
- dashboard.devicesConnectedUnlimited: {{count}} → {{used}} (+ «без лимита»)
- dashboard.maxUsage, gift.deviceCount, gift.activateSuccessDesc: потерянные
  значения возвращены
- dashboard.expired.activeUntil, subscription.trialInfo.remaining: убран
  {{date}}/{{count}} — значение выводится отдельным элементом
- trialInfo.description, trialOffer.freeDesc/paidDesc, gift.shareText:
  переписаны без {{days}}/{{price}}/{{code}}, которые код не передаёт
- wheel.payWithStars: одинарные {stars} (i18next их не подставляет) убраны

Мисперводы zh/fa (разошлись по смыслу с ru/en):
- subscription.cta.*Hint, expiredBanner.selectTariff, trialUpgrade.description,
  reduceDevicesDescription, menuEditor.customLabelsHint, myGiftsEmptyDesc,
  partners.settingsFields.*, remnawave.overview.bandwidth, shareModalActivateVia

fa admin.pinnedMessages: 40 строк были на английском → переведены на фарси.
zh common.all: 'All' → '全部'.
en admin.broadcasts.subtitle: устаревшее «History and management» →
соответствует ru «Массовая отправка сообщений пользователям».

Плейсхолдеры и синхронность en/ru проверены (locales.test.ts).
2026-07-26 02:09:24 +03:00
Fringg
f448c14994 feat(sbp-recurring): СБП-оформление подписки на форме покупки тарифа
Вторая CTA «Оформить с автооплатой СБП» под кнопкой покупки с баланса
(и в daily-ветке) — показывается по platega_recurrent_enabled из
purchase-options. POST /platega-recurrent/purchase → редирект в банк
(первое списание = подтверждение привязки), инвалидация подписочных и
sbp-запросов, переход в /subscriptions. Локали en/ru/zh/fa.
2026-07-24 19:07:23 +03:00
Fringg
2cd494e949 Merge PR #508: различать устаревшего бота и ненастроенного на логине
404 на /cabinet/branding/telegram-widget и deeplink-роутах = сборка бота
старше v3.24/v3.33 — вместо «бот не настроен» показываем auth.botOutdated
(4 локали). README: фактический минимум v3.33.0. Закрывает #345.
Спасибо @kewldan.
2026-07-24 15:00:36 +03:00
Fringg
05a7c6b02a Merge PR #506: приветствие без пустого имени для email-пользователей
displayName() получил фолбэк на локальную часть email; без имени вовсе —
dashboard.welcomeNoName без запятой (4 локали). Закрывает #423.
Спасибо @kewldan.
2026-07-24 14:50:21 +03:00
Fringg
42a7190241 fix(sbp-recurring): toast ошибки отмены + per-sub ключ inline-confirm в админке
- cancelSbpMutation: onError-тост с detail бэкенда (зеркально enable) —
  молчаливый провал отмены выглядел как успех
- SubscriptionTab: ключ подтверждения cancelSbpRecurring_{subId} —
  взведённый confirm не переживает переключение подписки в пикере
- Локали: subscription.sbpRecurring.cancelError (en/ru/zh/fa)
2026-07-24 07:28:50 +03:00
Fringg
9093aec207 feat(subscription): API и утилиты СБП-автопродления Platega 2026-07-24 02:14:41 +03:00
kewldan
b3b07c322d fix(auth): различать устаревшего бота и ненастроенного на экране логина 2026-07-23 04:09:06 +03:00
kewldan
147b1fbb2e fix(dashboard): приветствие без пустого имени для email-пользователей 2026-07-23 03:36:26 +03:00
Fringg
46c1bc79a4 fix(admin): дубликат ключа admin.paymentMethods.description
Ключ был объявлен дважды: как подзаголовок страницы списка методов
и как подпись поля формы. Побеждало последнее определение, поэтому
на странице «Платёжные методы» вместо «Настройка порядка и условий
отображения» показывалось «Описание».

Подзаголовок переименован в subtitle, пара description/descriptionHint
осталась за полем формы. В zh/fa подписи поля не было вовсе — там под
description лежал текст подзаголовка, поэтому форма показывала не то;
добавлены недостающие переводы вместо русского фолбэка.

Заодно форматирование AdminPaymentMethodEdit.tsx: шаг format:check
в CI не доходил до выполнения, пока падал lint.

Чинит красный lint в CI ветки dev.
2026-07-19 17:44:22 +03:00
Fringg
4b982175d7 feat(payments): способ оплаты cisPay
Регистрация нового провайдера пополнения (api.cispay.app, карта и СБП)
во фронтенде: метка в METHOD_LABELS, иконка, узел настроек
payments_cispay и локали ru/en/zh/fa.

Бэкенд отдаёт метод динамически, поэтому страницы статистики и транзакций
подхватывают его автоматически через METHOD_LABELS и PaymentMethodIcon.
2026-07-19 17:38:32 +03:00
Egor
6d1513092b Merge PR #496: кнопка построения полной реферальной сети 2026-07-19 16:36:28 +03:00
Egor
0b53e0e35f Merge PR #495: приоритет админского display_name/description над локалью на балансе 2026-07-19 16:34:40 +03:00
Egor
424d77455e Merge PR #493: состав промокода чекбоксами — баланс / дни / промогруппа 2026-07-19 16:29:08 +03:00
Egor
10b58cdc1f Merge PR #492: кнопка «Отправить сообщение» в карточке пользователя 2026-07-19 16:26:15 +03:00
Fringg
1fddd9473c test(i18n): опустошить allowlist после мержа #489 + перевод realtimeTitle
Поверх #491: #489 уже в dev — записи resetPassword.*/expiredDate_trial и
placeholder-исключение serverAccess из allowlist удалены (по инструкции
самого теста); последняя дыра admin.remnawave.traffic.realtimeTitle закрыта
переводом — проверка синхронности теперь строгая, без известных исключений
2026-07-19 16:23:17 +03:00
kewldan
19facf2ba9 feat(referral-network): кнопка построения полной реферальной сети
Со времён редизайна со скоуп-селектором граф строится только для
выбранных кампаний, партнёров или пользователей, и посмотреть сеть
целиком стало нечем. Бэкенд для этого уже есть — GET /cabinet/admin/
referral-network/ отдаёт полный граф и остался неиспользуемым после
того, как фронт перешёл на /scoped.

Возвращаем к нему доступ: в дропдауне селектора появляется закреплённый
пункт «Вся сеть», который грузит граф целиком. Режим взаимоисключающий
с выбором чипов — полный граф и так содержит все сущности.
2026-07-16 15:32:17 +03:00
smediainfo
112e09bc6d fix(payment-methods): admin display_name/description override locale on balance
White-label (per-method display_name + description in admin) was defeated on
balance/top-up pages: render used `translatedName || method.name` and
`translatedDesc || method.description`, so the bundled locale always won and
the admin override was ignored.

- Flip priority: admin-configured value first, locale as fallback
  (TopUpMethodSelect, Balance)
- Add missing Description field to payment-method edit page
  (mirrors Display name: description / reset_description)
- Add description to admin PaymentMethodConfig type
- Add description/descriptionHint i18n keys (ru, en)
2026-07-16 09:55:08 +03:00
Case211
9ce838bade 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,
порядок эффектов дни→баланс, валидации, бот-визард).
2026-07-14 08:52:06 +05:00
Case211
2f743d82db feat(users): кнопка «Отправить сообщение» в карточке пользователя
Паритет с бот-кнопкой «✉️ Отправить сообщение»: в блоке действий карточки
юзера появляется кнопка (RBAC users:send_message), открывающая модалку с
текстом (HTML, лимит 4096) — бот отправляет юзеру прямое сообщение через
POST /admin/users/{id}/send-message.

Для email-only юзеров без telegram_id кнопка задизейблена с подсказкой;
коды ошибок бэкенда (no_telegram_id / forbidden / bad_request) маппятся
в честные локализованные сообщения. Локали ru/en/fa/zh.
2026-07-14 07:28:33 +05:00
kewldan
dd50d648d0 fix(i18n): добавить недостающие английские переводы resetPassword и expiredDate_trial 2026-07-13 23:37:07 +03:00
Egor
eeb715c095 Merge PR #484: поле icon_custom_emoji_id у кастомных кнопок рассылки
feat(broadcasts): поле icon_custom_emoji_id у кастомных кнопок рассылки
2026-07-13 15:13:22 +03:00
Fringg
f9ee16a2e6 feat(admin): бейдж «Команда» для команд бота в таймлайне активности
Команды (/start и др.) приходят типом button_click с подтипом command —
добавлен перевод бейджа во всех 4 локалях.
2026-07-13 04:19:32 +03:00
Fringg
5fc8e7f97a feat(admin): клики кнопок бота и действия в кабинете во вкладке «Активность»
Два новых типа записей таймлайна: button_click (нажатия кнопок в боте,
с текстом кнопки) и cabinet_action (мутационные действия юзера в
кабинете, метод + нормализованный путь). Чип-фильтр «Клики», иконки
BotIcon/BoltIcon, переводы во всех 4 локалях.

biome check, type-check и build проходят (pre-commit пропущен: biome
не установлен локально).
2026-07-13 03:46:53 +03:00
Fringg
7547b49d7a feat(admin): вкладка «Активность» в карточке пользователя
Единый таймлайн действий юзера в боте и кабинете
(GET /cabinet/admin/users/{id}/activity): вертикальная лента с
иконками-кружками по типу записи, бейджи подтипов, суммы со знаком
(+зелёный / −красный), относительное время (абсолютное — в title),
чипы-фильтры по категориям (платежи, события, промо, тикеты, подарки,
рефералы, входы), счётчик StatCard и постраничная догрузка по house
load-more паттерну. Компонент самодостаточный (ActivityTab), как
TicketsTab. Переводы во всех 4 локалях; время переиспользует
admin.auditLog.time.*.

biome check, type-check и build проходят (pre-commit пропущен: biome
не установлен локально — бинарь есть только в CI).
2026-07-13 03:34:07 +03:00
kewldan
b4b4bca2cb feat(broadcasts): поле icon_custom_emoji_id для кастомных кнопок рассылки 2026-07-13 02:34:46 +03:00
Fringg
758a645dff Merge PR #479: счётчик символов и предупреждение о разбивке длинных текстов
Парный к боту #3049. В /admin/legal-pages под полем политики/оферты/правил:
постоянный счётчик символов + предупреждение с оценкой числа сообщений бота
(estimateTelegramParts зеркалит split_telegram_text, порог 3500). Скрыто там,
где документ в боте не показывается (recurrent-payments, web-only); счётчик
остаётся. Переводы ru/en с плюральными формами, прочие локали — en fallback.

Валидация: JSON-ключи, tsc, biome lint/format, build — чисто.
2026-07-12 23:57:05 +03:00
kewldan
93cfca46dc feat(admin): предупреждение о разбивке длинных текстов системных страниц в боте
Кабинет, в отличие от админки бота (лимит 4000 символов), позволяет
сохранять юридические тексты любой длины — и до парного фикса в боте
(BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot#3049) текст длиннее
~4096 символов ронял показ правил и политики при /start
(MESSAGE_TOO_LONG).

Теперь под текстовым полем политики, оферты и правил постоянно
показывается счётчик символов, а для текстов длиннее одного сообщения —
предупреждение с оценкой, на сколько сообщений бот разобьёт текст.
Оценка зеркалит жадную упаковку абзацев из split_telegram_text бота
(порог 3500 символов на кусок).

Предупреждение не показывается там, где оно неуместно:
- «Рекуррентные платежи» — документ не отображается в боте вовсе;
- режим отображения «Только веб» — бот документ не показывает.

Переводы добавлены в ru/en (с плюральными формами); остальные локали
используют английский fallback через defaultValue.
2026-07-12 19:41:03 +03:00
kewldan
799e986d84 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-фолбэк).
2026-07-11 23:52:13 +03:00
Fringg
20565b8f59 feat(legal): wire recurrent-payments document + restore footer link
Backend now serves a dedicated recurring-payments legal document, so:
- Add adminLegalPagesApi.get/updateRecurrentPayments and infoApi.getRecurrentPayments
  (+ InfoVisibility.recurrent).
- AdminLegalPages: new 'Рекуррентные платежи' editor tab (DocumentEditor now driven
  by a DOCUMENT_API dispatch map instead of privacy/offer ternaries).
- PublicLegal: add 'recurrent' doc; add public /recurrent-payments route.
- Re-add the recurrent link to the login LegalFooter (was removed pending backend).
- i18n: admin.legalPages.tabs.recurrent for ru/en/fa/zh (footer.recurrent already existed).

type-check, eslint, prettier and build all pass.
2026-07-09 18:47:24 +03:00
Fringg
37a53e40ea fix(auth): make login footer legal links open real pages, not the login tab
The login-page footer linked to /offer, /privacy and /recurrent-payments with
target=_blank, but none of those routes existed, so opening them hit the SPA
catch-all, which redirects unauthenticated users to /login — every footer link
just opened another login tab.

- Add a public PublicLegal page that fetches the offer / privacy document from the
  existing public /cabinet/info endpoints and renders sanitized HTML (DOMPurify via
  the shared formatContent helper).
- Register public /offer and /privacy routes.
- Extract sanitizeHtml/formatContent into src/utils/legalContent.ts and reuse from
  Info.tsx (was duplicated).
- Drop the /recurrent-payments footer link: there is no recurring-payments legal
  document or endpoint in the backend, so it had no real destination. Re-add once
  such a document exists.

type-check, eslint, prettier and build all pass.
2026-07-09 18:14:49 +03:00
Fringg
984d1b0776 feat(cabinet): legal footer on login + admin toggle
Adds a legal footer (Public Offer / Privacy / Recurring Payments) under the
login card, gated by CABINET_FOOTER_ENABLED (backend endpoint added in bot
PR #3011), plus an admin toggle in BrandingTab and ru/en/fa/zh i18n.

Footer-only subset of PR #464 — excludes the unrelated storePartnerClickId
helper and the version/CHANGELOG churn.

Co-authored-by: smediainfo <dev@matrixvpn.top>
2026-06-30 07:30:59 +03:00
Boris Kovalskii
ecc2d45562 fix(admin): gate overpay certificate block by settings permission
(cherry picked from commit 28068bbe45221145df3bd580a46e7bd7fce416e9)
2026-06-16 06:31:46 +03:00
Boris Kovalskii
f52360449b feat(admin): overpay certificate upload block
(cherry picked from commit b51b9d7d45db7b4597838d25a475291e453c7a7a)
2026-06-16 06:31:46 +03:00
c0mrade
9456c7f54e fix(ui): finish design-audit follow-ups
- useFeatureFlags: кэш флагов в localStorage — нижняя навигация больше
  не прыгает на холодном старте (таб «Рефералы»/«Колесо» появлялся после
  загрузки terms/config)
- applyThemeColors: частичный/битый ответ /branding/colors добивается
  дефолтами вместо падения всего приложения в ErrorBoundary
- карточка «Связаться с поддержкой»: кнопка больше не дублирует заголовок
  («Написать», без переноса)
- «Мои подписки»: заголовок и «Купить ещё» в одну строку на 390px
- StatCard: подписи плиток переносятся на две строки вместо обрезки
  многоточием («Бонус новому польз…»)
2026-06-11 16:00:19 +03:00
c0mrade
7f68e2cd5e fix(theme): readable text on any operator palette + admin consistency pass
Контраст-аудит (WCAG-обход всех текстовых узлов, 4 палитры × юзер+админ
страницы) показал системные провалы читаемости: вторичный текст 1.5-2.5
на светлой/кастомных темах, статусные цвета 1.3-1.6, белый текст на
светлых акцентах 1.9.

Корневые фиксы:
- useThemeColors: контраст-кламп производных серых токенов — dark-400/
  champagne-600 (вторичный текст) держат >=5.0 к поверхности, dark-500/
  champagne-500 (подсказки) >=3.8; палитры с достаточным контрастом
  рендерятся байт-в-байт как раньше
- --color-on-accent/-success/-warning/-error: чёрный или белый текст
  по фактической яркости цвета оператора; text-white на accent-заливках
  заменён на text-on-accent (135 мест + variant примитива Button +
  .btn-primary)
- .light: статусные текстовые оттенки *-300/400 ремапятся на тёмные
  *-700 (жёлтая «Админка» 1.26 -> 3.8+, зелёные суммы 1.4 -> 3.4+,
  бейджи состояний)

Замер после: операторская тёмная палитра — 0 нарушений (всё >=4.5),
худшие точки остальных подняты с 1.26-1.6 до 3.2+.

Консистентность:
- заголовки админ-страниц приведены к text-xl font-bold text-dark-100
  (были 18/20/24/30px и 600/700 вперемешку)
- «Создать FAQ»: warning-500/80+белый -> warning-500+text-on-warning
- formatTraffic: единицы из i18n (убран микс «GB»/«ГБ» на одном экране)
- categoryCanvas -> categoryCANVAS в локалях (битый ключ на настройках)
- «Управление сквадами» -> «Управление локациями» (юзерский экран)
- мин-макс сумм на выборе метода: dark-600 -> dark-400
- неактивные пункты нижнего нава: dark-500 -> dark-400
2026-06-11 15:49:17 +03:00
Boris Kovalskii
510e4bd738 feat(backgrounds): add constellation background 2026-06-11 11:14:57 +10:00
Boris Kovalskii
71160c6700 feat(backgrounds): add liquid-gradient background 2026-06-11 11:14:57 +10:00
Boris Kovalskii
ab91c86f81 feat(backgrounds): add matrix-rain background 2026-06-11 11:14:57 +10:00
Boris Kovalskii
3742f2f88d feat(backgrounds): add starfield background with depth projection 2026-06-11 11:14:57 +10:00
Boris Kovalskii
e3dbc4e663 feat(backgrounds): add snowfall background 2026-06-11 11:14:57 +10:00
Boris Kovalskii
1d00ca97e5 feat(backgrounds): add fireflies background 2026-06-11 11:14:57 +10:00
Boris Kovalskii
725bf8df39 fix(backgrounds): configurable wave colors and boxes grid color with explicit multicolor mode 2026-06-11 11:14:56 +10:00
Boris Kovalskii
acbccc8af8 fix(backgrounds): make beams and beams-collision colors configurable 2026-06-11 11:14:56 +10:00
Boris Kovalskii
f1f4281e11 fix(backgrounds): expose remaining hardcoded colors in shooting-stars, meteors, gradient-animation 2026-06-11 11:14:56 +10:00
Boris Kovalskii
515c3d73b8 fix(admin): harden legal pages editor state handling 2026-06-11 11:14:56 +10:00
Boris Kovalskii
0d6a85a4f9 feat: add system pages admin section 2026-06-11 11:14:56 +10:00