Skip to content

Commit e43ae68

Browse files
committed
feat(users-statistics-card): enhance user statistics card layout to include percentage
1 parent 3c6266c commit e43ae68

File tree

3 files changed

+89
-35
lines changed

3 files changed

+89
-35
lines changed

dashboard/public/statics/locales/fa.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,9 +1097,9 @@
10971097
"statistics.realTimeData": "داده‌های لحظه‌ای",
10981098
"statistics.historicalData": "داده‌های تاریخی",
10991099
"statistics.viewHistorical": "مشاهده تاریخی",
1100-
"statistics.viewRealtime": "مشاهده زمان واقعی",
1100+
"statistics.viewRealtime": "مشاهده لحظه‌ای",
11011101
"statistics.historical": "تاریخی",
1102-
"statistics.realtime": "زمان واقعی",
1102+
"statistics.realtime": "لحظه‌ای",
11031103
"statistics.realtimeDescription": "آمار و نمودارهای زنده برای سرورها و کاربران شما.",
11041104
"statistics.historicalDescription": "داده‌های عملکرد تاریخی سرور برای بازه زمانی انتخاب شده",
11051105
"statistics.subscriptions": "اشتراک‌ها",

dashboard/src/components/dashboard/admin-statistics-card.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AdminDetails, SystemStats, useGetSystemStats } from '@/service/api'
2-
import { UserCircleIcon } from 'lucide-react'
2+
import { UserCog, Users } from 'lucide-react'
33
import { useTranslation } from 'react-i18next'
44
import UserStatisticsCard from './users-statistics-card'
55
import DataUsageChart from './data-usage-chart'
@@ -35,11 +35,17 @@ const AdminStatisticsCard = ({
3535

3636
if (showAdminInfo)
3737
return (
38-
<div className="flex flex-col gap-6 rounded-lg border px-2.5 py-4 shadow-lg md:px-4">
38+
<div className="flex flex-col gap-6 rounded-lg py-4">
3939
<div className="flex flex-row items-center justify-between">
40-
<div className="flex flex-row items-center gap-2">
41-
<UserCircleIcon className="size-7 text-muted-foreground" />
42-
<span className="text-xl font-bold">{admin.username === 'Total' ? t('admins.total') : admin.username}</span>
40+
<div className="flex min-w-0 flex-row items-center gap-2">
41+
{admin.username === 'Total' ? (
42+
<Users className="size-6 text-muted-foreground md:size-7" />
43+
) : (
44+
<UserCog className="size-6 text-muted-foreground md:size-7" />
45+
)}
46+
<span className="truncate text-lg font-bold md:text-xl">
47+
{admin.username === 'Total' ? t('admins.total') : admin.username}
48+
</span>
4349
</div>
4450
</div>
4551
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">

dashboard/src/components/dashboard/users-statistics-card.tsx

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { ActivityIcon, UsersIcon } from 'lucide-react'
22
import { useTranslation } from 'react-i18next'
33
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'
4+
import { Badge } from '../ui/badge'
45
import { SystemStats } from '@/service/api'
56

67
const UserStatisticsCard = ({ data }: { data: SystemStats | undefined }) => {
78
const { t } = useTranslation()
9+
const totalUsers = data?.total_user ?? 0
10+
const percentOfTotal = (value: number | undefined) => {
11+
if (!totalUsers || value === undefined || value <= 0) return null
12+
return Math.round((value / totalUsers) * 100)
13+
}
814

915
return (
1016
<Card>
@@ -13,40 +19,82 @@ const UserStatisticsCard = ({ data }: { data: SystemStats | undefined }) => {
1319
<CardDescription>{t('monitorUsers')}</CardDescription>
1420
</CardHeader>
1521
<CardContent className="flex flex-col gap-2">
16-
<div className="flex flex-row items-center gap-3 rounded-lg border p-4 shadow">
17-
<UsersIcon className="text-muted-foreground" />
18-
{t('statistics.users')}
19-
<span className="ms-auto font-bold">{data?.total_user || 0}</span>
22+
<div className="flex min-w-0 flex-row items-center gap-2 rounded-lg border p-3 shadow-sm md:gap-3 md:p-4">
23+
<UsersIcon className="size-5 text-muted-foreground md:size-6" />
24+
<span className="truncate text-sm md:text-base">{t('statistics.users')}</span>
25+
<span className="ms-auto font-bold text-sm md:text-base">{totalUsers}</span>
2026
</div>
21-
<div className="flex flex-row items-center gap-3 rounded-lg border p-4 shadow">
22-
<ActivityIcon className="text-muted-foreground" />
23-
{t('statistics.activeUsers')}
24-
<span className="ms-auto font-bold">{data?.active_users || 0}</span>
27+
<div className="flex min-w-0 flex-row items-center gap-2 rounded-lg border p-3 shadow-sm md:gap-3 md:p-4">
28+
<ActivityIcon className="size-5 text-muted-foreground md:size-6" />
29+
<span className="truncate text-sm md:text-base">{t('statistics.activeUsers')}</span>
30+
<div className="ms-auto flex items-center gap-2">
31+
{percentOfTotal(data?.active_users) !== null && (
32+
<Badge variant="secondary" className="text-[10px] md:text-xs">
33+
{percentOfTotal(data?.active_users)}%
34+
</Badge>
35+
)}
36+
<span className="font-bold text-sm md:text-base">{data?.active_users || 0}</span>
37+
</div>
2538
</div>
26-
<div className="flex flex-row items-center gap-3 rounded-lg border p-4 shadow">
27-
<div className="size-4 rounded-full bg-green-600" />
28-
{t('statistics.onlineUsers')}
29-
<span className="ms-auto font-bold">{data?.online_users || 0}</span>
39+
<div className="flex min-w-0 flex-row items-center gap-2 rounded-lg border p-3 shadow-sm md:gap-3 md:p-4">
40+
<div className="size-2 rounded-full bg-green-600 md:size-3" />
41+
<span className="truncate text-sm md:text-base">{t('statistics.onlineUsers')}</span>
42+
<div className="ms-auto flex items-center gap-2">
43+
{percentOfTotal(data?.online_users) !== null && (
44+
<Badge variant="secondary" className="text-[10px] md:text-xs">
45+
{percentOfTotal(data?.online_users)}%
46+
</Badge>
47+
)}
48+
<span className="font-bold text-sm md:text-base">{data?.online_users || 0}</span>
49+
</div>
3050
</div>
31-
<div className="flex flex-row items-center gap-3 rounded-lg border p-4 shadow">
32-
<div className="size-4 rounded-full bg-red-600" />
33-
{t('statistics.expiredUsers')}
34-
<span className="ms-auto font-bold">{data?.expired_users || 0}</span>
51+
<div className="flex min-w-0 flex-row items-center gap-2 rounded-lg border p-3 shadow-sm md:gap-3 md:p-4">
52+
<div className="size-2 rounded-full bg-red-600 md:size-3" />
53+
<span className="truncate text-sm md:text-base">{t('statistics.expiredUsers')}</span>
54+
<div className="ms-auto flex items-center gap-2">
55+
{percentOfTotal(data?.expired_users) !== null && (
56+
<Badge variant="secondary" className="text-[10px] md:text-xs">
57+
{percentOfTotal(data?.expired_users)}%
58+
</Badge>
59+
)}
60+
<span className="font-bold text-sm md:text-base">{data?.expired_users || 0}</span>
61+
</div>
3562
</div>
36-
<div className="flex flex-row items-center gap-3 rounded-lg border p-4 shadow">
37-
<div className="size-4 rounded-full bg-orange-600" />
38-
{t('statistics.limitedUsers')}
39-
<span className="ms-auto font-bold">{data?.limited_users || 0}</span>
63+
<div className="flex min-w-0 flex-row items-center gap-2 rounded-lg border p-3 shadow-sm md:gap-3 md:p-4">
64+
<div className="size-2 rounded-full bg-orange-600 md:size-3" />
65+
<span className="truncate text-sm md:text-base">{t('statistics.limitedUsers')}</span>
66+
<div className="ms-auto flex items-center gap-2">
67+
{percentOfTotal(data?.limited_users) !== null && (
68+
<Badge variant="secondary" className="text-[10px] md:text-xs">
69+
{percentOfTotal(data?.limited_users)}%
70+
</Badge>
71+
)}
72+
<span className="font-bold text-sm md:text-base">{data?.limited_users || 0}</span>
73+
</div>
4074
</div>
41-
<div className="flex flex-row items-center gap-3 rounded-lg border p-4 shadow">
42-
<div className="size-4 rounded-full bg-purple-600" />
43-
{t('statistics.onHoldUsers')}
44-
<span className="ms-auto font-bold">{data?.on_hold_users || 0}</span>
75+
<div className="flex min-w-0 flex-row items-center gap-2 rounded-lg border p-3 shadow-sm md:gap-3 md:p-4">
76+
<div className="size-2 rounded-full bg-purple-600 md:size-3" />
77+
<span className="truncate text-sm md:text-base">{t('statistics.onHoldUsers')}</span>
78+
<div className="ms-auto flex items-center gap-2">
79+
{percentOfTotal(data?.on_hold_users) !== null && (
80+
<Badge variant="secondary" className="text-[10px] md:text-xs">
81+
{percentOfTotal(data?.on_hold_users)}%
82+
</Badge>
83+
)}
84+
<span className="font-bold text-sm md:text-base">{data?.on_hold_users || 0}</span>
85+
</div>
4586
</div>
46-
<div className="flex flex-row items-center gap-3 rounded-lg border p-4 shadow">
47-
<div className="size-4 rounded-full bg-slate-600" />
48-
{t('statistics.disabledUsers')}
49-
<span className="ms-auto font-bold">{data?.disabled_users || 0}</span>
87+
<div className="flex min-w-0 flex-row items-center gap-2 rounded-lg border p-3 shadow-sm md:gap-3 md:p-4">
88+
<div className="size-2 rounded-full bg-slate-600 md:size-3" />
89+
<span className="truncate text-sm md:text-base">{t('statistics.disabledUsers')}</span>
90+
<div className="ms-auto flex items-center gap-2">
91+
{percentOfTotal(data?.disabled_users) !== null && (
92+
<Badge variant="secondary" className="text-[10px] md:text-xs">
93+
{percentOfTotal(data?.disabled_users)}%
94+
</Badge>
95+
)}
96+
<span className="font-bold text-sm md:text-base">{data?.disabled_users || 0}</span>
97+
</div>
5098
</div>
5199
</CardContent>
52100
</Card>

0 commit comments

Comments
 (0)