Commit Graph

48 Commits

Author SHA1 Message Date
Fringg
39b9b8e1d2 fix(dashboard): счётчик устройств в плитке на главной
Дополнения к PR #544 при мерже.

Плитка на главной берёт число подключённых устройств из запроса ['devices'],
а он выключен ровно там, где плитка живёт: `enabled: !!subscription &&
!isMultiTariff`, тогда как блок с плиткой рендерится в мультитарифной ветке,
где `subscription` всегда null (свой запрос отключён по !isMultiTariff).
То есть счётчик показывал бы «0 из N» всегда, лимит устройств не срабатывал бы
никогда, а точки-индикаторы стояли бы пустыми — при том, что счётчик и есть
смысл плитки.

Добавлен запрос по конкретной подписке: getDevices принимает subscription_id,
ключ ['devices', id] — тот же, что на странице подписки, поэтому кэш общий и
лишнего похода в сеть при переходе туда не будет. Заодно вынес выбор
единственной подписки в переменную, чтобы условие показа и данные плитки не
разъезжались.

Отдельно поправлен потерянный пробел в className: `duration-300${...}` склеивал
класс в `duration-300cursor-not-allowed`, из-за чего оформление заблокированной
плитки не применялось. Баг приехал вместе с переносом из SubscriptionCardActive
(там он с самого начала), но раз компонент всё равно выделяется — чиню здесь.
2026-08-07 15:27:17 +03:00
Case211
06c7d13883 feat(dashboard): плитка «Подключить устройство» на главной
Подписку может выдать бонус рекламной кампании — она создаётся
автоматически, и человек попадает на главную с уже готовым доступом.
Плитка подключения жила только в карточке подписки на отдельной
странице, так что на главной ему нечем было воспользоваться: подписка
есть, а как подключить устройство — не видно.

Плитка вынесена из SubscriptionCardActive в отдельный компонент и
переиспользована обоими экранами, второй копии разметки не появилось.
На главной показывается, пока подписка одна: при нескольких выбор
устройства неоднозначен, и уместнее уходить в конкретную подписку.
2026-08-05 00:30:40 +05:00
kewldan
81fd96c2a7 fix(i18n): форматировать даты и числа локалью выбранного языка, а не браузера 2026-07-14 00:04:45 +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
1a3236f650 fix(cabinet): equal-height StatCards so dashboard balance/referral align
In a grid, the wrapping <Link> stretched to the row height but the
StatCard inside only took content height, so the referral card (which has
a subValue line) rendered taller than the balance card ('один толще
другого'). Make StatCard fill its cell with h-full and stretch the
StatsGrid links. Verified by rendering both cards against the built CSS:
balance 86px->94px, now equal to referral's 94px.
2026-06-06 21:44:07 +03:00
c0mrade
3b48abbb7b refactor(cabinet): unify all statistics onto the shared StatCard
Bring every stat strip across the admin panel and user cabinet onto the
canonical StatCard (icon chip + label-on-top + tone-coloured value),
matching the sales/Remnawave style the rest of the app already used.

- Convert ~20 hand-rolled stat strips across 21 pages/components
- Remove 4 divergent local StatCard components (Dashboard, Users,
  BanSystem, Payments) and migrate Remnawave's local StatCard (33 cards)
- Extend StatCard with optional subValue, loading skeleton, trailing slot,
  and icon-size normalisation (all backward-compatible)
- StatsGrid nav tiles now use StatCard while keeping link/chevron/loading
- Preserve all data, i18n keys, conditionals and interactive filters;
  fix the dead 'info' colour by mapping it to accent
2026-06-06 21:27:57 +03:00
c0mrade
800daf7506 fix(cabinet): sweep mobile flex-overflow across all pages + components
Full scan of every page/component for the Connected-Accounts overflow class
(long user data with no truncation pushing siblings off the card on mobile),
adversarially verified. Applied min-w-0 + truncate (break-all for copyable
values, break-words for titles) on text columns and shrink-0 on fixed
siblings across ~23 files: AppHeader, dashboard/SubscriptionCardActive,
subscription purchase/sheets (TariffPurchaseForm, ClassicPurchaseWizard,
SwitchTariffSheet), SuccessNotificationModal, PurchaseSuccess, Wheel sub
picker, ReferralNetwork UserDetailPanel, and the admin pages (AnalyticsTab,
BrandingTab, SubscriptionSubRow, user-detail Info/Referrals tabs,
ApplicationReview, PartnerCampaignAssign, Partners, PromocodeStats,
PromoOfferSend, RemnawaveSquadDetail, TariffCreate, Tickets, Withdrawals).
2026-06-04 23:32:59 +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
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
c0mrade
e18db02e42 fix(subscription): use the sparkle icon on the quick-renew button
The expired-card quick-renew button showed a lightning bolt, inconsistent with
the sparkle used on every other subscription CTA. Match the subscription CTA.
2026-05-31 19:24:43 +03:00
c0mrade
0a89838926 style(cabinet-icons): use regular Phosphor weight and switch wheel to dice
- 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)
2026-05-31 19:24:29 +03:00
c0mrade
d0e0b6b7e3 feat(cabinet): migrate all icons to the panel's Phosphor set
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)
2026-05-31 18:50:50 +03:00
c0mrade
75f775064c fix(decoration): strip 4 accent / status-hue leaks across hero surfaces
DESIGN.md 'Tunable-but-Scarce Rule' caps accent at <=10% of any
screen and reserves the accent + status hues for action / status,
never decoration. Four sites were spending those tokens on pure
chrome:

