feat: integrate real data into dashboard

- Created dashboard service with DB queries for users, economy, events
- Added client stats provider with 30s caching for Discord metrics
- Implemented /api/stats endpoint aggregating all dashboard data
- Created useDashboardStats React hook with auto-refresh
- Updated Dashboard.tsx to display real data with loading/error states
- Added comprehensive test coverage (11 tests passing)
- Replaced all mock values with live Discord and database metrics
This commit is contained in:
syntaxbullet
2026-01-08 18:50:44 +01:00
parent a207d511be
commit 17cb70ec00
10 changed files with 861 additions and 35 deletions

View File

@@ -0,0 +1,78 @@
import { useState, useEffect } from "react";
interface DashboardStats {
guilds: {
count: number;
};
users: {
active: number;
total: number;
};
commands: {
total: number;
};
ping: {
avg: number;
};
economy: {
totalWealth: string;
avgLevel: number;
topStreak: number;
};
recentEvents: Array<{
type: 'success' | 'error' | 'info';
message: string;
timestamp: string;
icon?: string;
}>;
uptime: number;
lastCommandTimestamp: number | null;
}
interface UseDashboardStatsResult {
stats: DashboardStats | null;
loading: boolean;
error: string | null;
}
/**
* Custom hook to fetch and auto-refresh dashboard statistics
* Polls the API every 30 seconds
*/
export function useDashboardStats(): UseDashboardStatsResult {
const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchStats = async () => {
try {
const response = await fetch("/api/stats");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setStats(data);
setError(null);
} catch (err) {
console.error("Failed to fetch dashboard stats:", err);
setError(err instanceof Error ? err.message : "Failed to fetch stats");
} finally {
setLoading(false);
}
};
useEffect(() => {
// Initial fetch
fetchStats();
// Set up polling every 30 seconds
const interval = setInterval(fetchStats, 30000);
// Cleanup on unmount
return () => clearInterval(interval);
}, []);
return { stats, loading, error };
}