Commit Graph

1988 Commits

Author SHA1 Message Date
c0mrade
ec23618a56 revert(sales-stats): убрать карточки возвратов из сводки
Возвраты для оператора всегда 0 (реальные возвраты только Apple/Tribute,
которых нет) — постоянно пустые карточки только путают. Сводка вернулась
к 8 карточкам (grid 2/4). Убрал также неиспользуемый invertDelta из
StatCard и ключи refunds из локалей.
2026-06-02 18:01:33 +03:00
c0mrade
56d8e1ffa7 fix(sales-stats): адаптив сетки Допов и сводки
Допы (5 карточек) рисовали 4+1: orphan-fill из двух arbitrary-вариантов
конфликтовал по специфичности — правило :last-child:nth-child(odd) бьёт
ресет :last-child, поэтому 5-я карточка тянулась на 2 колонки и на
десктопе. Заменил на один mobile-вариант max-lg:, как в AdminRemnawave:
десктоп — 5 в ряд, планшет/мобайл — 2 колонки + последняя на всю ширину.
Сводку тоже перевёл на lg:grid-cols-5 (было sm) — на планшете 5 колонок
давали ~115px и резали суммы; теперь до десктопа 2 колонки.
2026-06-02 17:15:18 +03:00
c0mrade
bd12113294 feat(sales-stats): карточки возвратов в сводке
Две карточки — количество и сумма возвратов за период (красный тон).
Сетку сводки перевёл на 2/5 (10 карточек делятся ровно, без пустых
ячеек). StatCard получил invertDelta: для «меньше — лучше» метрик рост
красится в красный, падение в зелёный (стрелка по факту). 4 локали.
2026-06-02 16:53:30 +03:00
c0mrade
40c684cc88 fix(sales-stats): «Выручка» → «Оборот по подпискам» во вкладке Продажи
Поле суммирует все подписочные платежи (новые + продления + докупки),
поэтому пересекалось по смыслу с вкладками Продления/Допы. Честное имя
«Оборот по подпискам» убирает путаницу: видно, что это итог по всем
подписочным платежам, а не только новые продажи. Все 4 локали.
2026-06-02 16:45:40 +03:00
c0mrade
c880517169 fix(datepicker): убрать скачки и поправить адаптив
- Числовой формат даты (01.06.2026) — постоянная ширина в любой локали,
  раньше короткое имя месяца («мая» vs «сент.») меняло ширину кнопки.
- min-w + justify-start + truncate на триггере: плейсхолдер и выбранная
  дата дают одинаковую ширину — поле не прыгает по горизонтали.
- fixedWeeks + showOutsideDays: всегда 6 недель, высота календаря не
  скачет при листании месяцев (5↔6 рядов).
- collisionPadding + max-w-[calc(100vw-1rem)]: поповер не вылезает за край
  экрана на мобайле.
- Контейнеры диапазона дат (продажи, трафик) переведены на flex-wrap /
  full-width на мобайле — два поля больше не вылезают за ширину.