- Subscription.tsx purchased-traffic bar: zone.mainHex linear-gradient
  on a non-status progress bar; the per-purchase bar inherited the
  user's GLOBAL traffic-zone color (a fresh purchase could read
  'critical' just because the user's overall usage was). Replaced
  with solid accent-500 fill — same affordance, honest semantics.

- SubscriptionCardActive tariff badge: zone-colored linear-gradient
  background + zone-colored label. The tariff name has no traffic-zone
  semantics, so tinting it by the global traffic zone was a
  Status-Hue Lockout violation. Switched to glass innerBg/innerBorder.

- Balance hero card: bg-gradient-to-br from-accent-500/10 + glow prop
  on Card. Removed; flat surface, the giant numeric is the affordance.
  Eliminates the SaaS hero-metric template tell.

- Login background: two stacked fixed inset gradients (linear +
  accent radial halo) read as the airdrop / crypto aesthetic
  PRODUCT.md explicitly anti-references. Replaced with the plain
  body bg-dark-950.
2026-05-26 23:26:20 +03:00
c0mrade
312a34f715 fix(subscription): clear critique P0 + P1 + part of P2
Three issues from the post-PRODUCT.md /impeccable critique pass:

P0 — backdrop-blur-xl shipped to mobile on the two hero cards
(SubscriptionCardActive + Subscription detail). DESIGN.md
'Mobile-Without-Blur Rule' restricts backdrop-blur to ≥1024px.
Gate with lg: prefix so phones (half the audience) get the opaque
surface and skip the scroll-jank-inducing GPU blur layer.

P1 — Two destructive device-deletion sites still called the bare
browser confirm(). The codebase already imports useDestructiveConfirm
and uses it correctly for revoke (line 484). Bring the all-devices
and per-device delete paths onto the same platform-aware path, so
inside Telegram the user gets the native destructive popup with
haptic + theme, and on web they get the inline destructive panel.

P2 (partial) — Removed the decorative ambient radial-gradient halo
behind the Subscription detail hero card and the trial-shimmer border
overlay. Same chrome was distilled out of SubscriptionCardActive
earlier in this branch; the rationale is identical (zone/accent hue
leaking into pure decoration violates DESIGN.md Tunable-but-Scarce
+ Status-Hue Lockout rules). Trial state is conveyed by the header
badge.
2026-05-26 23:23:47 +03:00
c0mrade
b83dced3af distill(SubscriptionCardActive): strip 3 decorative-only chromes
Critique flagged the card as carrying '12 chrome elements for 2 facts'.
Removing 8 of 12 without browser QA risks gutting functional info.
Apply the conservative 3-element strip: only kill chromes with zero
information value.

  • Background radial glow (200x200 circle, top-right, decorative tint
    based on traffic zone) — pure ornament, no signal.
  • Trial shimmer border (animate-trial-glow on inset wrapper) — trial
    state is already signalled by the badge in the header; the second
    signal was visual noise.
  • Trial badge own animate-trial-glow + 2-stop gradient bg — flatten
    to single bg-accent-400/10 + border-accent-400/25 + solid text.
    The badge still says TRIAL with brand color; it no longer pulses.
  • Zone dot 8px-radius color-tinted boxShadow + 'transition: all' —
    drop the glow, transition only background. Reduces filter+composite
    work, dot still signals zone via color.

