Commit Graph

19 Commits

Author SHA1 Message Date
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
9c5e38b594 refactor(theming): replace red-* palette with semantic error-* tokens
Sweep 30 files: text/bg/border/ring/from/to/via/fill/stroke/shadow/divide/decoration/
outline/placeholder-red-N → -error-N. All red usages were semantically error/danger
(no brand red), so tokens now flow through the design-system CSS variables and respond
to palette overrides.
2026-05-26 11:08:22 +03:00
Fringg
6ffd0ae824 fix(format): convert price by exchange rate on landings and gift page
formatPrice from utils/format.ts only swapped the currency symbol
(220 ₽ -> ¥220) without converting the underlying amount, because it
had no source of exchange rates and the landing/gift pages never called
the useCurrency hook that knew how to fetch them.

- Add a module-level rates cache in utils/format with setExchangeRates
  setter. formatPrice now converts kopeks/100 from RUB to the target
  currency via currencyApi.convertFromRub when rates are cached.
- useCurrency pushes its loaded rates into that cache so any subsequent
  formatPrice call benefits, including subcomponents that cannot easily
  receive a prop.
- Call useCurrency in QuickPurchase, GiftSubscription, and
  AdminLandingEditor — the only entry points whose subcomponents still
  use the synchronous formatPrice (everything else already routes
  through useCurrency.formatAmount).

For RU locale behavior is unchanged. For EN/ZH/FA the amount is now
divided by the per-currency rate and formatted via Intl.NumberFormat
with 2 fraction digits (0 for IRR since amounts are large).
2026-05-13 04:56:15 +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
2b03e7e514 feat: send language in payment requests for localized descriptions
- Add language field to balance topup and landing purchase requests
- Cap news article featured image height with max-h-96

Cherry-picked from PR #362 (without favicon change — files don't exist)
2026-03-29 05:18:40 +03:00
c0mrade
2dab25c5a0 fix: resolve telegram auth token expiration and clean up codebase
- Fix stale initData comparison in clearStaleSessionIfNeeded that destroyed
  valid refresh tokens on mobile WebView reopens
- Restrict X-Telegram-Init-Data header to auth endpoints only
- Close Mini App on auth retry to force fresh initData from Telegram
- Merge Connection page error/not-configured states for better UX
- Remove unnecessary comments across 40+ files (section dividers,
  restating comments, noise catch block comments)
- Configure ESLint allowEmptyCatch to properly handle intentional
  empty catch blocks (62 warnings resolved)
2026-03-13 17:50:49 +03:00
Fringg
9aae9cc0e6 fix: mobile layout overflow on landing page
- Add min-w-0 to grid children to prevent CSS Grid blowout on mobile
- Add break-words to SanitizedHtml to wrap long URLs in footer text
2026-03-08 14:58:23 +03:00
Fringg
bda95ed23f fix: show infinity symbol for unlimited traffic on landing page
Display ∞ instead of 0 when traffic_limit_gb is 0 (unlimited),
matching the pattern used in TrialOfferCard and Dashboard.
2026-03-08 14:26:05 +03:00
Fringg
66bb86a5f2 fix: allow animated background to show through on landing pages
The root div had an opaque bg-dark-950 that covered the portal-rendered
animated background at zIndex:-2. Remove the opaque background when
background_config is present, matching the AppShell pattern.
2026-03-07 15:34:59 +03:00
Fringg
a404690334 feat: add configurable animated background for landing pages
Extract BackgroundConfigEditor as reusable controlled component from
BackgroundEditor. Add background settings section to landing editor.
Render per-landing backgrounds on public purchase pages.

- Extract BackgroundConfigEditor (value/onChange) from BackgroundEditor
- Refactor BackgroundEditor to thin wrapper with API persistence
- Add StaticBackgroundRenderer for prop-based config (public pages)
- Add background_config to landing API types and editor form
- Render animated background on QuickPurchase page
2026-03-07 12:46:05 +03:00
Fringg
f7afa002f0 feat: add discount UI for landing pages
Add discount configuration in admin editor and countdown timer
display on public purchase page.

- Add discount section to AdminLandingEditor with toggle, percent
  slider, datetime pickers, per-tariff overrides, live preview
- Add DiscountBanner with countdown timer to QuickPurchase page
  using AnimatePresence exit animation and proper interval cleanup
- Add strikethrough prices and discount badges to tariff cards,
  summary card and pay button with null/equality guards
