@@ -4,7 +4,7 @@ import { useEffect, useState, useMemo } from "react";
44import {
55 Users , Activity , Smartphone , Monitor , Globe ,
66 RefreshCw , ArrowUpDown , ChevronUp , ChevronDown ,
7- UserCheck , TrendingUp , Database , Zap
7+ UserCheck , TrendingUp , Database , Zap , Mail , Search , MessageSquare
88} from "lucide-react" ;
99import { useRouter } from "next/navigation" ;
1010import { motion , AnimatePresence } from "framer-motion" ;
@@ -26,6 +26,7 @@ type SortConfig = {
2626
2727export default function StatsDashboardClient ( { data, userAgent, country, isMobile } : StatsDashboardClientProps ) {
2828 const router = useRouter ( ) ;
29+ const loggedInUsers = data . loggedInUsers ?? [ ] ;
2930 const [ isRefreshing , setIsRefreshing ] = useState ( false ) ;
3031 const [ selectedRange , setSelectedRange ] = useState < HistoryRange > ( "24h" ) ;
3132 const [ sortConfig , setSortConfig ] = useState < SortConfig > ( { key : 'lastSeen' , direction : 'desc' } ) ;
@@ -104,6 +105,9 @@ export default function StatsDashboardClient({ data, userAgent, country, isMobil
104105 const displayedVisitors = useMemo ( ( ) => {
105106 return sortedVisitors . slice ( 0 , visibleCount ) ;
106107 } , [ sortedVisitors , visibleCount ] ) ;
108+ const displayedLoggedInUsers = useMemo ( ( ) => {
109+ return loggedInUsers . slice ( 0 , 100 ) ;
110+ } , [ loggedInUsers ] ) ;
107111
108112 const requestSort = ( key : keyof VisitorData | 'id' ) => {
109113 let direction : 'asc' | 'desc' = 'asc' ;
@@ -187,7 +191,7 @@ export default function StatsDashboardClient({ data, userAgent, country, isMobil
187191 </ motion . div >
188192
189193 { /* Main KPIs */ }
190- < div className = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4" >
194+ < div className = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4" >
191195 < StatsCard
192196 title = "Total Visitors"
193197 value = { data . totalVisitors }
@@ -214,6 +218,12 @@ export default function StatsDashboardClient({ data, userAgent, country, isMobil
214218 subValue = "Last 5 minutes"
215219 icon = { < div className = "relative" > < Globe className = "w-5 h-5 text-green-400" /> { activeNow > 0 && < div className = "absolute -top-1 -right-1 w-2 h-2 bg-green-500 rounded-full animate-ping" /> } </ div > }
216220 />
221+ < StatsCard
222+ title = "Logged Accounts"
223+ value = { data . totalLoggedInUsers ?? loggedInUsers . length }
224+ subValue = { `${ loggedInUsers . length } shown` }
225+ icon = { < UserCheck className = "w-5 h-5 text-cyan-400" /> }
226+ />
217227 < StatsCard
218228 title = "KV Storage"
219229 value = { formatSize ( data . kvStats ?. currentSize || 0 ) }
@@ -513,6 +523,87 @@ export default function StatsDashboardClient({ data, userAgent, country, isMobil
513523 </ div >
514524 </ div >
515525
526+ { /* Logged-In Accounts Table */ }
527+ < div className = "bg-zinc-900/50 border border-white/10 rounded-2xl overflow-hidden" >
528+ < div className = "px-6 py-5 border-b border-white/10 flex items-center justify-between" >
529+ < h2 className = "text-xl font-semibold" > Logged-In Accounts (Postgres)</ h2 >
530+ < span className = "text-xs text-zinc-500 font-mono" >
531+ Showing { displayedLoggedInUsers . length } of { loggedInUsers . length }
532+ </ span >
533+ </ div >
534+ < div className = "overflow-x-auto" >
535+ < table className = "w-full text-left text-sm whitespace-nowrap" >
536+ < thead className = "bg-zinc-900/80 text-zinc-400 font-medium" >
537+ < tr >
538+ < th className = "px-6 py-4" > Account</ th >
539+ < th className = "px-6 py-4" > Email</ th >
540+ < th className = "px-6 py-4 text-right" > Queries</ th >
541+ < th className = "px-6 py-4 text-right" > Scans</ th >
542+ < th className = "px-6 py-4 text-right" > Searches</ th >
543+ < th className = "px-6 py-4 text-right" > Chats</ th >
544+ < th className = "px-6 py-4" > Last Activity (IST)</ th >
545+ </ tr >
546+ </ thead >
547+ < tbody className = "divide-y divide-white/5" >
548+ { displayedLoggedInUsers . map ( ( user ) => (
549+ < tr key = { user . id } className = "hover:bg-white/[0.02] transition-colors" >
550+ < td className = "px-6 py-4" >
551+ < div className = "flex flex-col" >
552+ < span className = "text-zinc-200 font-medium" >
553+ { user . githubLogin ? `@${ user . githubLogin } ` : user . id . slice ( 0 , 10 ) }
554+ </ span >
555+ < span className = "text-[10px] text-zinc-500 font-mono uppercase" >
556+ Joined { formatIST ( user . createdAt ) }
557+ </ span >
558+ </ div >
559+ </ td >
560+ < td className = "px-6 py-4 text-zinc-300" >
561+ < span className = "inline-flex items-center gap-2" >
562+ < Mail className = "w-3 h-3 text-zinc-500" />
563+ { user . email || "N/A" }
564+ </ span >
565+ </ td >
566+ < td className = "px-6 py-4 text-right font-mono text-zinc-200" > { user . queryCount } </ td >
567+ < td className = "px-6 py-4 text-right font-mono text-zinc-200" > { user . scanCount } </ td >
568+ < td className = "px-6 py-4 text-right font-mono text-zinc-200" >
569+ < span className = "inline-flex items-center gap-1" >
570+ < Search className = "w-3 h-3 text-zinc-500" />
571+ { user . searchCount }
572+ </ span >
573+ </ td >
574+ < td className = "px-6 py-4 text-right font-mono text-zinc-200" >
575+ < span className = "inline-flex items-center gap-1" >
576+ < MessageSquare className = "w-3 h-3 text-zinc-500" />
577+ { user . chatCount }
578+ </ span >
579+ </ td >
580+ < td className = "px-6 py-4" >
581+ { user . lastActivityAt ? (
582+ < div className = "flex flex-col" >
583+ < span className = "text-zinc-300" > { formatIST ( user . lastActivityAt ) } </ span >
584+ < span className = "text-[10px] text-zinc-500 font-mono uppercase" > { getRelativeTime ( user . lastActivityAt ) } </ span >
585+ </ div >
586+ ) : (
587+ < span className = "text-zinc-500" > No activity</ span >
588+ ) }
589+ </ td >
590+ </ tr >
591+ ) ) }
592+ { loggedInUsers . length === 0 && (
593+ < tr >
594+ < td colSpan = { 7 } className = "px-6 py-12 text-center text-zinc-500" >
595+ < div className = "flex flex-col items-center gap-2" >
596+ < UserCheck className = "w-8 h-8 opacity-20" />
597+ < p > No logged-in account activity yet.</ p >
598+ </ div >
599+ </ td >
600+ </ tr >
601+ ) }
602+ </ tbody >
603+ </ table >
604+ </ div >
605+ </ div >
606+
516607 { /* Visitors Table */ }
517608 < div className = "bg-zinc-900/50 border border-white/10 rounded-2xl overflow-hidden" >
518609 < div className = "px-6 py-5 border-b border-white/10 flex items-center justify-between" >
0 commit comments