feat: (ui) new design

This commit is contained in:
syntaxbullet
2026-01-09 15:12:35 +01:00
parent a5b8d922e3
commit 1b84dbd36d
23 changed files with 1023 additions and 1536 deletions

View File

@@ -1,50 +0,0 @@
import { useState, useEffect } from "react";
export interface ActivityData {
hour: string;
commands: number;
transactions: number;
}
interface UseActivityStatsResult {
data: ActivityData[];
loading: boolean;
error: string | null;
refresh: () => Promise<void>;
}
/**
* Custom hook to fetch hourly activity data for charts.
* Data is cached on the server for 5 minutes.
*/
export function useActivityStats(): UseActivityStatsResult {
const [data, setData] = useState<ActivityData[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchActivity = async () => {
setLoading(true);
try {
const response = await fetch("/api/stats/activity");
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const jsonData = await response.json();
setData(jsonData);
setError(null);
} catch (err) {
console.error("Failed to fetch activity stats:", err);
setError(err instanceof Error ? err.message : "Failed to fetch activity");
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchActivity();
// Refresh every 5 minutes to match server cache
const interval = setInterval(fetchActivity, 5 * 60 * 1000);
return () => clearInterval(interval);
}, []);
return { data, loading, error, refresh: fetchActivity };
}

View File

@@ -1,143 +0,0 @@
import { useState, useEffect } from "react";
interface DashboardStats {
bot: {
name: string;
avatarUrl: string | null;
};
guilds: {
count: number;
};
users: {
active: number;
total: number;
};
commands: {
total: number;
};
ping: {
avg: number;
};
economy: {
totalWealth: string;
avgLevel: number;
topStreak: number;
totalItems: number;
};
activeLootdrops: Array<{
rewardAmount: number;
currency: string;
createdAt: string;
expiresAt: string | null;
}>;
leaderboards: {
topLevels: Array<{ username: string; level: number | null }>;
topWealth: Array<{ username: string; balance: string }>;
};
recentEvents: Array<{
type: 'success' | 'error' | 'info' | 'warn';
message: string;
timestamp: string;
icon?: string;
}>;
uptime: number;
lastCommandTimestamp: number | null;
maintenanceMode: boolean;
}
interface UseDashboardStatsResult {
stats: DashboardStats | null;
loading: boolean;
error: string | null;
}
/**
* Custom hook to fetch and auto-refresh dashboard statistics using WebSockets with HTTP fallback
*/
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();
// WebSocket setup
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${protocol}//${window.location.host}/ws`;
let socket: WebSocket | null = null;
let reconnectTimeout: Timer | null = null;
const connect = () => {
socket = new WebSocket(wsUrl);
socket.onopen = () => {
console.log("🟢 [WS] Connected to dashboard live stream");
setError(null);
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = null;
}
};
socket.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
if (message.type === "STATS_UPDATE") {
setStats(message.data);
} else if (message.type === "NEW_EVENT") {
setStats(prev => {
if (!prev) return prev;
return {
...prev,
recentEvents: [message.data, ...prev.recentEvents].slice(0, 10)
};
});
}
} catch (e) {
console.error("Error parsing WS message:", e);
}
};
socket.onclose = () => {
console.log("🟠 [WS] Connection lost. Attempting reconnect in 5s...");
reconnectTimeout = setTimeout(connect, 5000);
};
socket.onerror = (err) => {
console.error("🔴 [WS] Socket error:", err);
socket?.close();
};
};
connect();
// Cleanup on unmount
return () => {
if (socket) {
socket.onclose = null; // Prevent reconnect on intentional close
socket.close();
}
if (reconnectTimeout) clearTimeout(reconnectTimeout);
};
}, []);
return { stats, loading, error };
}

View File

@@ -1,19 +0,0 @@
import * as React from "react"
const MOBILE_BREAKPOINT = 768
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
}
mql.addEventListener("change", onChange)
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
return () => mql.removeEventListener("change", onChange)
}, [])
return !!isMobile
}

View File

@@ -0,0 +1,61 @@
import { useEffect, useState, useRef } from "react";
import type { DashboardStats } from "@shared/modules/dashboard/dashboard.types";
export function useSocket() {
const [isConnected, setIsConnected] = useState(false);
const [stats, setStats] = useState<DashboardStats | null>(null);
const socketRef = useRef<WebSocket | null>(null);
useEffect(() => {
// Determine WS protocol based on current page schema
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const host = window.location.host;
const wsUrl = `${protocol}//${host}/ws`;
function connect() {
const ws = new WebSocket(wsUrl);
socketRef.current = ws;
ws.onopen = () => {
console.log("Connected to dashboard websocket");
setIsConnected(true);
};
ws.onmessage = (event) => {
try {
const payload = JSON.parse(event.data);
if (payload.type === "STATS_UPDATE") {
setStats(payload.data);
}
} catch (err) {
console.error("Failed to parse WS message", err);
}
};
ws.onclose = () => {
console.log("Disconnected from dashboard websocket");
setIsConnected(false);
// Simple reconnect logic
setTimeout(connect, 3000);
};
ws.onerror = (err) => {
console.error("WebSocket error:", err);
ws.close();
};
}
connect();
return () => {
if (socketRef.current) {
// Prevent reconnect on unmount
socketRef.current.onclose = null;
socketRef.current.close();
}
};
}, []);
return { isConnected, stats };
}