Commit Graph

1936 Commits

Author SHA1 Message Date
kewldan
b40f939e91 fix(ui): фиксированная ширина 100vw — страницы не сдвигаются при появлении скроллбара 2026-07-13 01:46:22 +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
Fringg
63b19645e6 Merge PR #478: stagger-анимация скрывала контент, пришедший из API после mount
framer-motion stagger раздаёт детям команду показаться один раз при монтировании
родителя; дети, смонтированные позже (когда resolve-ится react-query), оставались
в initial (opacity:0) — пустые страницы после F5/прямого захода.

- ConnectedAccounts, SavedCards: key={isLoading?...} перемонтирует контейнер
  после загрузки → stagger проигрывается со всеми детьми;
- Balance (2), Profile, Support: явные initial/animate на условных staggerItem —
  элемент самоанимируется при mount, вне оркестрации родителя;
- Wheel: исправлены несуществующие варианты hidden/show → initial/animate
  (stagger истории спинов молча не работал).

Валидация на смердженном дереве: tsc, biome lint/format, build — чисто.
Правки #477 и #478 в общих файлах (ConnectedAccounts/Profile) сосуществуют.
2026-07-12 23:45:08 +03:00
Fringg
0e6f80c090 Merge PR #477: нормализовать detail из ответов API перед рендером (краш на 422)
FastAPI/Pydantic на 422 кладёт в detail массив объектов {type,loc,msg,...};
компоненты рендерили его в JSX как есть → React падал в ErrorBoundary
(Objects are not valid as a React child), пользователь не видел причину.

Все такие обработчики (Login, Profile, ConnectedAccounts, admin AnalyticsTab
и MenuEditorTab) переведены на существующий getApiErrorMessage — строковый
detail возвращается как есть (ветки .includes работают), Pydantic-массив
разворачивается в строку field: msg; ..., любой формат безопасен для рендера.

