Commit Graph

212 Commits

Author SHA1 Message Date
Fringg
9546e0ffe6 fix: add missing balance cache invalidation after mutations
- Subscription.tsx: devicePurchase + trafficPurchase onSuccess → invalidate ['balance']
- RenewSubscription.tsx: renewMutation onSuccess → invalidate ['balance']
- SubscriptionPurchase.tsx: purchaseMutation onSuccess → invalidate ['balance'] + ['subscriptions-list']
- subscription.ts: refreshTraffic uses withSubId (already fixed, formatting)
2026-03-26 10:44:00 +03:00
c0mrade
c7c2167908 fix: admin per-subscription panel data + hide purchased tariffs in create
- getPanelInfo/getNodeUsage/getUserDevices: pass subscriptionId
- deleteUserDevice/resetUserDevices: pass subscriptionId
- Reload panel data when switching between subscriptions
- Create subscription: filter out already purchased tariffs
2026-03-24 16:55:09 +03:00
c0mrade
fe75fa45f0 feat: delete expired subscriptions with confirmation dialog
- API: deleteSubscription() calls DELETE /subscriptions/:id
- Subscription detail: "Удалить подписку" button for expired subs
  in multi-tariff mode
- Radix Dialog confirmation with warning text, cancel/confirm buttons
- Navigates to /subscriptions after successful deletion
2026-03-24 15:31:10 +03:00
c0mrade
2921d8cae6 feat: wheel subscription picker for multi-tariff mode
- API: spin() accepts subscriptionId, WheelConfig has eligible_subscriptions
- Auto-select subscription when only one eligible
- Show subscription picker when multiple eligible (days payment)
- Disable spin button until subscription selected (when >1 eligible)
- Daily tariffs excluded from eligible list
2026-03-24 14:41:20 +03:00
c0mrade
348d654892 fix: send subscription_id as query param in ALL POST/PATCH endpoints
All backend endpoints accept subscription_id via Query(...) parameter,
but frontend was sending it in the request body via bodyWithSubId helper.
This caused multi-tariff operations (pause, autopay, renew, traffic,
devices, servers, tariff switch) to silently fail — backend couldn't
find subscription_id in query params and fell back to first subscription.

Changed bodyWithSubId to return [body, config] tuple with subscription_id
in params, spread into axios calls as separate arguments.
2026-03-24 12:45:47 +03:00
c0mrade
b0421b94a6 fix: send subscription_id as query param in autopay PATCH, not in body 2026-03-24 12:05:48 +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
13d27a5929 fix: tag color bug, FormData interceptor, falsy id check
- Fix tag using category_color instead of its own color when editing
- Add FormData auto-detection interceptor in client.ts (root cause of 422)
- Remove redundant Content-Type overrides from all 5 upload endpoints
- Fix falsy id check (id: 0 treated as null) with strict comparison
- Await invalidateQueries to prevent stale dropdown data
2026-03-23 15:36:22 +03:00
Fringg
4bcae6ce11 fix: add multipart/form-data header to news media upload request 2026-03-23 15:31:52 +03:00
Fringg
2ae01c95ae feat: add category/tag management UI with ColoredItemCombobox
- ColoredItemCombobox component with search, color swatches, inline creation
- Replace text inputs with combobox dropdowns for category and tag selection
- API methods for categories/tags CRUD
- TypeScript types for NewsCategory, NewsTag
- i18n keys for combobox (ru, en)
2026-03-23 15:30:02 +03:00
Fringg
5c0eb129f4 fix: register DOMPurify hooks once, abort featured upload, fix double drop
- Register DOMPurify hooks at module init (not per sanitizeHtml call)
- Always set iframe allow attribute (not conditional on existing)
- Add AbortController to featured image upload for unmount cleanup
- Check defaultPrevented in React onDrop to prevent double upload with TipTap
- Remove explicit Content-Type header on upload (axios auto-sets boundary)
2026-03-23 12:43:47 +03:00
Fringg
74080004e8 fix: media upload security hardening from 6-agent review
- uploadCount: always decrement in finally (prevent permanent uploading state)
- AbortSignal: pass to actual HTTP request (cancel network, not just UI)
- Concurrent uploads: use Set<AbortController> instead of single ref
- DOMPurify hooks: scope inside sanitizeHtml() to avoid global pollution
- Video src: restrict to HTTPS only (was allowing HTTP)
- GIF: remove from accept attributes (backend rejects GIF)
- Ref mutation: move handleMediaUploadRef update to useEffect
2026-03-23 12:05:55 +03:00
Fringg
723591e5c3 feat: add media upload to news editor with drag-drop, paste, and file picker
Upload images/videos directly to server from editor. DOMPurify video
sanitization, XSS protection, upload cancellation on unmount.
2026-03-23 11:58:18 +03:00
Fringg
74e6d52fee fix: news feature security, accessibility, performance improvements
- DOMPurify strict allowlist with sandbox on iframes
- safeColor() CSS injection prevention for category_color
- URL validation (http/https) for images and links
- encodeURIComponent for slug in API calls
- Keyboard navigation on news cards (Enter/Space)
- aria-label, aria-pressed, type=button on all buttons
- 44px touch targets on remaining buttons
- memo() wrappers + stable callbacks (fewer re-renders)
- Canvas draw call batching (~75% reduction)
- TipTap extensions useMemo deps fix ([t] -> [])
- Featured image loading=eager fetchPriority=high (LCP)
- Unsafe Number(id) NaN guard
2026-03-23 11:09:52 +03:00
Fringg
99fc33625e feat: add news section with admin editor and public article view
- NewsSection with animated grid background, category filters, featured cards
- AdminNews list page with toggle publish/featured, pagination
- AdminNewsCreate with TipTap rich text editor, URL validation
- NewsArticle detail page with DOMPurify sanitization, Telegram back button
- Toggle component with WCAG 44px touch targets, role=switch
- Prose styles for highlight marks and responsive iframes
- i18n translations (en, ru, zh, fa)
2026-03-23 10:49:46 +03:00
Fringg
86d997d01d feat: custom broadcast buttons UI and fix stale mediaType bug
- Add inline custom button editor (no popups): label, type
  (callback/URL), action value with byte-level validation
