Commit Graph

1426 Commits

Author SHA1 Message Date
Fringg
e2706c7c26 fix: select-all subscriptions + per-page selector
- Fix allVisibleSubscriptionIds: include ALL users' filtered subs
  (was only users with >1 subs — single-sub users were excluded)
- Add per-page selector (25/50/100/200) in pagination bar
- Make limit a state variable instead of constant
- perPage i18n keys (ru + en)
2026-04-24 07:23:58 +03:00
Fringg
98871af656 fix: multi-tariff filter now works server-side + trial-only checkbox
- Send comma-separated tariff_ids to backend (was only single ID)
  — server now filters with IN() operator, pagination correct
- Remove client-side tariff filtering (server handles it)
- Add "Только триал" checkbox filter: amber checkbox, filters users
  with trial subscriptions client-side
- Remove "trial" from status dropdown (was not working via
  subscription_status server filter)
- i18n: trialOnly key (ru + en)
2026-04-24 07:16:53 +03:00
Fringg
efca40d9c8 fix: multi-tariff filter shows all users when >1 tariffs selected
Server only supports single tariff_id filter. When multiple tariffs
are selected, apply client-side filtering to hide users who have no
subscriptions matching any of the selected tariffs.
2026-04-24 07:08:40 +03:00
Fringg
c32bcfeb9e fix: floating action bar hidden behind mobile bottom nav — raise z-index + bottom padding 2026-04-24 07:04:44 +03:00
Fringg
eeda567622 feat: second header checkbox for bulk subscription selection
Add green checkbox in table header next to the user checkbox for
mass-selecting all visible subscriptions. Shows filled/partial/empty
states matching user checkbox pattern. Only visible in multi-tariff
mode. Wider select column (56px) to fit both checkboxes.
2026-04-24 07:03:53 +03:00
Fringg
3cb15177cc fix: show sub-rows for single-subscription users + fix floating bar position
- Auto-expand ALL users with subscriptions (was only > 1), so users
  with single subscription also show their sub-row for selection
- FloatingActionBar: render via createPortal to document.body to
  ensure fixed positioning works correctly regardless of parent
  overflow/transform contexts (was trapped inside scrollable area)
2026-04-24 06:59:55 +03:00
Fringg
c726d3d74e feat: bulk delete subscription + fix sub-rows showing for filtered single subs
- Add "Удалить подписки" action with red trash icon, destructive
  warning dialog (red border, irreversible action notice)
- Fix canExpand: show sub-rows when isMultiTariff && subs > 0
  (was > 1, hiding single filtered subscription results)
- i18n: deleteSubscription action + warning/hint (ru + en)
2026-04-24 06:51:21 +03:00
Fringg
5969c74f92 feat: devices in subscription rows, set_devices action, table columns
- Remove "Days" column from main table (each subscription shows own)
- Tariff column shows ALL subscription tariffs as comma-separated list
- Subscription sub-rows show device count with phone icon
- New bulk action "Изменить устройства" (set_devices) with input 1-50
- Update UserListItemSubscription type with device_limit field
- i18n: setDevices + deviceLimit keys (ru + en)
2026-04-24 06:36:04 +03:00
Fringg
f13b7239a5 feat: multi-select tariff filter, select-all subscriptions, auto-expand
1. Tariff filter: replace single-select dropdown with MultiSelectDropdown
   component — checkboxes for each tariff, "Select all" / "Deselect all"
   controls, shows selected names or count in button label
2. Client-side subscription filtering: when tariffs selected in filter,
   only show matching subscriptions in expanded sub-rows
3. "Select all subscriptions" button in FloatingActionBar — toggles all
   visible subscription checkboxes and auto-expands relevant rows
4. Auto-expand: all user rows with multiple subscriptions are expanded
   by default when data loads (no need to click chevron)