2026-06-02 16:38:56 +03:00
c0mrade
54998c8195 fix(sales-stats): убрать пустые ячейки в сетках карточек
Сводка (8 карточек) была 2/3/4 колонки — на 3-колоночном брейкпоинте
последний ряд оставлял пустую ячейку. Перевёл на 2/4 (8 делится ровно).
Допы (5 карточек) тянули сироту на 2- и 3-колоночном — теперь 2/5 с
orphan-fill: последняя нечётная карточка занимает всю ширину на мобайле
(паттерн как в новостях/подписках).
2026-06-02 16:32:25 +03:00
c0mrade
a8213ce5d5 fix(datepicker): читаемость календаря в любой теме
Переменные --rdp-* библиотека объявляет на самом .rdp-root, поэтому
оверрайды на родителе (.rdp-dark) шадовились — отсюда синий акцент,
синяя рамка вместо заливки и тусклые дни. Теперь целимся в
`.rdp-dark .rdp-root` (специфичность выше базовой). Выбранный день —
залит акцентом, числа — яркие, сегодня — акцентная подсветка, все
цвета через токены темы (dark/accent), так что инвертируются для
светлых тем (champagne) и остаются читаемыми везде.
2026-06-02 16:32:24 +03:00
c0mrade
016da9f638 feat(ui): заменить все нативные date-инпуты на DateField
Тёмный DateField теперь везде, где был браузерный <input type="date">: фильтр
периода в Трафике, диапазон дат в Платежах и Аудит-логе, срок действия промокода.
Единый вид во всём кабинете. (datetime-local не трогал — там нужно ещё и время.)
2026-06-02 16:17:10 +03:00
c0mrade
edd2d25024 feat(ui): тёмный DateField на react-day-picker вместо нативного input
Нативный <input type="date"> выглядел чужеродно (браузерный светлый календарь).
Новый общий компонент DateField: react-day-picker внутри Radix Popover, тёмная
тема через CSS-переменные .rdp-dark, навигация — наши иконки из барреля,
локализация месяцев/дней по языку кабинета (date-fns). Принимает/отдаёт
'YYYY-MM-DD' — drop-in замена нативного инпута. Подключён в селекторе периода
статистики (свой период). Остальные места — следующими шагами.
2026-06-02 16:10:48 +03:00
c0mrade
9a05f23a04 feat(sales-stats): пресет «Этот месяц» (с 1-го числа) и он же по умолчанию
Статистика по умолчанию открывается за текущий месяц с 1-го числа до сегодня
(month-to-date), а не за фиксированные 30 дней. Добавлена кнопка «Этот месяц»
в селектор периода; util getMonthToDateRange/isMonthToDate.
2026-06-02 15:55:10 +03:00
c0mrade
80ac63102b feat(sales-stats): иконки на карточках во всех вкладках
Доделал душевный вид: карточки внутри вкладок (Триалы/Продажи/Продления/Допы/
Депозиты) тоже получили иконки в цветных чипах с тонами — единый стиль со сводкой
(регистрации/триалы/конверсия/длительность, продажи/выручка/чек/топ-тариф,
продления/%, допы/GB/устройства, пополнения/сумма/средний). Крупные суммы — без
копеек.
2026-06-02 15:45:40 +03:00
c0mrade
622ec318da fix(sales-stats): ровные иконки в карточках + не дублировать иконку
- StatCard: лейбл сверху, чип+значение в одной строке (чип центрируется ровно по
  значению, дельта ушла под строку) — иконки больше не «плавают» по вертикали
  из-за разной высоты карточек.
- «Активные подписки»: TicketIcon вместо SubscriptionIcon (та визуально как искра
  и дублировала «Новые триалы»).
2026-06-02 15:39:25 +03:00
c0mrade
dfc7768a40 feat(sales-stats): душевные карточки с иконками + фикс скачка при смене периода
- StatCard: иконка в мягком цветном чипе (tone success/accent/warning/…) в духе
  статистики Remnawave; дельта под значением. Обратно совместимо (valueClassName).
- Карточки сводки получили осмысленные иконки (доход/подписки/триалы/конверсия/
  продления/допы/пополнения) и цветовые тоны.
- Скачок при переключении периодов убран: на все запросы статистики добавлен
  placeholderData: keepPreviousData — старые данные остаются на экране, пока
  грузятся новые, без вспышки скелетона и прыжка вёрстки.
2026-06-02 15:32:53 +03:00
c0mrade
a687cc3d18 fix(sales-stats): крупные суммы сводки без копеек (короче, не режет)
formatWithCurrency по умолчанию даёт 2 знака без разделителей разрядов
(«2521000.00 ₽») — длинно и режется. Для итоговых сумм сводки (доход, доход с
допов, ручные пополнения) показываем целые рубли.
2026-06-02 15:26:12 +03:00
c0mrade
631e922f78 fix(sales-stats): не резать значения в карточках сводки (адаптив)
Карточки сводки резали суммы («252…», «100.0…»): на десктопе их было 8 в ряд
(слишком узко), а дельта стояла в той же строке и доедала место.
- Дельта переехала ПОД значение — значение получает всю ширину карточки.
- Сетка сводки: убрал xl:grid-cols-8, максимум 4 в ряд (карточки шире).
2026-06-02 15:22:33 +03:00
c0mrade
bb97ee8430 feat(sales-stats): разбивка допов по пакетам тоже на BreakdownList
Для консистентности: «Допы → по пакетам» теперь список с долями % и мини-баром,
как остальные категорийные разбивки, вместо отдельного bar-графика.
2026-06-02 15:17:34 +03:00
c0mrade
a685e542aa feat(sales-stats): дельты период-к-периоду на карточках сводки
StatCard получил опциональную дельту (стрелка + %), а страница статистики тянет
сводку ещё и за предыдущий равный период и считает рост/падение для метрик
периода: выручка, новые триалы, продления, доход с допов, ручные пополнения.
Для «всего времени» сравнение не показывается (не с чем сравнивать).
2026-06-02 15:15:34 +03:00
c0mrade
a75f42d056 feat(sales-stats): читаемые разбивки и stacked-бары, иконки платёжек, выручка
Полировка статистики продаж (читаемее/информативнее):
- BreakdownList — категорийные разбивки списком (иконка/цвет + значение + доля % +
  пропорциональный мини-бар) вместо bar-графика. В «Депозиты → по методам» теперь
  с иконками платёжек (PaymentMethodIcon), в «Продажи → по тарифам» — с долями.