Валидация на смердженном дереве: tsc, biome lint/format, build — чисто.
2026-07-12 23:22:26 +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
adaaa23da3 fix(ui): контент, пришедший из API после mount, оставался невидимым (stagger)
Оркестрация staggerContainer/staggerItem у framer-motion раздаёт детям
команду «показаться» один раз — при анимации родителя на mount. Дети,
смонтированные позже (когда resolve'ится запрос react-query), в
оркестрацию не попадают и навсегда остаются в initial (opacity: 0, y: 8).

Симптом: после жёсткой перезагрузки (F5) на странице виден только
заголовок — карточки есть в DOM, но прозрачны. При SPA-навигации кэш
react-query тёплый, дети монтируются вместе с родителем — поэтому баг
проявлялся только при холодном заходе и маскировался на проде (нет
StrictMode, same-origin API, тёплый кэш почти всегда выигрывают гонку
с ~300-мс окном анимации).

Два способа фикса в зависимости от формы страницы:

1. key={isLoading ? 'loading' : 'ready'} на контейнере — перемонтирование
   после загрузки, stagger проигрывается заново уже со всеми детьми.
   Для страниц, где весь контент асинхронный (каскад сохраняется):
   - ConnectedAccounts — карточки провайдеров
   - SavedCards — список сохранённых карт

2. Явные initial="initial" animate="animate" на условном staggerItem —
   элемент анимирует себя сам, не дожидаясь оркестрации родителя. Для
   страниц, где асинхронны только 1-2 опциональные карточки (не
   перезапускает анимацию всей страницы на каждый resolve):
   - Balance — «Способы пополнения» и ссылка «Сохранённые карты»
   - Support — карточка «Написать нам» (support_type='both')
   - Profile — реферальный виджет

Попутно: в Wheel у контейнера истории спинов стояли несуществующие
варианты initial="hidden"/animate="show" (у staggerContainer/staggerItem
ключи — initial/animate/exit), из-за чего stagger там молча не работал
вовсе; имена исправлены.

Проверено и не требует правок: TopUpAmount (early-return до загрузки
методов), TopUpMethodSelect и MergeAccounts (динамический контент —
обычный HTML внутри всегда смонтированных staggerItem), вложенный
контейнер транзакций в Balance (самоанимирующийся).
2026-07-12 19:08:09 +03:00
kewldan
9b972931cf fix(ui): нормализовать detail из ответов API перед рендером — краш на 422
Что: все обработчики ошибок, которые клали сырой response.data.detail в
состояние и затем в JSX, переведены на существующий хелпер
getApiErrorMessage (src/utils/api-error.ts):

- Login.tsx — Telegram-auth, вход/регистрация по email, восстановление пароля
- Profile.tsx — смена email (запрос и подтверждение кода), повторная
  отправка письма верификации
- ConnectedAccounts.tsx — привязка email к аккаунту
- admin/AnalyticsTab.tsx, admin/MenuEditorTab.tsx — сохранение настроек

Почему: на невалидный по схеме запрос FastAPI/Pydantic отвечает 422, где
detail — не строка, а массив объектов {type, loc, msg, input, ctx}.
Компоненты рендерили его как есть, React падал с «Objects are not valid
as a React child», и вся страница (включая логин) уходила в ErrorBoundary
«Something went wrong» — пользователь не видел причину отказа вообще.

Зачем: getApiErrorMessage сводит Pydantic-массив к строке
«field: message; ...», поэтому вместо краша форма показывает настоящий
текст ошибки валидации. Ветвления по detail.includes(...) сохранены и
работают по нормализованной строке — поведение прежнее для строковых
detail, но безопасное для любой формы ответа.
2026-07-12 18:37:04 +03:00
Fringg
9f840a9a47 Merge PR #474: миграция тулинга с ESLint + Prettier на Biome
Замена ESLint + Prettier (9 dev-зависимостей) на Biome (одна): линт ~10×,
формат-чек ~49× быстрее. Паритет форматирования (single quotes/semi/width
100/trailing/arrowParens/jsxDouble) и Telegram WebView-гвардов (перенесены
GritQL-плагином). Имена npm-скриптов lint/format:check сохранены — CI не тронут.

Ревью-фиксы поверх PR (восстановление точного паритета):
- grit-исключение сужено до якорного src/platform/ (было bare-substring
  "platform", щадившее любой файл с этим словом в имени)
- вернул *.min.js/*.min.css в biome ignore (были в .prettierignore)
- вернул *.css в lint-staged pre-commit
- поправил устаревшую метку CI-шага
2026-07-12 01:36:15 +03:00
Fringg
fd85ba1ae1 chore(tooling): восстановить точный паритет с eslint/prettier
- telegram-webview-guards.grit: исключение сузил с подстроки "platform"
  до якорного "src/platform/" — прежний eslint-override действовал ровно на
  src/platform/**, а bare-substring щадил бы любой файл с "platform" в имени
  (проверено: src/platform всё ещё exempt, гвард срабатывает в обычных файлах)
- biome.json: вернул исключения *.min.js/*.min.css из .prettierignore
- lint-staged: вернул форматирование *.css в pre-commit (biome format --write) —
  иначе CI-формат-чек ловил бы css, который хук не трогал
- lint.yml: метка шага "Run ESLint" → "Run linter" (выполняет biome)
2026-07-12 01:35:44 +03:00
kewldan
0abc2f30ae chore(tooling): точечный CSS-линт вместо полного отключения
По итогам разбора Biome 2.5 x Tailwind v3: noUnknownAtRules и
noUnknownFunction не выключаем, а используем их опцию ignore
(tailwind; theme/screen) - детект опечаток в at-rules и функциях
сохраняется. noDuplicateProperties off: наши дубли - прогрессивные
фолбэки (100dvh -> var(--tg-viewport-stable-height)), опции
"разрешить фолбэки" нет (biomejs/biome#6051). noImportantStyles off:
все 31 !important осознанные. @tailwind в ignore из-за
biomejs/biome#7899 (tailwindDirectives их пока не покрывает).
2026-07-12 00:44:35 +03:00
Fringg
f1b87a73b3 Merge PR #473: раздел оптовых купонов + публичная страница активации
UI для фичи «Купоны» (backend — remnawave-bot #3044):
- админ-раздел /admin/coupons: список партий со статистикой, форма создания
  с готовыми ссылками (копировать/скачать .txt), карточка партии с экспортом
  и отзывом непогашенных за PermissionGate coupons:edit
- публичная /coupon/:token: партнёр видит тариф/срок, кнопки активации в боте
  и (для залогиненных) в кабинете с маппингом машинных кодов ошибок
- локали ru/en; zh/fa через ru-фолбэк

Ревью-фиксы поверх PR:
- единый formatShortDate вместо 3 копий formatDate + инлайн localeMap
- статистика списка показывается только для одной страницы (суммы по
  странице иначе противоречат глобальному total)
- retry:false на публичном статусе (404 — штатный ответ)
2026-07-12 00:34:59 +03:00
Fringg
b011b55f04 refactor(coupons): единый formatShortDate + честная статистика списка + retry
- formatShortDate вынесен в utils/format вместо трёх копий formatDate +
  инлайн localeMap (AdminCoupons/AdminCouponDetail/CouponStatus)
- список партий: агрегатные карточки Активны/Погашено/Отозвано считались
  только по текущей странице и противоречили глобальному total «Партий» —
  показываем их лишь когда весь набор влезает на одну страницу
- CouponStatus: retry:false на публичном статусе (404 — ожидаемый ответ на
  невалидный/погашенный токен, ретрай зря бьёт rate-limited эндпоинт)
2026-07-12 00:34:41 +03:00
kewldan
c03182e6cf chore(tooling): biome 2.3+ — нативный tailwind-парсер и lineEnding auto
По фичам Biome v2.3: css.parser.tailwindDirectives снимает исключение
CSS из Biome (globals.css снова под формат-контролем; линт CSS выключен
override-ом — tailwind-функции theme() и пр. правила пока не знают),
lineEnding auto повторяет поведение prettier endOfLine: auto (CRLF в
Windows-чекаутах, LF на CI). Однократный формат-дифф globals.css
45+/45- — расхождения CSS-форматтера Biome с Prettier.
2026-07-12 00:28:42 +03:00
kewldan
cf3837163e chore(tooling): миграция с ESLint + Prettier на Biome
Один инструмент вместо девяти dev-зависимостей. Замеры на этом репо
(483 файла, медиана из 3 прогонов, один и тот же компьютер):

- eslint .            ~20.4s  ->  biome lint .    ~2s
- prettier --check .  ~8.8s   ->  biome format .  ~0.2s
- суммарно линт+формат ~29s   ->  biome check .   ~2-3s (~10x)

Паритет с прежними правилами:
- форматирование бинарно совместимо с Prettier-конфигом (biome format
  --write тронул 9 файлов из ~480 — микроразличия);
- запрет window.confirm/alert/open/prompt и navigator.clipboard
  (Telegram WebView) перенесён GritQL-плагином
  biome-plugins/telegram-webview-guards.grit c теми же сообщениями
  и исключениями для src/platform/** и clipboard.ts; плагин сразу
  нашёл window.open в Wheel.tsx под старым eslint-disable —
  переведён на biome-ignore;
- react-hooks-правила: useExhaustiveDependencies/useHookAtTopLevel;
- новые для проекта правила (a11y и пр.) не включались или понижены
  до warning, чтобы миграция не смешивалась с чисткой кода — включать
  можно отдельными PR.

Ограничения (задокументированы): сортировка tailwind-классов
(prettier-plugin-tailwindcss) в Biome пока nursery — не включена;
CSS исключён из Biome (парсер спотыкается о Tailwind-синтаксис
globals.css) — файл один и правится редко.

CI не меняется: имена npm-скриптов lint/format:check сохранены.
lint-staged переведён на biome check --write.
2026-07-12 00:12:15 +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
8f31311112 fix(connection): keep the Happ add-subscription button working on Remnawave 2.8.0
Panel 2.8.0 removed the server-side happ-encrypt endpoint, so for users
without a stored crypto link the backend leaves {{HAPP_CRYPT4_LINK}}
unresolved and the subscriptionLink button silently disappeared
(isValidDeepLink requires '://').

- BlockButtons: resolve button templates client-side at render (with username,
  like the panel's own subpage) instead of dropping the button; collapse
  double-prefixed happ://crypt4/happ://cryptN/... resolvedUrl from old backends
- templateEngine: collapse a hardcoded happ://cryptN/ prefix before
  {{HAPP_CRYPT[34]_LINK}}; cache crypt-link generation (jsencrypt RSA-4096 is
  slow on weak devices and re-randomizes every call)
- Connection: in happ_cryptolink mode force the crypt link only when the button
  fell back to the plain subscription link or kept unresolved templates - an
  explicit Subpage link (e.g. happ://add/...) now wins, so Subpage edits apply
2026-07-02 16:12:18 +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
Fringg
fd5500c85b fix: align SubscriptionRequestRecord type with Remnawave 2.8.0
The panel renamed the subscription-request-history field userUuid (uuid)
-> userId (number) in 2.8.0. The field is never read in the UI (render
uses id/requestAt/requestIp/userAgent), so this is a type-accuracy fix
only. tsc --noEmit passes.
2026-06-30 01:43:06 +03:00
Fringg
ff0b119ebc feat(admin-tickets): deep-link to a specific ticket from notifications
Add an /admin/tickets/:ticketId route that pre-selects the ticket, and a
StartParamNavigator that maps a Telegram Mini App start param (admin_ticket_<id>,
delivered by a group-chat t.me/<bot>/<app>?startapp deep link) to that route.

This lets the bot's admin ticket notification buttons open the cabinet straight on
the ticket — web_app buttons for private chats, the startapp deep link for groups
where web_app is unavailable. Selection mirrors the URL param (cleared on the bare
/admin/tickets list). The static /admin/tickets/settings route still out-ranks the
new dynamic segment.

Pairs with bot feature BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot#2988.
2026-06-19 12:57:27 +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
Fringg
6721e25df6 fix(auth): send Bearer token for account merge endpoints
Remove '/cabinet/auth/merge/' from AUTH_ENDPOINTS so the request
interceptor attaches the Authorization header. The backend merge
preview/execute endpoints require an authenticated initiator
(get_current_cabinet_user); omitting the token produced
401 'Authentication required' and merges never completed after
Telegram confirmation or email-code verification.
2026-06-16 02:00:18 +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
c0mrade
47119c4c1f Merge pull request #456 from LLC-INCY/feature/pages-and-backgrounds
feat: per-method quick amounts, system pages admin, animated backgrounds overhaul
2026-06-11 13:33:02 +03:00
Boris Kovalskii
9e6372afba fix: address review feedback on admin pages and backgrounds
- guard config.default_quick_amounts with ?? [] so the edit page survives a
  cabinet deployed ahead of the bot
- roll back the first faq reorder request when the second fails so both
  questions don't end up with the same display_order
- stop overriding the aurora palette in render for the legacy seed colors
- add the missing legalPages / displayMode / quickAmounts keys to fa and zh
2026-06-11 20:00:52 +10:00
Boris Kovalskii
acd0d7c69e fix(backgrounds): keep constellation particles in bounds on resize 2026-06-11 11:14:57 +10: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
3df2ef0f39 fix(backgrounds): live speed and size updates for fireflies 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
caf499a824 fix(backgrounds): legacy config compatibility and locale sync 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
66c08b5d6d fix(backgrounds): read aurora gradient colors and speed from settings 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
39758a18b0 feat: hide info tabs by display mode visibility 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
Boris Kovalskii
04cbbb5e51 feat: add display mode selector to info page editor 2026-06-11 11:14:56 +10:00
Boris Kovalskii
e979aa8863 feat: add admin legal pages api client and display mode types 2026-06-11 11:14:56 +10:00
Boris Kovalskii
92198854b2 fix(balance): align quick amounts edge cases with bot behavior 2026-06-11 11:14:56 +10:00
Boris Kovalskii
bb8b823b37 feat(admin): quick amounts editor in payment method settings 2026-06-11 11:14:56 +10:00
Boris Kovalskii
25784dbe18 feat(balance): per-method quick amounts on top-up page 2026-06-11 11:14:56 +10:00
c0mrade
8b2501d908 feat(admin): show common placeholders group in email template editor
Companion to the bot-side change: the variables hint now renders two
groups — type-specific vars and common ones ({service_name},
{cabinet_url}, {support_username}, {username}, {email}, {date}) that
work in every template. The block is visible even for types with no
vars of their own (previously it was hidden entirely, looking like
placeholders didn't exist). Common chips insert at cursor the same way.
2026-06-10 23:37:31 +03:00