5. i18n: new keys for tariff multi-select and subscription select-all
2026-04-24 06:20:43 +03:00
Fringg
9673f26469 fix: make subscription checkboxes same size as user checkboxes (h-5 w-5) 2026-04-24 06:11:43 +03:00
Fringg
3801c3626a fix: expand chevron invisible on accent background
ChevronExpandIcon used text-dark-400 (gray) on the accent gradient
circle (green) — completely invisible. Changed to text-white.
2026-04-24 06:08:57 +03:00
Fringg
0844144e23 fix: make checkboxes visible on dark theme in bulk actions
- Increase user checkbox size: h-4.5 → h-5 w-5 with rounded-md border-2
- Increase subscription checkbox: h-3.5 → h-4.5 with rounded-md border-2
- Brighter unselected border: dark-600 → dark-500 with lighter bg
- Hover effect: accent-500/50 border glow on hover
- Selected glow: accent shadow ring for clear visual feedback
- Smooth transition-all duration-150 for all state changes
2026-04-24 06:02:11 +03:00
Fringg
78b41dc338 feat: multi-tariff bulk actions UI — subscription-level selection
- Detect multi-tariff mode when users have multiple subscriptions
- Expandable user rows with chevron: click to show subscription sub-rows
- Each subscription sub-row shows: tariff name, status badge, days
  remaining (color-coded green/amber/red), traffic progress bar
- Independent subscription checkboxes for subscription-level actions
- FloatingActionBar shows dual counters: users (accent) + subscriptions
  (green), actions grouped by target type with disabled state
- Subscription-level actions send subscription_ids, user-level send
  user_ids to the backend
- Backward compatible: single-tariff mode unchanged (no chevrons,
  no sub-rows, user-only selection)
- i18n: subscriptionsSelected, usersSelected, expand/collapse,
  daysUnit, trafficOf, trafficGbUnit (ru + en)
2026-04-24 05:49:38 +03:00
Fringg
161fde4301 fix: bulk actions — add tariff info to user list, fix tariff column display
- Update UserListItem type to include tariff_id, tariff_name,
  traffic_used_gb, traffic_limit_gb, device_limit, days_remaining
  from the backend (now returned in user list API)
- Fix tariff column: was showing subscription_status, now shows
  actual tariff_name from the API response
2026-04-24 05:28:08 +03:00
Fringg
8dd5e4985f fix: bulk progress log shows generic error instead of backend message
The live ProgressView log used entry.error (never sent by backend)
instead of entry.message, falling back to a generic i18n string.
Now checks entry.message first to display the actual error from
the backend (e.g. "No subscription found").
2026-04-24 05:15:13 +03:00
Fringg
fc8170fa1f fix: align frontend bulk action types with backend API contract
- Fix BulkActionType enum: 'extend' → 'extend_subscription',
  'cancel' → 'cancel_subscription', 'activate' → 'activate_subscription'
- Fix BulkActionParams: 'balance_kopeks' → 'amount_kopeks' to match backend
- Fix SSE complete handler: build errors from progress log (backend
  complete event has no errors array)
- Add skipped_count to all BulkActionResult construction paths
- Add dry_run support to BulkActionRequest type
- Transform backend results[] to frontend errors[] in non-streaming path
- Remove unused errors variable in streaming fallback path
2026-04-24 04:52:30 +03:00
Fringg
312e0b4927 feat: bulk actions — live progress, grant subscription, error details
- Real-time progress: SSE streaming with animated progress bar,
  live success/error counters, scrollable log of last 10 results
  with auto-scroll, percentage display
- New action "Выдать подписку": tariff selector + days input,
  warning about users with existing subscriptions being skipped
- Error details: collapsible section with per-user error list,
  colored summary line (green successes / red errors)
- Fix: modal cannot be closed during loading (backdrop + Escape)
- Fix: row checkbox aria-labels now user-specific
- i18n: full progress/grant/error translations (ru + en)
2026-04-24 04:40:53 +03:00
Fringg
ebe9d9be48 fix: bulk actions frontend — selectedUserIds mapping, debounce cleanup
- Fix selectedUserIds: was mapping row keys as array indices instead
  of using them directly as user IDs (getRowId returns String(user.id))