- StackedBarChart — «по дням разбивкой» теперь stacked-бар вместо наслаивающихся
  полупрозрачных area (читалось плохо); тултип по дню показывает все серии.
- Продажи: добавлена карточка «Выручка» (total_revenue бэк отдавал, но не показывался).
2026-06-02 15:10:06 +03:00
c0mrade
5e837621ad fix(stats-charts): убрать focus-outline артефакт при клике по графику
В recharts столбцы/секторы/точки становятся фокусируемыми, и при клике браузер
рисует свою обводку — артефакт на read-only графиках. Добавил гашение outline
для .recharts-wrapper/.surface/.sector/.rectangle.
2026-06-02 14:57:40 +03:00
c0mrade
33f8ddac9a fix(payment-labels): добавить лейбл OverPay (показывался сырым ключом)
METHOD_LABELS не содержал overpay, хотя метод есть в боте — в статистике/истории
он отображался как сырой ключ «overpay». Добавил человекочитаемое название.
2026-06-02 14:45:43 +03:00
c0mrade
ec346d38d8 fix(navigation): single-tariff subscription detail всегда Close, не Back
Регрессия после a62d689: Back прятался только при deep-link входе
(depthRef === 0). При внутреннем переходе на /subscriptions/:id (depth > 0)
показывался Back, а в single-tariff «назад» уходил в /subscriptions, который
тут же редиректит обратно на деталь (Subscriptions.tsx) — петля, и юзер видел
Back вместо Close.

Решение depth-независимое: если список-редирект всё равно отбрасывает на эту
же страницу (single-tariff, ≤1 подписки = listRedirectsToDetail, инверсия
listIsSafe из обработчика), деталь — это верхний экран. Прячем Back всегда,
Telegram показывает нативный Close. Петля невозможна по построению: без кнопки
Back обработчик navigate(-1) в редиректящий список просто не вызвать.

