Commit Graph

239 Commits

Author SHA1 Message Date
c0mrade
3adda6fa03 feat(sales-stats): конкретика в сводке
- «Активные подписки» теперь с % (нетто-изменение за период: новые
  платные − истёкшие; обычная дельта снимка была бы всегда 0).
- Новая карточка «Новые платные подписки» (с дельтой период-к-периоду).
- «Доход» больше не включает ручные пополнения (бэк отвязал).
- Убрал «Средняя длит.» из триалов — была вечный 0 и непонятна.
- Сводка 9 карточек: сетка 2/3 с orphan-fill на мобайле, без пустот.
2026-06-02 19:38:38 +03:00
c0mrade
436d9f860c feat(sales-stats): вкладка «Оплаты» — success-rate и неудачные покупки
Новая вкладка в статистике продаж:
- success-rate платежей (оплачено/создано) — общий + по каждому шлюзу с
  цветными барами (зелёный ≥90%, жёлтый ≥70%, красный ниже);
- кол-во попыток оплат;
- «Неудачные покупки» — внутренние откаты при ошибке покупки (сигнал
  надёжности, не возвраты).
Данные из нового эндпоинта /payment-health. Шлюзы без активности не
показываются. Карточки 1/3 (без сирот), адаптив. 4 локали.
2026-06-02 18:46:42 +03:00
c0mrade
ec23618a56 revert(sales-stats): убрать карточки возвратов из сводки
Возвраты для оператора всегда 0 (реальные возвраты только Apple/Tribute,
которых нет) — постоянно пустые карточки только путают. Сводка вернулась
к 8 карточкам (grid 2/4). Убрал также неиспользуемый invertDelta из
StatCard и ключи refunds из локалей.
2026-06-02 18:01:33 +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
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
a75f42d056 feat(sales-stats): читаемые разбивки и stacked-бары, иконки платёжек, выручка
Полировка статистики продаж (читаемее/информативнее):
- BreakdownList — категорийные разбивки списком (иконка/цвет + значение + доля % +
  пропорциональный мини-бар) вместо bar-графика. В «Депозиты → по методам» теперь
  с иконками платёжек (PaymentMethodIcon), в «Продажи → по тарифам» — с долями.
- StackedBarChart — «по дням разбивкой» теперь stacked-бар вместо наслаивающихся
  полупрозрачных area (читалось плохо); тултип по дню показывает все серии.
- Продажи: добавлена карточка «Выручка» (total_revenue бэк отдавал, но не показывался).
2026-06-02 15:10:06 +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
e1f401c333 fix(i18n): backfill 120 missing keys + close 2 typo regressions from purchase refactor
Surface: users (and especially Серёжа в bugs topic) were seeing raw i18n keys
like `subscription.promoGroup.title` rendered as literal text on the renewal
screen. Audit found 118 keys referenced in code that were absent from all 4
locale files, plus 140 keys missing only in zh/fa (silently falling back to
Russian for those users).

Root causes:
- cf52999 (extract TariffPickerGrid) introduced `subscription.promoGroup.title`
  that never existed — should have been `subscription.promoGroup.yourGroup`.
- Subscription.tsx used `subscription.autopay.title` (object path) while
  SubscriptionListCard.tsx used `subscription.autopay` (leaf) — i18next can't
  hold both shapes for the same key.
- Many user-flow keys (subscription.notFound, subscriptions.empty, common.ok,
  successNotification.*, etc.) were never added when their components landed.
- Historical drift had ~140 keys in ru/en that never got zh/fa translations.

Fix:
- Rename TariffPickerGrid usage `promoGroup.title` → `promoGroup.yourGroup`.
- Rename Subscription.tsx usage `autopay.title` → `autopay`.
- Add 116 brand-new keys across ru/en/zh/fa (464 entries).
- Add 8 partially-missing keys (en+ru gaps) for landing periods, dashboard
  campaign/referrer stats, and promo offer failure counts.
- Translate 280 entries to close the zh/fa drift.

Final state: 0 keys missing in any of the 4 locale files for any code-referenced
translation key. Build + tsc green.
2026-05-28 18:47:58 +03:00
c0mrade
80898ee2fe clarify(locales): sweep em-dashes from copy across 4 locales
Audit P1: 40 em-dash hits in src/locales/. Impeccable rule:
'No em dashes. Use commas, colons, semicolons, periods, or
parentheses.' Sentence-level em-dashes obscure structure;
explicit punctuation parses faster.