- Add useEffect cleanup for search debounce timer on unmount
2026-04-24 04:24:33 +03:00
Fringg
1772d9632e feat: admin bulk actions page with TanStack Table
Full-featured admin page for mass operations on users:

- TanStack Table with checkbox selection (select all / individual)
- Server-side pagination, search, and filters (status, tariff, promo group)
- Columns: user, subscription status, tariff, balance, days left, promo group
- Floating glass-effect action bar with 7 bulk operations:
  extend subscription, deactivate, activate, change tariff,
  add traffic, add balance, assign promo group
- Action modal dialogs with input fields, loading state, result summary
- Responsive design for desktop, mobile, and Telegram Mini App
- Full i18n support (ru, en)
- Dark theme with accent highlights and status color badges
2026-04-24 04:15:28 +03:00
Fringg
33f32648f0 feat: improve squad management UX — rename, swap icons, color gift button
1. Rename "Управление серверами" → "Управление сквадами" across all
   4 locales (ru, en, zh, fa) — title, subtitle, nav, subscription
   options. The page manages RemnaWave squads, not individual servers.

2. Swap toggle button semantics — the button now shows the ACTION
   that will happen on click, not the current state:
   - Active squad: red X icon (click to deactivate)
   - Inactive squad: green checkmark (click to activate)

3. Gift/trial button: amber/orange when functional (clearly clickable),
   gray when disabled. Previously always gray, making it look inactive
   even when it could be clicked.
2026-04-23 05:00:36 +03:00
Fringg
2d6815a88e fix: landing page currency symbol not changing with locale
formatPrice() was hardcoded to ru-RU locale and RUB currency.
Now it reads the current language from i18next and maps it to
the appropriate currency (ru→RUB, en→USD, zh→CNY, fa→IRR).

All consumers (QuickPurchase landing, GiftSubscription, admin)
automatically get locale-aware currency formatting without
any component-level changes.
2026-04-23 04:55:36 +03:00
Fringg
1d5ce2d4ea fix: complete is_limited status handling across all views
- SubscriptionListCard: show traffic progress bar for limited subs
  in multi-tariff list (was hidden because isActive excluded limited)
- SubscriptionListCard: amber border/background for limited status
  cards (was using default neutral style, inconsistent with rest of UI)
- Subscription page: hide delete button for limited subs in
  multi-tariff mode (limited subs still have valid days remaining)
- SubscriptionPurchase: allow tariff switch for limited subs
  (was blocked by is_active-only gate in canSwitch formula)
- AdminUserDetail: include limited status in "already purchased"
  tariff filter to prevent duplicate subscription creation
2026-04-23 04:04:11 +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
Fringg
e6403eb7d0 fix: gift page shows only 1 tariff when tariffs have different periods
The gift page filtered tariffs by selected period, hiding tariffs
whose single period didn't match. When each tariff has a unique
period (30d, 60d, 180d, 360d), only the first tariff was visible.