Multi-tariff и >1 подписки сохраняют реальный Back к осмысленному списку.
2026-06-01 20:29:03 +03:00
c0mrade
827e428f33 Merge pull request #446 from BEDOLAGA-DEV/release-please--branches--main--components--cabinet-frontend
chore(main): release 1.55.0
2026-06-01 18:35:43 +03:00
github-actions[bot]
c50a3d9416 chore(main): release 1.55.0 2026-06-01 15:35:17 +00:00
c0mrade
388fceddd9 Merge pull request #445 from BEDOLAGA-DEV/dev
Релиз: миграция иконок, /admin/remnawave, колесо, флаги, подарки
2026-06-01 18:34:17 +03:00
c0mrade
484c3ad005 fix(subscriptions): fill the orphan card so a lone subscription has no empty cell
The My Subscriptions list is a 2-col grid on sm+, so a single subscription (e.g. one tariff in multi-tariff mode) left an empty cell. Stretch the last card to span both columns when it's the lone one in its row — same orphan-fill used for the news grid and admin stat cards.
2026-06-01 18:19:57 +03:00
c0mrade
bc8503469d fix(wheel): remove the spin-only sparkle lights entirely
The extra lights that appear during a spin (and bunched into a line through the centre) were the SPARKLE_POSITIONS effect — yellow animate-ping dots rendered only while isSpinning. Moving/scattering them never satisfied; they're unwanted during the spin. Remove them outright: while spinning, only the rim LEDs remain — no extra lights, no centre line.
2026-06-01 18:12:55 +03:00
c0mrade
2ce966cece fix(wheel): remove CSS filters from the spinning SVG (kill GPU-composite streak)
The center streak doesn't appear in screen recordings, which is the signature of a GPU-compositing artifact — CSS filters on SVG elements mis-render when the wheel group is promoted to a layer during the spin. Replace the two SVG CSS filters with compositing-safe equivalents: LED-glow blur -> radial gradient fill, emoji drop-shadow -> SVG-native feDropShadow. (HTML background blur left as-is; it's not on the SVG and isn't a light.)
2026-06-01 18:00:00 +03:00
c0mrade
6c5d1ee0b1 fix(wheel): keep spin sparkles in the outer ring, never near the centre
Confirmed from the user's spin video: the sparkles bunched into the middle during a spin because they were placed at 16-40% from centre (some right by the hub). Push them all to a 34-43% radius (golden-angle distributed) so they ring the outer wheel and the centre stays clear — no sparkles crossing or near the centre.
2026-06-01 17:46:36 +03:00
c0mrade
7716e32eec fix(wheel): scatter spin sparkles so they don't streak through the centre
The real cause of the center line: SPARKLE_POSITIONS (yellow dots shown only while spinning) used `20 + (i*10)%60` / `15 + (i*13)%70`. For the first ~6 sparkles the modulo never wraps, so top/left increment linearly and the dots land on a straight diagonal through the centre — the 'line of lights' during the spin. Scatter them on a golden-angle spiral inside the wheel instead.

Reverts the earlier LED-glow change (it wasn't the cause): the rim-light blur is restored to the original.
2026-06-01 16:45:35 +03:00
c0mrade
8a41aa7781 fix(wheel): kill the center-line LED artifact during spin (drop CSS blur filter)
The rim lights are static cx/cy SVG circles; their glow used a CSS filter:blur(2px). During the spin the wheel's transform transition promotes the SVG to a GPU layer, and Chrome mis-renders CSS filters on geometry-positioned SVG elements — the blurs streak toward the view-box origin, drawing a line of lights through the centre. Replace the blur with a soft radial-gradient fill (no filter, so the artefact can't occur).

