forked from syntaxbullet/AuroraBot-discord
feat: more stat components
This commit is contained in:
@@ -2,9 +2,10 @@ import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useSocket } from "../hooks/use-socket";
|
||||
import { Badge } from "../components/ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
|
||||
import { Skeleton } from "../components/ui/skeleton";
|
||||
import { Server, Users, Terminal, Activity } from "lucide-react";
|
||||
import { StatCard } from "../components/stat-card";
|
||||
import { RecentActivity } from "../components/recent-activity";
|
||||
import { LootdropCard } from "../components/lootdrop-card";
|
||||
import { Server, Users, Terminal, Activity, Coins, TrendingUp, Flame, Package } from "lucide-react";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
export function Dashboard() {
|
||||
@@ -60,102 +61,115 @@ export function Dashboard() {
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4 animate-in fade-in slide-up">
|
||||
<Card className="glass-card border-none bg-card/40 hover-glow">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">Total Servers</CardTitle>
|
||||
<Server className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{stats ? (
|
||||
<>
|
||||
<div className="text-2xl font-bold">{stats.guilds.count.toLocaleString()}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{stats.guilds.changeFromLastMonth
|
||||
? `${stats.guilds.changeFromLastMonth > 0 ? '+' : ''}${stats.guilds.changeFromLastMonth} from last month`
|
||||
: "Active Guilds"
|
||||
}
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-8 w-[60px]" />
|
||||
<Skeleton className="h-3 w-[100px]" />
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<StatCard
|
||||
title="Total Servers"
|
||||
icon={Server}
|
||||
isLoading={!stats}
|
||||
value={stats?.guilds.count.toLocaleString()}
|
||||
subtitle={stats?.guilds.changeFromLastMonth
|
||||
? `${stats.guilds.changeFromLastMonth > 0 ? '+' : ''}${stats.guilds.changeFromLastMonth} from last month`
|
||||
: "Active Guilds"
|
||||
}
|
||||
/>
|
||||
|
||||
<Card className="glass-card border-none bg-card/40 hover-glow delay-100">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">Total Users</CardTitle>
|
||||
<Users className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{stats ? (
|
||||
<>
|
||||
<div className="text-2xl font-bold">{stats.users.total.toLocaleString()}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{stats.users.active.toLocaleString()} active now
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-8 w-[60px]" />
|
||||
<Skeleton className="h-3 w-[100px]" />
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<StatCard
|
||||
title="Total Users"
|
||||
icon={Users}
|
||||
isLoading={!stats}
|
||||
value={stats?.users.total.toLocaleString()}
|
||||
subtitle={stats ? `${stats.users.active.toLocaleString()} active now` : undefined}
|
||||
className="delay-100"
|
||||
/>
|
||||
|
||||
<Card className="glass-card border-none bg-card/40 hover-glow delay-200">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">Commands</CardTitle>
|
||||
<Terminal className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{stats ? (
|
||||
<>
|
||||
<div className="text-2xl font-bold">{stats.commands.total.toLocaleString()}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{stats.commands.active} active · {stats.commands.disabled} disabled
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-8 w-[60px]" />
|
||||
<Skeleton className="h-3 w-[100px]" />
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<StatCard
|
||||
title="Commands"
|
||||
icon={Terminal}
|
||||
isLoading={!stats}
|
||||
value={stats?.commands.total.toLocaleString()}
|
||||
subtitle={stats ? `${stats.commands.active} active · ${stats.commands.disabled} disabled` : undefined}
|
||||
className="delay-200"
|
||||
/>
|
||||
|
||||
<Card className="glass-card border-none bg-card/40 hover-glow delay-300">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">System Ping</CardTitle>
|
||||
<Activity className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{stats ? (
|
||||
<>
|
||||
<div className={cn(
|
||||
"text-2xl font-bold transition-colors duration-300",
|
||||
stats.ping.avg < 100 ? "text-emerald-500" :
|
||||
stats.ping.avg < 200 ? "text-yellow-500" : "text-red-500"
|
||||
)}>
|
||||
{Math.round(stats.ping.avg)}ms
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Average latency
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-8 w-[60px]" />
|
||||
<Skeleton className="h-3 w-[100px]" />
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<StatCard
|
||||
title="System Ping"
|
||||
icon={Activity}
|
||||
isLoading={!stats}
|
||||
value={stats ? `${Math.round(stats.ping.avg)}ms` : undefined}
|
||||
subtitle="Average latency"
|
||||
className="delay-300"
|
||||
valueClassName={stats ? cn(
|
||||
"transition-colors duration-300",
|
||||
stats.ping.avg < 100 ? "text-emerald-500" :
|
||||
stats.ping.avg < 200 ? "text-yellow-500" : "text-red-500"
|
||||
) : undefined}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 lg:grid-cols-3 animate-in fade-in slide-up delay-300">
|
||||
{/* Economy Stats */}
|
||||
<div className="lg:col-span-2 space-y-4">
|
||||
<h2 className="text-xl font-semibold tracking-tight">Economy Overview</h2>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<StatCard
|
||||
title="Total Wealth"
|
||||
icon={Coins}
|
||||
isLoading={!stats}
|
||||
value={stats ? `${Number(stats.economy.totalWealth).toLocaleString()} AU` : undefined}
|
||||
subtitle="Astral Units in circulation"
|
||||
valueClassName="text-primary"
|
||||
iconClassName="text-primary"
|
||||
/>
|
||||
|
||||
<StatCard
|
||||
title="Items Circulating"
|
||||
icon={Package}
|
||||
isLoading={!stats}
|
||||
value={stats?.economy.totalItems?.toLocaleString()}
|
||||
subtitle="Total items owned by users"
|
||||
className="delay-75"
|
||||
valueClassName="text-blue-500"
|
||||
iconClassName="text-blue-500"
|
||||
/>
|
||||
|
||||
<StatCard
|
||||
title="Average Level"
|
||||
icon={TrendingUp}
|
||||
isLoading={!stats}
|
||||
value={stats ? `Lvl ${stats.economy.avgLevel}` : undefined}
|
||||
subtitle="Global player average"
|
||||
className="delay-100"
|
||||
valueClassName="text-secondary"
|
||||
iconClassName="text-secondary"
|
||||
/>
|
||||
|
||||
<StatCard
|
||||
title="Top /daily Streak"
|
||||
icon={Flame}
|
||||
isLoading={!stats}
|
||||
value={stats?.economy.topStreak}
|
||||
subtitle="Days daily streak"
|
||||
className="delay-200"
|
||||
valueClassName="text-destructive"
|
||||
iconClassName="text-destructive"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Activity */}
|
||||
{/* Recent Activity & Lootdrops */}
|
||||
<div className="space-y-4">
|
||||
<LootdropCard
|
||||
drop={stats?.activeLootdrops?.[0]}
|
||||
state={stats?.lootdropState}
|
||||
isLoading={!stats}
|
||||
/>
|
||||
<h2 className="text-xl font-semibold tracking-tight">Live Feed</h2>
|
||||
<RecentActivity
|
||||
events={stats?.recentEvents || []}
|
||||
isLoading={!stats}
|
||||
className="h-[calc(100%-2rem)]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user