- Drop the Duotone variants (their translucent secondary fill looked murky on
the dark theme) in favour of the clean regular weight across all icons
- WheelIcon: PiSteeringWheel → PiDiceFive (a steering wheel made no sense for
the fortune-wheel feature)
Replace the cabinet's hand-written heroicons-style SVG icon components with
the Remnawave panel's own icon family — Phosphor via react-icons/pi (Duotone).
All icons now live in a single central barrel (src/components/icons:
index + extended-icons + editor-icons, 144 icons); every feature file imports
from it instead of redefining inline SVGs (~440 icon defs removed).
- Add react-icons dependency
- Desktop nav (AppShell) now uses the central Phosphor icons, removing a
stroke-vs-duotone inconsistency the partial migration introduced
- Icons with custom props (SortIcon's direction, expandable chevrons) kept as
thin Phosphor wrappers; the RemnawaveIcon brand logo and animated SVGs
(checkmarks, spinners) are intentionally left as-is
- Restore the original per-call-site icon sizes wherever the centralized
default differed (211 call sites)
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).
- 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>
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
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
Add interactive sub-option toggles (Card/SBP/Crypto) to the landing editor
payment method configuration. Extract SortableFeatureItem and
SortableSelectedMethodCard into dedicated component files to reduce
AdminLandingEditor from ~1160 to ~860 lines.
- Add PendingActivationState component with activate button
- Add double-click ref guard on activation handler
- Add activatePurchase API function
- Update PurchaseStatus type with new fields
- Show gift message and recipient contact in success state
- Add pending activation i18n translations (ru, en, zh, fa)
- Improve AdminLandingEditor with allowed_periods support
- Use data.id instead of positional index for tariffPeriodsMap
- Clean up allowedPeriods when tariff is unchecked
- Filter out empty period arrays and orphaned tariff keys on submit
Мультиязычность:
- 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
- Способы оплаты: загрузка из системного API вместо ручного ввода, toggleable чекбоксы
- Периоды тарифов: загрузка из period_prices через useQueries вместо произвольного ввода
- Публичная страница: глобальный список периодов, скрытие тарифов без цены для выбранного периода
- Локализация: добавлены ключи для 4 языков (ru/en/zh/fa)
- QuickPurchase: публичная страница покупки с выбором тарифа/периода/оплаты
- PurchaseSuccess: статус покупки с поллингом и копированием ссылки
- AdminLandings: управление лендингами (создание, сортировка, удаление)
- AdminLandingEditor: полнофункциональный редактор лендинга
- API-клиент landings.ts для всех эндпоинтов
- Роутинг /buy/:slug и /buy/success/:token
- Локализация ru/en/zh/fa для всех текстов лендинга
- Санитизация HTML/CSS, rate limit защита, DOMPurify