Also drive both the chase speed and the per-dot delay from one --led-dur var so the chase stays in order at the faster spin speed instead of scrambling (the delay was a fixed 6s value that broke once the duration dropped to 2s).
2026-06-01 16:24:02 +03:00
c0mrade
b877d7f175 fix(flags): render flag emoji on Windows globally via a scoped flag font
Windows ships no glyphs for regional-indicator flag emoji, so flags fell back to country letters everywhere react-twemoji wasn't manually wrapped. Bundle the Twemoji country-flags woff2 locally (no runtime CDN — matters for our audience) and add it as an @font-face scoped with unicode-range to flag codepoints only, then put it first in every Tailwind font stack (sans/display/mono). The browser now uses it solely for flag characters, so every flag renders correctly app-wide with zero markup changes and no effect on any other text.
2026-06-01 16:00:28 +03:00
c0mrade
7b7ff53672 fix(news): fill the orphan card so the grid has no empty cell
On sm+ the regular news cards sit in a 2-col grid; an odd count left an empty cell. Move the featured card out of the grid (it was col-span-full anyway, which also skewed nth-child parity) and stretch the last regular card to span both columns when it's the lone one in its row — same orphan-fill used for the admin stat cards.
2026-06-01 15:49:29 +03:00
c0mrade
97d436721a fix(admin-remnawave): node metrics 3-rows on mobile, original single row on desktop
Jumping only happened on mobile (the wrap reshuffled when speeds changed width); desktop fits on one line and never jumped. So keep the 3 fixed rows for mobile (sm:hidden) and restore the original single flex-wrap row for desktop (hidden sm:flex). On mobile, download/upload now sit tight in a flex row (gap-4) instead of a full-width 2-col grid that looked too spread out.
2026-06-01 15:42:29 +03:00
c0mrade
43acb70ab9 fix(admin-remnawave): lay node metrics in 3 fixed rows (processor/traffic/versions)
The metrics sat in one flex-wrap, so when realtime speeds change width (Kb/s<->Mb/s, large values) elements reflowed and jumped between lines, and the card height shifted. Split into 3 fixed rows: processor (CPU load + RAM), traffic (down/up), versions (remnanode + xray). Traffic uses a 2-column grid so a wide value never pushes its sibling; tabular-nums keeps digit widths constant.
2026-06-01 14:52:28 +03:00
c0mrade
1b1046bb5c feat(admin-remnawave): add node/xray version icons like the panel
The panel marks each node version with its product logo. Add an XrayIcon (the Xray core brand pinwheel, custom SVG like RemnawaveIcon) and put RemnawaveIcon before the remnanode version + XrayIcon before the xray version, matching the original node rows. Drop the redundant 'xray' word since the logo conveys it.
2026-06-01 14:43:11 +03:00
c0mrade
297d75a92c fix(admin-remnawave): add RAM/CPU icons to node metrics like the panel
Checked the live panel — every node metric carries an icon there, but our card showed RAM% and load average as bare text while the speeds had icons. Add MemoryIcon before RAM% and CpuIcon before the load average so the metrics row matches the original.
2026-06-01 14:37:36 +03:00
c0mrade
1ef4b21614 fix(admin-remnawave): truncate long tags in node traffic accordion
Give the inbound/outbound tag min-w-0 flex-1 so a long tag shrinks and ellipsizes instead of pushing the byte counters off-screen on narrow viewports; tighten the counter gap.
2026-06-01 14:28:27 +03:00
c0mrade
6c9a77d419 feat(admin-remnawave): merge Traffic into Nodes as a per-node accordion
Drop the separate Traffic tab and fold its per-node inbound/outbound breakdown into the Nodes tab: click a node card to expand its traffic breakdown, click again to collapse (chevron indicator; action buttons stop propagation). Realtime totals (down/up/Σ) move to a compact line above the node list. Realtime stats now load on the Nodes tab.
2026-06-01 14:24:07 +03:00
c0mrade
50d7d21462 fix(gift): stretch my-gifts cards full-width instead of cramped 2-col grid
The 2-column grid inside the max-w-2xl container made each gift card a narrow ~330px half. Gift cards are content-rich (header, code, share preview, buttons), so stack them single-column full-width — they now stretch to the container width on every breakpoint.
2026-06-01 14:11:50 +03:00
c0mrade
7ca9c043dd feat(admin-remnawave): meaningful icons for users-by-status cards
Each Remnawave user status now gets a distinct icon instead of the same people glyph: ACTIVE -> check-circle, DISABLED -> prohibit, LIMITED -> gauge, EXPIRED -> clock (people icon as fallback for unknown statuses).
2026-06-01 13:52:22 +03:00
c0mrade
bd5e39fa56 fix(admin-remnawave): resolve provider favicon from site URL
The panel's provider.faviconLink is the provider's site URL (e.g. https://waicore.com/), not an image, so rendering it directly as <img src> failed and onError hid it. Derive the real favicon image from the URL's hostname via Google's favicon service, like the panel does.
2026-06-01 13:42:07 +03:00
c0mrade
0d024aec58 fix(i18n): spell the brand "Remnawave" instead of camelCase "RemnaWave"
Replace the user-facing 'RemnaWave' spelling with 'Remnawave' across all locale values (en/ru/zh/fa, 67 strings), t() display fallbacks, and code comments. Translation keys (refreshRemnaWave, ...) and code identifiers (RemnaWaveService, setRemnaWaveUuid, ...) are left untouched.
2026-06-01 13:31:12 +03:00
c0mrade
9309643b7e feat(admin-remnawave): provider favicon, mobile node card, meaningful icons
- Render the provider favicon in the node badge (new provider_favicon / provider_name fields), with graceful fallback.
- Mobile-friendly node card: stop the versions block from shifting right on wrapped rows, truncate long addresses, cap the provider badge.
- Give node-summary cards (total/online/offline/disabled) and traffic-period cards (2d/7d/30d/month/year) distinct, meaningful icons instead of all-the-same globe / bar-chart.
- Show traffic as 'used / limit' (e.g. 2.35 TB / ∞).
- Route every icon through the central barrel (drop the direct react-icons/pi import).
2026-06-01 13:08:30 +03:00
c0mrade
6ea767253f fix(gift): working bot deep-link + desktop layout + barrel icons
- Build the share deep-link as t.me/<bot>?start=GIFT_<code> (literal underscore). The old GIFT%5F encoding was passed to the bot verbatim by Telegram, so its startswith('GIFT_') never matched and activation silently failed.
- Source the bot username from the runtime branding config (env var is only a fallback), so the 'activate via bot' line no longer vanishes on builds without VITE_TELEGRAM_BOT_USERNAME.
- Widen the page on desktop (max-w-2xl) and show sent gifts in a 2-col grid; it was cramped to a mobile column.
- Replace the remaining hand-written SVG icons with the central barrel.
2026-06-01 13:08:18 +03:00
c0mrade
872f73157f feat(icons): add node/gift glyphs to the central barrel
Add Key, Inbox, Export, WarningCircle, Heartbeat, CalendarBlank, CalendarStar, ChartPie, ChartDonut, Cpu, Memory, Pulse as Phosphor Regular wrappers so feature pages stop hand-writing SVGs or importing react-icons/pi directly.
2026-06-01 13:08:06 +03:00
c0mrade
9aae399ce8 fix(cabinet-icons): correct two mismatched glyphs from the svg migration
A personal glyph-by-glyph re-review of the migration (the "steering wheel"
guard) found two wrong matches:
- MaintenanceScreen: a wrench/maintenance svg had been mapped to AdjustmentsIcon
  (sliders) → use a proper WrenchIcon (PiWrench).
- InstallationGuide tutorial button: an open-book svg had been mapped to
  DocumentIcon (file) → use BookOpenIcon (PiBookOpen).

Add WrenchIcon and BookOpenIcon to the central barrel. All other replacements
were verified correct (paper-plane→Send, server→HardDrives, warning-triangle→
Warning, etc.).
2026-05-31 23:43:32 +03:00
c0mrade
b5088c70a1 refactor(cabinet): migrate inline SVG icons to the central react-icons set
Audit-driven sweep: replaced 168 hand-written inline <svg> icons across 77
files with the central Phosphor (react-icons/pi) components from
@/components/icons, preserving each icon's size classes and colour (dynamic
stroke colours via the parent's currentColor, RefreshIcon's spinning state,
conditional rotate-180 chevrons).

