diff --git a/src/components/InsufficientBalancePrompt.tsx b/src/components/InsufficientBalancePrompt.tsx index 75ebae1..1f4de9e 100644 --- a/src/components/InsufficientBalancePrompt.tsx +++ b/src/components/InsufficientBalancePrompt.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { useNavigate, useLocation } from 'react-router'; import { useTranslation } from 'react-i18next'; import { useCurrency } from '../hooks/useCurrency'; +import { InfoIcon, WalletIcon, PlusIcon } from '@/components/icons'; interface InsufficientBalancePromptProps { /** Amount missing in kopeks */ @@ -55,19 +56,7 @@ export default function InsufficientBalancePrompt({ className={`flex items-center justify-between gap-3 rounded-xl border border-error-500/30 bg-error-500/10 p-3 ${className}`} >
- - - + {message || t('balance.insufficientFunds')}:{' '} @@ -96,19 +85,7 @@ export default function InsufficientBalancePrompt({ >
- - - +
{t('balance.insufficientFunds')}
@@ -132,15 +109,7 @@ export default function InsufficientBalancePrompt({ ) : ( <> - - - + {t('balance.topUpBalance')} )} diff --git a/src/components/LanguageSwitcher.tsx b/src/components/LanguageSwitcher.tsx index 218b9f6..45585f3 100644 --- a/src/components/LanguageSwitcher.tsx +++ b/src/components/LanguageSwitcher.tsx @@ -1,6 +1,7 @@ import { useTranslation } from 'react-i18next'; import { useState, useRef, useEffect } from 'react'; import { infoApi, type LanguageInfo } from '@/api/info'; +import { ChevronDownIcon } from '@/components/icons'; export default function LanguageSwitcher() { const { i18n } = useTranslation(); @@ -57,14 +58,9 @@ export default function LanguageSwitcher() { > {currentLang.flag} {currentLang.code.toUpperCase()} - - - + /> {isOpen && ( diff --git a/src/components/ProviderIcon.tsx b/src/components/ProviderIcon.tsx index 758f0b3..ca7a98f 100644 --- a/src/components/ProviderIcon.tsx +++ b/src/components/ProviderIcon.tsx @@ -1,4 +1,5 @@ import { cn } from '@/lib/utils'; +import { EmailIcon as CentralEmailIcon } from '@/components/icons'; import OAuthProviderIcon from './OAuthProviderIcon'; export function TelegramIcon({ className = 'h-5 w-5' }: { className?: string }) { @@ -13,22 +14,7 @@ export function TelegramIcon({ className = 'h-5 w-5' }: { className?: string }) } export function EmailIcon({ className = 'h-5 w-5' }: { className?: string }) { - return ( - - ); + return ; } export default function ProviderIcon({ diff --git a/src/components/Toast.tsx b/src/components/Toast.tsx index 06f551f..dfd0493 100644 --- a/src/components/Toast.tsx +++ b/src/components/Toast.tsx @@ -8,6 +8,8 @@ import { ReactNode, } from 'react'; +import { CheckIcon, XIcon, ExclamationIcon, InfoIcon } from '@/components/icons'; + interface ToastOptions { type?: 'success' | 'error' | 'info' | 'warning'; message: string; @@ -163,58 +165,10 @@ function ToastItem({ toast, onClose }: { toast: Toast; onClose: () => void }) { }; const defaultIcons = { - success: ( - - - - ), - error: ( - - - - ), - warning: ( - - - - ), - info: ( - - - - ), + success: , + error: , + warning: , + info: , }; return ( diff --git a/src/components/admin/AnalyticsTab.tsx b/src/components/admin/AnalyticsTab.tsx index 4065eea..0a1ba98 100644 --- a/src/components/admin/AnalyticsTab.tsx +++ b/src/components/admin/AnalyticsTab.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { brandingApi } from '../../api/branding'; -import { CheckIcon, CloseIcon } from './icons'; +import { CheckIcon, CloseIcon, PencilIcon } from './icons'; export function AnalyticsTab() { const { t } = useTranslation(); @@ -143,19 +143,7 @@ export function AnalyticsTab() { }} className="rounded-lg p-1.5 text-dark-400 transition-colors hover:bg-dark-700 hover:text-dark-200" > - - - +
)} @@ -294,19 +282,7 @@ export function AnalyticsTab() { }} className="rounded-lg p-1.5 text-dark-400 transition-colors hover:bg-dark-700 hover:text-dark-200" > - - - +
)} @@ -360,19 +336,7 @@ export function AnalyticsTab() { }} className="rounded-lg p-1.5 text-dark-400 transition-colors hover:bg-dark-700 hover:text-dark-200" > - - - +
)} diff --git a/src/components/admin/ButtonsTab.tsx b/src/components/admin/ButtonsTab.tsx index 0d3eefe..5d1ec29 100644 --- a/src/components/admin/ButtonsTab.tsx +++ b/src/components/admin/ButtonsTab.tsx @@ -9,6 +9,7 @@ import { ButtonSection, BOT_LOCALES, } from '../../api/buttonStyles'; +import { ChevronDownIcon } from '@/components/icons'; import { Toggle } from './Toggle'; import { useNotify } from '../../platform/hooks/useNotify'; import { useNativeDialog } from '../../platform/hooks/useNativeDialog'; @@ -284,15 +285,9 @@ export function ButtonsTab() { {t('admin.buttons.customLabels')} {hasCustomLabels && } - - - + /> {isExpanded && (
diff --git a/src/components/admin/ColoredItemCombobox.tsx b/src/components/admin/ColoredItemCombobox.tsx index 2c05260..d023f7b 100644 --- a/src/components/admin/ColoredItemCombobox.tsx +++ b/src/components/admin/ColoredItemCombobox.tsx @@ -1,5 +1,6 @@ import { useState, useRef, useEffect, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +import { CheckIcon, ChevronDownIcon, CloseIcon, PlusIcon } from '@/components/icons'; import { cn } from '../../lib/utils'; import { useHapticFeedback } from '../../platform/hooks/useHaptic'; @@ -209,9 +210,7 @@ export function ColoredItemCombobox({ className="shrink-0 rounded p-0.5 text-dark-500 transition-colors hover:text-dark-300" aria-label={t('news.admin.combobox.clear')} > - - - + ) : ( @@ -222,16 +221,12 @@ export function ColoredItemCombobox({ )} - - - + /> {/* Dropdown */} @@ -279,13 +274,7 @@ export function ColoredItemCombobox({ /> {item.name} {value?.id === item.id && ( - - - + )} {onDelete && ( )} @@ -365,9 +352,7 @@ export function ColoredItemCombobox({
) : ( <> - - - + {t('news.admin.combobox.create')} )} diff --git a/src/components/admin/SortableSelectedMethodCard.tsx b/src/components/admin/SortableSelectedMethodCard.tsx index e0bc956..90af4a3 100644 --- a/src/components/admin/SortableSelectedMethodCard.tsx +++ b/src/components/admin/SortableSelectedMethodCard.tsx @@ -4,6 +4,7 @@ import { useSortable } from '@dnd-kit/sortable'; import { PiCaretDown } from 'react-icons/pi'; import { CSS } from '@dnd-kit/utilities'; import { cn } from '../../lib/utils'; +import { CheckIcon } from '@/components/icons'; import { GripIcon, TrashIcon } from '../icons/LandingIcons'; import type { AdminLandingPaymentMethod, EditableMethodField } from '../../api/landings'; import type { PaymentMethodSubOptionInfo } from '../../types'; @@ -218,17 +219,7 @@ export function SortableSelectedMethodCard({ : 'border border-dark-600 bg-dark-700', )} > - {enabled && ( - - - - )} + {enabled && }
{opt.name} diff --git a/src/components/admin/bulkActions/ActionModal.tsx b/src/components/admin/bulkActions/ActionModal.tsx index 8c14c51..3371b36 100644 --- a/src/components/admin/bulkActions/ActionModal.tsx +++ b/src/components/admin/bulkActions/ActionModal.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import { cn } from '@/lib/utils'; -import { CheckIcon, XCloseIcon } from '@/components/icons'; +import { CheckIcon, ChevronDownIcon, XCloseIcon, XIcon } from '@/components/icons'; import { useFocusTrap } from '@/hooks/useFocusTrap'; import { DropdownSelect } from './DropdownSelect'; import type { UserListItem } from '../../../api/adminUsers'; @@ -98,31 +98,11 @@ function ProgressView({ progress }: { progress: ProgressState }) {
{entry.success ? ( ) : ( )} @@ -159,18 +139,12 @@ function ErrorDetails({ result }: { result: BulkActionResult }) { {t('admin.bulkActions.errors.title', { count: result.error_count })} - - - + /> {expanded && (
@@ -178,15 +152,7 @@ function ErrorDetails({ result }: { result: BulkActionResult }) { {result.errors.map((err, idx) => (
{err.username ? `@${err.username}` : `#${err.user_id}`} diff --git a/src/components/admin/bulkActions/FloatingActionBar.tsx b/src/components/admin/bulkActions/FloatingActionBar.tsx index cbc7df0..3808c06 100644 --- a/src/components/admin/bulkActions/FloatingActionBar.tsx +++ b/src/components/admin/bulkActions/FloatingActionBar.tsx @@ -1,6 +1,7 @@ import { useEffect, useRef, useState, type ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { cn } from '@/lib/utils'; +import { TrashIcon } from '@/components/icons'; import { ChevronDownIcon } from './DropdownSelect'; import { isSubscriptionLevelAction } from './actionTargets'; import type { BulkActionType } from '../../../api/adminBulkActions'; @@ -112,21 +113,7 @@ export function FloatingActionBar({ { type: 'delete_subscription', labelKey: 'admin.bulkActions.actions.deleteSubscription', - icon: ( - - - - ), + icon: , colorClass: 'text-error-400 hover:bg-error-500/10', }, { diff --git a/src/components/admin/bulkActions/MultiSelectDropdown.tsx b/src/components/admin/bulkActions/MultiSelectDropdown.tsx index e2fc165..b4db3da 100644 --- a/src/components/admin/bulkActions/MultiSelectDropdown.tsx +++ b/src/components/admin/bulkActions/MultiSelectDropdown.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { cn } from '@/lib/utils'; +import { CheckIcon } from '@/components/icons'; import { ChevronDownIcon } from './DropdownSelect'; // ────────────────────────────────────────────────────────────────── @@ -135,21 +136,7 @@ export function MultiSelectDropdown({ : 'border-dark-500 bg-dark-700/60', )} > - {isChecked && ( - - - - )} + {isChecked && }
{option.label} diff --git a/src/components/admin/bulkActions/SubscriptionSubRow.tsx b/src/components/admin/bulkActions/SubscriptionSubRow.tsx index c03cae3..387ff02 100644 --- a/src/components/admin/bulkActions/SubscriptionSubRow.tsx +++ b/src/components/admin/bulkActions/SubscriptionSubRow.tsx @@ -1,5 +1,6 @@ import { useTranslation } from 'react-i18next'; import { cn } from '@/lib/utils'; +import { CheckIcon } from '@/components/icons'; import type { UserListItemSubscription } from '../../../api/adminUsers'; // ────────────────────────────────────────────────────────────────── @@ -68,17 +69,7 @@ export function SubscriptionSubRow({ : t('admin.bulkActions.selectUser', { name: subscription.tariff_name || '' }) } > - {isSelected && ( - - - - )} + {isSelected && }
)} diff --git a/src/components/admin/trafficUsage/filters/CountryFilter.tsx b/src/components/admin/trafficUsage/filters/CountryFilter.tsx index 391897a..97f9b43 100644 --- a/src/components/admin/trafficUsage/filters/CountryFilter.tsx +++ b/src/components/admin/trafficUsage/filters/CountryFilter.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef, useState } from 'react'; +import { CheckIcon } from '@/components/icons'; import { ChevronDownIcon, GlobeIcon } from '../TrafficIcons'; import { getFlagEmoji } from '../trafficUsageHelpers'; @@ -71,17 +72,7 @@ export function CountryFilter({ allSelected ? 'border-accent-500 bg-accent-500' : 'border-dark-600' }`} > - {allSelected && ( - - - - )} + {allSelected && }
All @@ -102,21 +93,7 @@ export function CountryFilter({ checked ? 'border-accent-500 bg-accent-500' : 'border-dark-600' }`} > - {checked && ( - - - - )} + {checked && } {getFlagEmoji(code)} {code.toUpperCase()} {count} diff --git a/src/components/admin/trafficUsage/filters/NodeFilter.tsx b/src/components/admin/trafficUsage/filters/NodeFilter.tsx index 1a23e39..a690110 100644 --- a/src/components/admin/trafficUsage/filters/NodeFilter.tsx +++ b/src/components/admin/trafficUsage/filters/NodeFilter.tsx @@ -1,6 +1,7 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ChevronDownIcon, ServerIcon } from '../TrafficIcons'; +import { CheckIcon } from '@/components/icons'; import { getFlagEmoji } from '../trafficUsageHelpers'; import type { TrafficNodeInfo } from '../../../../api/adminTraffic'; @@ -75,17 +76,7 @@ export function NodeFilter({ allSelected ? 'border-accent-500 bg-accent-500' : 'border-dark-600' }`} > - {allSelected && ( - - - - )} + {allSelected && } {t('admin.trafficUsage.allNodes')} @@ -106,21 +97,7 @@ export function NodeFilter({ checked ? 'border-accent-500 bg-accent-500' : 'border-dark-600' }`} > - {checked && ( - - - - )} + {checked && } {getFlagEmoji(node.country_code)} {node.node_name} diff --git a/src/components/admin/trafficUsage/filters/StatusFilter.tsx b/src/components/admin/trafficUsage/filters/StatusFilter.tsx index dc00641..78e2350 100644 --- a/src/components/admin/trafficUsage/filters/StatusFilter.tsx +++ b/src/components/admin/trafficUsage/filters/StatusFilter.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { CheckIcon } from '@/components/icons'; import { ChevronDownIcon, StatusIcon } from '../TrafficIcons'; // Status colour pills shared with the StatusFilter dropdown. @@ -86,17 +87,7 @@ export function StatusFilter({ allSelected ? 'border-accent-500 bg-accent-500' : 'border-dark-600' }`} > - {allSelected && ( - - - - )} + {allSelected && } {t('admin.trafficUsage.allStatuses')} @@ -117,21 +108,7 @@ export function StatusFilter({ checked ? 'border-accent-500 bg-accent-500' : 'border-dark-600' }`} > - {checked && ( - - - - )} + {checked && } {statusLabel(s)} diff --git a/src/components/admin/trafficUsage/filters/TariffFilter.tsx b/src/components/admin/trafficUsage/filters/TariffFilter.tsx index ccdaaee..e67627b 100644 --- a/src/components/admin/trafficUsage/filters/TariffFilter.tsx +++ b/src/components/admin/trafficUsage/filters/TariffFilter.tsx @@ -1,6 +1,7 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ChevronDownIcon, FilterIcon } from '../TrafficIcons'; +import { CheckIcon } from '@/components/icons'; export function TariffFilter({ available, @@ -73,17 +74,7 @@ export function TariffFilter({ allSelected ? 'border-accent-500 bg-accent-500' : 'border-dark-600' }`} > - {allSelected && ( - - - - )} + {allSelected && } {t('admin.trafficUsage.allTariffs')} @@ -104,21 +95,7 @@ export function TariffFilter({ checked ? 'border-accent-500 bg-accent-500' : 'border-dark-600' }`} > - {checked && ( - - - - )} + {checked && } {tariff} diff --git a/src/components/admin/userDetail/GiftsTab.tsx b/src/components/admin/userDetail/GiftsTab.tsx index 2247089..b053aff 100644 --- a/src/components/admin/userDetail/GiftsTab.tsx +++ b/src/components/admin/userDetail/GiftsTab.tsx @@ -1,4 +1,5 @@ import { useTranslation } from 'react-i18next'; +import { SendIcon } from '@/components/icons'; import { useCurrency } from '../../../hooks/useCurrency'; import type { AdminUserGiftItem, AdminUserGiftsResponse } from '../../../api/adminUsers'; @@ -224,19 +225,7 @@ export function GiftsTab({ giftsLoading, giftsData, locale, onNavigateToUser }: {/* Sent Gifts */}

- - - + {t('admin.users.detail.gifts.sentTitle')} ({giftsData.sent_total})

diff --git a/src/components/admin/userDetail/InfoTab.tsx b/src/components/admin/userDetail/InfoTab.tsx index 358afb5..226687d 100644 --- a/src/components/admin/userDetail/InfoTab.tsx +++ b/src/components/admin/userDetail/InfoTab.tsx @@ -9,6 +9,7 @@ import type { UserSubscriptionInfo, } from '../../../api/adminUsers'; import type { PromoGroup } from '../../../api/promocodes'; +import { ServerIcon } from '@/components/icons'; // ────────────────────────────────────────────────────────────────── // Local status badge (parent has its own — duplicating here to keep @@ -260,19 +261,7 @@ export function InfoTab(props: InfoTabProps) { {t('admin.users.detail.lastNode')}
- - - + {panelInfo.last_connected_node_name}
diff --git a/src/components/admin/userDetail/ReferralsTab.tsx b/src/components/admin/userDetail/ReferralsTab.tsx index a745df3..05acd29 100644 --- a/src/components/admin/userDetail/ReferralsTab.tsx +++ b/src/components/admin/userDetail/ReferralsTab.tsx @@ -5,6 +5,7 @@ import { useNavigate } from 'react-router'; import { useCurrency } from '../../../hooks/useCurrency'; import { useNotify } from '../../../platform/hooks/useNotify'; import { adminUsersApi, type UserDetailResponse, type UserListItem } from '../../../api/adminUsers'; +import { XIcon } from '@/components/icons'; // ────────────────────────────────────────────────────────────────── // Referrals tab — top-of-graph referrer + stats + referrals list, @@ -481,15 +482,7 @@ export function ReferralsTab({ user, userId, onUserRefresh }: ReferralsTabProps) className="rounded-lg p-2 text-dark-500 transition-colors hover:bg-error-500/10 hover:text-error-400 disabled:opacity-50" title={t('admin.users.detail.referrals.removeReferral')} > - - - +
))} diff --git a/src/components/admin/userDetail/SubscriptionTab.tsx b/src/components/admin/userDetail/SubscriptionTab.tsx index b382c1d..b6fec90 100644 --- a/src/components/admin/userDetail/SubscriptionTab.tsx +++ b/src/components/admin/userDetail/SubscriptionTab.tsx @@ -1,5 +1,15 @@ import { useTranslation } from 'react-i18next'; -import { MinusIcon, PlusIcon, RefreshIcon } from '@/components/icons'; +import { + BackIcon, + CheckIcon, + ChevronDownIcon, + ChevronRightIcon, + EditIcon, + MinusIcon, + PlusIcon, + RefreshIcon, + XIcon, +} from '@/components/icons'; import { DEVICE_ALIAS_MAX_LENGTH } from '../../../constants/devices'; import { createNumberInputHandler } from '../../../utils/inputHelpers'; import { getFlagEmoji } from '../../../utils/subscriptionHelpers'; @@ -217,19 +227,7 @@ export function SubscriptionTab(props: SubscriptionTabProps) { - - - +
@@ -309,15 +307,7 @@ export function SubscriptionTab(props: SubscriptionTabProps) { onClick={() => onSubscriptionDetailViewChange(false)} className="flex items-center gap-1.5 text-sm text-dark-400 transition-colors hover:text-dark-200" > - - - + {t('admin.users.detail.subscription.backToList', 'Все подписки')} )} @@ -943,19 +933,7 @@ export function SubscriptionTab(props: SubscriptionTabProps) { )} aria-label={t('admin.users.detail.devices.renameSave', 'Сохранить')} > - + ) : ( @@ -995,19 +961,7 @@ export function SubscriptionTab(props: SubscriptionTabProps) { title={t('admin.users.detail.devices.rename', 'Переименовать')} aria-label={t('admin.users.detail.devices.rename', 'Переименовать')} > - +
- - - + /> {requestHistoryExpanded && ( diff --git a/src/components/admin/userDetail/TicketsTab.tsx b/src/components/admin/userDetail/TicketsTab.tsx index 3a16b8d..0dbd4f0 100644 --- a/src/components/admin/userDetail/TicketsTab.tsx +++ b/src/components/admin/userDetail/TicketsTab.tsx @@ -4,6 +4,7 @@ import { useQuery } from '@tanstack/react-query'; import { adminApi, type AdminTicket, type AdminTicketDetail } from '../../../api/admin'; import { MessageMediaGrid } from '../../tickets/MessageMediaGrid'; import { linkifyText } from '../../../utils/linkify'; +import { ChatIcon, BackIcon, SendIcon } from '@/components/icons'; // ────────────────────────────────────────────────────────────────── // Tickets tab — list view + chat view (selected ticket replaces list). @@ -155,19 +156,7 @@ function EmptyState() { const { t } = useTranslation(); return (
- - - +

{t('admin.users.detail.noTickets')}

); @@ -265,15 +254,7 @@ function ChatView({ aria-label={t('common.back', 'Back')} className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-dark-800 transition-colors hover:bg-dark-700 sm:h-8 sm:w-8" > - - - +
@@ -372,19 +353,7 @@ function ChatView({ {replySending ? (
) : ( - - - + )}
diff --git a/src/components/blocking/AccountDeletedScreen.tsx b/src/components/blocking/AccountDeletedScreen.tsx index d94faf5..fb2e319 100644 --- a/src/components/blocking/AccountDeletedScreen.tsx +++ b/src/components/blocking/AccountDeletedScreen.tsx @@ -1,5 +1,6 @@ import { useTranslation } from 'react-i18next'; import { usePlatform } from '@/platform'; +import { InfoIcon } from '@/components/icons'; import { useBlockingStore } from '../../store/blocking'; import { useFocusTrap } from '../../hooks/useFocusTrap'; @@ -55,20 +56,7 @@ export default function AccountDeletedScreen() {
- +
diff --git a/src/components/blocking/BlacklistedScreen.tsx b/src/components/blocking/BlacklistedScreen.tsx index 2bb99e7..de533f5 100644 --- a/src/components/blocking/BlacklistedScreen.tsx +++ b/src/components/blocking/BlacklistedScreen.tsx @@ -1,6 +1,7 @@ import { useTranslation } from 'react-i18next'; import { useBlockingStore } from '../../store/blocking'; import { useFocusTrap } from '../../hooks/useFocusTrap'; +import { BanIcon } from '@/components/icons'; export default function BlacklistedScreen() { const { t } = useTranslation(); @@ -20,19 +21,7 @@ export default function BlacklistedScreen() { {/* Icon */}
- - - +
diff --git a/src/components/blocking/ChannelSubscriptionScreen.tsx b/src/components/blocking/ChannelSubscriptionScreen.tsx index a4c78e8..e213c6c 100644 --- a/src/components/blocking/ChannelSubscriptionScreen.tsx +++ b/src/components/blocking/ChannelSubscriptionScreen.tsx @@ -4,6 +4,7 @@ import { useBlockingStore } from '../../store/blocking'; import { apiClient, isChannelSubscriptionError } from '../../api/client'; import { usePlatform } from '../../platform'; import { useFocusTrap } from '../../hooks/useFocusTrap'; +import { TelegramIcon, ClockIcon, CheckIcon } from '@/components/icons'; const CHECK_COOLDOWN_SECONDS = 5; @@ -96,9 +97,7 @@ export default function ChannelSubscriptionScreen() { {/* Icon */}
- - - +
@@ -183,31 +182,12 @@ export default function ChannelSubscriptionScreen() { ) : cooldown > 0 ? ( <> - - - + {t('blocking.channel.waitSeconds', { seconds: cooldown })} ) : ( <> - - - + {t('blocking.channel.checkSubscription')} )} diff --git a/src/components/blocking/MaintenanceScreen.tsx b/src/components/blocking/MaintenanceScreen.tsx index 6adc56f..2326a33 100644 --- a/src/components/blocking/MaintenanceScreen.tsx +++ b/src/components/blocking/MaintenanceScreen.tsx @@ -1,6 +1,7 @@ import { useTranslation } from 'react-i18next'; import { useBlockingStore } from '../../store/blocking'; import { useFocusTrap } from '../../hooks/useFocusTrap'; +import { AdjustmentsIcon } from '@/components/icons'; export default function MaintenanceScreen() { const { t } = useTranslation(); @@ -20,19 +21,7 @@ export default function MaintenanceScreen() { {/* Icon */}
- - - +
diff --git a/src/components/connection/InstallationGuide.tsx b/src/components/connection/InstallationGuide.tsx index 73d4e19..8e79c74 100644 --- a/src/components/connection/InstallationGuide.tsx +++ b/src/components/connection/InstallationGuide.tsx @@ -12,7 +12,7 @@ import { useTheme } from '@/hooks/useTheme'; import { CardsBlock, TimelineBlock, AccordionBlock, MinimalBlock, BlockButtons } from './blocks'; import type { BlockRendererProps } from './blocks'; import TvQuickConnect from './TvQuickConnect'; -import { BackIcon } from '@/components/icons'; +import { BackIcon, ChevronIcon, DocumentIcon } from '@/components/icons'; const platformOrder = ['ios', 'android', 'windows', 'macos', 'linux', 'androidTV', 'appleTV']; @@ -259,15 +259,7 @@ export default function InstallationGuide({ ))}
- - - +
)} @@ -315,19 +307,7 @@ export default function InstallationGuide({ rel="noopener noreferrer" className="btn-secondary w-full justify-center" > - - - + {getBaseTranslation('tutorial', 'subscription.connection.tutorial')} )} diff --git a/src/components/connection/blocks/AccordionBlock.tsx b/src/components/connection/blocks/AccordionBlock.tsx index fd46c36..d07bcad 100644 --- a/src/components/connection/blocks/AccordionBlock.tsx +++ b/src/components/connection/blocks/AccordionBlock.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { ChevronDownIcon } from '@/components/icons'; import { getColorGradient } from '@/utils/colorParser'; import { ThemeIcon } from './ThemeIcon'; import type { BlockRendererProps } from './types'; @@ -52,15 +53,9 @@ export function AccordionBlock({ {getLocalizedText(block.title)} - - - + /> {/* Panel */}
{/* Gift icon */}
- - - +
{/* Content */} diff --git a/src/components/dashboard/SubscriptionCardActive.tsx b/src/components/dashboard/SubscriptionCardActive.tsx index 7f947f9..6aa7be5 100644 --- a/src/components/dashboard/SubscriptionCardActive.tsx +++ b/src/components/dashboard/SubscriptionCardActive.tsx @@ -10,7 +10,7 @@ import { useTrafficZone } from '../../hooks/useTrafficZone'; import { formatTraffic } from '../../utils/formatTraffic'; import { getGlassColors } from '../../utils/glassTheme'; import { HoverBorderGradient } from '../ui/hover-border-gradient'; -import { RefreshIcon } from '@/components/icons'; +import { CalendarIcon, RefreshIcon } from '@/components/icons'; import { useHaptic } from '../../platform'; import type { Subscription } from '../../types'; @@ -309,20 +309,14 @@ export default function SubscriptionCardActive({ background: daysLeft <= 3 ? 'rgba(var(--color-warning-400), 0.1)' : g.hoverBg, }} > - + +
{t('dashboard.remaining')}
diff --git a/src/components/dashboard/SubscriptionCardExpired.tsx b/src/components/dashboard/SubscriptionCardExpired.tsx index 8fec23c..5431944 100644 --- a/src/components/dashboard/SubscriptionCardExpired.tsx +++ b/src/components/dashboard/SubscriptionCardExpired.tsx @@ -10,7 +10,7 @@ import { useCurrency } from '../../hooks/useCurrency'; import { useHapticFeedback } from '../../platform/hooks/useHaptic'; import { getGlassColors } from '../../utils/glassTheme'; import { getInsufficientBalanceError } from '../../utils/subscriptionHelpers'; -import { SubscriptionIcon } from '@/components/icons'; +import { ClockIcon, ExclamationIcon, PlusIcon, SubscriptionIcon } from '@/components/icons'; interface SubscriptionCardExpiredProps { subscription: Subscription; @@ -168,39 +168,13 @@ export default function SubscriptionCardExpired({ style={{ background: `rgba(${accent.r},${accent.g},${accent.b},0.1)`, border: `1px solid rgba(${accent.r},${accent.g},${accent.b},0.15)`, + color: accent.hex, }} > {isLimited ? ( - + ) : ( - + )}

@@ -275,19 +249,7 @@ export default function SubscriptionCardExpired({ boxShadow: `0 4px 20px rgba(${accent.r},${accent.g},${accent.b},0.2)`, }} > - + {t('subscription.buyTraffic')} ) : ( @@ -330,19 +292,7 @@ export default function SubscriptionCardExpired({ boxShadow: `0 4px 20px rgba(${accent.r},${accent.g},${accent.b},0.2)`, }} > - + {t('dashboard.expired.topUp')} )} diff --git a/src/components/dashboard/TrialOfferCard.tsx b/src/components/dashboard/TrialOfferCard.tsx index 4afba16..025ca01 100644 --- a/src/components/dashboard/TrialOfferCard.tsx +++ b/src/components/dashboard/TrialOfferCard.tsx @@ -5,6 +5,7 @@ import type { TrialInfo } from '../../types'; import { useCurrency } from '../../hooks/useCurrency'; import { useTheme } from '../../hooks/useTheme'; import { getGlassColors } from '../../utils/glassTheme'; +import { BoltIcon, SparklesIcon } from '@/components/icons'; interface TrialOfferCardProps { trialInfo: TrialInfo; @@ -94,37 +95,21 @@ export default function TrialOfferCard({ }} > {isFree ? ( - + + ) : ( - + + )} {/* Glow effect */}
- +
{t('news.title')} diff --git a/src/components/subscription/PurchaseCTAButton.tsx b/src/components/subscription/PurchaseCTAButton.tsx index 1ca1402..3d3b515 100644 --- a/src/components/subscription/PurchaseCTAButton.tsx +++ b/src/components/subscription/PurchaseCTAButton.tsx @@ -1,7 +1,7 @@ import { Link } from 'react-router'; import { useTranslation } from 'react-i18next'; import { HoverBorderGradient } from '../ui/hover-border-gradient'; -import { SubscriptionIcon } from '@/components/icons'; +import { ChevronRightIcon, SubscriptionIcon } from '@/components/icons'; import type { Subscription } from '../../types'; interface PurchaseCTAButtonProps { @@ -86,20 +86,7 @@ export default function PurchaseCTAButton({
{/* Right: chevron */} - +
diff --git a/src/components/subscription/SubscriptionListCard.tsx b/src/components/subscription/SubscriptionListCard.tsx index 1f6bb14..3d4b101 100644 --- a/src/components/subscription/SubscriptionListCard.tsx +++ b/src/components/subscription/SubscriptionListCard.tsx @@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next'; import { useTheme } from '../../hooks/useTheme'; import { getGlassColors } from '../../utils/glassTheme'; import { useHaptic } from '../../platform'; +import { CalendarIcon, CheckIcon, ChevronRightIcon, DevicesIcon } from '@/components/icons'; import type { SubscriptionListItem } from '../../types'; function formatDate(iso: string | null, locale?: string): string { @@ -136,15 +137,7 @@ export default function SubscriptionListCard({ - - - + {/* Traffic mini progress bar */} @@ -177,29 +170,11 @@ export default function SubscriptionListCard({ style={{ color: g.textSecondary }} > - - - - + {subscription.device_limit} - - - - + {formatDate(subscription.end_date, i18n.language)} {!isTrial && @@ -213,19 +188,19 @@ export default function SubscriptionListCard({ - - {enabled ? ( - - ) : ( + {enabled ? ( + + ) : ( + - )} - + + )} {label} ); diff --git a/src/components/subscription/purchase/TariffPickerGrid.tsx b/src/components/subscription/purchase/TariffPickerGrid.tsx index a22ade2..b1204ad 100644 --- a/src/components/subscription/purchase/TariffPickerGrid.tsx +++ b/src/components/subscription/purchase/TariffPickerGrid.tsx @@ -4,6 +4,7 @@ import { useTheme } from '../../../hooks/useTheme'; import { useCurrency } from '../../../hooks/useCurrency'; import { usePromoDiscount } from '../../../hooks/usePromoDiscount'; import { getGlassColors } from '../../../utils/glassTheme'; +import { ArrowDownIcon, DevicesIcon, RestartIcon } from '@/components/icons'; import type { Tariff, Subscription, PurchaseOptions } from '../../../types'; // ────────────────────────────────────────────────────────────────── @@ -169,35 +170,11 @@ export function TariffPickerGrid({
- - - + {tariff.traffic_limit_label}
- - - + {tariff.device_limit === 0 ? '∞' @@ -206,19 +183,7 @@ export function TariffPickerGrid({
{tariff.traffic_reset_mode && tariff.traffic_reset_mode !== 'NO_RESET' && (
- - - + {t(`subscription.trafficReset.${tariff.traffic_reset_mode}`)} diff --git a/src/components/subscription/sheets/DeleteSubscriptionSheet.tsx b/src/components/subscription/sheets/DeleteSubscriptionSheet.tsx index b00c46f..23243f9 100644 --- a/src/components/subscription/sheets/DeleteSubscriptionSheet.tsx +++ b/src/components/subscription/sheets/DeleteSubscriptionSheet.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { TrashIcon } from '@/components/icons'; import { subscriptionApi } from '../../../api/subscription'; import { usePlatform } from '../../../platform'; import { useDestructiveConfirm } from '../../../platform/hooks/useNativeDialog'; @@ -72,19 +73,7 @@ export function DeleteSubscriptionSheet({ disabled={deleteLoading} className="flex w-full items-center justify-center gap-2 rounded-2xl border border-error-400/20 bg-error-400/5 p-3.5 text-sm font-medium text-error-400 transition-colors hover:bg-error-400/10 disabled:opacity-50" > - - - + {t('subscription.delete', 'Удалить подписку')} ); diff --git a/src/components/tickets/MessageMediaGrid.tsx b/src/components/tickets/MessageMediaGrid.tsx index 267454c..df3496f 100644 --- a/src/components/tickets/MessageMediaGrid.tsx +++ b/src/components/tickets/MessageMediaGrid.tsx @@ -1,5 +1,8 @@ import { useState, useCallback, useEffect } from 'react'; import { createPortal } from 'react-dom'; + +import { ChevronLeftIcon, ChevronRightIcon, DocumentIcon, XIcon } from '@/components/icons'; + import { ticketsApi } from '../../api/tickets'; export interface MediaItem { @@ -150,19 +153,7 @@ export function MessageMediaGrid({ rel="noopener noreferrer" className="inline-flex items-center gap-2 rounded-lg bg-dark-700 px-3 py-2 text-sm text-dark-200 transition-colors hover:bg-dark-600" > - - - + {item.caption || `Download ${item.type}`} ); @@ -180,15 +171,7 @@ export function MessageMediaGrid({ className="absolute right-4 top-4 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white text-black shadow-xl transition-colors hover:bg-gray-200" onClick={closeFullscreen} > - - - + {photoItems.length > 1 && ( @@ -199,15 +182,7 @@ export function MessageMediaGrid({ onClick={() => setFullscreenIndex(fullscreenIndex - 1)} className="absolute left-4 top-1/2 z-10 flex h-12 w-12 -translate-y-1/2 items-center justify-center rounded-full bg-white/90 text-black shadow-xl transition-colors hover:bg-white disabled:opacity-30" > - - - +
{fullscreenIndex + 1} / {photoItems.length} diff --git a/src/pages/AdminApps.tsx b/src/pages/AdminApps.tsx index fb7abe2..a69c416 100644 --- a/src/pages/AdminApps.tsx +++ b/src/pages/AdminApps.tsx @@ -3,6 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { adminAppsApi } from '../api/adminApps'; import { usePlatform } from '../platform/hooks/usePlatform'; +import { BackIcon } from '@/components/icons'; export default function AdminApps() { const { t } = useTranslation(); @@ -45,15 +46,7 @@ export default function AdminApps() { onClick={() => navigate('/admin')} className="flex h-10 w-10 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 transition-colors hover:border-dark-600" > - - - + )}

{t('admin.apps.title')}

diff --git a/src/pages/AdminBanSystem.tsx b/src/pages/AdminBanSystem.tsx index 62a1cbf..46d9842 100644 --- a/src/pages/AdminBanSystem.tsx +++ b/src/pages/AdminBanSystem.tsx @@ -34,6 +34,9 @@ import { TrafficIcon, ReportIcon, HealthIcon, + ExclamationIcon, + BackIcon, + XIcon, } from '@/components/icons'; type TabType = @@ -393,34 +396,10 @@ export default function AdminBanSystem() {
- - - +
- - - +
@@ -454,19 +433,7 @@ export default function AdminBanSystem() { onClick={() => window.history.back()} className="flex w-full items-center justify-center gap-2 rounded-lg border border-dark-600 bg-dark-700 px-4 py-2 text-sm font-medium text-dark-200 transition-all duration-200 hover:border-dark-500 hover:bg-dark-600 hover:text-dark-100" > - - - + {t('common.back')}
@@ -1444,14 +1411,7 @@ export default function AdminBanSystem() { aria-label={t('common.close')} className="text-dark-400 hover:text-dark-200" > - - - +
diff --git a/src/pages/AdminCampaignCreate.tsx b/src/pages/AdminCampaignCreate.tsx index a88d75b..3195831 100644 --- a/src/pages/AdminCampaignCreate.tsx +++ b/src/pages/AdminCampaignCreate.tsx @@ -13,7 +13,7 @@ import { partnerApi } from '../api/partners'; import { AdminBackButton } from '../components/admin'; import { createNumberInputHandler, toNumber } from '../utils/inputHelpers'; import Twemoji from 'react-twemoji'; -import { CampaignIcon, CheckIcon, RefreshIcon } from '@/components/icons'; +import { CampaignIcon, CheckIcon, LinkIcon, RefreshIcon } from '@/components/icons'; // Bonus type config const bonusTypeConfig: Record< @@ -273,19 +273,7 @@ export default function AdminCampaignCreate() { {partnerId && partner && (
- - - +

{t('admin.campaigns.form.partnerAutoAssign', { name: partner.first_name || partner.username || `#${partnerId}`, diff --git a/src/pages/AdminCampaignStats.tsx b/src/pages/AdminCampaignStats.tsx index cb2644c..e4eb79b 100644 --- a/src/pages/AdminCampaignStats.tsx +++ b/src/pages/AdminCampaignStats.tsx @@ -10,7 +10,7 @@ import { PARTNER_STATS } from '../constants/partner'; import { useCurrency } from '../hooks/useCurrency'; import { copyToClipboard } from '../utils/clipboard'; import { useHaptic } from '../platform'; -import { ChartIcon, CopyIcon, LinkIcon, UsersIcon } from '@/components/icons'; +import { ChartIcon, ChevronDownIcon, CopyIcon, LinkIcon, UsersIcon } from '@/components/icons'; // Bonus type config const bonusTypeConfig: Record< @@ -449,15 +449,9 @@ export default function AdminCampaignStats() { {t('admin.campaigns.stats.users')} ({stats.registrations})

- - - + /> {showUsers && ( diff --git a/src/pages/AdminPartnerDetail.tsx b/src/pages/AdminPartnerDetail.tsx index 20159b7..a50a50b 100644 --- a/src/pages/AdminPartnerDetail.tsx +++ b/src/pages/AdminPartnerDetail.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import { partnerApi } from '../api/partners'; import { AdminBackButton } from '../components/admin'; import { useCurrency } from '../hooks/useCurrency'; +import { XIcon } from '@/components/icons'; // Status badge config — keys must match backend PartnerStatus enum values const statusConfig: Record = { @@ -259,19 +260,7 @@ export default function AdminPartnerDetail() { className="rounded p-1 text-dark-500 transition-colors hover:bg-error-500/10 hover:text-error-400" title={t('admin.partnerDetail.campaigns.unassign')} > - - - +
diff --git a/src/pages/AdminPartners.tsx b/src/pages/AdminPartners.tsx index 7aa063e..b1d07fe 100644 --- a/src/pages/AdminPartners.tsx +++ b/src/pages/AdminPartners.tsx @@ -9,6 +9,7 @@ import { } from '../api/partners'; import { AdminBackButton } from '../components/admin'; import { useCurrency } from '../hooks/useCurrency'; +import { ChevronRightIcon, SettingsIcon } from '@/components/icons'; export default function AdminPartners() { const { t } = useTranslation(); @@ -50,24 +51,7 @@ export default function AdminPartners() { className="rounded-lg bg-dark-800 p-2 text-dark-400 transition-colors hover:bg-dark-700 hover:text-dark-200" title={t('admin.partners.settings')} > - - - - +
@@ -167,19 +151,7 @@ export default function AdminPartners() { - - - + ))} diff --git a/src/pages/AdminPayments.tsx b/src/pages/AdminPayments.tsx index 7630e20..76daaab 100644 --- a/src/pages/AdminPayments.tsx +++ b/src/pages/AdminPayments.tsx @@ -6,7 +6,13 @@ import { adminPaymentsApi, type SearchStats } from '../api/adminPayments'; import { useCurrency } from '../hooks/useCurrency'; import type { PendingPayment, PaginatedResponse } from '../types'; import { usePlatform } from '../platform/hooks/usePlatform'; -import { BackIcon, SearchIcon, CalendarIcon } from '@/components/icons'; +import { + BackIcon, + SearchIcon, + CalendarIcon, + RefreshIcon, + CheckCircleIcon, +} from '@/components/icons'; interface StatusBadgeProps { status: string; @@ -210,19 +216,7 @@ export default function AdminPayments() { @@ -574,19 +568,7 @@ export default function AdminPayments() { ) : (
- - - +
{t('admin.payments.noPayments')}
diff --git a/src/pages/AdminPolicyEdit.tsx b/src/pages/AdminPolicyEdit.tsx index e3f6504..87c8893 100644 --- a/src/pages/AdminPolicyEdit.tsx +++ b/src/pages/AdminPolicyEdit.tsx @@ -4,6 +4,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { rbacApi, AccessPolicy, CreatePolicyPayload, UpdatePolicyPayload } from '@/api/rbac'; import { AdminBackButton } from '@/components/admin'; +import { XIcon } from '@/components/icons'; // === Types === @@ -150,15 +151,7 @@ function IpTagInput({ values, onChange }: IpTagInputProps) { className="text-dark-400 transition-colors hover:text-dark-200" aria-label={t('admin.policies.conditions.removeIp', { ip })} > - - - + ))} diff --git a/src/pages/AdminPromoOfferSend.tsx b/src/pages/AdminPromoOfferSend.tsx index 4e72e2e..2c8f5a2 100644 --- a/src/pages/AdminPromoOfferSend.tsx +++ b/src/pages/AdminPromoOfferSend.tsx @@ -19,6 +19,7 @@ import { UserIcon, SearchIcon, CloseIcon, + XIcon, } from '@/components/icons'; const getOfferTypeIcon = (offerType: string): string => { @@ -188,25 +189,9 @@ export default function AdminPromoOfferSend() { }`} > {result.isSuccess ? ( - - - + ) : ( - - - + )}

{result.title}

diff --git a/src/pages/AdminTariffs.tsx b/src/pages/AdminTariffs.tsx index f92fb2d..dc139c2 100644 --- a/src/pages/AdminTariffs.tsx +++ b/src/pages/AdminTariffs.tsx @@ -105,19 +105,7 @@ function SortableTariffCard({ )} {tariff.show_in_gift && ( - - - + {t('admin.tariffs.giftBadge')} )} diff --git a/src/pages/AdminTickets.tsx b/src/pages/AdminTickets.tsx index 73eaa58..d33bc9d 100644 --- a/src/pages/AdminTickets.tsx +++ b/src/pages/AdminTickets.tsx @@ -9,7 +9,7 @@ import { adminApi, AdminTicket, AdminTicketDetail } from '../api/admin'; import { ticketsApi } from '../api/tickets'; import { copyToClipboard as copyText } from '../utils/clipboard'; import { usePlatform } from '../platform/hooks/usePlatform'; -import { BackIcon } from '@/components/icons'; +import { BackIcon, SettingsIcon, TicketIcon, XIcon } from '@/components/icons'; interface MediaAttachment { id: string; @@ -270,24 +270,7 @@ export default function AdminTickets() { onClick={() => navigate('/admin/tickets/settings')} className="btn-secondary flex items-center gap-2" > - - - - + {t('admin.tickets.settings')} @@ -433,19 +416,7 @@ export default function AdminTickets() { {!selectedTicketId ? (
- - - +
{t('admin.tickets.selectTicket')}
@@ -593,19 +564,7 @@ export default function AdminTickets() { onClick={() => removeAttachment(idx)} className="absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-dark-600 text-dark-300 hover:bg-error-500 hover:text-white" > - - - + ))} diff --git a/src/pages/AdminWithdrawalDetail.tsx b/src/pages/AdminWithdrawalDetail.tsx index 6dbb260..b292bc3 100644 --- a/src/pages/AdminWithdrawalDetail.tsx +++ b/src/pages/AdminWithdrawalDetail.tsx @@ -3,6 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { withdrawalApi } from '../api/withdrawals'; import { AdminBackButton } from '../components/admin'; +import { WarningIcon } from '@/components/icons'; import { useCurrency } from '../hooks/useCurrency'; import { formatDate, @@ -237,19 +238,7 @@ export default function AdminWithdrawalDetail() { key={index} className="flex items-start gap-2 rounded-lg bg-error-500/10 px-3 py-2" > - - - + {flag} ))} diff --git a/src/pages/AdminWithdrawals.tsx b/src/pages/AdminWithdrawals.tsx index a09814a..2af61d6 100644 --- a/src/pages/AdminWithdrawals.tsx +++ b/src/pages/AdminWithdrawals.tsx @@ -4,6 +4,7 @@ import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { withdrawalApi, AdminWithdrawalItem } from '../api/withdrawals'; import { AdminBackButton } from '../components/admin'; +import { ChevronRightIcon } from '@/components/icons'; import { useCurrency } from '../hooks/useCurrency'; import { formatDate, getWithdrawalStatusBadge, getRiskColor } from '../utils/withdrawalUtils'; @@ -150,19 +151,7 @@ export default function AdminWithdrawals() { {/* Chevron right */} - - - + ); diff --git a/src/pages/AutoLogin.tsx b/src/pages/AutoLogin.tsx index 0587951..44731dd 100644 --- a/src/pages/AutoLogin.tsx +++ b/src/pages/AutoLogin.tsx @@ -3,6 +3,7 @@ import { useSearchParams, useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; import { authApi } from '../api/auth'; import { useAuthStore } from '../store/auth'; +import { XIcon } from '@/components/icons'; export default function AutoLogin() { const { t } = useTranslation(); @@ -51,15 +52,7 @@ export default function AutoLogin() { {error ? (
- - - +

{t('landing.autoLoginFailed')}

diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index dcc1202..18db455 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -21,7 +21,7 @@ import { promoApi } from '../api/promo'; import PendingGiftCard from '../components/dashboard/PendingGiftCard'; import SubscriptionListCard from '../components/subscription/SubscriptionListCard'; import { API } from '../config/constants'; -import { ChevronRightIcon } from '@/components/icons'; +import { ChevronRightIcon, StarIcon } from '@/components/icons'; export default function Dashboard() { const { t } = useTranslation(); @@ -263,16 +263,7 @@ export default function Dashboard() { color: 'rgb(var(--color-accent-400))', }} > - + {promoGroupData.group_name} )} diff --git a/src/pages/DeepLinkRedirect.tsx b/src/pages/DeepLinkRedirect.tsx index f77317e..7422e35 100644 --- a/src/pages/DeepLinkRedirect.tsx +++ b/src/pages/DeepLinkRedirect.tsx @@ -4,6 +4,13 @@ import { useTranslation } from 'react-i18next'; import { useQuery } from '@tanstack/react-query'; import { brandingApi } from '../api/branding'; import { copyToClipboard } from '../utils/clipboard'; +import { + CheckIcon, + CopyIcon, + ExclamationIcon, + ExternalLinkIcon, + LinkIcon, +} from '@/components/icons'; type Status = 'countdown' | 'fallback' | 'error'; @@ -209,19 +216,7 @@ export default function DeepLinkRedirect() { onClick={openDeepLink} className="btn-primary flex w-full items-center justify-center gap-2 py-3" > - - - + {t('deepLink.openApp')} @@ -243,36 +238,12 @@ export default function DeepLinkRedirect() { > {copied ? ( <> - - - + {t('deepLink.copied')} ) : ( <> - - - + {t('deepLink.copyLink')} )} @@ -283,19 +254,7 @@ export default function DeepLinkRedirect() { onClick={openDeepLink} className="btn-secondary flex w-full items-center justify-center gap-2" > - - - + {t('deepLink.tryAgain')} @@ -327,19 +286,7 @@ export default function DeepLinkRedirect() { {status === 'error' && (
- - - +

{t('deepLink.errorTitle')}

{t('deepLink.errorDesc')}

@@ -351,19 +298,7 @@ export default function DeepLinkRedirect() { {/* Footer */}
- - - + VPN Config Redirect
diff --git a/src/pages/GiftResult.tsx b/src/pages/GiftResult.tsx index a9f2bb8..6b78350 100644 --- a/src/pages/GiftResult.tsx +++ b/src/pages/GiftResult.tsx @@ -9,6 +9,7 @@ import { AnimatedCheckmark } from '@/components/ui/AnimatedCheckmark'; import { AnimatedCrossmark } from '@/components/ui/AnimatedCrossmark'; import { cn } from '@/lib/utils'; import { copyToClipboard } from '@/utils/clipboard'; +import { CheckIcon, CopyIcon, InfoIcon, ExclamationIcon, ClockIcon } from '@/components/icons'; const MAX_POLL_MS = 10 * 60 * 1000; // 10 minutes @@ -147,32 +148,12 @@ function CodeOnlySuccessState({ > {copied ? ( <> - - - + {t('common.copied', 'Copied!')} ) : ( <> - - - + {t('gift.copyMessage', 'Copy message')} )} @@ -272,19 +253,7 @@ function PendingActivationState({ > {/* Info icon */}
- - - +
@@ -372,19 +341,7 @@ function PollErrorState() { className="flex flex-col items-center gap-6 text-center" >
- - - +
@@ -420,19 +377,7 @@ function PollTimedOutState({ onRetry }: { onRetry: () => void }) { className="flex flex-col items-center gap-6 text-center" >
- - - +

@@ -467,19 +412,7 @@ function NoTokenState() { className="flex flex-col items-center gap-6 text-center" >
- - - +

{t('gift.noToken', 'Invalid link')}

diff --git a/src/pages/GiftSubscription.tsx b/src/pages/GiftSubscription.tsx index 151d5cb..d1e4650 100644 --- a/src/pages/GiftSubscription.tsx +++ b/src/pages/GiftSubscription.tsx @@ -21,6 +21,7 @@ import { getApiErrorMessage } from '../utils/api-error'; import { formatPrice } from '../utils/format'; import { useCurrency } from '../hooks/useCurrency'; import { usePlatform, useHaptic } from '@/platform'; +import { SparklesIcon } from '@/components/icons'; function GiftIcon({ className }: { className?: string }) { return ( @@ -686,17 +687,7 @@ function BuyTabContent({ {config.promo_group_name && (
- - - +
@@ -713,17 +704,7 @@ function BuyTabContent({ {config.active_discount_percent != null && config.active_discount_percent > 0 && (
- - - +
{t('promo.discountApplied')} -{config.active_discount_percent}% diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 7cf03f9..4fa0b0d 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -23,6 +23,7 @@ import TelegramLoginButton from '../components/TelegramLoginButton'; import OAuthProviderIcon from '../components/OAuthProviderIcon'; import { saveOAuthState } from '../utils/oauth'; import { getPendingReferralCode } from '../utils/referral'; +import { UsersIcon, EmailIcon, RefreshIcon, ChevronDownIcon } from '@/components/icons'; export default function Login() { const { t } = useTranslation(); @@ -362,19 +363,7 @@ export default function Login() { {referralCode && isEmailAuthEnabled && (
- - - + {t('auth.referralInvite')}
@@ -385,19 +374,7 @@ export default function Login() { {registeredEmail ? (
- - - +

{t('auth.checkEmail', 'Check your email')} @@ -447,19 +424,7 @@ export default function Login() { onClick={handleRetryTelegramAuth} className="btn-primary mx-auto flex items-center gap-2 px-5 py-2.5" > - - - + {t('auth.tryAgain')}

@@ -516,29 +481,11 @@ export default function Login() { onClick={() => setShowEmailForm(!showEmailForm)} className="flex items-center gap-1.5 rounded-full border border-dark-700 bg-dark-800/60 px-3.5 py-1.5 text-xs font-medium text-dark-300 transition-all hover:border-dark-600 hover:bg-dark-700 hover:text-dark-200" > - - - + {t('auth.loginWithEmail')} - - - + />

@@ -557,19 +504,7 @@ export default function Login() { forgotPasswordSent ? (
- - - +

{t('auth.checkEmail', 'Check your email')} diff --git a/src/pages/OAuthCallback.tsx b/src/pages/OAuthCallback.tsx index 42aaa21..1f36b59 100644 --- a/src/pages/OAuthCallback.tsx +++ b/src/pages/OAuthCallback.tsx @@ -11,6 +11,7 @@ import { getErrorDetail, } from '../utils/oauth'; import type { ServerCompleteResponse } from '../types'; +import { CheckIcon, ExclamationIcon } from '@/components/icons'; type CallbackMode = 'login' | 'link-browser' | 'link-server'; @@ -145,15 +146,7 @@ export default function OAuthCallback() {

- - - +

{t('profile.accounts.linkSuccess')} @@ -209,19 +202,7 @@ export default function OAuthCallback() {
- - - +

{t('auth.loginFailed')}

{error}

diff --git a/src/pages/Polls.tsx b/src/pages/Polls.tsx index f71c4d1..2b46fba 100644 --- a/src/pages/Polls.tsx +++ b/src/pages/Polls.tsx @@ -3,7 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { pollsApi, PollInfo, PollQuestion } from '../api/polls'; import { useFocusTrap } from '../hooks/useFocusTrap'; -import { ClipboardIcon, GiftIcon, CheckIcon } from '@/components/icons'; +import { ClipboardIcon, GiftIcon, CheckIcon, CloseIcon } from '@/components/icons'; export default function Polls() { const { t } = useTranslation(); @@ -135,14 +135,7 @@ export default function Polls() { aria-label={t('common.close')} className="text-dark-400 hover:text-dark-200" > - - - +
diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index bb12b92..fe82370 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -319,15 +319,7 @@ export default function Profile() {

{t('profile.accounts.subtitle')}

- - - +
diff --git a/src/pages/PurchaseSuccess.tsx b/src/pages/PurchaseSuccess.tsx index 0f27b1a..8afddd9 100644 --- a/src/pages/PurchaseSuccess.tsx +++ b/src/pages/PurchaseSuccess.tsx @@ -8,6 +8,7 @@ import { landingApi } from '../api/landings'; import { authApi } from '../api/auth'; import { useAuthStore } from '../store/auth'; import { copyToClipboard } from '../utils/clipboard'; +import { CheckIcon, ClipboardIcon, ClockIcon, ExclamationIcon } from '@/components/icons'; import { Spinner } from '@/components/ui/Spinner'; import { AnimatedCheckmark } from '@/components/ui/AnimatedCheckmark'; import { AnimatedCrossmark } from '@/components/ui/AnimatedCrossmark'; @@ -299,32 +300,12 @@ function SuccessState({ > {copied ? ( <> - - - + {t('landing.copied', 'Copied!')} ) : ( <> - - - + {t('landing.copyLink', 'Copy link')} )} @@ -383,19 +364,7 @@ function PendingActivationState({ > {/* Warning icon */}
- - - +
@@ -560,19 +529,7 @@ function PollTimedOutState({ onRetry }: { onRetry: () => void }) { className="flex flex-col items-center gap-6 text-center" >
- - - +

diff --git a/src/pages/QuickPurchase.tsx b/src/pages/QuickPurchase.tsx index c373ee6..6504a8a 100644 --- a/src/pages/QuickPurchase.tsx +++ b/src/pages/QuickPurchase.tsx @@ -15,6 +15,7 @@ import type { PurchaseRequest, } from '../api/landings'; import { StaticBackgroundRenderer } from '../components/backgrounds/BackgroundRenderer'; +import { CheckCircleIcon, CheckIcon, DevicesIcon, DownloadIcon } from '@/components/icons'; import LanguageSwitcher from '../components/LanguageSwitcher'; import { cn } from '../lib/utils'; import { getApiErrorMessage } from '../utils/api-error'; @@ -285,52 +286,18 @@ function TariffCard({ isSelected ? 'border-accent-500 bg-accent-500' : 'border-dark-600', )} > - {isSelected && ( - - - - )} + {isSelected && }

{/* Info row */}
- - - + {tariff.traffic_limit_gb === 0 ? '∞' : tariff.traffic_limit_gb} {t('landing.gb', 'GB')} - - - + {tariff.device_limit} {t('landing.devices', 'devices')}
@@ -550,15 +517,7 @@ function SummaryCard({ {config.features.map((feature, idx) => (
- - - +

{feature.title}

diff --git a/src/pages/Referral.tsx b/src/pages/Referral.tsx index eac094a..e669e67 100644 --- a/src/pages/Referral.tsx +++ b/src/pages/Referral.tsx @@ -14,9 +14,12 @@ import { CheckIcon, ClockIcon, CopyIcon, + ExclamationIcon, LinkIcon, PartnerIcon, ShareIcon, + TelegramIcon, + UsersIcon, WalletIcon, } from '@/components/icons'; @@ -212,19 +215,7 @@ export default function Referral() { return (
- - - +

{t('referral.title')}

@@ -273,9 +264,7 @@ export default function Referral() { {botReferralLink && (
- - - + {t('referral.botLink')}
@@ -302,19 +291,7 @@ export default function Referral() { {/* Cabinet link */}
- - - + {t('referral.cabinetLink')}
@@ -383,19 +360,7 @@ export default function Referral() { ) : (
- - - +
{t('referral.noReferrals')}
@@ -519,19 +484,7 @@ export default function Referral() {
- - - +

diff --git a/src/pages/ReferralNetwork/components/CampaignDetailPanel.tsx b/src/pages/ReferralNetwork/components/CampaignDetailPanel.tsx index 84c1ac6..2661911 100644 --- a/src/pages/ReferralNetwork/components/CampaignDetailPanel.tsx +++ b/src/pages/ReferralNetwork/components/CampaignDetailPanel.tsx @@ -1,5 +1,6 @@ import { useTranslation } from 'react-i18next'; import { useQuery } from '@tanstack/react-query'; +import { CloseIcon } from '@/components/icons'; import { referralNetworkApi } from '@/api/referralNetwork'; import { useReferralNetworkStore } from '@/store/referralNetwork'; import { formatKopeksToRubles } from '../utils'; @@ -37,15 +38,7 @@ export function CampaignDetailPanel({ campaignId, className }: CampaignDetailPan className="rounded-lg p-1 text-dark-500 transition-colors hover:bg-dark-800 hover:text-dark-300" aria-label={t('common.close')} > - - - +

diff --git a/src/pages/ReferralNetwork/components/NetworkControls.tsx b/src/pages/ReferralNetwork/components/NetworkControls.tsx index fbd4478..2bab30c 100644 --- a/src/pages/ReferralNetwork/components/NetworkControls.tsx +++ b/src/pages/ReferralNetwork/components/NetworkControls.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { MinusIcon, PlusIcon } from '@/components/icons'; import { getSigmaInstance } from '../sigmaGlobals'; interface NetworkControlsProps { @@ -62,32 +63,12 @@ export function NetworkControls({ className }: NetworkControlsProps) { { label: t('admin.referralNetwork.controls.zoomIn'), onClick: handleZoomIn, - icon: ( - - - - ), + icon: , }, { label: t('admin.referralNetwork.controls.zoomOut'), onClick: handleZoomOut, - icon: ( - - - - ), + icon: , }, { label: t('admin.referralNetwork.controls.resetZoom'), diff --git a/src/pages/ReferralNetwork/components/NetworkFilters.tsx b/src/pages/ReferralNetwork/components/NetworkFilters.tsx index 94c53d3..5ac1c6f 100644 --- a/src/pages/ReferralNetwork/components/NetworkFilters.tsx +++ b/src/pages/ReferralNetwork/components/NetworkFilters.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; +import { CloseIcon, FilterIcon } from '@/components/icons'; import { useReferralNetworkStore } from '@/store/referralNetwork'; import type { NetworkGraphData } from '@/types/referralNetwork'; @@ -120,19 +121,7 @@ export function NetworkFilters({ data, className }: NetworkFiltersProps) { : 'border-dark-700/50 bg-dark-800/80 text-dark-300 hover:border-dark-600 hover:text-dark-100' }`} > - - - + {t('admin.referralNetwork.filters.title')} {hasActiveFilters && ( @@ -152,15 +141,7 @@ export function NetworkFilters({ data, className }: NetworkFiltersProps) { aria-label={t('common.close')} className="text-dark-500 transition-colors hover:text-dark-300" > - - - +
{panelContent} @@ -182,15 +163,7 @@ export function NetworkFilters({ data, className }: NetworkFiltersProps) { aria-label={t('common.close')} className="rounded-lg p-1 text-dark-500 transition-colors hover:bg-dark-800 hover:text-dark-300" > - - - +
{panelContent} diff --git a/src/pages/ReferralNetwork/components/ScopeSelector.tsx b/src/pages/ReferralNetwork/components/ScopeSelector.tsx index 00d453f..fd35b6e 100644 --- a/src/pages/ReferralNetwork/components/ScopeSelector.tsx +++ b/src/pages/ReferralNetwork/components/ScopeSelector.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery } from '@tanstack/react-query'; import { referralNetworkApi } from '@/api/referralNetwork'; +import { CheckIcon, CloseIcon, PlusIcon, SearchIcon } from '@/components/icons'; import { MAX_SCOPE_ITEMS } from '@/store/referralNetwork'; import type { ScopeSelection, ScopeType } from '@/types/referralNetwork'; @@ -30,28 +31,6 @@ const AVATAR_LETTERS: Record = { user: 'U', }; -function CheckIcon() { - return ( - - - - ); -} - -function CloseIcon({ className = 'h-3 w-3' }: { className?: string }) { - return ( - - - - ); -} - function Spinner({ size = 'h-5 w-5' }: { size?: string }) { return (
- + ))} @@ -273,15 +252,7 @@ export function ScopeSelector({ value, onAdd, onRemove, onClear, className }: Sc }`} disabled={isMaxReached && !isDropdownOpen} > - - - +
@@ -323,19 +294,7 @@ export function ScopeSelector({ value, onAdd, onRemove, onClear, className }: Sc
- - - + - - - +
diff --git a/src/pages/ResetPassword.tsx b/src/pages/ResetPassword.tsx index 97f1a05..9863fef 100644 --- a/src/pages/ResetPassword.tsx +++ b/src/pages/ResetPassword.tsx @@ -3,6 +3,7 @@ import { useSearchParams, Link, useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; import { authApi } from '../api/auth'; import LanguageSwitcher from '../components/LanguageSwitcher'; +import { CheckIcon } from '@/components/icons'; export default function ResetPassword() { const { t } = useTranslation(); @@ -97,15 +98,7 @@ export default function ResetPassword() { {status === 'success' ? (
- - - +

{t('resetPassword.success', 'Password changed!')} diff --git a/src/pages/Subscription.tsx b/src/pages/Subscription.tsx index 6d7a2c1..cc5017c 100644 --- a/src/pages/Subscription.tsx +++ b/src/pages/Subscription.tsx @@ -17,7 +17,16 @@ import InsufficientBalancePrompt from '../components/InsufficientBalancePrompt'; import { useCurrency } from '../hooks/useCurrency'; import { useCloseOnSuccessNotification } from '../store/successNotification'; import PurchaseCTAButton from '../components/subscription/PurchaseCTAButton'; -import { CopyIcon, CheckIcon, PauseIcon } from '../components/icons'; +import { + CopyIcon, + CheckIcon, + PauseIcon, + CalendarIcon, + RefreshIcon, + DevicesIcon, + DownloadIcon, + TrashIcon, +} from '../components/icons'; import { useHaptic } from '../platform'; import { resolveConnectionUrlForUi } from '../utils/connectionLink'; import { @@ -97,26 +106,17 @@ const CountdownTimer = memo(function CountdownTimer({ : g.hoverBg, }} > - + +

{t('dashboard.remaining')}
@@ -774,20 +774,10 @@ export default function Subscription() { disabled={refreshTrafficMutation.isPending || trafficRefreshCooldown > 0} className="flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium text-dark-50/30 transition-colors hover:bg-dark-50/[0.05] hover:text-dark-50/50 disabled:cursor-not-allowed disabled:opacity-50" > - + {trafficRefreshCooldown > 0 ? `${trafficRefreshCooldown}s` : t('common.refresh')} @@ -827,23 +817,9 @@ export default function Subscription() { >
- +
@@ -1000,21 +976,9 @@ export default function Subscription() {
- +
{purchase.traffic_gb} {t('common.units.gb')} @@ -1121,23 +1085,9 @@ export default function Subscription() { >
- +
{t('subscription.noSubscription')}
diff --git a/src/pages/SubscriptionPurchase.tsx b/src/pages/SubscriptionPurchase.tsx index 28be245..d5e1387 100644 --- a/src/pages/SubscriptionPurchase.tsx +++ b/src/pages/SubscriptionPurchase.tsx @@ -12,6 +12,7 @@ import { SwitchTariffSheet } from '../components/subscription/sheets/SwitchTarif import { TariffPurchaseForm } from '../components/subscription/purchase/TariffPurchaseForm'; import { TariffPickerGrid } from '../components/subscription/purchase/TariffPickerGrid'; import { ClassicPurchaseWizard } from '../components/subscription/purchase/ClassicPurchaseWizard'; +import { ExclamationIcon, SparklesIcon } from '@/components/icons'; export default function SubscriptionPurchase() { const { t } = useTranslation(); @@ -177,23 +178,12 @@ export default function SubscriptionPurchase() {
- +
- +
void }) { className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-2xl" style={{ background: g.innerBg }} > - - - +

{t('subscriptions.empty', 'Нет подписок')} @@ -128,15 +117,7 @@ export default function Subscriptions() { border: '1px solid rgba(var(--color-accent-400), 0.2)', }} > - - - + {t('subscriptions.buyAnother', 'Новый тариф')} )} diff --git a/src/pages/Support.tsx b/src/pages/Support.tsx index 4e0c67c..5d44498 100644 --- a/src/pages/Support.tsx +++ b/src/pages/Support.tsx @@ -12,7 +12,7 @@ import type { TicketDetail } from '../types'; import { Card } from '@/components/data-display/Card'; import { Button } from '@/components/primitives/Button'; import { staggerContainer, staggerItem } from '@/components/motion/transitions'; -import { CloseIcon, ImageIcon, PlusIcon, SendIcon } from '@/components/icons'; +import { ChatIcon, CloseIcon, ImageIcon, PlusIcon, SendIcon } from '@/components/icons'; import { usePlatform } from '@/platform'; import { linkifyText } from '../utils/linkify'; @@ -266,19 +266,7 @@ export default function Support() {
- - - +

{supportMessage.title}

{supportMessage.message}

@@ -366,19 +354,7 @@ export default function Support() {
- - - +
{t('support.contactUs')}
@@ -440,19 +416,7 @@ export default function Support() { ) : (
- - - +
{t('support.noTickets')}
diff --git a/src/pages/TelegramRedirect.tsx b/src/pages/TelegramRedirect.tsx index f7583ed..6c05007 100644 --- a/src/pages/TelegramRedirect.tsx +++ b/src/pages/TelegramRedirect.tsx @@ -8,6 +8,7 @@ import { brandingApi } from '../api/branding'; import { isInTelegramWebApp, getTelegramInitData } from '../hooks/useTelegramSDK'; import { tokenStorage } from '../utils/token'; import { getSafeRedirectPath } from '../utils/safeRedirect'; +import { CheckIcon, XIcon, ExclamationIcon } from '@/components/icons'; const MAX_RETRY_ATTEMPTS = 3; const RETRY_COUNT_KEY = 'telegram_redirect_retry_count'; @@ -158,15 +159,7 @@ export default function TelegramRedirect() { {status === 'success' && (
- - - +

{t('auth.loginSuccess')}

{t('telegramRedirect.redirecting')}

@@ -177,15 +170,7 @@ export default function TelegramRedirect() { {status === 'error' && (
- - - +

{t('auth.loginFailed')}

{errorMessage}

@@ -204,19 +189,7 @@ export default function TelegramRedirect() { {status === 'not-telegram' && (
- - - +

{t('telegramRedirect.openInTelegram')}

{t('telegramRedirect.openInTelegramDesc')}

diff --git a/src/pages/TopUpAmount.tsx b/src/pages/TopUpAmount.tsx index d0ef21e..a4cc3d7 100644 --- a/src/pages/TopUpAmount.tsx +++ b/src/pages/TopUpAmount.tsx @@ -20,6 +20,7 @@ import { CheckIcon, CopyIcon, CryptoIcon, + ExclamationIcon, ExternalLinkIcon, SparklesIcon, StarIcon, @@ -520,19 +521,7 @@ export default function TopUpAmount() { variants={staggerItem} className="flex items-center gap-2 rounded-xl border border-error-500/20 bg-error-500/10 p-3" > - - - + {error} )}