@@ -3,7 +3,7 @@ import useDirDetection from '@/hooks/use-dir-detection'
33import { cn } from '@/lib/utils'
44import { SystemStats } from '@/service/api'
55import { formatBytes } from '@/utils/formatByte'
6- import { Cpu , Database , Download , HardDrive , MemoryStick , Upload , Users } from 'lucide-react'
6+ import { Cpu , Database , Download , HardDrive , MemoryStick , Upload , UserCheck , Users , Wifi } from 'lucide-react'
77import { useTranslation } from 'react-i18next'
88import { CircularProgress } from '../ui/circular-progress'
99import { Card , CardContent } from '../ui/card'
@@ -15,20 +15,31 @@ const DashboardStatistics = ({ systemData }: { systemData: SystemStats | undefin
1515 // Show skeleton loader while data is being fetched
1616 if ( ! systemData ) {
1717 return (
18- < div className = { cn ( 'grid h-full w-full gap-3 sm:gap-4 lg:gap-6' , 'grid-cols-1 sm:grid-cols-2' , 'auto-rows-fr' , dir === 'rtl' && 'lg:grid-flow-col-reverse' ) } >
18+ < div className = { cn ( 'grid h-full w-full gap-3 sm:gap-4 lg:gap-6' , 'grid-cols-1 sm:grid-cols-2' , dir === 'rtl' && 'lg:grid-flow-col-reverse' ) } >
1919 { [ ...Array ( 5 ) ] . map ( ( _ , i ) => (
20- < Card key = { i } className = " h-full overflow-hidden border" >
20+ < Card key = { i } className = { cn ( ' h-full overflow-hidden border' , i === 4 && 'sm:col-span-2' ) } >
2121 < CardContent className = "flex h-full flex-col justify-between p-4 sm:p-5 lg:p-6" >
2222 < div className = "mb-2 flex items-start justify-between sm:mb-3" >
2323 < div className = "flex items-center gap-2 sm:gap-3" >
2424 < Skeleton className = "h-7 w-7 rounded-lg sm:h-9 sm:w-9" />
2525 < Skeleton className = "h-4 w-24 sm:h-5" />
2626 </ div >
2727 </ div >
28- < div className = "flex items-end justify-between gap-2" >
29- < Skeleton className = "h-8 w-20 sm:h-10 sm:w-32 lg:h-12 lg:w-40" />
30- < Skeleton className = "h-6 w-16 sm:h-7 sm:w-20" />
31- </ div >
28+ { i === 4 ? (
29+ < div className = "grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3 sm:gap-3" >
30+ { [ ...Array ( 3 ) ] . map ( ( _ , metricIndex ) => (
31+ < div key = { metricIndex } className = "rounded-lg border bg-background/60 p-3 sm:p-4" >
32+ < Skeleton className = "mb-2 h-4 w-24 sm:h-5 sm:w-28" />
33+ < Skeleton className = "h-8 w-20 sm:h-10 sm:w-24" />
34+ </ div >
35+ ) ) }
36+ </ div >
37+ ) : (
38+ < div className = "flex items-end justify-between gap-2" >
39+ < Skeleton className = "h-8 w-20 sm:h-10 sm:w-32 lg:h-12 lg:w-40" />
40+ < Skeleton className = "h-6 w-16 sm:h-7 sm:w-20" />
41+ </ div >
42+ ) }
3243 </ CardContent >
3344 </ Card >
3445 ) ) }
@@ -91,15 +102,18 @@ const DashboardStatistics = ({ systemData }: { systemData: SystemStats | undefin
91102 const cpu = getCpuInfo ( )
92103 const memoryPercent = Math . min ( Math . max ( memory . percentage , 0 ) , 100 )
93104 const diskPercent = Math . min ( Math . max ( disk . percentage , 0 ) , 100 )
105+ const totalUsers = Number ( systemData . total_user ) || 0
106+ const activeUsers = Number ( systemData . active_users ) || 0
107+ const onlineUsers = Number ( systemData . online_users ) || 0
108+ const activeUsersPercent = totalUsers > 0 ? Math . min ( Math . max ( ( activeUsers / totalUsers ) * 100 , 0 ) , 100 ) : 0
109+ const onlineUsersPercent = totalUsers > 0 ? Math . min ( Math . max ( ( onlineUsers / totalUsers ) * 100 , 0 ) , 100 ) : 0
94110
95111 return (
96112 < div
97113 className = { cn (
98114 'grid h-full w-full gap-3 sm:gap-4 lg:gap-6' ,
99115 // Responsive grid: 1 column on mobile, 2 on tablet, 4 on desktop
100116 'grid-cols-1 sm:grid-cols-2' ,
101- // Ensure equal height for all cards
102- 'auto-rows-fr' ,
103117 dir === 'rtl' && 'lg:grid-flow-col-reverse' ,
104118 ) }
105119 >
@@ -135,7 +149,7 @@ const DashboardStatistics = ({ systemData }: { systemData: SystemStats | undefin
135149
136150 { cpu . cores > 0 && (
137151 < div className = "flex shrink-0 items-center gap-1 rounded-md bg-muted/50 px-1.5 py-1 text-xs text-muted-foreground sm:px-2 sm:text-sm" >
138- < Cpu className = "h-3 w-3" />
152+ < Cpu className = "h-3 w-3 text-primary " />
139153 < span className = "whitespace-nowrap font-medium" >
140154 { cpu . cores } { t ( 'statistics.cores' ) }
141155 </ span >
@@ -250,13 +264,13 @@ const DashboardStatistics = ({ systemData }: { systemData: SystemStats | undefin
250264 { /* Incoming/Outgoing Details */ }
251265 < div className = "flex shrink-0 items-center gap-2 text-xs" >
252266 < div className = "flex items-center gap-1 rounded-md bg-muted/50 px-1.5 py-1 text-green-600 dark:text-green-400" >
253- < Download className = "h-3 w-3" />
267+ < Download className = "h-3 w-3 text-primary " />
254268 < span dir = "ltr" className = "font-medium" >
255269 { formatBytes ( getIncomingBandwidth ( ) || 0 , 1 ) }
256270 </ span >
257271 </ div >
258272 < div className = "flex items-center gap-1 rounded-md bg-muted/50 px-1.5 py-1 text-blue-600 dark:text-blue-400" >
259- < Upload className = "h-3 w-3" />
273+ < Upload className = "h-3 w-3 text-primary " />
260274 < span dir = "ltr" className = "font-medium" >
261275 { formatBytes ( getOutgoingBandwidth ( ) || 0 , 1 ) }
262276 </ span >
@@ -267,8 +281,8 @@ const DashboardStatistics = ({ systemData }: { systemData: SystemStats | undefin
267281 </ Card >
268282 </ div >
269283
270- { /* Online Users */ }
271- < div className = " h-full w-full animate-fade-in" style = { { animationDuration : '600ms' , animationDelay : '450ms' } } >
284+ { /* Users Overview */ }
285+ < div className = { cn ( ' h-full w-full animate-fade-in' , 'sm:col-span-2' ) } style = { { animationDuration : '600ms' , animationDelay : '450ms' } } >
272286 < Card dir = { dir } className = "group relative h-full w-full overflow-hidden rounded-lg border transition-all duration-300 hover:shadow-lg" >
273287 < div
274288 className = { cn (
@@ -284,15 +298,60 @@ const DashboardStatistics = ({ systemData }: { systemData: SystemStats | undefin
284298 < Users className = "h-4 w-4 text-primary sm:h-5 sm:w-5" />
285299 </ div >
286300 < div className = "min-w-0 flex-1" >
287- < p className = "truncate text-xs font-medium text-muted-foreground sm:text-sm" > { t ( 'statistics.onlineUsers ' ) } </ p >
301+ < p className = "truncate text-xs font-medium text-muted-foreground sm:text-sm" > { t ( 'statistics.users ' ) } </ p >
288302 </ div >
289303 </ div >
304+ { totalUsers > 0 && (
305+ < div dir = "ltr" className = "shrink-0 rounded-md bg-muted/60 px-2 py-1 text-xs font-medium text-muted-foreground sm:text-sm" >
306+ { activeUsersPercent . toFixed ( 1 ) } %
307+ </ div >
308+ ) }
290309 </ div >
291310
292- < div className = "flex items-center gap-1 sm:gap-2" >
293- < span dir = "ltr" className = "text-xl font-bold transition-all duration-300 sm:text-2xl lg:text-3xl" >
294- { systemData ?. online_users || 0 }
295- </ span >
311+ < div className = "grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3 sm:gap-3" >
312+ < div className = "rounded-lg border bg-background/60 p-3 sm:p-4" >
313+ < div className = "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground sm:gap-2 sm:text-sm" >
314+ < Users className = "h-3.5 w-3.5 text-primary sm:h-4 sm:w-4" />
315+ < span > { t ( 'statistics.users' ) } </ span >
316+ </ div >
317+ < span dir = "ltr" className = "text-xl font-bold transition-all duration-300 sm:text-2xl lg:text-3xl" >
318+ { totalUsers }
319+ </ span >
320+ </ div >
321+
322+ < div className = "rounded-lg border bg-background/60 p-3 sm:p-4" >
323+ < div className = "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground sm:gap-2 sm:text-sm" >
324+ < UserCheck className = "h-3.5 w-3.5 text-primary sm:h-4 sm:w-4" />
325+ < span > { t ( 'statistics.activeUsers' ) } </ span >
326+ </ div >
327+ < div className = "flex items-end justify-between gap-2" >
328+ < span dir = "ltr" className = "text-xl font-bold transition-all duration-300 sm:text-2xl lg:text-3xl" >
329+ { activeUsers }
330+ </ span >
331+ { totalUsers > 0 && (
332+ < span dir = "ltr" className = "whitespace-nowrap rounded-md bg-muted/60 px-1.5 py-1 text-xs font-medium text-muted-foreground sm:px-2" >
333+ { activeUsersPercent . toFixed ( 1 ) } %
334+ </ span >
335+ ) }
336+ </ div >
337+ </ div >
338+
339+ < div className = "rounded-lg border bg-background/60 p-3 sm:p-4" >
340+ < div className = "mb-1 flex items-center gap-1.5 text-xs font-medium text-muted-foreground sm:gap-2 sm:text-sm" >
341+ < Wifi className = "h-3.5 w-3.5 text-primary sm:h-4 sm:w-4" />
342+ < span > { t ( 'statistics.onlineUsers' ) } </ span >
343+ </ div >
344+ < div className = "flex items-end justify-between gap-2" >
345+ < span dir = "ltr" className = "text-xl font-bold transition-all duration-300 sm:text-2xl lg:text-3xl" >
346+ { onlineUsers }
347+ </ span >
348+ { totalUsers > 0 && (
349+ < span dir = "ltr" className = "whitespace-nowrap rounded-md bg-muted/60 px-1.5 py-1 text-xs font-medium text-muted-foreground sm:px-2" >
350+ { onlineUsersPercent . toFixed ( 1 ) } %
351+ </ span >
352+ ) }
353+ </ div >
354+ </ div >
296355 </ div >
297356 </ CardContent >
298357 </ Card >
0 commit comments