- Remove period-first filtering (visibleTariffs/allPeriods)
- Show all tariffs directly from config.tariffs
- Auto-select the chosen tariff's period when switching tariffs
- Show tariff cards when more than 1 tariff exists
2026-04-23 03:29:00 +03:00
Dmitry Lunin
d75440bfe4 fix: wrap server display names with Twemoji for cross-platform emoji rendering 2026-04-21 23:03:20 +03:00
Fringg
996840f627 fix: change aurapay icon to pink gradient to avoid kassa_ai color collision 2026-04-18 00:12:15 +03:00
Fringg
48827e430d feat: add AuraPay payment provider to cabinet frontend
- Payment method label and icon (violet gradient, AP)
- Admin settings tree entry for AURAPAY category
- Locale strings and descriptions for ru, en, zh, fa
2026-04-18 00:05:56 +03:00
Fringg
4eed0f358e fix: differentiate rollypay icon from riopay (teal gradient, RY text) 2026-04-16 05:58:58 +03:00
Fringg
770a5803d6 feat: add RollyPay payment provider to cabinet frontend
- Payment method label and icon (green gradient, RP)
- Admin settings tree entry for ROLLYPAY category
- Locale strings and descriptions for ru, en, zh, fa
2026-04-16 05:55:21 +03:00
Fringg
5cbf29a1e8 fix: add paypear payment method description to ru/en locales 2026-04-16 05:38:34 +03:00
Fringg
31ac511a09 feat: add PayPear payment provider to cabinet frontend
- Payment method label and icon (amber gradient)
- Admin settings tree entry for PAYPEAR category
- Locale strings for ru, en, zh, fa
2026-04-16 05:26:09 +03:00
Fringg
d15c2408df fix: always send period_discounts in promo group updates
When all period discounts were removed, the frontend sent undefined
(field absent from JSON), so the backend never cleared them.
Now always sends the field - empty object {} to clear, or populated
object to update.
2026-04-15 04:13:53 +03:00
Fringg
3a865a472b fix: allow saving 0% period discount in promo groups
Changed condition from percent > 0 to percent >= 0 so that
admins can explicitly set 0% discount for a period. Previously
0% entries were silently dropped and not sent to the backend.
2026-04-15 03:40:09 +03:00
Dxnil
c80e1aff75 fix(connection): happ cryptolink flow + ui fixes (#385)
* fix(connection): respect happ_cryptolink mode

- read connect_mode from connection-link endpoint
- auto-redirect to happ cryptolink flow when mode is HAPP_CRYPTOLINK
- keep installation guide behavior for other modes

* docs(readme): fix source build step 2

- use compose service name for build/create commands
- copy static files from created compose container
- remove compose container via docker compose rm

* fix(connection): handle mode and ws path

- treat any non-guide connect mode as direct happ redirect
- wait for connection-link response before rendering installation guide
- build websocket URL from VITE_API_URL (supports /api and absolute URLs)

* docs(readme): fix docker cp static path

- copy /usr/share/nginx/html contents via html/.
- prevent nested /srv/cabinet/html deployment and 404 on root

* fix(connection): keep guide, use happ links

- restore guide page behavior for /connection (no auto redirect)
- use happ cryptolink URL in /connection/qr when happ crypt mode is active
- replace subscription page connection URL with happ cryptolink from connection-link API

* fix(connection): avoid wrong redirect flow

- force happ scheme deeplink in HAPP_CRYPTOLINK mode for guide connect buttons
- use cabinet redirect page only inside Telegram Mini App
- open deeplink directly in regular browsers

* fix(connection): enforce happ cryptolink

- prioritize backend crypt deeplink fields in HAPP_CRYPTOLINK mode
- fallback to local crypt4/crypt3 generation from subscription URL when backend returns plain happ://sub...
- apply same resolution for guide open action, qr page source and subscription page link display/copy

* fix(subscription): truncate long connect link

- render connection URL in a single line with ellipsis on /subscriptions
- preserve full URL in tooltip for easier manual copy
- keep copy button behavior unchanged

* fix(connection): build cryptolink from happ sub

- allow cryptolink fallback generation from happ://sub... URLs in addition to http(s)
- prevent plain happ://sub links from leaking into UI in HAPP_CRYPTOLINK mode
2026-04-15 03:29:54 +03:00
zavul0nn
32dba93f1a Make SBP default and first top-up option (#389) 2026-04-15 03:27:47 +03:00
c0mrade
30d8d28475 fix: show API error details in menu editor save + allow tg:// URLs
Save mutation was showing generic error instead of backend validation
detail, making it impossible to diagnose 422 errors. Also added tg://
to frontend URL validation for Telegram deep link support.
2026-04-13 17:07:29 +03:00
c0mrade
a46c89e45e feat: send campaign_slug during standalone email registration
Frontend now reads campaign slug from localStorage and includes it
in the registration API call, ensuring campaign attribution works
even if email verification happens in a different browser.
2026-04-13 14:43:39 +03:00
c0mrade
3d0fc76ecd fix: subscription status badge overflow + inline password validation
- Status badge (e.g. 'ПРИОСТАНОВЛЕНА (НЕДОСТАТОЧНО СРЕДСТВ)') now
  constrained with max-w-[55%] to prevent overlapping tariff name
- Password too short error shown inline under password field during
  registration instead of at the top of the page (invisible on mobile)
2026-04-13 14:43:29 +03:00
c0mrade
f787cc2de8 fix: add back button to AdminBanSystem page 2026-04-13 14:43:18 +03:00
c0mrade
7b0e6d8bc8 fix: add Object.hasOwn polyfill for old iOS/Android WebViews
@telegram-apps/sdk v3 depends on valibot which uses Object.hasOwn.
On Safari < 15.4 and old Android WebViews this method is missing,
causing LaunchParamsRetrieveError and broken initData auth.

Ref: https://github.com/Telegram-Mini-Apps/tma.js/issues/683
2026-04-13 14:42:26 +03:00
c0mrade
362a69812f feat: add broadcast category selector (system/news/promo) in admin UI 2026-04-10 17:04:22 +03:00
c0mrade
e6f6713b0e fix: batch bug fixes matching backend changes
- Promo code errors: add inactive/not_yet_valid translations (4 locales) + Balance.tsx parser
- Daily 100% discount: render price block when originalDailyPrice > 0
- Webvisor: enable in Yandex Metrika init config
- Referral attribution: consume campaign/referral slugs only after successful auth (all login methods)
- registerWithEmail: consume referral code after success
- AdminSettings: move TARIFF_MODE_SETTINGS to module scope (fix eslint warning)
2026-04-08 18:33:47 +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
207af81c95 fix: support 100% discount display + fix Telegram link widget race condition
- formatPrice shows "Бесплатно/Free" when price is 0 (SubscriptionPurchase, Subscription, RenewSubscription)
- Add subscription.free locale key (ru/en/zh/fa)
- Fix TelegramLinkWidget NS_BINDING_ABORTED: move legacy widget callback to ref pattern, remove unstable deps (showToast, t, navigate) from useEffect — prevents iframe destruction on re-render
- Clean up OIDC effect deps
2026-04-03 17:23:38 +03:00
Fringg
7892630e3b fix: add key prop to AnimatePresence, accessibility labels, remove hardcoded fallback
- 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')
2026-04-02 07:16:00 +03:00
Fringg
6fd0668a24 feat: move email linking form into Connected Accounts page
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)
2026-04-02 07:06:38 +03:00
Fringg
e2f81ad28d fix: subscription tab not highlighted on detail pages
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.
2026-04-02 05:44:06 +03:00
c0mrade
fe609a505b fix: admin users search not working on pages beyond first
Separated input state (searchInput/emailSearchInput) from committed
search state (search/emailSearch) to prevent every keystroke from
triggering API calls with stale pagination offset.
2026-03-31 18:19:39 +03:00
c0mrade
826a82aa1c fix: unblock page rendering when Telegram CDN is unavailable
- Google Fonts: non-render-blocking via media="print" onload trick
- telegram-web-app.js: load only inside Telegram environment
- Telegram SDK init: skip entirely outside Telegram
- Script load timeout: 8000ms → 2000ms for faster deep link fallback
- Email login form: expanded by default (80% of registrations)
2026-03-31 14:10:22 +03:00
Fringg
60f11e97a2 feat: show MULTI_TARIFF_ENABLED setting only in tariffs sales mode
Hide MULTI_TARIFF_ENABLED and MAX_ACTIVE_SUBSCRIPTIONS from admin
settings when SALES_MODE is not 'tariffs'. Add Russian translations.
2026-03-29 07:04:34 +03:00