- Frontend validates callback_data in UTF-8 bytes via TextEncoder
- URLs restricted to https:// and tg:// matching backend
- Form wrapped in <form> for Enter key submit support
- Fix pre-existing stale mediaType bug in file upload handler
- Translations added for ru, en, zh, fa locales
2026-03-22 01:54:38 +03:00
Fringg
3c034d2e70 fix: referral system — stop cabinet redirect to Telegram, fix deep link code handling
- Remove redirect to Telegram bot when email auth disabled + referral code present
  (cabinet links should stay on cabinet, bot links should go to bot)
- Remove referral_code from deep link auth (existing users can't get referrals)
- Don't consume referralCode in deep link path — leave it in localStorage for
  OIDC/widget auth methods that actually send it to the backend
- Consume campaign slug once into ref to survive retries (codesConsumedRef pattern)
- Update loginWithDeepLink to only accept (token, campaignSlug) — no referralCode
- Update pollDeepLinkToken API to match backend schema change
2026-03-22 01:54:38 +03:00
Fringg
c34375e579 fix: infinite reload loop on login when Telegram widget unavailable
When the Telegram OIDC widget fails to load (ad blockers, DNS, CSP),
the deep link polling fallback caused an infinite page reload:

1. pollDeepLinkToken received 202 (pending) which axios treated as success
2. loginWithDeepLink stored undefined tokens with isAuthenticated=true
3. checkAdminStatus() fired with invalid token → 401
4. Response interceptor called safeRedirectToLogin() → page reload → loop

Fixes:
- pollDeepLinkToken: validateStatus rejects non-200 (202 now caught in poll loop)
- AUTH_ENDPOINTS: add /cabinet/auth/deeplink/ and /cabinet/auth/login/auto
- safeRedirectToLogin: guard against redirect when already on /login
- loginWithDeepLink: validate response tokens before storing
- setTokens: validate tokens in store method (protects AutoLogin, VerifyEmail, etc.)
2026-03-20 22:28:50 +03:00
Fringg
db76cd0c64 feat: multi-select scope for referral network graph
Replace single-select scope with multi-select supporting multiple
campaigns, partners, and users simultaneously. Chips UI with remove
buttons, clear-all, sorted query keys for cache stability.
2026-03-20 02:31:11 +03:00
Fringg
a6faf702ec feat: redesign referral network with scope selector
- 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
2026-03-20 01:50:09 +03:00
Fringg
235eaec85f feat: add referral network graph visualization page
Interactive Sigma.js + graphology graph with ForceAtlas2 web worker
layout, campaign/user node coloring, filters via nodeReducer pattern,
search highlighting, user/campaign detail side panels, and 4-locale
translations. Dependencies: sigma v3, graphology, graphology-layout-forceatlas2.
2026-03-19 07:56:01 +03:00
Fringg
84f0e4e9b4 feat: add media attachment support for admin ticket replies
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.
2026-03-19 06:39:05 +03:00
Fringg
a3a6fad9b7 feat: display manual admin top-ups in sales statistics 2026-03-18 22:41:02 +03:00
Fringg
df73b3f77e feat: редизайн страницы платежей в админке — поиск, фильтры, статистика
Поиск по инвойсу/клиенту, фильтры по статусу/периоду/методу, карточки статистики,
кастомный диапазон дат, обработка ошибок, локализация на 4 языка.
2026-03-18 05:40:56 +03:00
Fringg
11e81917af fix: скрыть плашку верификации email при выключенной верификации
- Добавлен verification_enabled в EmailAuthEnabled тип
- Баннер "Подтвердите email" скрыт когда верификация выключена
- Бейдж "Не подтверждён" скрыт когда верификация выключена
2026-03-18 01:53:10 +03:00
Fringg
6a1a9f5db7 feat: deep link авторизация при блокировке oauth.telegram.org
Добавлен fallback через бота когда виджет Telegram не загружается:
- Таймаут 8 сек на загрузку скрипта (OIDC + legacy)
- Автоматический переход на deep link auth
- Polling каждые 2.5 сек до подтверждения в боте
- ConnectedAccounts: таймаут + сообщение при недоступности
- Переводы: ru, en, zh, fa
2026-03-17 21:29:21 +03:00
c0mrade
2dab25c5a0 fix: resolve telegram auth token expiration and clean up codebase
- 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)
2026-03-13 17:50:49 +03:00
Fringg
5a5a987893 feat: add show_in_gift toggle UI for tariffs in admin panel
Add pill toggle in tariff create/edit form and purple gift badge
in tariff list. Includes i18n for ru, en, zh, fa locales.
2026-03-12 04:16:04 +03:00
Fringg
695ab42e03 feat: add gifts tab to admin user detail page
Display sent and received gift subscriptions with status badges,
extracted GiftCard component, and full i18n support (ru/en/zh/fa).
2026-03-11 03:30:13 +03:00
Fringg
03c9e73a37 feat: display promo group and active discount banners on gift page
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.
2026-03-11 02:58:43 +03:00
firewookie
97ce8d751c Merge remote-tracking branch 'origin/dev_nikita' into dev_nikita 2026-03-10 15:29:14 +05:00
firewookie
0e3cf30341 FIX PR Problems 2026-03-10 15:28:46 +05:00
FireWookie
798c702704 Merge pull request #1 from FireWookie/dev
Dev
2026-03-10 15:11:31 +05:00
FireWookie
377513d782 Merge branch 'BEDOLAGA-DEV:main' into dev_nikita 2026-03-10 15:08:17 +05:00
Fringg
1bafcca1ef fix: gift UI improvements — declension, GB display, share modal, deep links
- Fix Russian device count declension (_one/_few/_many)
- Show traffic GB and tariff description in buy tab
- Replace copy/share buttons with share modal (bot + cabinet links)
- GiftResult: code-only success state with formatted share message
- Support ?tab=activate&code=TOKEN for auto-activation via cabinet link
- Add is_code_only + purchase_token to API types
2026-03-10 06:03:28 +03:00
Fringg
af3e535c69 feat: gift subscription redesign — code-only purchase + 3-tab UI
- Remove recipient input from buy flow (code-only gifts)
- 3-tab UI: Buy, Activate, My Gifts with animations
- After purchase, switch to My Gifts tab to show gift code
- Gift code display with copy/share buttons
- Activate tab: enter gift code to activate subscription
- My Gifts tab: sent/received gifts with status badges
- Fix: use navigator.language instead of hardcoded ru-RU for dates
- Fix: spread array before sort to prevent React Query cache mutation
- Fix: ARIA tab linkage with proper id/aria-labelledby
- Fix: guard VITE_BOT_USERNAME against undefined in share URLs
- Fix: wrap clipboard/share fallbacks in try/catch
- Add error state to MyGiftsTabContent
2026-03-10 05:37:57 +03:00
Fringg
638844ef47 feat: add open_in setting for custom buttons (external browser / Telegram miniapp) 2026-03-09 23:33:05 +03:00
Fringg
23aa86f1a8 feat: add menu editor tab with drag-and-drop rows, custom URL buttons, and button configuration
- Add menuLayout.ts API client with normalizeConfig and type definitions
- Add MenuEditorTab.tsx with DnD rows, button CRUD, style/emoji/label editors
- Replace ButtonsTab with MenuEditorTab on admin settings page
- Add menu editor translations for ru, en, zh, fa locales
- Loading/error states, useMemo optimizations, proper ref patterns
2026-03-09 23:07:38 +03:00
Fringg
dc740ae266 fix: restore session from refresh token when access token is missing
When access token is absent from sessionStorage (tab reopen, page refresh)
but refresh token exists in localStorage, the request interceptor now
attempts token refresh before sending the request. Previously it would
send the request without Authorization header, causing 401 errors on
endpoints like /cabinet/auth/email/register.
2026-03-09 21:58:46 +03:00
Fringg
4322d58ff8 feat: read gift warning from status response, soften poll error state
- Add warning field to GiftPurchaseStatus type
- Read warning from status response with URL param fallback
- Show PollErrorState for all poll errors (softer UX for gateway payments)
2026-03-09 21:26:02 +03:00
Fringg
9542607832 feat: add gift subscription toggle to admin branding settings
Add CABINET_GIFT_ENABLED toggle in Admin Settings > Branding > Interface Options,
alongside existing fullscreen and email auth toggles. Includes updateGiftEnabled
API method and i18n keys for all 4 locales (ru/en/zh/fa).
2026-03-09 20:56:42 +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
a49520566e feat: add gift subscription API client and feature flag 2026-03-09 18:49:21 +03:00
Fringg
880b2d45fe fix: support OIDC mode in TelegramLinkWidget for account linking
TelegramLinkWidget used legacy widget only, ignoring OIDC config.
Now queries widgetConfig from backend, supports OIDC popup flow with
id_token, and falls back to bot_username from server config.
2026-03-09 06:23:02 +03:00
Fringg
7ce5341e95 fix: support method query param fallback for external browser redirects
When payment providers redirect to external browser, sessionStorage is
unavailable. TopUpResult now reads method from URL query params and
polls via /latest endpoint as fallback.
2026-03-09 04:34:37 +03:00
Fringg
b59122818c feat: add dedicated TopUpResult page for payment return flow
Replace toast-based payment return with animated result page showing
pending/success/failed/timeout states. Extract shared Spinner,
AnimatedCheckmark and AnimatedCrossmark components. Add sessionStorage
persistence with validation and TTL for cross-redirect payment data.
Unify ProtectedRoute with withLayout prop, add aria-live accessibility.
2026-03-09 03:58:56 +03:00
Fringg
eed077b019 feat: add gift purchase UI states for telegram recipients
- Add recipient_in_bot and bot_link fields to PurchaseStatus API type
- Show bot link instruction when gift recipient is not in bot
- Show "gift sent via Telegram" message when recipient is in bot
- Fix duplicate text rendering in GiftPendingActivationState
- Handle recipientInBot=null edge case safely
- Add gift notification translations for ru, en, zh, fa locales
2026-03-07 20:33:55 +03:00
Fringg
68e6ce1bce fix: send Bearer token on email register (link to Telegram account)
/cabinet/auth/email/register requires authentication (links email to
existing Telegram account) but was in the AUTH_ENDPOINTS skip-list,
causing the request interceptor to omit the Bearer token → 401.

Change the skip-list entry to the specific /standalone path which
genuinely doesn't need a Bearer token.
2026-03-07 15:59:45 +03:00
Fringg
a404690334 feat: add configurable animated background for landing pages
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
2026-03-07 12:46:05 +03:00