- Added key="email-link-form" on motion.div inside AnimatePresence
for proper exit animations
- Added htmlFor/id pairs on all three label+input groups for
screen reader accessibility
- Replaced hardcoded "your@email.com" placeholder with neutral format
- Removed hardcoded English fallback from t('profile.invalidEmail')
Users couldn't find the email linking form buried in the Profile page.
Now the Email row in Connected Accounts shows a "Привязать" button
(consistent with Google/Yandex/Discord) that expands an inline form
with email, password, and confirm password fields.
- Added email to isLinkableProvider check
- Inline AnimatePresence form expands under Email card (no popups)
- Removed duplicate registration form from Profile page
- Profile page now shows a redirect button to Connected Accounts
for users without email (email change/verify stays in Profile)
isActive used exact pathname match (===), so navigating to
/subscriptions/:id or /subscriptions/:id/renew would not highlight
the Subscription tab. Changed to startsWith for non-root paths
in both MobileBottomNav and DesktopSidebar.
Separated input state (searchInput/emailSearchInput) from committed
search state (search/emailSearch) to prevent every keystroke from
triggering API calls with stale pagination offset.
- Encode underscores as %5F in share URLs (bot link + cabinet link)
to prevent Telegram auto-link detection from stripping trailing _
- Strip GIFT-/GIFT_ prefix from URL code param before pre-filling input
- Use ?? instead of || for prefix stripping to preserve empty strings
- 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)
- Add-referral search: exclude users already in referrals list
- Both search debounces: clear stale results immediately when query
changes to prevent previous results flashing during debounce
- 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
- Commission display: use conditional instead of nullish coalescing
to avoid rendering "default%" (now shows "default" without %)
- loadReferrals: add || [] guard on data.users to prevent crash
- Search debounce: add cancelled flag to prevent stale responses
overwriting fresher results
- Search dropdown: add loading state to prevent "No users found"
flash during debounce wait
- 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)
- SettingsTab: use searchQuery.trim() instead of searchQuery to prevent
whitespace-only input from triggering empty search results view
- AdminSettings: fallback to branding when activeSection is unknown
instead of rendering blank content area
- SettingsSearchMobile: change sm:hidden to lg:hidden to match mobile
layout breakpoint (search was invisible on 640-1023px tablets)
- SettingsTableRow: sync isLongValue suffixes with SettingInput
(_list, _json, _periods, _discounts were missing)
- SettingsTableRow: read-only bools now show enabled/disabled labels
instead of raw "true"/"false" strings
- Use activeSection instead of activeTreeInfo as useMemo dependency
(activeTreeInfo is derived from activeSection, new object each render)
- Add category label matching to sidebar autocomplete search filter
to match global search behavior
- 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)
- Add NodeRealtimeStats and InboundTraffic TypeScript interfaces
- Type getNodesRealtime return as NodeRealtimeStats[]
- Add new Traffic tab to AdminRemnawave page showing per-node
inbound/outbound traffic sorted by total bytes
- Display download/upload/total per inbound tag for each node
- ServerInfo: remove cpu_physical_cores, memory_available
- NodeInfo: replace cpu_count/cpu_model/total_ram with versions/system,
xray_uptime changed to number, users_online now required
- NodeStatus: same field updates, remove dead uptime field
- Extract formatUptime to shared utils/format.ts
- Update AdminRemnawave and AdminDashboard components
The CosmosBackground orbs were overlapping the app's existing theme
background, creating a double-layer effect. Removed the component
entirely so only the theme background is visible.
- 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)
- 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
- device_price_kopeks: change > 0 to >= 0 so 0 (free) is sent instead of undefined
- traffic_topup_packages: change > 0 to >= 0 so free traffic packages can be added
- Reset isKeyboardOpen on route change so bottom nav always restores
- Disable autoFocus on payment input in Telegram Mini App
- Skip auto-focus on Profile email inputs in Telegram
When a new build changes chunk hashes, users with cached index.html
get "Failed to fetch dynamically imported module" errors. Now:
- lazyWithRetry() wraps all lazy imports with auto-reload on failure
- ErrorBoundary catches chunk errors and auto-reloads once
- 30s guard in sessionStorage prevents reload loops