Kept (functional):
  • Big percentage + traffic-used metadata (primary info, not chrome)
  • Progress bar (primary signal)
  • Connect Device CTA with HoverBorderGradient (primary action;
    rotating border is its own UX debate, deferred)
  • Sparkline + connected devices count (functional metadata)

Full 12→4 collapse the critique recommended needs browser QA and a
design-direction call (move days/tariff/refresh to the detail page
header). Bigger restructure as separate PR.
2026-05-26 21:56:13 +03:00
c0mrade
f75b243f03 polish: clear 33/34 detector findings (bg-black, animate-bounce, spinner, transition-width, color)
After re-audit/critique cycle the deterministic detector (npx impeccable)
flagged 34 design-spec violations. Knock out 33 of them.

  • 25 × pure-black-white: sed-sweep bg-black/X → bg-dark-950/X across
    18+ files (modal scrims, photo viewer backdrop, code blocks). The
    base resolves to rgba(10,15,26,X) — visually identical to true
    black, satisfies the 'no #000' impeccable rule.
  • 3 × bounce-easing: SuccessNotificationModal celebration icon and
    SyncTab loading arrows used animate-bounce; replaced with
    animate-pulse. Bounce easing reads dated; pulse conveys 'in
    progress' without the cartoon feel.
  • 3 × border-accent-on-rounded: TelegramCallback + VerifyEmail
    spinners used 'border-b-2 border-accent-500' on rounded-full —
    detector reads it as a side-stripe even though it's a ring loader.
    Switch to canonical 'border-2 border-accent-500 border-t-transparent'
    (3/4 ring colored). Same visual, no spec violation.
  • 1 × ai-color-palette: AdminLandingStats had text-purple-400 on a
    gift-stats heading; purple is not in the brand palette. Swap to
    text-accent-400.
  • 1 × layout-transition: TrafficProgressBar.tsx fill bar still used
    transition: width 1.2s (slipped past the earlier optimize pass).
    Convert to transform: scaleX with origin-left. Same gradient, same
    duration, runs on the compositor.

Remaining: 1 finding in third-party Aceternity background-beams-collision
component (indigo-500 gradient on decorative WebGL background) — left
as-is, it's lifted decorative third-party code.

