mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
refactor(cabinet): unify all statistics onto the shared StatCard
Bring every stat strip across the admin panel and user cabinet onto the canonical StatCard (icon chip + label-on-top + tone-coloured value), matching the sales/Remnawave style the rest of the app already used. - Convert ~20 hand-rolled stat strips across 21 pages/components - Remove 4 divergent local StatCard components (Dashboard, Users, BanSystem, Payments) and migrate Remnawave's local StatCard (33 cards) - Extend StatCard with optional subValue, loading skeleton, trailing slot, and icon-size normalisation (all backward-compatible) - StatsGrid nav tiles now use StatCard while keeping link/chevron/loading - Preserve all data, i18n keys, conditionals and interactive filters; fix the dead 'info' colour by mapping it to accent
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
import { PiCaretRight } from 'react-icons/pi';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router';
|
||||
import { useCurrency } from '../../hooks/useCurrency';
|
||||
import { useTheme } from '../../hooks/useTheme';
|
||||
import { getGlassColors } from '../../utils/glassTheme';
|
||||
import { StatCard } from '@/components/stats';
|
||||
import { CardIcon, ChevronRightIcon, UsersIcon } from '@/components/icons';
|
||||
|
||||
interface StatsGridProps {
|
||||
balanceRubles: number;
|
||||
@@ -12,10 +11,6 @@ interface StatsGridProps {
|
||||
refLoading: boolean;
|
||||
}
|
||||
|
||||
const ChevronIcon = ({ color }: { color: string }) => (
|
||||
<PiCaretRight width={16} height={16} style={{ flexShrink: 0, color }} aria-hidden="true" />
|
||||
);
|
||||
|
||||
export default function StatsGrid({
|
||||
balanceRubles,
|
||||
referralCount,
|
||||
@@ -24,122 +19,31 @@ export default function StatsGrid({
|
||||
}: StatsGridProps) {
|
||||
const { t } = useTranslation();
|
||||
const { formatAmount, currencySymbol } = useCurrency();
|
||||
const { isDark } = useTheme();
|
||||
const g = getGlassColors(isDark);
|
||||
|
||||
const accentColor = 'rgb(var(--color-accent-400))';
|
||||
const accentBg = 'rgba(var(--color-accent-400), 0.07)';
|
||||
|
||||
const cards = [
|
||||
{
|
||||
label: t('dashboard.stats.balance'),
|
||||
value: `${formatAmount(balanceRubles)} ${currencySymbol}`,
|
||||
valueColor: accentColor,
|
||||
to: '/balance',
|
||||
icon: (color: string) => (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke={color}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<rect x="2" y="6" width="20" height="14" rx="2" />
|
||||
<path d="M2 10h20" />
|
||||
<path d="M6 14h.01M10 14h.01" />
|
||||
</svg>
|
||||
),
|
||||
iconBg: accentBg,
|
||||
iconColor: accentColor,
|
||||
loading: false,
|
||||
onboarding: 'balance',
|
||||
},
|
||||
{
|
||||
label: t('dashboard.stats.referrals'),
|
||||
value: `${referralCount}`,
|
||||
valueColor: g.text,
|
||||
subtitle: `+${formatAmount(earningsRubles)} ${currencySymbol}`,
|
||||
subtitleColor: accentColor,
|
||||
to: '/referral',
|
||||
icon: (color: string) => (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke={color}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" />
|
||||
</svg>
|
||||
),
|
||||
iconBg: g.trackBg,
|
||||
iconColor: g.textSecondary,
|
||||
loading: refLoading,
|
||||
},
|
||||
];
|
||||
const chevron = <ChevronRightIcon className="h-4 w-4 shrink-0 text-dark-500" />;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-2.5">
|
||||
{cards.map((card, i) => (
|
||||
<Link
|
||||
key={i}
|
||||
to={card.to}
|
||||
className="group relative overflow-hidden rounded-[18px] transition-all duration-200"
|
||||
style={{
|
||||
background: g.cardBg,
|
||||
border: `1px solid ${g.cardBorder}`,
|
||||
boxShadow: g.shadow,
|
||||
padding: '18px 20px 20px',
|
||||
}}
|
||||
data-onboarding={card.onboarding}
|
||||
>
|
||||
{/* Top row: icon + label + arrow */}
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-[9px] transition-colors duration-500"
|
||||
style={{ background: card.iconBg }}
|
||||
>
|
||||
{card.icon(card.iconColor)}
|
||||
</div>
|
||||
<span className="text-[13px] font-medium text-dark-50/45">{card.label}</span>
|
||||
</div>
|
||||
<ChevronIcon color={g.textFaint} />
|
||||
</div>
|
||||
|
||||
{/* Value */}
|
||||
{card.loading ? (
|
||||
<div className="skeleton h-8 w-20" />
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
className="text-[28px] font-bold leading-tight tracking-tight transition-colors duration-500"
|
||||
style={{ color: card.valueColor }}
|
||||
>
|
||||
{card.value}
|
||||
</div>
|
||||
{card.subtitle && (
|
||||
<div
|
||||
className="mt-0.5 text-[13px] font-semibold"
|
||||
style={{ color: card.subtitleColor }}
|
||||
>
|
||||
{card.subtitle}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
<Link to="/balance" className="block" data-onboarding="balance">
|
||||
<StatCard
|
||||
label={t('dashboard.stats.balance')}
|
||||
value={`${formatAmount(balanceRubles)} ${currencySymbol}`}
|
||||
icon={<CardIcon className="h-5 w-5" />}
|
||||
tone="accent"
|
||||
trailing={chevron}
|
||||
/>
|
||||
</Link>
|
||||
<Link to="/referral" className="block">
|
||||
<StatCard
|
||||
label={t('dashboard.stats.referrals')}
|
||||
value={`${referralCount}`}
|
||||
subValue={`+${formatAmount(earningsRubles)} ${currencySymbol}`}
|
||||
icon={<UsersIcon className="h-5 w-5" />}
|
||||
tone="neutral"
|
||||
loading={refLoading}
|
||||
trailing={chevron}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user