diff --git a/src/api/adminSalesStats.ts b/src/api/adminSalesStats.ts index 5017520..bcd1eea 100644 --- a/src/api/adminSalesStats.ts +++ b/src/api/adminSalesStats.ts @@ -16,6 +16,8 @@ export interface SalesSummary { active_subscriptions: number; active_trials: number; new_trials: number; + new_paid_subscriptions: number; + expired_subscriptions: number; trial_to_paid_conversion: number; renewals_count: number; addon_revenue_kopeks: number; diff --git a/src/components/sales-stats/TrialsTab.tsx b/src/components/sales-stats/TrialsTab.tsx index f041105..a074907 100644 --- a/src/components/sales-stats/TrialsTab.tsx +++ b/src/components/sales-stats/TrialsTab.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import type { SalesStatsParams } from '../../api/adminSalesStats'; import { salesStatsApi } from '../../api/adminSalesStats'; import { SALES_STATS } from '../../constants/salesStats'; -import { ClockIcon, GiftIcon, PercentIcon, UserPlusIcon } from '../../components/icons'; +import { GiftIcon, PercentIcon, UserPlusIcon } from '../../components/icons'; import { StatCard } from '../stats'; import { DonutChart } from './DonutChart'; @@ -61,7 +61,7 @@ export function TrialsTab({ params }: TrialsTabProps) { return (
-
+
} tone="success" /> - } - tone="neutral" - />
diff --git a/src/locales/en.json b/src/locales/en.json index eef7a36..41c7c93 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1304,6 +1304,7 @@ "activeSubs": "Active Subs", "activeTrials": "Active Trials", "newTrials": "New Trials", + "newPaid": "New Paid Subscriptions", "conversion": "Conversion", "renewals": "Renewals", "addonRevenue": "Add-on Revenue", @@ -1314,7 +1315,6 @@ "totalRegistrations": "Registrations", "trialsIssued": "Trials Issued", "conversion": "Conversion Rate", - "avgDuration": "Avg Duration", "byProvider": "By Registration Source", "dailyChart": "Daily Registrations & Trials", "registrations": "Registrations" diff --git a/src/locales/fa.json b/src/locales/fa.json index 377d506..00ef8c1 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -1177,6 +1177,7 @@ "activeSubs": "اشتراک فعال", "activeTrials": "آزمایشی فعال", "newTrials": "آزمایشی جدید", + "newPaid": "اشتراک‌های پولی جدید", "conversion": "تبدیل", "renewals": "تمدید", "addonRevenue": "درآمد خدمات اضافی", @@ -1187,7 +1188,6 @@ "totalRegistrations": "ثبت‌نام‌ها", "trialsIssued": "آزمایشی صادرشده", "conversion": "نرخ تبدیل", - "avgDuration": "میانگین مدت", "byProvider": "بر اساس منبع", "dailyChart": "ثبت‌نام و آزمایشی روزانه", "registrations": "ثبت‌نام" diff --git a/src/locales/ru.json b/src/locales/ru.json index 3ad0fe5..d7bd867 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1326,6 +1326,7 @@ "activeSubs": "Активные подписки", "activeTrials": "Активные триалы", "newTrials": "Новые триалы", + "newPaid": "Новые платные подписки", "conversion": "Конверсия", "renewals": "Продления", "addonRevenue": "Доп. услуги", @@ -1336,7 +1337,6 @@ "totalRegistrations": "Регистрации", "trialsIssued": "Выданные триалы", "conversion": "Конверсия", - "avgDuration": "Средняя длит.", "byProvider": "По источнику регистрации", "dailyChart": "Регистрации и триалы по дням", "registrations": "Регистрации" diff --git a/src/locales/zh.json b/src/locales/zh.json index 9a188d9..9143769 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1177,6 +1177,7 @@ "activeSubs": "活跃订阅", "activeTrials": "活跃试用", "newTrials": "新试用", + "newPaid": "新增付费订阅", "conversion": "转化率", "renewals": "续订", "addonRevenue": "附加服务收入", @@ -1187,7 +1188,6 @@ "totalRegistrations": "注册数", "trialsIssued": "已发放试用", "conversion": "转化率", - "avgDuration": "平均时长", "byProvider": "按注册来源", "dailyChart": "每日注册与试用", "registrations": "注册" diff --git a/src/pages/AdminSalesStats.tsx b/src/pages/AdminSalesStats.tsx index 78a366e..ce59c47 100644 --- a/src/pages/AdminSalesStats.tsx +++ b/src/pages/AdminSalesStats.tsx @@ -12,6 +12,7 @@ import { BanknotesIcon, GiftIcon, PercentIcon, + PlusIcon, RepeatIcon, RocketIcon, SparklesIcon, @@ -113,12 +114,26 @@ export default function AdminSalesStats() { return { revenue: computeDelta(summary.total_revenue_kopeks, prevSummary.total_revenue_kopeks), newTrials: computeDelta(summary.new_trials, prevSummary.new_trials), + newPaid: computeDelta(summary.new_paid_subscriptions, prevSummary.new_paid_subscriptions), renewals: computeDelta(summary.renewals_count, prevSummary.renewals_count), addonRevenue: computeDelta(summary.addon_revenue_kopeks, prevSummary.addon_revenue_kopeks), manualTopup: computeDelta(summary.manual_topup_kopeks, prevSummary.manual_topup_kopeks), }; }, [summary, prevSummary]); + // Active-subscriptions trend = net change over the period (new paid − expired), + // shown relative to the count at the start of the period. The active count + // itself is a "right now" snapshot, so a plain period-over-period delta would + // always be zero — this shows real growth/shrinkage instead. + const activeDelta = useMemo(() => { + if (!summary) return null; + const net = summary.new_paid_subscriptions - summary.expired_subscriptions; + if (net === 0) return null; + const base = summary.active_subscriptions - net; + const percent = base > 0 ? Math.round((Math.abs(net) / base) * 1000) / 10 : 100; + return { percent, trend: net > 0 ? 'up' : 'down' }; + }, [summary]); + const tabs: { id: TabId; label: string }[] = [ { id: 'trials', label: t('admin.salesStats.tabs.trials') }, { id: 'sales', label: t('admin.salesStats.tabs.sales') }, @@ -150,7 +165,7 @@ export default function AdminSalesStats() { {t('admin.salesStats.loadError')}
)} -
+
} tone="accent" + delta={activeDelta} + /> + } + tone="success" + delta={deltas?.newPaid} /> } + icon={} tone="accent" delta={deltas?.addonRevenue} />