Verified: tsc + vite build + eslint clean; an adversarial diff review of all
changed files found the replacements correct (70 files clean, 0 blockers).
Remaining inline <svg> dropped from 262 to ~95 — the survivors are legitimate
non-icons (brand/provider logos, loading spinners & framer-motion animations,
charts/sparklines, background decoration) plus a few ambiguous glyphs.
2026-05-31 23:37:12 +03:00
c0mrade
08f12daa40 feat(admin-remnawave): panel-style node rows with live metrics
Redesign the Nodes tab to match the Remnawave panel's node list:
- per-node row: status dot, online users, flag + name, provider badge, IP,
  traffic used with a progress bar (∞ when unlimited), uptime
- live metrics strip: RAM %, load average (1/5/15m), realtime ↓/↑ throughput
  and the node + xray versions, sourced from system.stats (memory, loadAvg,
  interface rx/txBytesPerSec) already returned by /api/nodes/
- provider name comes from the realtime stats (joined by node uuid), which is
  now loaded on the Nodes tab too
- poll the nodes endpoint every 5s so the live metrics stay fresh, like the panel
- formatSpeed mirrors the panel's bits-with-1024-step formatting
2026-05-31 22:53:33 +03:00
c0mrade
8613cb20aa fix(subscription): use the nav sparkle icon on the renew CTA button
PurchaseCTAButton (the "Продлить подписку" CTA) rendered a single inline
sparkle; swap it for the SubscriptionIcon component so it matches the two-star
sparkle in the navigation. Colour is inherited from the badge container.
2026-05-31 19:43:54 +03:00
c0mrade
5340f81a95 fix(subscription): use the nav SubscriptionIcon on the quick-renew button
The inline single sparkle didn't match the two-star sparkle shown in the nav.
Render the same SubscriptionIcon component so the button matches the nav exactly.
2026-05-31 19:38:57 +03:00