- Add discount translations for ru and en locales
- Fix delete/toggle API return types to match backend contracts
- Fix isoToDatetimeLocal to preserve seconds on re-edit
- Fix preview rounding to match backend integer division
2026-03-07 07:01:46 +03:00
Fringg
6d5c6fb9b3 fix: mobile layout and period label translations for quick purchase landing
- Add i18n period labels (ru/en/zh/fa) instead of backend English-only labels
- Period tabs wrap on mobile instead of horizontal scroll
- Prevent horizontal overflow shifting the layout
2026-03-07 04:04:39 +03:00
Fringg
83fbd0e445 fix: auto-select single sub-option and remove unused return_url field
- Auto-select sub-option even when only 1 exists (backend needs suffix)
- Remove return_url from public LandingPaymentMethod (backend doesn't send it)
- Add else branch in auto-select for defensive null reset
2026-03-07 00:01:47 +03:00
Fringg
58e93cd2b7 feat: add payment sub-option selection on quick purchase page
Show sub-options (e.g. СБП, Карта) as selectable buttons when a
payment method has multiple enabled variants. Send suffixed
payment_method ID to backend for correct provider routing.
Split LandingPaymentMethod into public and admin types.
2026-03-06 23:53:31 +03:00
Fringg
ab13616b0f feat: мультиязычные лендинги + переключатель языка + исправления по ревью
Мультиязычность:
- LanguageSwitcher на публичной странице покупки
- Вкладки локалей в админ-редакторе (ru/en/zh/fa)
- LocaleTabs и LocalizedInput компоненты
- Типы: LocaleDict, AdminLandingFeature, toLocaleDict хелпер
- ?lang= параметр при запросе конфига лендинга

Исправления по ревью:
- contentIndicators включает все локализуемые поля
- LocaleTabs вынесен над секциями аккордеона
- text-left → text-start для RTL
- nonEmptyDict: защита от undefined
- featureIds/methodIds обёрнуты в useMemo
- toggleSection/updateFeature* обёрнуты в useCallback
- formatPrice вынесен в shared utils/format.ts
- RTL через LOCALE_META.rtl вместо хардкода
- Переводы zh/fa для валидации и landing
- Variable shadowing: t → tariff
2026-03-06 16:11:58 +03:00
Fringg
e01c9f5143 fix: админ-редактор — системные методы оплаты, реальные периоды тарифов, фильтрация на публичной странице
- Способы оплаты: загрузка из системного API вместо ручного ввода, toggleable чекбоксы
- Периоды тарифов: загрузка из period_prices через useQueries вместо произвольного ввода
- Публичная страница: глобальный список периодов, скрытие тарифов без цены для выбранного периода
- Локализация: добавлены ключи для 4 языков (ru/en/zh/fa)
2026-03-06 07:33:19 +03:00
Fringg
3cea48235f fix: безопасность и UX лендингов — 16 исправлений
- CRITICAL: усиленная CSS-санитизация (escape-последовательности, at-rules)
- CRITICAL: очистка setTimeout при unmount
- HIGH: useMutation для создания покупки (вместо ручного fetch)
- HIGH: DOMPurify + rel="noopener noreferrer" на ссылках
- HIGH: ErrorBoundary для публичных страниц покупки
- MEDIUM: стабильные UUID для DnD элементов
- MEDIUM: race condition в delete timeout
- MEDIUM: клиентская валидация формы лендинга
- MEDIUM: staleTime для админ-запросов
- MEDIUM: общая утилита getApiErrorMessage
- MEDIUM: уведомление о таймауте поллинга
- LOW: ARIA роли для radio-карточек и форм
- LOW: дедупликация иконок в LandingIcons.tsx
- LOW: cn() вместо template literals
2026-03-06 07:23:11 +03:00
Fringg
8b5d777f0a feat: публичные лендинг-страницы для быстрой покупки VPN-подписок
- QuickPurchase: публичная страница покупки с выбором тарифа/периода/оплаты
- PurchaseSuccess: статус покупки с поллингом и копированием ссылки
- AdminLandings: управление лендингами (создание, сортировка, удаление)
- AdminLandingEditor: полнофункциональный редактор лендинга
- API-клиент landings.ts для всех эндпоинтов
- Роутинг /buy/:slug и /buy/success/:token
- Локализация ru/en/zh/fa для всех текстов лендинга
- Санитизация HTML/CSS, rate limit защита, DOMPurify
2026-03-06 07:02:21 +03:00