30 em-dashes replaced across en/ru/zh/fa:
  • 'X — Y' joining a clause          → 'X, Y'
  • 'X — Y' introducing value/detail  → 'X: Y'  ('X:Y' in zh)
  • 'X — Y' sentence-ending fragment  → 'X. Y'  ('X。Y' in zh)

Affected keys (same in all 4 locales unless noted):
  telegramReopenHint (ru only)
  freeDesc
  searchResults
  openUrlDirectHint
  deleteSuccessWithSubscriptions
  promoGroupsHint
  activePaidWarning (ru only)
  errors.title
  activateSuccessDesc
  noBonusDescription (en, zh, fa — no ru key)
  campaignRegistrations (en, zh, fa)

10 em-dashes kept intentionally (not copy violations):
  • noTariff: '—'                    (4 locales) — IS the value
                                       placeholder, not text
  • previewEmpty: '— empty —'        (ru + en) — decorative
                                       brackets framing 'empty'
  • statusLegend: ' — X •  — Y …' (4 locales) — legend
                                       convention, icon→label
                                       separator; switching to ':'
                                       would need a wider rework
                                       of the visual rhythm

All 4 JSON files parse cleanly; TSC/lint/build green.
2026-05-26 20:36:19 +03:00
Fringg
16b47119c9 feat(blocking): account-deleted recovery screen with bot deep-link
Pairs with backend feat(deleted-users) — when the cabinet returns
403 {detail: {code: 'account_deleted', message, bot_username,
telegram_deep_link}} the SPA shows a dedicated screen instead of the
generic auth-error toast.

* Adds AccountDeletedScreen with localized title/description, an
  Open-bot button using the backend-provided telegram_deep_link, and a
  Retry button that clears the block + reloads (so the next request
  observes the now-revived row).
* useBlockingStore gains accountDeleted slice mirroring the existing
  maintenance/channel-subscription/blacklisted slots — consistent with
  the precedent for cross-cutting 403 codes.
* apiClient: AccountDeletedError type + isAccountDeletedError guard +
  response interceptor routes the 403 into the blocking store.
* i18n: ru/en/zh/fa parity for title, description, openBot, retry, hint.