Detector: 34 → 1.
2026-05-26 21:52:56 +03:00
c0mrade
cef675825b refactor(tokens): extract subscription urgent + critical semantic colors
The #FFB800 (23 hits) and #FF3B5C (11 hits) raw hex literals across
Subscription / SubscriptionPurchase / TrialOfferCard / useTrafficZone
all represent the same semantic: 'expiring soon' and 'expired'.
Distinct from warning-500 (#f59e0b) and error-500 (#ef4444) — the
subscription timeline needs a sharper, hotter signal than the
system-wide warning/error roles.

Add two semantic tokens:
  --color-urgent-400:   255, 184, 0    (#FFB800)
  --color-critical-500: 255, 59, 92    (#FF3B5C)

Exposed via Tailwind as urgent.400 / critical.500. Sweep all
single-and double-quoted hex literals to rgb(var(--color-...))
form so style attrs, SVG stroke=, and ternary string values all
resolve through CSS vars (auto-respects future light-theme
variants).

Left as-is: 4 hex appearances inside gradient pair strings —
linear-gradient(135deg, #FFB800, #FF8C00) — where the partner
color (#FF8C00 / #FF6B35) isn't in the token set. Partial-replace
inside gradient strings would just add noise; full extraction of
the orange-ramp companion is out of scope for this pass.
2026-05-26 20:20:05 +03:00
c0mrade
8e0b63bac8 feat(a11y): cross-platform hardening + modal focus-trap from impeccable audit
Responsive / viewport:
- add .min-h-viewport / .h-viewport utilities (100dvh + Telegram --tg-viewport-stable-height)
- migrate min-h-screen / h-screen / 100vh across 18 files
- reveal hover-only controls on focus-within + touch (desktop nav, admin settings)
- grid breakpoints (TopUpAmount, Contests), larger touch targets, nav aria-labels

Platform routing (Telegram WebView correctness):
- clipboard -> copyToClipboard util with execCommand fallback (10 files)
- window.open -> openTelegramLink / openLink (Profile, Referral, ChannelSubscriptionScreen)
- window.confirm -> useNativeDialog (admin pages); PromoOffersSection -> useDestructiveConfirm
- window.prompt -> usePrompt / PromptDialogHost (TipTap link editors)
- ESLint no-restricted-properties guard against regressions (adapters/clipboard util exempt)

Modal accessibility:
- new useFocusTrap hook (focus trap, Esc, scroll lock, focus restore)
- role=dialog / aria-modal / focus-trap: Polls, SuccessNotificationModal, AdminPolicies,
  AdminPromoGroups, AdminCampaigns, BroadcastPreview (Telegram + Email)
- new global usePrompt store + PromptDialogHost
2026-05-25 23:16:07 +03:00
Fringg
e44a09312e fix(subscription): forward subscription_id to purchaseTariff to fix renew race
Companion to bedolaga-bot a527df23. Bot's purchase-tariff endpoint
now accepts optional subscription_id to resolve the EXACT target row
by ID instead of doing a (user_id, tariff_id) re-lookup that races
with concurrent panel webhooks (produces 'Тариф уже активен' +
refund + no extension).

Frontend changes:
* subscriptionApi.purchaseTariff() — new optional subscriptionId arg
  appended after trafficGb. Forwarded as subscription_id in POST body.
* SubscriptionPurchase.tsx — forwards URL searchParam subscriptionId
  (set when user lands here from 'Renew this subscription').
* SubscriptionCardExpired.tsx — passes subscription.id when renewing
  an expired daily tariff (the .purchaseTariff(tariff_id, 1) call).
2026-05-16 16:10:32 +03:00
Fringg
4de01ccd1d fix(dashboard): show full name on welcome, fix gender mismatch on trial-expired card
User reported single-letter welcome "Добро пожаловать, О!" (short first_name
"Олег"-style users) and a confused "Истекла" feminine label on the masculine
"Пробный период истёк" trial-expired card.

* Add `src/utils/displayName.ts` helper that composes `first_name + last_name`
  with `username` and `#telegram_id` fallbacks. Single source of truth for
  user-facing name rendering across the app.
* Apply `displayName(user)` in Dashboard welcome, AppHeader mobile drawer,
  and DesktopSidebar profile chip. Now a user with `first_name="О"` and
  `last_name="Иванов"` sees "О Иванов" instead of just "О".
* `SubscriptionCardExpired` — context-aware Russian label: for trial
  subscriptions render masculine "Истёк" (agrees with "пробный период"),
  for paid subscriptions keep feminine "Истекла" (agrees with "подписка").
  Uses i18next `context: subscription.is_trial ? 'trial' : ''` — falls back
  to base key for EN/ZH/FA which are grammatically neutral.
* Add `expiredDate_trial: "Истёк"` only to `ru.json` (no changes needed for
  other locales — i18next context falls back to `expiredDate`).
2026-05-13 09:02:10 +03:00
Fringg
f68b46699c fix: limited (traffic exhausted) subscription shows as expired
- Dashboard card: change date label from "Истекла" to "Активна до"
  for limited subscriptions (traffic exhausted but time remaining)
- Dashboard button: change text from "Трафик исчерпан" (dead-end) to
  "Докупить трафик" (actionable) for limited status
- Subscription page: CountdownTimer shows remaining time for limited
  subs instead of "Истекла" (was passing is_active=false)
- PurchaseCTAButton: don't treat limited as expired — limited subs
  still have time, should show extend CTA not "buy new subscription"
2026-04-23 03:56:07 +03:00
c0mrade
1cf9526713 fix: complete sparkles icon in trial offer card (was showing only 1 of 3 sparkle paths) 2026-04-03 18:36:11 +03:00
c0mrade
fd01c0f393 fix: preserve subscription context in navigation and cache keys
SubscriptionCardActive: passes ?sub= to /connection navigation.
Connection: QR state includes subscriptionId for back-navigation.
ConnectionQR: preserves subscription context on back button.
Dashboard + Subscription: localStorage traffic cache keyed per-subscription
to prevent cooldown bleed between different subscriptions.
2026-03-25 11:47:31 +03:00
c0mrade
f4de6d8ad8 fix: multi-subscription UI audit fixes and cache invalidation improvements
- Fix AdminUserDetail multi-subscription display with useMemo optimization
- Update Dashboard subscription cards and purchase entry points
- Fix WebSocket notification handlers for multi-subscription context
- Update SubscriptionPurchase cache invalidation for multi-tariff mode
- Fix Subscriptions list page and navigation patterns
- Update App routing, DeepLinkRedirect, and CommandPalette for multi-sub
- Add SubscriptionListCard component
- Fix TopUpResult and PromoOffersSection navigation
2026-03-23 18:57:05 +03:00
c0mrade
820ba46cc4 feat: multi-subscription frontend support
- Add SubscriptionListItem and SubscriptionsListResponse types
- Add getSubscriptions/getSubscriptionById API methods
- Add optional subscriptionId parameter to all 30+ API methods
- Create /subscriptions page with subscription list cards
- Add /subscription/:subscriptionId route for per-subscription management
- Dashboard: show multi-tariff navigation block when user has 2+ subscriptions
- Subscription page: read subscriptionId from URL params, pass to all queries
- SubscriptionPurchase: accept subscriptionId from query params
- SubscriptionCardExpired: pass subscription.id to API calls
- Full backward compatibility: without subscriptionId, works as before
2026-03-23 18:57:05 +03:00
Fringg
8636bd7659 feat: блокировка кнопки устройств при лимите + убраны дубли трафика
- Кнопка «Подключить устройство» заблокирована при достижении лимита
- Haptic feedback при нажатии на заблокированную кнопку
- Предупреждение «Отключите устройства для подключения новых»
- Убран дублирующийся текст трафика под прогресс-баром (безлимит)
2026-03-18 04:21:34 +03:00
Fringg
b4f9f332cf feat: add LIMITED subscription status support with traffic-exhausted UX
- Add is_limited to Subscription type
- Show yellow traffic-exhausted badge on Subscription and AdminUsers pages
- SubscriptionCardExpired: amber theme with warning icon and buy-traffic CTA for limited
- Dashboard: route limited subscriptions to SubscriptionCardExpired
- Subscription page: show additional options (buy traffic) for limited subs
- Use is_limited boolean instead of status string comparison
- Add trafficLimited translations for ru, en, zh, fa
- AdminUserDetail StatusBadge: add limited yellow styling
2026-03-12 18:37:22 +03:00
Fringg
8b056e0b46 fix: hide Quick Renew for expired trial subscriptions
Show only Tariffs button as primary CTA when trial expires.
Remove duplicate gift.copyMessage i18n keys.
2026-03-10 20:33:31 +03:00
Fringg
8ab740f8cb fix: route PendingGiftCard to gift activation tab instead of landing endpoint
PendingGiftCard was linking to /buy/success/:token which uses the landing
API (exact token match). Since gift tokens are now truncated to 12 chars
in API responses, this caused 404s. Route to /gift?tab=activate&code=
which uses the gift API with prefix-based lookup.
2026-03-10 07:08:13 +03:00
Fringg
8629cfea18 fix: daily tariff renewal uses purchaseTariff instead of renewSubscription
- Expired daily tariffs now call purchaseTariff(tariff_id, 1) for 1-day purchase
- Balance check uses daily_price_kopeks for daily tariffs instead of 100 kopeks minimum
- Disabled daily tariffs still use togglePause for resume
2026-03-10 03:30:13 +03:00
Fringg
6ea1de2e8a fix: harden gift subscription frontend after multi-agent review
- Handle expired status in GiftResult (stop polling + show FailedState)
- Add PollErrorState for balance mode poll errors (softer UX)
- Remove non-null assertions in handleSubmit (explicit narrowing)
- Wrap gift routes in ErrorBoundary
- Add pollErrorTitle/pollErrorDesc i18n keys to all 4 locales
2026-03-09 20:34:41 +03:00
Fringg
7940410d7d feat: support disabled daily subscription status in cabinet UI
- Show SubscriptionCardExpired for disabled daily subscriptions
- Use togglePause() API for resume instead of renewSubscription()
- Add "Suspended" status badge and balance query invalidation
- Add dashboard.suspended and subscription.pause.suspended translations (ru, en, zh, fa)
2026-03-08 21:36:10 +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
Fringg
d526d095de fix: заменить хардкодный зелёный (#3EDBB0) на акцентный цвет из темы
Все вхождения #3EDBB0, #2BC49A и rgba(62,219,176,...) заменены на
CSS-переменные --color-accent-400/500 из системы тем.

Затронутые компоненты:
- TrialOfferCard: кнопка, бордер, свечение, иконка
- Subscription: лейблы, иконки, кнопки паузы/копирования
- SubscriptionCardActive: бордер карточки, бейдж статуса
- SubscriptionPurchase: градиент триал-блока
- PurchaseCTAButton: градиент кнопки, иконка
- TrafficProgressBar: цвета прогресс-бара
- StatsGrid: убрана зависимость от useTrafficZone, используется accent напрямую
- trafficZone.ts: добавлены mainVar/mainVarRaw для CSS-переменных
- tailwind.config.js: trialGlow keyframes на accent цвет
2026-03-05 10:56:45 +03:00
Fringg
a3ddddfa8c fix: replace hardcoded green with theme-aware accent color
Normal traffic zone now uses configurable accent palette instead of
hardcoded success/green. Added useTrafficZone hook that resolves
mainHex dynamically from theme colors. Replaced emerald/teal classes
with success palette in SuccessNotificationModal. Fixed hardcoded
green hex values in Subscription and SubscriptionPurchase pages.
2026-03-04 02:50:28 +03:00
Fringg
4cdff9730b fix: improve light theme visibility for dashboard and subscription cards
- Increase card opacity and shadow contrast in glassTheme for light mode
- Add accent-tinted borders and shadows to subscription cards (active, expired, trial)
- Fix grid patterns to use dark lines instead of white in light mode
- Fix trial CTA button: solid green with dark text instead of invisible transparent
- Fix trial icon background: light accent gradient instead of hardcoded dark
- Add + prefix to referral earnings in StatsGrid
2026-02-28 06:37:40 +03:00
Fringg
d2f02d605c fix: clean up expired trial card - remove redundant badge and subtitle
- Remove red "Пробный период" badge from expired trial card
- Remove "Пробный период завершен" subtitle text
- Simplify header layout (no justify-between needed without badge)
- Change "Тарифы" button link to /subscription/purchase
2026-02-27 09:38:33 +03:00
Fringg
126f9ab9b9 refactor: extract purchase/renewal flow to separate page
Move ~1900 lines of purchase/renewal logic from Subscription.tsx into
a new SubscriptionPurchase.tsx page at /subscription/purchase.

- Create SubscriptionPurchase.tsx with tariffs grid, classic mode wizard,
  switch preview modal, and promo handling
- Create PurchaseCTAButton.tsx with animated gradient border, state-aware
  colors and context-aware text
- Create subscriptionHelpers.ts with shared utilities
- Add CopyIcon to shared icons module
- Register /subscription/purchase route with lazy loading
- Update SubscriptionCardExpired and PromoOffersSection navigators
- Add CTA translation keys to ru.json and en.json
- Remove all scrollToExtend references
2026-02-27 06:47:30 +03:00
Fringg
f4d7a2cc8d fix: subscription UI improvements - expired card, duplicate badges, live countdown
- SubscriptionCardExpired: use trialTitle for expired trials, remove traffic/devices grid, show only expiry date
- Subscription page: remove duplicate trial badge from zone indicator, fix expired trial badge showing "Пробный период" instead of "Истекла"
- Subscription page: replace static days counter with live HH:MM:SS countdown timer, merge "Действует до" into countdown block
- Subscription page: remove duplicate traffic percentage block
- Handle expired state in countdown (show "Истекла" in red instead of 00:00:00)
- Use live countdown.days for urgency threshold instead of stale server value
2026-02-27 06:04:06 +03:00
Fringg
27f85a1db1 fix: add subscription tab to desktop nav, fix device dots overflow, show available referral balance
- Add Subscription tab to desktop header navigation (was only in mobile)
- Fix device dots overflow for large limits (>10) by using progress bar
- Show available referral balance on dashboard instead of total earnings
- Add available_balance and withdrawn fields to ReferralInfo type
2026-02-27 04:53:37 +03:00
Fringg
21fc75a17d refactor: simplify dashboard stats grid to 2 cards
Remove Subscription and Earnings cards from StatsGrid.
Merge earnings display into Referrals card as subtitle.
Remove subscription-status onboarding step (target removed).
2026-02-25 12:04:35 +03:00
Fringg
70e1ed60bd feat: add animated gradient border to Connect Device buttons
Pure CSS conic-gradient animation with @property --border-angle,
dynamic accent color from traffic zone, light theme + reduced motion support.
2026-02-25 11:18:39 +03:00
Fringg
f474067efb feat: adapt dashboard and subscription page for light theme
- Add glassTheme utility with theme-aware color tokens
- Replace hardcoded rgba(255,255,255,...) with dynamic values
- Replace text-white with text-dark-50 for auto theme remapping
- Restyle subscription page block to match dashboard glassmorphic design
- Light mode: white semi-opaque cards with subtle shadows
- Dark mode: unchanged visual appearance
2026-02-25 11:00:14 +03:00
Fringg
a819f309c8 fix: show actual connected devices count instead of device limit 2026-02-25 10:30:37 +03:00
Fringg
bef5102a71 fix: remove duplicate tariff info line, make tariff card clickable 2026-02-25 10:27:44 +03:00
Fringg
6b688ad451 refactor: rewrite dashboard components to match prototype design
- Rewrite TrafficProgressBar with multi-segment gradient fill, flex-based
  zone tints, shimmer + highlight overlays, radial glow at fill edge
- Rewrite SubscriptionCardActive with zone header, big percentage on right,
  connect device card with device dots, tariff/days-left two-column row,
  sparkline placeholder, traffic refresh controls
- Rewrite SubscriptionCardExpired with red glow, grid pattern, gradient
  renew button, outline tariffs button
- Rewrite TrialOfferCard with animated glow background, grid pattern,
  icon glow animation, price tag with old price, gradient CTA buttons
- Rewrite StatsGrid with 2x2 card layout, icon+label+chevron header,
  big value numbers, zone-colored balance/earnings cards
- Update Sparkline with color prop and last-point dot indicator
- Update useAnimatedNumber to use easeOutExpo matching prototype
- Add formatTraffic utility for MB/GB/TB unit formatting
- Add mainHex to trafficZone for inline style support
- Update animations to match prototype timing
- Add new i18n keys for redesigned components
2026-02-25 10:22:50 +03:00
Fringg
909374d369 feat: add dashboard sub-components for subscription cards and stats grid
SubscriptionCardActive with animated zone dot, traffic bar, connect button.
SubscriptionCardExpired with red theme and renewal actions.
TrialOfferCard with stats grid and free/paid activation.
StatsGrid 2x2 with font-display values and font-mono labels.
2026-02-25 10:07:57 +03:00
Fringg
eb1f788033 feat: add TrafficProgressBar and Sparkline components
TrafficProgressBar with zone tints, shimmer animation, glow dot,
threshold markers, scale labels, unlimited/compact modes, and full
ARIA support. Sparkline SVG component ready for daily usage data.
2026-02-25 10:07:49 +03:00