"use client"; import { useMemo } from "react"; import { Zap, Loader2, MapPin, Clock, CalendarDays, Sparkles, } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { useEvents, useEventHistory } from "@/hooks/use-events"; import type { GameEvent } from "@/lib/types"; function formatDate(dateStr: string): string { const date = new Date(dateStr); return date.toLocaleDateString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }); } function formatRelativeTime(dateStr: string): string { const now = Date.now(); const then = new Date(dateStr).getTime(); const diffMs = then - now; if (diffMs <= 0) return "Ended"; const diffS = Math.floor(diffMs / 1000); if (diffS < 60) return `${diffS}s remaining`; const diffM = Math.floor(diffS / 60); if (diffM < 60) return `${diffM}m remaining`; const diffH = Math.floor(diffM / 60); if (diffH < 24) return `${diffH}h ${diffM % 60}m remaining`; return `${Math.floor(diffH / 24)}d ${diffH % 24}h remaining`; } function getEventDescription(event: GameEvent): string { if (event.data.description && typeof event.data.description === "string") { return event.data.description; } if (event.data.name && typeof event.data.name === "string") { return event.data.name; } return "Game event"; } function getEventLocation(event: GameEvent): string | null { if (event.data.map && typeof event.data.map === "string") { return event.data.map; } if ( event.data.x !== undefined && event.data.y !== undefined ) { return `(${event.data.x}, ${event.data.y})`; } return null; } function getEventExpiry(event: GameEvent): string | null { if (event.data.expiration && typeof event.data.expiration === "string") { return event.data.expiration; } if (event.data.expires_at && typeof event.data.expires_at === "string") { return event.data.expires_at; } return null; } const EVENT_TYPE_COLORS: Record = { portal: "text-purple-400 border-purple-500/30", boss: "text-red-400 border-red-500/30", resource: "text-green-400 border-green-500/30", bonus: "text-amber-400 border-amber-500/30", special: "text-cyan-400 border-cyan-500/30", }; function getEventTypeStyle(type: string): string { return EVENT_TYPE_COLORS[type] ?? "text-muted-foreground border-border"; } function ActiveEventCard({ event }: { event: GameEvent }) { const location = getEventLocation(event); const expiry = getEventExpiry(event); return (
{event.type}
{expiry && (
{formatRelativeTime(expiry)}
)}

{getEventDescription(event)}

{location && (
{location}
)}
{formatDate(event.created_at)}
); } export default function EventsPage() { const { data: activeEvents, isLoading: loadingActive, error } = useEvents(); const { data: historicalEvents, isLoading: loadingHistory } = useEventHistory(); const sortedHistory = useMemo(() => { if (!historicalEvents) return []; return [...historicalEvents].sort( (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime() ); }, [historicalEvents]); return (

Events

View active game events and historical event data. Updates every 10 seconds.

{error && (

Failed to load events. Make sure the backend is running.

)} {/* Active Events */}

Active Events

{loadingActive && (
)} {activeEvents && activeEvents.length > 0 && (
{activeEvents.map((event, idx) => ( ))}
)} {activeEvents && activeEvents.length === 0 && !loadingActive && (

No active events right now. Check back later.

)}
{/* Historical Events */}

Event History

{loadingHistory && (
)} {sortedHistory.length > 0 && ( Type Description Location Date {sortedHistory.map((event, idx) => ( {event.type} {getEventDescription(event)} {getEventLocation(event) ?? "-"} {formatDate(event.created_at)} ))}
)} {sortedHistory.length === 0 && !loadingHistory && (

No historical events recorded yet.

)}
); }