"use client"; import { useEffect, useState, useCallback } from "react"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Skeleton } from "@/components/ui/skeleton"; import { ScrollText, RefreshCw, Search, ChevronLeft, ChevronRight } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; interface AuditEntry { log: { id: string; userId: string; action: string; target: string; targetId: string | null; details: string | null; ipAddress: string | null; createdAt: number; }; userName: string | null; userEmail: string | null; } const ACTION_COLORS: Record = { "server.start": "bg-emerald-500/20 text-emerald-400 border-emerald-500/30", "server.stop": "bg-zinc-500/20 text-zinc-400 border-zinc-500/30", "server.restart": "bg-blue-500/20 text-blue-400 border-blue-500/30", "player.ban": "bg-red-500/20 text-red-400 border-red-500/30", "player.unban": "bg-emerald-500/20 text-emerald-400 border-emerald-500/30", "player.kick": "bg-amber-500/20 text-amber-400 border-amber-500/30", "file.upload": "bg-blue-500/20 text-blue-400 border-blue-500/30", "file.delete": "bg-red-500/20 text-red-400 border-red-500/30", "plugin.enable": "bg-emerald-500/20 text-emerald-400 border-emerald-500/30", "plugin.disable": "bg-zinc-500/20 text-zinc-400 border-zinc-500/30", }; function getActionColor(action: string): string { return ACTION_COLORS[action] ?? "bg-zinc-500/20 text-zinc-400 border-zinc-500/30"; } export default function AuditPage() { const [entries, setEntries] = useState([]); const [loading, setLoading] = useState(true); const [page, setPage] = useState(1); const [search, setSearch] = useState(""); const [hasMore, setHasMore] = useState(true); const LIMIT = 50; const fetchLogs = useCallback(async () => { setLoading(true); try { const params = new URLSearchParams({ page: String(page), limit: String(LIMIT), }); if (search) params.set("action", search); const res = await fetch(`/api/audit?${params}`); if (res.ok) { const data = await res.json(); setEntries(data.logs); setHasMore(data.logs.length === LIMIT); } } finally { setLoading(false); } }, [page, search]); useEffect(() => { fetchLogs(); }, [fetchLogs]); return (

Audit Log

Complete history of admin actions

{/* Filter */}
{ setSearch(e.target.value); setPage(1); }} className="pl-9 bg-zinc-900 border-zinc-700 text-white placeholder:text-zinc-500 focus:border-emerald-500/50" />
{loading ? (
{Array.from({ length: 10 }).map((_, i) => ( ))}
) : entries.length === 0 ? (

No audit log entries

) : (
Action Details User Time
{entries.map(({ log, userName, userEmail }) => (
{log.action}
{log.targetId && (

Target:{" "} {log.targetId}

)} {log.details && (

{log.details}

)} {log.ipAddress && (

IP: {log.ipAddress}

)}

{userName ?? "Unknown"}

{userEmail ?? ""}

{formatDistanceToNow(new Date(log.createdAt), { addSuffix: true, })}

))}
)}
{/* Pagination */}

Page {page}

); }