refactor(cabinet): migrate inline SVG icons to the central react-icons set

Audit-driven sweep: replaced 168 hand-written inline <svg> icons across 77
files with the central Phosphor (react-icons/pi) components from
@/components/icons, preserving each icon's size classes and colour (dynamic
stroke colours via the parent's currentColor, RefreshIcon's spinning state,
conditional rotate-180 chevrons).

Verified: tsc + vite build + eslint clean; an adversarial diff review of all
changed files found the replacements correct (70 files clean, 0 blockers).
Remaining inline <svg> dropped from 262 to ~95 — the survivors are legitimate
non-icons (brand/provider logos, loading spinners & framer-motion animations,
charts/sparklines, background decoration) plus a few ambiguous glyphs.
This commit is contained in:
c0mrade
2026-05-31 23:37:12 +03:00
parent 08f12daa40
commit b5088c70a1
76 changed files with 331 additions and 1985 deletions

View File

@@ -1,6 +1,7 @@
import { Link } from 'react-router';
import { useTranslation } from 'react-i18next';
import { motion } from 'framer-motion';
import { GiftIcon } from '@/components/icons';
import type { PendingGift } from '../../api/gift';
interface PendingGiftCardProps {
@@ -28,19 +29,7 @@ export default function PendingGiftCard({ gifts, className }: PendingGiftCardPro
<div className="relative flex items-start gap-4">
{/* Gift icon */}
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-accent-500/20">
<svg
className="h-6 w-6 text-accent-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={1.5}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M21 11.25v8.25a1.5 1.5 0 01-1.5 1.5H5.25a1.5 1.5 0 01-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 109.375 7.5H12m0-2.625V7.5m0-2.625A2.625 2.625 0 1114.625 7.5H12m0 0V21m-8.625-9.75h18c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125h-18c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z"
/>
</svg>
<GiftIcon className="h-6 w-6 text-accent-400" />
</div>
{/* Content */}

View File

@@ -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,
}}
>
<svg
width="13"
height="13"
viewBox="0 0 24 24"
fill="none"
stroke={daysLeft <= 3 ? 'rgb(var(--color-warning-400))' : g.textSecondary}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
<span
style={{
color: daysLeft <= 3 ? 'rgb(var(--color-warning-400))' : g.textSecondary,
}}
aria-hidden="true"
>
<rect x="3" y="4" width="18" height="18" rx="2" />
<path d="M16 2v4M8 2v4M3 10h18" />
</svg>
<CalendarIcon className="h-[13px] w-[13px]" />
</span>
</div>
{t('dashboard.remaining')}
</div>

View File

@@ -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 ? (
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke={accent.hex}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" />
<line x1="12" y1="9" x2="12" y2="13" />
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
<ExclamationIcon className="h-[22px] w-[22px]" />
) : (
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke={accent.hex}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="10" />
<path d="M12 6v6l4 2" />
</svg>
<ClockIcon className="h-[22px] w-[22px]" />
)}
</div>
<h2 className="text-lg font-bold tracking-tight text-dark-50">
@@ -275,19 +249,7 @@ export default function SubscriptionCardExpired({
boxShadow: `0 4px 20px rgba(${accent.r},${accent.g},${accent.b},0.2)`,
}}
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M12 4.5v15m7.5-7.5h-15" />
</svg>
<PlusIcon className="h-4 w-4" />
{t('subscription.buyTraffic')}
</Link>
) : (
@@ -330,19 +292,7 @@ export default function SubscriptionCardExpired({
boxShadow: `0 4px 20px rgba(${accent.r},${accent.g},${accent.b},0.2)`,
}}
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M12 4.5v15m7.5-7.5h-15" />
</svg>
<PlusIcon className="h-4 w-4" />
{t('dashboard.expired.topUp')}
</button>
)}

View File

@@ -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 ? (
<svg
width="26"
height="26"
viewBox="0 0 24 24"
fill="none"
stroke="rgb(var(--color-accent-400))"
strokeWidth="1.5"
<span
className="flex"
style={{ color: 'rgb(var(--color-accent-400))' }}
aria-hidden="true"
>
<path
d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.455 2.456L21.75 6l-1.036.259a3.375 3.375 0 00-2.455 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<SparklesIcon className="h-[26px] w-[26px]" />
</span>
) : (
<svg
width="26"
height="26"
viewBox="0 0 24 24"
fill="none"
stroke="rgb(var(--color-urgent-400))"
strokeWidth="1.5"
<span
className="flex"
style={{ color: 'rgb(var(--color-urgent-400))' }}
aria-hidden="true"
>
<path
d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<BoltIcon className="h-[26px] w-[26px]" />
</span>
)}
{/* Glow effect */}
<div