mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
style(admin-landings): rebuild stats summary on the StatCard pattern
The landing stats page used plain, icon-less centered boxes that looked inconsistent with the Sales/Remnawave stats. Rebuild the summary on the shared StatCard (tinted icon chip + tone + value), consolidating the two scattered stat rows into a clean 2×4 grid (created/paid/revenue/conversion + purchases/regular/gifts/avg) with barrel Phosphor icons. Chart and list containers now use the same bento-card surface as the rest of the admin stats.
This commit is contained in:
@@ -26,12 +26,20 @@ import { useCurrency } from '../hooks/useCurrency';
|
|||||||
import { useChartColors } from '../hooks/useChartColors';
|
import { useChartColors } from '../hooks/useChartColors';
|
||||||
import { CHART_COMMON } from '../constants/charts';
|
import { CHART_COMMON } from '../constants/charts';
|
||||||
import { AdminBackButton } from '../components/admin';
|
import { AdminBackButton } from '../components/admin';
|
||||||
|
import { StatCard } from '../components/stats';
|
||||||
import {
|
import {
|
||||||
ChartIcon,
|
ChartIcon,
|
||||||
EmailIcon,
|
EmailIcon,
|
||||||
TelegramSmallIcon,
|
TelegramSmallIcon,
|
||||||
ArrowRightIcon,
|
ArrowRightIcon,
|
||||||
GiftIcon,
|
GiftIcon,
|
||||||
|
EyeIcon,
|
||||||
|
CheckCircleIcon,
|
||||||
|
BanknotesIcon,
|
||||||
|
PercentIcon,
|
||||||
|
TicketIcon,
|
||||||
|
CardIcon,
|
||||||
|
WalletIcon,
|
||||||
ChevronLeftIcon as ChevronLeftSmall,
|
ChevronLeftIcon as ChevronLeftSmall,
|
||||||
ChevronRightIcon as ChevronRightSmall,
|
ChevronRightIcon as ChevronRightSmall,
|
||||||
} from '@/components/icons';
|
} from '@/components/icons';
|
||||||
@@ -339,39 +347,62 @@ export default function AdminLandingStats() {
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Summary Cards */}
|
{/* Summary Cards */}
|
||||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4 text-center">
|
<StatCard
|
||||||
<div className="text-xl font-bold sm:text-2xl">
|
label={t('admin.landings.stats.created', 'Created')}
|
||||||
<span className="text-warning-400">{stats.total_created}</span>
|
value={stats.total_created}
|
||||||
<span className="mx-1 text-dark-600">/</span>
|
icon={<EyeIcon className="h-5 w-5" />}
|
||||||
<span className="text-success-400">{stats.total_successful}</span>
|
tone="warning"
|
||||||
</div>
|
/>
|
||||||
<div className="text-xs text-dark-500">
|
<StatCard
|
||||||
{t('admin.landings.stats.created', 'Created')} /{' '}
|
label={t('admin.landings.stats.paid', 'paid')}
|
||||||
{t('admin.landings.stats.paid', 'paid')}
|
value={stats.total_successful}
|
||||||
</div>
|
icon={<CheckCircleIcon className="h-5 w-5" />}
|
||||||
</div>
|
tone="success"
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4 text-center">
|
/>
|
||||||
<div className="truncate text-xl font-bold text-accent-400 sm:text-2xl">
|
<StatCard
|
||||||
{formatWithCurrency(stats.total_revenue_kopeks / CHART_COMMON.KOPEKS_DIVISOR)}
|
label={t('admin.landings.stats.revenue')}
|
||||||
</div>
|
value={formatWithCurrency(stats.total_revenue_kopeks / CHART_COMMON.KOPEKS_DIVISOR)}
|
||||||
<div className="text-xs text-dark-500">{t('admin.landings.stats.revenue')}</div>
|
icon={<BanknotesIcon className="h-5 w-5" />}
|
||||||
</div>
|
tone="success"
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4 text-center">
|
/>
|
||||||
<div className="text-xl font-bold text-accent-400 sm:text-2xl">{stats.total_gifts}</div>
|
<StatCard
|
||||||
<div className="text-xs text-dark-500">{t('admin.landings.stats.giftPurchases')}</div>
|
label={t('admin.landings.stats.conversionRate')}
|
||||||
</div>
|
value={`${stats.conversion_rate}%`}
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4 text-center">
|
icon={<PercentIcon className="h-5 w-5" />}
|
||||||
<div className="text-xl font-bold text-dark-200 sm:text-2xl">
|
tone="accent"
|
||||||
{stats.conversion_rate}%
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-dark-500">{t('admin.landings.stats.conversionRate')}</div>
|
|
||||||
</div>
|
{/* Breakdown Cards */}
|
||||||
|
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||||
|
<StatCard
|
||||||
|
label={t('admin.landings.stats.purchases')}
|
||||||
|
value={stats.total_purchases}
|
||||||
|
icon={<TicketIcon className="h-5 w-5" />}
|
||||||
|
tone="accent"
|
||||||
|
/>
|
||||||
|
<StatCard
|
||||||
|
label={t('admin.landings.stats.regularPurchases')}
|
||||||
|
value={stats.total_regular}
|
||||||
|
icon={<CardIcon className="h-5 w-5" />}
|
||||||
|
/>
|
||||||
|
<StatCard
|
||||||
|
label={t('admin.landings.stats.giftPurchases')}
|
||||||
|
value={stats.total_gifts}
|
||||||
|
icon={<GiftIcon className="h-5 w-5" />}
|
||||||
|
tone="accent"
|
||||||
|
/>
|
||||||
|
<StatCard
|
||||||
|
label={t('admin.landings.stats.avgPurchase')}
|
||||||
|
value={formatWithCurrency(stats.avg_purchase_kopeks / CHART_COMMON.KOPEKS_DIVISOR)}
|
||||||
|
icon={<WalletIcon className="h-5 w-5" />}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Charts */}
|
{/* Charts */}
|
||||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
{/* Daily Purchases & Revenue */}
|
{/* Daily Purchases & Revenue */}
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4">
|
<div className="bento-card">
|
||||||
<h3 className="mb-4 font-medium text-dark-200">
|
<h3 className="mb-4 font-medium text-dark-200">
|
||||||
{t('admin.landings.stats.dailyChart')}
|
{t('admin.landings.stats.dailyChart')}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -501,7 +532,7 @@ export default function AdminLandingStats() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Daily Purchases Bar Chart */}
|
{/* Daily Purchases Bar Chart */}
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4">
|
<div className="bento-card">
|
||||||
<h3 className="mb-4 font-medium text-dark-200">
|
<h3 className="mb-4 font-medium text-dark-200">
|
||||||
{t('admin.landings.stats.dailyPurchases', 'Daily purchases')}
|
{t('admin.landings.stats.dailyPurchases', 'Daily purchases')}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -557,7 +588,7 @@ export default function AdminLandingStats() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tariff Distribution -- full width */}
|
{/* Tariff Distribution -- full width */}
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4">
|
<div className="bento-card">
|
||||||
<h3 className="mb-4 font-medium text-dark-200">
|
<h3 className="mb-4 font-medium text-dark-200">
|
||||||
{t('admin.landings.stats.tariffChart')}
|
{t('admin.landings.stats.tariffChart')}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -614,39 +645,9 @@ export default function AdminLandingStats() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Additional Stats Row */}
|
|
||||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4">
|
|
||||||
<div className="mb-1 text-sm text-dark-400">
|
|
||||||
{t('admin.landings.stats.avgPurchase')}
|
|
||||||
</div>
|
|
||||||
<div className="text-lg font-medium text-dark-200">
|
|
||||||
{formatWithCurrency(stats.avg_purchase_kopeks / CHART_COMMON.KOPEKS_DIVISOR)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4">
|
|
||||||
<div className="mb-1 text-sm text-dark-400">
|
|
||||||
{t('admin.landings.stats.regularPurchases')}
|
|
||||||
</div>
|
|
||||||
<div className="text-lg font-medium text-dark-200">{stats.total_regular}</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-2 rounded-xl border border-dark-700 bg-dark-800 p-4 sm:col-span-1">
|
|
||||||
<div className="mb-1 text-sm text-dark-400">{t('admin.landings.stats.funnel')}</div>
|
|
||||||
<div className="text-lg font-medium text-dark-200">
|
|
||||||
{stats.total_created}{' '}
|
|
||||||
<span className="text-sm text-dark-500">{t('admin.landings.stats.created')}</span>
|
|
||||||
{' / '}
|
|
||||||
{stats.total_successful}{' '}
|
|
||||||
<span className="text-sm text-dark-500">
|
|
||||||
{t('admin.landings.stats.paid', 'paid')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Gift vs Regular Donut */}
|
{/* Gift vs Regular Donut */}
|
||||||
{stats.total_purchases > 0 && (
|
{stats.total_purchases > 0 && (
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4">
|
<div className="bento-card">
|
||||||
<h3 className="mb-4 font-medium text-dark-200">
|
<h3 className="mb-4 font-medium text-dark-200">
|
||||||
{t('admin.landings.stats.giftBreakdown')}
|
{t('admin.landings.stats.giftBreakdown')}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -706,7 +707,7 @@ export default function AdminLandingStats() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Purchases List */}
|
{/* Purchases List */}
|
||||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4">
|
<div className="bento-card">
|
||||||
{/* Header row: title + status filter */}
|
{/* Header row: title + status filter */}
|
||||||
<div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
<div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<h3 className="font-medium text-dark-200">{t('admin.landings.purchases.title')}</h3>
|
<h3 className="font-medium text-dark-200">{t('admin.landings.purchases.title')}</h3>
|
||||||
|
|||||||
Reference in New Issue
Block a user