diff --git a/src/api/adminSalesStats.ts b/src/api/adminSalesStats.ts index 7b5f261..8faabc8 100644 --- a/src/api/adminSalesStats.ts +++ b/src/api/adminSalesStats.ts @@ -19,6 +19,8 @@ export interface SalesSummary { trial_to_paid_conversion: number; renewals_count: number; addon_revenue_kopeks: number; + refunds_count: number; + refunds_kopeks: number; } // ============ Trials ============ diff --git a/src/components/stats/StatCard.tsx b/src/components/stats/StatCard.tsx index 79f2600..92189ae 100644 --- a/src/components/stats/StatCard.tsx +++ b/src/components/stats/StatCard.tsx @@ -26,6 +26,9 @@ interface StatCardProps { valueClassName?: string; /** Optional period-over-period change shown under the value. */ delta?: StatCardDelta | null; + /** For "lower is better" metrics (e.g. refunds): keep the real arrow but flip + * the colour, so a rise reads as bad (red) and a drop as good (green). */ + invertDelta?: boolean; } export function StatCard({ @@ -35,10 +38,18 @@ export function StatCard({ tone = 'neutral', valueClassName, delta, + invertDelta = false, }: StatCardProps) { const toneStyle = TONE[tone]; const valueClass = valueClassName ?? toneStyle.value; - const trendStyle = delta ? (TREND_STYLES[delta.trend] ?? TREND_STYLES.stable) : null; + const baseTrend = delta ? (TREND_STYLES[delta.trend] ?? TREND_STYLES.stable) : null; + const trendStyle = + baseTrend && invertDelta && delta && delta.trend !== 'stable' + ? { + arrow: baseTrend.arrow, + className: TREND_STYLES[delta.trend === 'up' ? 'down' : 'up'].className, + } + : baseTrend; return (
diff --git a/src/locales/en.json b/src/locales/en.json index 6a482c4..68bfaac 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1306,7 +1306,9 @@ "conversion": "Conversion", "renewals": "Renewals", "addonRevenue": "Add-on Revenue", - "manualTopup": "Manual Top-ups" + "manualTopup": "Manual Top-ups", + "refundsCount": "Refunds", + "refundsAmount": "Refunded amount" }, "trials": { "total": "Total Trials", diff --git a/src/locales/fa.json b/src/locales/fa.json index 26cde24..2c744e2 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -1179,7 +1179,9 @@ "conversion": "تبدیل", "renewals": "تمدید", "addonRevenue": "درآمد خدمات اضافی", - "manualTopup": "شارژ دستی" + "manualTopup": "شارژ دستی", + "refundsCount": "بازپرداخت‌ها", + "refundsAmount": "مبلغ بازپرداخت" }, "trials": { "total": "کل آزمایشی", diff --git a/src/locales/ru.json b/src/locales/ru.json index 872bd7c..b77dbec 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1328,7 +1328,9 @@ "conversion": "Конверсия", "renewals": "Продления", "addonRevenue": "Доп. услуги", - "manualTopup": "Ручные пополнения" + "manualTopup": "Ручные пополнения", + "refundsCount": "Возвраты", + "refundsAmount": "Сумма возвратов" }, "trials": { "total": "Всего триалов", diff --git a/src/locales/zh.json b/src/locales/zh.json index dca06ba..6a9d6a1 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1179,7 +1179,9 @@ "conversion": "转化率", "renewals": "续订", "addonRevenue": "附加服务收入", - "manualTopup": "手动充值" + "manualTopup": "手动充值", + "refundsCount": "退款", + "refundsAmount": "退款金额" }, "trials": { "total": "试用总数", diff --git a/src/pages/AdminSalesStats.tsx b/src/pages/AdminSalesStats.tsx index 55cdb8d..bd044db 100644 --- a/src/pages/AdminSalesStats.tsx +++ b/src/pages/AdminSalesStats.tsx @@ -13,6 +13,7 @@ import { GiftIcon, PercentIcon, RepeatIcon, + ResetIcon, RocketIcon, SparklesIcon, TicketIcon, @@ -115,6 +116,7 @@ export default function AdminSalesStats() { 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), + refunds: computeDelta(summary.refunds_kopeks, prevSummary.refunds_kopeks), }; }, [summary, prevSummary]); @@ -148,7 +150,7 @@ export default function AdminSalesStats() { {t('admin.salesStats.loadError')}
)} -
+
+ } + tone="error" + /> + } + tone="error" + delta={deltas?.refunds} + invertDelta + />
{/* Tabs */}