No behavior change for ACTIVE users. BLOCKED users still get the
generic 403 (the backend keeps the message opaque for admin actions).
2026-05-16 05:16:24 +03:00
Fringg
e964780e6f refactor(devices): close all MED/LOW review items
- Extract DEVICE_ALIAS_MAX_LENGTH to src/constants/devices.ts so the
  user page and admin page share one source of truth (mirroring the
  bot's ALIAS_MAX_LENGTH=64). Was duplicated as a magic 64 in both
  files (MED-5).
- Replace ✓ / ✕ / ✏️ glyphs in AdminUserDetail.tsx with inline SVGs,
  matching the icon style already used by Subscription.tsx for the
  same rename/save/cancel actions. Eliminates the platform-dependent
  emoji rendering inconsistency (MED-4).
- Add haptic feedback to the user-side renameDeviceMutation —
  notification('success') on save, notification('error') on failure.
  Brings it in line with the other Subscription.tsx mutations
  (LOW-9).
- Translate the new rename keys into zh.json and fa.json (en.json
  already covered in the previous follow-up). Cabinet now ships full
  parity across all 4 locales for the rename UI.
2026-05-16 04:07:25 +03:00
Fringg
aa8bfc9d08 feat(topup): direct-open payment page when method.open_url_direct is set
User asked for the gift-style seamless flow on balance top-up: provider
checkout opens inside Telegram MiniApp WebView without a click-to-open link
panel. Made it an admin per-method toggle so it can be enabled selectively.

src/pages/TopUpAmount.tsx — on topup mutation success:
* Move saveTopUpPendingInfo to BEFORE any redirect so /balance/top-up/result
  can still pick up the pending payment after the provider's return_url fires
* If method?.open_url_direct === true AND the URL is not a t.me/ deep link
  (Stars/CryptoBot), window.location.href = redirectUrl and return early
* Otherwise fall through to the existing setPaymentUrl panel — preserves
  current behavior for methods without the flag enabled

The t.me/ guard is important: window.location.href to a Telegram deep link
inside a MiniApp WebView is unreliable (native shell cant always intercept).
Those URLs continue to go through openTelegramLink / openInvoice in the panel
path. Stars never reaches topUpMutation.onSuccess anyway (handled by the
separate starsPaymentMutation); the guard is defense-in-depth for CryptoBot
and any future t.me-deep-link providers.

src/pages/AdminPaymentMethodEdit.tsx — added Open URL directly toggle with
the same slider styling as the is_enabled toggle. Defaults to off.

src/types/index.ts
* PaymentMethod.open_url_direct?: boolean (user-facing)
* PaymentMethodConfig.open_url_direct: boolean (admin shape)

Translations added for ru/en/zh/fa with a hint clarifying behavior and the
t.me/ exemption.
2026-05-13 10:50:31 +03:00
Fringg
b4eb0fa859 feat: add Lava payment provider support
- METHOD_LABELS entry for lava
- Custom SVG icon: dark base with orange-red lava flow and ember dots
- Admin settings tree node: payments_lava
- Locale strings (ru/en/zh/fa) for descriptions and admin labels
2026-05-04 20:16:01 +03:00
Fringg
7def84718b feat: add Jupiter and Donut payment provider support
- METHOD_LABELS entries for jupiter and donut
- Custom SVG icons: Jupiter (planet with ring), Donut (glazed donut)
- Admin settings tree nodes: payments_jupiter, payments_donut
- Locale strings (ru/en/zh/fa) for descriptions and admin labels
2026-05-04 19:36:35 +03:00
Fringg
f7cc445127 feat: add subscription reissue button with cooldown timer
- Add revokeSubscription API method
- Add reissue button in Additional Options with amber styling,
  destructive confirm dialog, 15-min cooldown with countdown timer,
  localStorage persistence across page reloads
- Add i18n keys in all 4 locales (en, ru, zh, fa)
2026-05-04 17:22:05 +03:00
Fringg
7d940091fa feat: add Antilopay payment provider support
- Add antilopay to METHOD_LABELS for stats charts
- Add PaymentMethodIcon case with amber gradient SVG
- Add payments_antilopay to admin settings tree
- Add i18n keys in all 4 locales (en, ru, zh, fa)
2026-05-04 07:44:52 +03:00
Fringg
85a34b1947 feat: add Etoplatezhi payment provider support
- Add etoplatezhi to METHOD_LABELS for stats charts
- Add PaymentMethodIcon case with green gradient SVG
- Add payments_etoplatezhi to admin settings tree
- Add i18n keys in all 4 locales (en, ru, zh, fa)
2026-05-04 07:18:14 +03:00
Fringg
65f94931c5 feat: add TV Quick Connect to connection page for Android TV / Apple TV
When user selects Android TV or Apple TV platform (from non-TV device),
shows 5-char code input + QR scanner to send subscription to TV via
Happ TV API. Hybrid QR strategy: native Telegram scanner on mobile,
html5-qrcode CDN fallback on desktop. i18n keys for ru/en/zh/fa.

Based on PR #415 by @smediainfo.
2026-05-04 06:22:29 +03:00
Fringg
58887138fc feat: add Apple IAP (apple_iap) payment method support
Add apple_iap across all payment surfaces: method label, icon (Apple
logo SVG), admin settings tree (APPLE_IAP category), and all four
locale files (en, ru, fa, zh) with payment description + tree label.
2026-05-04 06:01:20 +03:00
Fringg
020f4c95e2 feat: landing analytics goals, daily bar chart, referrer tracking, contact persistence
- Add per-landing analytics goals (view/click) with admin editor toggle
- Add sticky pay button option for mobile landing pages
- Add daily purchases bar chart (created vs paid) to landing stats
- Replace single purchase count with created/paid split in stats summary
- Add referrer tracking to purchases with hostname display in stats
- Add time display to purchase cards alongside date
- Pass user timezone to stats API for correct daily grouping
- Clamp referrer (500 chars) and subid (255 chars) to backend limits
- Persist contact value per-landing-slug in localStorage
- Fire buy_success analytics goal on successful delivery
- Export USER_TIMEZONE from format utils
- Add analytics/stats translations for fa.json and zh.json locales

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 09:17:52 +03:00
Fringg
80059681da feat: Yandex Metrika CID tracking, offline conversions UI, sticky pay button
- Pass yandex_cid to all auth endpoints (telegram, email, OIDC, OAuth)
- Add OfflineConvGoal interface and offline_conv_* fields to AnalyticsCounters
- Add storeYandexCid API method for server-side CID persistence
- Add analytics fields to LandingConfig (view/click goals, sticky_pay_button)
- Add yandex_cid/referrer/subid to PurchaseRequest
- Add offline conversions UI block in admin AnalyticsTab
- Add cacheYandexCid, syncYandexCid, fireAnalyticsEvent to analytics hook
- Create yandexCid.ts utility (localStorage get/set helpers)
- Add sticky pay button with portal on mobile in QuickPurchase
- Fire view/click analytics goals on landing pages
- Persist contact value and referrer/subid in session/localStorage
- Add i18n keys for offlineConv and apiKey in all 4 locales
2026-04-29 08:59:19 +03:00
Fringg
e1d2f8cee4 feat: show VPN connection info and subscription request history on admin user detail
- Load panelInfo eagerly on page load (not just on subscription tab)
- Add VPN connection card to info tab: last/first connection, online status indicator, last node
- Rename "Last activity" to "Bot activity" to distinguish from VPN connection
- Show cabinet_last_login field that was never rendered
- Add collapsible subscription request history section in subscription tab with paginated table
- Add subscription request history API types and method
- Add i18n keys for all 4 locales (ru, en, zh, fa)
2026-04-29 06:11:12 +03:00
Fringg
b01ffe3309 feat: add campaign/partner filters, delete_user action, and fix modal positioning in AdminBulkActions
Add campaign and partner filter dropdowns for user filtering, implement
delete_user bulk action with delete_from_panel checkbox, fix ActionModal
to use createPortal with viewport-fixed positioning and safe area insets.
2026-04-24 16:04:20 +03:00
Fringg
656946952a feat: info pages — tab replacement, custom pages on /info, responsive fixes
- Add replaces_tab field to admin editor with conflict detection
- Show custom InfoPages as extra tabs on /info page
- Replace built-in tab content when replacement is configured
- Rich DOMPurify sanitizer for TipTap content (images, video, iframe)
- Fix query race: built-in queries wait for tab-replacements to load
- Horizontal scroll for tabs on mobile, 44px touch targets
- Responsive prose: scrollable tables, h-auto on img/video, light theme borders
- Loyalty tier names truncation, progress bar labels stack on mobile
2026-04-24 15:01:11 +03:00
Fringg
1ee0f18343 feat: FAQ pages — Q&A builder in admin, accordion view for users
Admin editor:
- page_type selector: "Страница" / "FAQ" radio buttons
- When FAQ: FaqBuilder component replaces TipTap editor
  - Question input + answer textarea per Q&A item
  - Add/remove items, up/down reorder buttons
  - Per-locale Q&A (stored as JSON array in content JSONB)
- "Создать FAQ" button in list alongside "Создать страницу"
- Filter tabs: Все / Страницы / FAQ
- Page type badge on each row (accent for pages, amber for FAQ)

Public view (InfoPageView):
- Auto-detects page_type='faq' and renders FaqView component
- Accordion with smooth height animation (300ms ease-in-out)
- Search filter appears when >3 questions
- DOMPurify sanitization on FAQ answers
- Single-item expand (click to open/close)

i18n: full FAQ keys for all 4 locales (questions, answers,
search, counts, empty states, builder labels)
2026-04-24 14:02:03 +03:00
Fringg
7d6d0ba344 feat: information pages — admin editor with TipTap, public viewer
Admin panel:
- New section "Информационные страницы" in admin nav
- Admin list: slug, title, active toggle, edit/delete, sort order
- Admin editor: full TipTap (bold, italic, headings, lists, links,
  image/video upload, alignment, highlight, code blocks)
  with locale tabs (RU/EN/ZH/FA) for title and content
- Create/edit via URL params, slug auto-generation

Public:
- /info/:slug route with DOMPurify sanitized content
- Locale fallback, Telegram safe area, loading skeleton, 404

i18n: all 4 locales
2026-04-24 08:11:20 +03:00
Fringg
33f32648f0 feat: improve squad management UX — rename, swap icons, color gift button
1. Rename "Управление серверами" → "Управление сквадами" across all
   4 locales (ru, en, zh, fa) — title, subtitle, nav, subscription
   options. The page manages RemnaWave squads, not individual servers.

2. Swap toggle button semantics — the button now shows the ACTION
   that will happen on click, not the current state:
   - Active squad: red X icon (click to deactivate)
   - Inactive squad: green checkmark (click to activate)

3. Gift/trial button: amber/orange when functional (clearly clickable),
   gray when disabled. Previously always gray, making it look inactive
   even when it could be clicked.
2026-04-23 05:00:36 +03:00
Fringg
48827e430d feat: add AuraPay payment provider to cabinet frontend
- Payment method label and icon (violet gradient, AP)
- Admin settings tree entry for AURAPAY category
- Locale strings and descriptions for ru, en, zh, fa
2026-04-18 00:05:56 +03:00
Fringg
770a5803d6 feat: add RollyPay payment provider to cabinet frontend
- Payment method label and icon (green gradient, RP)
- Admin settings tree entry for ROLLYPAY category
- Locale strings and descriptions for ru, en, zh, fa
2026-04-16 05:55:21 +03:00
Fringg
31ac511a09 feat: add PayPear payment provider to cabinet frontend
- Payment method label and icon (amber gradient)
- Admin settings tree entry for PAYPEAR category
- Locale strings for ru, en, zh, fa
2026-04-16 05:26:09 +03:00
c0mrade
e6f6713b0e fix: batch bug fixes matching backend changes
- Promo code errors: add inactive/not_yet_valid translations (4 locales) + Balance.tsx parser
- Daily 100% discount: render price block when originalDailyPrice > 0
- Webvisor: enable in Yandex Metrika init config
- Referral attribution: consume campaign/referral slugs only after successful auth (all login methods)
- registerWithEmail: consume referral code after success
- AdminSettings: move TARIFF_MODE_SETTINGS to module scope (fix eslint warning)
2026-04-08 18:33:47 +03:00
c0mrade
207af81c95 fix: support 100% discount display + fix Telegram link widget race condition
- formatPrice shows "Бесплатно/Free" when price is 0 (SubscriptionPurchase, Subscription, RenewSubscription)
- Add subscription.free locale key (ru/en/zh/fa)
- Fix TelegramLinkWidget NS_BINDING_ABORTED: move legacy widget callback to ref pattern, remove unstable deps (showToast, t, navigate) from useEffect — prevents iframe destruction on re-render
- Clean up OIDC effect deps
2026-04-03 17:23:38 +03:00
Fringg
222a1239ef feat: add "Add Referral" button to referrals tab
- New search UI in referrals list section to find and bind users
  as this user's referrals (calls assign-referrer on target user)
- Debounced search with cancelled flag and loading state
- Click-outside to close dropdown
- i18n: addReferral, referralAdded keys in all 4 locales
2026-03-29 04:02:40 +03:00
Fringg
e32663f291 feat: add Referrals tab to admin user detail page
- New Рефералы tab with three sections:
  - Referred By: shows who referred this user, assign/remove referrer
  - Stats grid: total referrals, earnings, commission, referral code
  - Referrals list: all referred users with navigate and remove buttons
- User search with debounce for assigning referrer
- Shows warning when selected user already has a referrer
- API methods: assignReferrer, removeReferrer, removeReferral
- i18n translations for all 4 locales (en, ru, zh, fa)
2026-03-29 03:43:47 +03:00
Fringg
21813ef82e feat: redesign admin settings with tree navigation and compact layout
- Replace flat sidebar with collapsible tree groups (7 groups, 52 sub-items)
- Map all 61 backend category keys to sidebar navigation
- Add QuickToggles panel for boolean settings
- Replace card-style SettingRow with compact SettingsTableRow
- Add breadcrumb navigation for tree sub-items
- Add source badges (modified/DB/ENV) on settings
- Implement two-level mobile navigation with group chips
- Add i18n translations for all new keys (en, ru, zh, fa)
2026-03-29 02:38:52 +03:00
Fringg
5d7b94fc48 fix: add i18n keys for Traffic tab, prevent sort mutation
- Add traffic tab/section locale keys to ru, en, zh, fa
- Use spread copy before .sort() to avoid mutating React Query cache
2026-03-28 22:52:30 +03:00
Fringg
0bb064e2c0 feat: redesign admin panel with glass morphism UI, animated background, and stats bar
- Rewrite AdminPanel with cosmic floating orbs background using theme colors
- Add stats bar: uptime, bot version, cabinet version, trial/paid counts from API
- Glass morphism cards with 3D tilt effect (rAF-throttled) and entry animations
- Live search with Cmd+K shortcut, highlight matches, permission-aware filtering
- Register Tailwind `light:` variant for proper light theme support
- Mobile optimizations: reduced orbs (3), lower blur (80px), compact 2-col stats
- Telegram MiniApp safe area support via useTelegramSDK
- Full accessibility: aria-hidden on decorative SVGs, aria-labels, role="status"
- React.memo on StatsBar, GlassCard, CosmosBackground for render performance
- Memo-stable section refs via Set<id> pattern to preserve GlassCard memo during search
- i18n keys added to all 4 locales (ru, en, zh, fa)
2026-03-28 21:17:51 +03:00
Fringg
aa989a6ade feat: add subscription selector to admin sync tab for multi-tariff
- Add subscription dropdown to sync tab when user has 2+ subscriptions
- Pass subscription_id to getSyncStatus, syncFromPanel, syncToPanel
- loadSyncStatus re-fetches when selected subscription changes
- Show tariff name in UUID info section from API response
- Add selectSubscription i18n keys to all 4 locales
2026-03-28 19:50:10 +03:00
c0mrade
a50c06c101 feat: add Remnawave panel 2.7.0 support
- Add MONTH_ROLLING traffic reset mode (button, locales: ru/en/zh/fa)
- Add NodeInfo config_profile and active_plugin_uuid fields
- Update bandwidth display: remove /s suffix, label as Inbound Traffic
- Update tariffs.ts type comment with MONTH_ROLLING
2026-03-28 12:35:47 +03:00
Fringg
723591e5c3 feat: add media upload to news editor with drag-drop, paste, and file picker
Upload images/videos directly to server from editor. DOMPurify video
sanitization, XSS protection, upload cancellation on unmount.
2026-03-23 11:58:18 +03:00
Fringg
38b0f4be9a fix: add news link to admin panel, prevent empty news section flash
- Add News item to marketing group in AdminPanel with NewspaperIcon
- Add news nav/panel translations for all 4 locales (ru, en, zh, fa)
- Don't render NewsSection until query confirms articles exist
- Remove skeleton that caused brief flash when news list is empty
2026-03-23 11:21:25 +03:00
Fringg
74e6d52fee fix: news feature security, accessibility, performance improvements
- DOMPurify strict allowlist with sandbox on iframes
- safeColor() CSS injection prevention for category_color
- URL validation (http/https) for images and links
- encodeURIComponent for slug in API calls
- Keyboard navigation on news cards (Enter/Space)
- aria-label, aria-pressed, type=button on all buttons
- 44px touch targets on remaining buttons
- memo() wrappers + stable callbacks (fewer re-renders)
- Canvas draw call batching (~75% reduction)
- TipTap extensions useMemo deps fix ([t] -> [])
- Featured image loading=eager fetchPriority=high (LCP)
- Unsafe Number(id) NaN guard
2026-03-23 11:09:52 +03:00
Fringg
99fc33625e feat: add news section with admin editor and public article view
- NewsSection with animated grid background, category filters, featured cards
- AdminNews list page with toggle publish/featured, pagination
- AdminNewsCreate with TipTap rich text editor, URL validation
- NewsArticle detail page with DOMPurify sanitization, Telegram back button
- Toggle component with WCAG 44px touch targets, role=switch
- Prose styles for highlight marks and responsive iframes
- i18n translations (en, ru, zh, fa)
2026-03-23 10:49:46 +03:00
Fringg
5b12784ab8 feat: show subscription revenue and referral earnings in network stats
Add subscription revenue (total from subscription payments) to the
stats panel alongside referral earnings. Layout updated to 2-column
grid with subscription revenue highlighted in accent color.
2026-03-22 10:31:29 +03:00
Fringg
cc64f7b8ea fix: rename "Regular user" to "No subscription" in legend
The gray color in the referral network legend represents users without
a subscription, not "regular/organic" users. Updated labels in all
4 locales (en, ru, fa, zh) to reflect this.
2026-03-22 09:51:43 +03:00
Fringg
b18f3fb211 feat: add fill+border dual-color nodes, radial layout, and dark labels
Node fill color now encodes subscription status (paid/trial active/expired),
while border color indicates referral role (partner/top/active referrer).
Added BFS-based radial initial positioning to spread referral branches outward.
Updated legend with two sections and adjusted FA2 gravity for less clustering.
2026-03-22 09:25:23 +03:00
Fringg
b289ea9c23 feat: color-code referral network nodes by subscription status
Add subscription status colors: green (paid active), blue (trial active),
red (paid expired), orange (trial expired). Extract NODE_COLORS constant
as single source of truth. Add SubscriptionStatus union type. Show status
badge in user detail panel. Add i18n for all 4 locales.
2026-03-22 09:07:32 +03:00
Fringg
8e59af96c5 fix: superadmin assignments show ENV badge, block UI revoke
Superadmin role (level 999) is managed via env config only. Hide X
button for superadmin assignments and show ENV badge instead. Filter
superadmin from assignable roles dropdown. Add envManaged translation
to all 4 locales.
2026-03-22 08:41:41 +03:00