window.confirm() is silently suppressed in Telegram Mini Apps and iOS
WebView, making unlink completely non-functional on mobile. Replaced
with two-click inline confirmation: first click shows destructive
"Confirm disconnect?" button, second click executes unlink. Button
resets on blur.
- Add aria-hidden="true" to all decorative SVGs (TelegramIcon, EmailIcon
in MergeAccounts + ConnectedAccounts, all 4 OAuthProviderIcon SVGs)
- Add role="radiogroup" with aria-label to subscription choice container
- Clear react-query cache after merge to prevent stale data
- Wrap checkAdminStatus() in try-catch to prevent throw after token set
- Navigate to /profile/accounts after merge instead of /
- Import useQueryClient for cache management
- Fix false success toast when response.success is false (CRITICAL)
- Remove mergeToken! non-null assertion in queryFn
- Add early return for missing mergeToken param
- Zero-pad minutes in formatCountdown (MM:SS format)
- Clamp negative seconds in formatCountdown
- Block all unlink buttons while any unlink mutation is pending
- Clear OAuth state only after validation succeeds (not before)
- Split getAndClearLinkOAuthState into read + clear functions
Add Connected Accounts page (link/unlink OAuth providers), Link OAuth
Callback handler, and Merge Accounts page with subscription comparison
and user choice. Includes API methods, TypeScript types, routes in
App.tsx, navigation from Profile, and i18n for all 4 locales (ru, en,
zh, fa). Merge page works without JWT auth (validated by merge token).
Each numbered rule (1, 2, 3...) was rendered as separate <ol> elements
all starting from 1. Added start attribute to preserve original numbering
and allowed it through DOMPurify sanitization.
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.
- Add sales stats API client with typed interfaces for all endpoints
- Implement AdminSalesStats page with summary cards and period selector
- Add TrialsTab with provider breakdown donut chart and daily area chart
- Add SalesTab with tariff/period breakdown and daily revenue chart
- Add RenewalsTab with period comparison and trend indicators
- Add AddonsTab with package breakdown bar chart
- Add DepositsTab with payment method breakdown bar chart
- Add SimpleBarChart, SimpleAreaChart, DonutChart components
- Add PeriodSelector with preset periods and custom date range
- Full responsive design (mobile/tablet/desktop/TG Mini App)
- ARIA accessibility (tablist, tab, tabpanel, aria-selected)
- Error handling with isError on all queries
- Empty data guards on all chart components
- i18n support across all 4 locales (en, ru, zh, fa)
- Extract shared chart constants to constants/charts.ts
- Add TREND_STYLES to stats/constants.ts for reuse
- Fix useChartColors with MutationObserver for theme changes
- Fix DailyChart date parsing to prevent timezone shift
- Add truncate to StatCard value for overflow safety
- Fix CampaignCard copy feedback inside try block
- Add NaN validation and staleTime to AdminCampaignStats
- Replace hardcoded strings with i18n keys in Referral page
- Add missing i18n keys across all 4 locales (en, ru, zh, fa)
- Add sales stats route to AdminPanel and App router
- Move shared chart components (DailyChart, PeriodComparison, StatCard) to src/components/stats/ with configurable label props
- Create shared types for chart data decoupled from partner API
- Add campaign chart data API client with deposits/spending split
- Enhance AdminCampaignStats with daily trends chart, period comparison, top registrations, and deposit/spending stat cards
- Add useChartColors hook with SSR guard and theme-aware CSS variable parsing
- Add cross-platform clipboard utility with execCommand fallback for Telegram WebView
- Add i18n keys for chart analytics across all 4 locales (en, ru, zh, fa)
- Responsive fixes: truncation for long names/values, responsive text sizing, touch-friendly copy buttons
- Add registrations_count, referrals_count, earnings_kopeks to campaign type
- Show per-campaign stats grid (registrations, referrals, earnings) under each campaign card
- Use conditional coloring for zero earnings (neutral instead of green)
- 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
Reset traffic on tariff switch is an admin-only setting.
Backend handles it via admin settings system (data-driven, no frontend code needed).
Reverted all user-facing toggle UI, API params, types, and translations.
- Add reset_traffic parameter to previewTariffSwitch and switchTariff API calls
- Add reset_traffic_available/reset_traffic_default fields to TariffSwitchPreview type
- Add toggle switch in tariff switch preview modal (shown when backend enables it)
- Initialize toggle from server default (reset_traffic_default)
- Pass reset_traffic flag on switch execution
- Add translations for all 4 locales (ru, en, zh, fa)
Remove the separate devices count card from the stats row since device
info is already shown in the connect-device card above. Stretch the
countdown timer to full width with timer on the left and expiry date
on the right. Revert squeezed font sizes now that space is available.
Match the Dashboard behavior: show individual dots for <= 10 devices,
switch to a compact progress bar for higher limits (e.g. 18 devices).
Prevents dot overflow breaking the connect-device card layout on mobile.
Add min-w-0 + overflow-hidden to the countdown card to prevent it from
blowing out the grid column. Reduce number font from 20px to 18px on
mobile (20px on sm+), tighten gaps and colon sizes to fit within the
50% grid column on 375px screens.
- Extract CountdownTimer into React.memo component to isolate 1s
interval re-renders from the full Subscription page
- Add box-shadow transition to .hover-border-gradient (was instant)
- Remove conflicting p-[1.5px] from PurchaseCTAButton (CSS border
already handles 1.5px)
- Replace transition-all with specific properties on bento-card-hover,
bento-card-glow, device dots, toggle switches, progress bars
- Reduce translateY hover from -4px to -2px to prevent cursor
losing hover target during fast vertical mouse movement
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
- 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
- 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
1. Add Math.round() to rubles→kopecks conversion (floating point safety)
2. Add missing {{currency}} template var in fa.json amountPlaceholder
3. Show desired_commission_percent in admin applications list
1. Add desired commission percent field to partner application form
and admin review page (Bug 1)
2. Add prominent "Requested Amount" row in admin withdrawal detail (Bug 2)
3. Switch withdrawal input from kopecks to rubles with auto-conversion
on submit (Bug 3)
Includes i18n updates for all 4 locales (ru, en, zh, fa).
- New ConnectionQR page with fullscreen QR display (no modal)
- QR button in InstallationGuide header next to platform selector
- Respects hideLink setting: shows URL below QR only when visible
- i18n translations for ru, en, zh, fa
- Add has_subscription to WheelConfig type
- Disable spin button when user has no subscription
- Add noSubscription guard in handleUnifiedSpin handler
- Show warning banner directing user to activate subscription
- Prevent banner stacking (noSubscription takes priority over daily limit)
- Add noSubscription i18n keys for ru, en, zh, fa
- 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
Decompose monolithic Dashboard into SubscriptionCardActive,
SubscriptionCardExpired, TrialOfferCard, and StatsGrid sub-components.
Replace Subscription page progress bar with TrafficProgressBar compact.
Add i18n keys for traffic zones, stats labels, expired/trial states.
- Create AdminRoleEdit.tsx for /admin/roles/create and /admin/roles/:id/edit
- Create AdminPolicyEdit.tsx for /admin/policies/create and /admin/policies/:id/edit
- Remove create/edit modals from AdminRoles.tsx and AdminPolicies.tsx
- Add new lazy routes in App.tsx with proper permission gates
- Keep delete confirmation as small inline modal (appropriate for destructive action)
- Read policy role_id from top-level field instead of conditions object
- Use policy.role_name for display, remove conditions.role_id lookup
- Remove duplicate role_id injection into conditions payload on submit
- Fix PermissionGate strings: policies:* -> roles:* to match backend
- Remove redundant client-side status filtering in audit log (already
filtered server-side)
- Move RBAC translations from banSystem to admin namespace in all 4 locales
- Fix AccessPolicy type: action (string) -> actions (string[])
- Fix AuditLogEntry type to match backend: add status, request_method, request_path, resource_type, user info
- Fix AuditLogFilters: resource -> resource_type, add status filter
- Handle null details safely in audit log expanded view
- Fix policy form submission and display to use actions array