mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
feat: add daily traffic & device purchase chart to addons stats
Add DualAreaChart to addons tab showing daily traffic purchases vs device purchases with date-union merge. Add daily_devices field to AddonsStats type and i18n keys for all 4 locales.
This commit is contained in:
@@ -119,6 +119,11 @@ export interface DailyAddonItem {
|
|||||||
total_gb: number;
|
total_gb: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DailyDeviceItem {
|
||||||
|
date: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AddonsStats {
|
export interface AddonsStats {
|
||||||
total_purchases: number;
|
total_purchases: number;
|
||||||
total_gb_purchased: number;
|
total_gb_purchased: number;
|
||||||
@@ -127,6 +132,7 @@ export interface AddonsStats {
|
|||||||
device_revenue_kopeks: number;
|
device_revenue_kopeks: number;
|
||||||
by_package: AddonByPackageItem[];
|
by_package: AddonByPackageItem[];
|
||||||
daily: DailyAddonItem[];
|
daily: DailyAddonItem[];
|
||||||
|
daily_devices: DailyDeviceItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Deposits ============
|
// ============ Deposits ============
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@@ -7,7 +8,7 @@ import { SALES_STATS } from '../../constants/salesStats';
|
|||||||
import { useCurrency } from '../../hooks/useCurrency';
|
import { useCurrency } from '../../hooks/useCurrency';
|
||||||
import { StatCard } from '../stats';
|
import { StatCard } from '../stats';
|
||||||
|
|
||||||
import { SimpleAreaChart } from './SimpleAreaChart';
|
import { DualAreaChart } from './DualAreaChart';
|
||||||
import { SimpleBarChart } from './SimpleBarChart';
|
import { SimpleBarChart } from './SimpleBarChart';
|
||||||
|
|
||||||
interface AddonsTabProps {
|
interface AddonsTabProps {
|
||||||
@@ -24,6 +25,18 @@ export function AddonsTab({ params }: AddonsTabProps) {
|
|||||||
staleTime: SALES_STATS.STALE_TIME,
|
staleTime: SALES_STATS.STALE_TIME,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dailyChartData = useMemo(() => {
|
||||||
|
if (!data) return [];
|
||||||
|
const trafficByDate = new Map(data.daily.map((d) => [d.date, d.count]));
|
||||||
|
const deviceByDate = new Map(data.daily_devices.map((d) => [d.date, d.count]));
|
||||||
|
const allDates = Array.from(new Set([...trafficByDate.keys(), ...deviceByDate.keys()])).sort();
|
||||||
|
return allDates.map((date) => ({
|
||||||
|
date,
|
||||||
|
series1: trafficByDate.get(date) ?? 0,
|
||||||
|
series2: deviceByDate.get(date) ?? 0,
|
||||||
|
}));
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="animate-pulse space-y-4">
|
<div className="animate-pulse space-y-4">
|
||||||
@@ -43,11 +56,6 @@ export function AddonsTab({ params }: AddonsTabProps) {
|
|||||||
value: item.count,
|
value: item.count,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const dailyData = data.daily.map((item) => ({
|
|
||||||
date: item.date,
|
|
||||||
value: item.count,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-5">
|
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-5">
|
||||||
@@ -77,12 +85,14 @@ export function AddonsTab({ params }: AddonsTabProps) {
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||||
<SimpleBarChart data={packageBarData} title={t('admin.salesStats.addons.byPackage')} />
|
<SimpleBarChart data={packageBarData} title={t('admin.salesStats.addons.byPackage')} />
|
||||||
<SimpleAreaChart
|
<DualAreaChart
|
||||||
data={dailyData}
|
data={dailyChartData}
|
||||||
title={t('admin.salesStats.addons.dailyChart')}
|
title={t('admin.salesStats.addons.dailyChart')}
|
||||||
chartId="addons-daily"
|
chartId="addons-daily"
|
||||||
valueLabel={t('admin.salesStats.addons.purchases')}
|
series1Label={t('admin.salesStats.addons.trafficPurchases')}
|
||||||
color={SALES_STATS.BAR_COLORS[5]}
|
series2Label={t('admin.salesStats.addons.devicePurchasesDaily')}
|
||||||
|
series1Color={SALES_STATS.BAR_COLORS[5]}
|
||||||
|
series2Color={SALES_STATS.BAR_COLORS[3]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1140,8 +1140,10 @@
|
|||||||
"revenue": "Revenue",
|
"revenue": "Revenue",
|
||||||
"devicePurchases": "Device Purchases",
|
"devicePurchases": "Device Purchases",
|
||||||
"deviceRevenue": "Device Revenue",
|
"deviceRevenue": "Device Revenue",
|
||||||
|
"trafficPurchases": "Traffic",
|
||||||
|
"devicePurchasesDaily": "Devices",
|
||||||
"byPackage": "By Package Size",
|
"byPackage": "By Package Size",
|
||||||
"dailyChart": "Daily Purchases",
|
"dailyChart": "Daily Add-on Purchases",
|
||||||
"purchases": "Purchases"
|
"purchases": "Purchases"
|
||||||
},
|
},
|
||||||
"deposits": {
|
"deposits": {
|
||||||
|
|||||||
@@ -963,8 +963,10 @@
|
|||||||
"revenue": "درآمد",
|
"revenue": "درآمد",
|
||||||
"devicePurchases": "خرید دستگاه",
|
"devicePurchases": "خرید دستگاه",
|
||||||
"deviceRevenue": "درآمد دستگاه",
|
"deviceRevenue": "درآمد دستگاه",
|
||||||
|
"trafficPurchases": "ترافیک",
|
||||||
|
"devicePurchasesDaily": "دستگاه",
|
||||||
"byPackage": "بر اساس بسته",
|
"byPackage": "بر اساس بسته",
|
||||||
"dailyChart": "خرید روزانه",
|
"dailyChart": "خرید روزانه خدمات اضافی",
|
||||||
"purchases": "خرید"
|
"purchases": "خرید"
|
||||||
},
|
},
|
||||||
"deposits": {
|
"deposits": {
|
||||||
|
|||||||
@@ -1161,8 +1161,10 @@
|
|||||||
"revenue": "Доход",
|
"revenue": "Доход",
|
||||||
"devicePurchases": "Покупки устройств",
|
"devicePurchases": "Покупки устройств",
|
||||||
"deviceRevenue": "Доход от устройств",
|
"deviceRevenue": "Доход от устройств",
|
||||||
|
"trafficPurchases": "Трафик",
|
||||||
|
"devicePurchasesDaily": "Устройства",
|
||||||
"byPackage": "По размеру пакета",
|
"byPackage": "По размеру пакета",
|
||||||
"dailyChart": "Покупки по дням",
|
"dailyChart": "Покупки доп. услуг по дням",
|
||||||
"purchases": "Покупки"
|
"purchases": "Покупки"
|
||||||
},
|
},
|
||||||
"deposits": {
|
"deposits": {
|
||||||
|
|||||||
@@ -963,8 +963,10 @@
|
|||||||
"revenue": "收入",
|
"revenue": "收入",
|
||||||
"devicePurchases": "设备购买",
|
"devicePurchases": "设备购买",
|
||||||
"deviceRevenue": "设备收入",
|
"deviceRevenue": "设备收入",
|
||||||
|
"trafficPurchases": "流量",
|
||||||
|
"devicePurchasesDaily": "设备",
|
||||||
"byPackage": "按套餐大小",
|
"byPackage": "按套餐大小",
|
||||||
"dailyChart": "每日购买",
|
"dailyChart": "每日附加服务购买",
|
||||||
"purchases": "购买"
|
"purchases": "购买"
|
||||||
},
|
},
|
||||||
"deposits": {
|
"deposits": {
|
||||||
|
|||||||
Reference in New Issue
Block a user