- Raise portal z-index to z-50 so it covers MobileBottomNav
- Use TG SDK bottom safe area (JS) via CSS variable instead of unreliable env()
- Fix TicketNotificationBell regression: conditional inline style for fullscreen only
- Add bottom safe area padding to detail panel scroll areas
- Extract useHeaderHeight() hook — single source of truth for header
height across AppShell, ReferralNetwork, and TicketNotificationBell
- Portal top offset now computed dynamically from TG safe area insets
instead of hardcoded top-16 (fixes overlap with TG header bar)
- Two-row mobile layout: title + selector on separate rows so dropdown
gets full width on small screens
- Add MOBILE_HEADER_HEIGHT_PX and DESKTOP_HEADER_HEIGHT_PX to constants
- Fix TicketNotificationBell using hardcoded 45px for all platforms
- removeScope preserves graph state when items remain (only resets on empty)
- Extract shared CheckIcon, CloseIcon, Spinner, ScopeListItem components
- Unify three handleSelect* into single handleToggle
- Fix shared mutable Set in reset state (factory function)
- Add MAX_SCOPE_ITEMS UI feedback (disabled button, warning banner)
- Correct ARIA: dialog > tablist + listbox > option structure
- Event listeners only attached when dropdown is open
- isSelected uses Set lookup via useMemo instead of O(n) scan
- Clear user search only on add, not on deselect
- Replace full graph with scoped view (campaign/partner/user)
- ScopeSelector component with tabs and searchable dropdown
- Store resets all state on scope change (selection, hover, filters)
- Remove dead NetworkSearch component and graphInstance globals
- i18n keys for scope selector in all 4 locales
Portal to document.body escapes main's contain:content which broke
fixed positioning (container had height:0 and was clipped to main).
Visual fixes for 662+ node graphs:
- labels only on large nodes (threshold 6→14, density 0.5→0.12)
- leaf nodes 5px, referrers 10px, campaigns 16px
- edge opacity reduced to prevent white starburst
- default edge color opacity lowered
- fix top offset breakpoint: sm:top-14 → lg:top-14 to match AppShell
header visibility (mobile header is lg:hidden with h-16)
- add gravity to FA2 layout to prevent disconnected nodes from flying
to extreme positions, making auto-rescale shrink everything to dots
- clamp outlier node positions after FA2 finishes (3σ threshold)
- add ResizeObserver to sync sigma canvas with container size changes
- call sigma.resize() before camera reset after FA2
- increase node sizes: user min 8→12, max 40→50, campaign 18→24
- reduce FA2 duration 5s→3s and slowDown 5→2 for tighter layout
- Switch to CSS Grid layout for reliable content area height
- Use inferSettings() for ForceAtlas2 auto-tuning based on graph structure
- Enable linLogMode and adjustSizes for better node clustering
- Fit camera after layout settles with animatedReset
- Increase node sizes for better visibility (MIN 8, MAX 40)
- Center controls at bottom, fix overlay positions
- Page uses top-16/sm:top-14 to start below AppShell header (z-50)
instead of inset-0 which renders behind the header
- Sigma init polls with RAF until container has real dimensions
instead of allowInvalidContainer which creates broken canvas
- Removed overflow-hidden from content area (not needed)
Container has no computed height when useEffect fires immediately
after data arrives. Defer to next animation frame so browser layout
completes. Also add allowInvalidContainer as safety net.
- Page now uses fixed inset-0 overlay to fill entire viewport
- Simplified top bar to single row (back + title + search + filter)
- Filter dropdown uses absolute positioning on desktop (no layout shift)
- Filter button always visible, toggles dropdown on click
- Split top bar into two rows on mobile: title + filter icon, then
full-width search below
- Desktop keeps single-row layout with search and filter inline
- Filter button: icon-only on mobile with active indicator dot
- Filter panel: full-screen overlay with backdrop on mobile,
inline dropdown on desktop
- Outside-click closes filter panel
- Removed subtitle (only on desktop, saves space)
- Increase FA2 scalingRatio to 10 and decrease gravity to 0.5 for
better node spacing (less cluttered graph)
- Use 100dvh instead of 100vh for Telegram Mini App viewport compat
- Hide legend on mobile to prevent overlay collision
- Move controls to bottom-right on mobile, bottom-center on desktop
- Make stats single-column on mobile for compact layout
Admin can now attach photos, videos, and documents when replying to
tickets. Upload happens on file selection via existing media upload
endpoint. Includes attachment preview with type-specific icons,
upload progress, error handling, race condition protection, and
proper blob URL lifecycle management.
Added video player rendering for video attachments in message thread.
Updated ticket list preview to show [media_type] for media-only messages.
Добавлен fallback через бота когда виджет Telegram не загружается:
- Таймаут 8 сек на загрузку скрипта (OIDC + legacy)
- Автоматический переход на deep link auth
- Polling каждые 2.5 сек до подтверждения в боте
- ConnectedAccounts: таймаут + сообщение при недоступности
- Переводы: ru, en, zh, fa
- add bot referral link section with Telegram icon (conditionally rendered)
- add cabinet referral link section with chain-link icon
- independent copy button state per link type
- add bot_referral_link to ReferralInfo type
- add i18n keys: botLink, cabinetLink in ru/en/fa/zh
- Add varsIgnorePattern to no-unused-vars for destructuring patterns
- Fix all react-hooks/exhaustive-deps by adding missing dependencies
- Refactor useAnimatedNumber to use ref instead of stale state closure
- Wrap handleLinkResult in useCallback for stable deps
- Extract OAuth utilities from OAuthCallback.tsx to utils/oauth.ts
- Extract background config utilities to utils/backgroundConfig.ts
- Remove unused catch parameter in GiftSubscription
- 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)
- 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
- Use openInvoice() with capabilities.hasInvoice guard for Stars payments
- Fall back to redirect for web environments without invoice support
- Add haptic feedback on payment success/failure
Add promo group banner (green) and active discount banner (orange) to
gift subscription page. PeriodCard now shows original price strikethrough
and discount percentage badge from server-calculated values.
Use createPortal to render toast at document.body level,
escaping framer-motion's transform on the tab content wrapper.
Remove unused useRef import and shareToastTapCopy i18n key.
GiftResult.tsx referenced gift.shareModalCopied and shareModalCopyAll
which were removed with the modal. Use common.copied and add new
gift.copyMessage key instead.