"use client"; import { AlertCircle, ArrowLeft, BarChart3, ChevronRight, Clock, Hash, RefreshCw, Search, Shield, Sparkles, Users, } from "lucide-react"; import Image from "next/image"; import { useCallback, useEffect, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Progress } from "@/components/ui/progress"; import { Skeleton } from "@/components/ui/skeleton"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { dashboardApi } from "@/lib/api"; import type { DashboardChannel, DashboardChannelDetail, DashboardStats, DashboardUser, DashboardUserDetail, } from "@/lib/types"; import { cn } from "@/lib/utils"; type View = "stats" | "users" | "channels" | "user-detail" | "channel-detail"; export function DashboardPanel({ guildId }: { guildId: string }) { const [view, setView] = useState("stats"); const [activeUser, setActiveUser] = useState( null, ); const [activeChannel, setActiveChannel] = useState(null); const renderView = () => { switch (view) { case "stats": return ; case "users": return ( { try { const detail = await dashboardApi.getUserDetail(userId); setActiveUser(detail); setView("user-detail"); } catch { // ignore } }} /> ); case "channels": return ( { try { const detail = await dashboardApi.getChannelDetail(channelId); setActiveChannel(detail); setView("channel-detail"); } catch { // ignore } }} /> ); case "user-detail": return activeUser ? ( setView("users")} /> ) : ( {}} /> ); case "channel-detail": return activeChannel ? ( setView("channels")} /> ) : ( {}} /> ); } }; return (
{/* Sub-navigation using shadcn Tabs */} setView(v as View)} > setView("stats")}> Stats setView("users")}> Users setView("channels")}> Channels {renderView()}
); } // ── Stats View ────────────────────────────────── function StatsView() { const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchStats = useCallback(async () => { setLoading(true); setError(null); try { const data = await dashboardApi.getStats(); setStats(data); } catch (err) { setError(err instanceof Error ? err.message : "Failed to load stats"); } finally { setLoading(false); } }, []); useEffect(() => { fetchStats(); }, [fetchStats]); if (error) { return (

{error}

); } return (
{/* Metric cards */} {loading ? (
{Array.from({ length: 8 }, (_, i) => ( ))}
) : stats ? ( <>
{/* Top Channels + Moderation Queue */}
Top Channels {stats.top_channels.length === 0 ? (

No channel data yet.

) : (
{stats.top_channels.map((ch, i) => { const maxCount = stats.top_channels[0].message_count; const pct = maxCount > 0 ? (ch.message_count / maxCount) * 100 : 0; return (
#{ch.channel_name ?? ch.channel_id.slice(0, 8)} {formatNumber(ch.message_count)}
); })}
)}
Moderation Queue
{stats.moderation_overview.pending}
Pending
{stats.moderation_overview.processing}
Processing
{stats.moderation_overview.error}
Errors
) : null}
); } function StatCard({ label, value, variant, icon: Icon, }: { label: string; value: number; variant?: "default" | "danger" | "success"; icon: React.ComponentType<{ className?: string }>; }) { return (

{label}

{formatNumber(value)}

); } // ── Users View ────────────────────────────────── function UsersView({ onSelectUser, }: { onSelectUser: (userId: string) => void; }) { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [_cursor, setCursor] = useState(null); const [search, setSearch] = useState(""); const fetchUsers = useCallback(async (searchQuery?: string) => { setLoading(true); try { const result = await dashboardApi.listUsers(20, undefined, searchQuery); setUsers(result.data); setCursor(result.nextCursor); } catch { // ignore } finally { setLoading(false); } }, []); useEffect(() => { fetchUsers(); }, [fetchUsers]); useEffect(() => { const timer = setTimeout(() => { if (search) fetchUsers(search); else fetchUsers(); }, 300); return () => clearTimeout(timer); }, [search, fetchUsers]); return (
setSearch(e.target.value)} className="pl-9 h-9" />
{loading ? (
{Array.from({ length: 6 }, (_, i) => ( ))}
) : (
{users.length === 0 ? (

No users found.

) : ( users.map((user) => ( onSelectUser(user.user_id)} >
{user.avatar_url ? ( ) : ( (user.username ?? "?").charAt(0).toUpperCase() )}

{user.username ?? "Unknown"}

{user.total_messages} messages {user.flagged_count > 0 && ( {user.flagged_count} flagged )}

)) )}
)}
); } // ── Channels View ─────────────────────────────── function ChannelsView({ onSelectChannel, guildId, }: { onSelectChannel: (channelId: string) => void; guildId: string; }) { const [channels, setChannels] = useState([]); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(""); const fetchChannels = useCallback( async (searchQuery?: string) => { setLoading(true); try { const result = await dashboardApi.listChannels( 20, searchQuery, guildId || undefined, ); setChannels(result.data); } catch { // ignore } finally { setLoading(false); } }, [guildId], ); useEffect(() => { fetchChannels(); }, [fetchChannels]); useEffect(() => { const timer = setTimeout(() => { if (search) fetchChannels(search); else fetchChannels(); }, 300); return () => clearTimeout(timer); }, [search, fetchChannels]); return (
setSearch(e.target.value)} className="pl-9 h-9" />
{loading ? (
{Array.from({ length: 6 }, (_, i) => ( ))}
) : (
{channels.length === 0 ? (

No channels found.

) : ( channels.map((ch) => ( onSelectChannel(ch.channel_id)} >

{ch.channel_name ?? ch.channel_id.slice(0, 8)}

{ch.total_messages} messages {ch.flagged_count > 0 && ( {ch.flagged_count} flagged )}

{ch.culture_summary && (

"{ch.culture_summary}"

)}
)) )}
)}
); } // ── User Detail View ──────────────────────────── function UserDetailView({ user, onBack, }: { user: DashboardUserDetail; onBack: () => void; }) { return (
{user.avatar_url ? ( ) : ( (user.username ?? "?").charAt(0).toUpperCase() )}

{user.username ?? "Unknown"}

{user.user_id}

{user.profile_summary && (

AI Profile

{user.profile_summary}

)} {user.recent_messages.length > 0 && (

Recent Messages

{user.recent_messages.slice(0, 5).map((msg) => (

{new Date(msg.created_at).toLocaleString()}

{msg.content}

))}
)}
); } // ── Channel Detail View ───────────────────────── function ChannelDetailView({ channel, onBack, }: { channel: DashboardChannelDetail; onBack: () => void; }) { return (

{channel.channel_name ?? channel.channel_id.slice(0, 8)}

{channel.channel_id}

{channel.culture_summary && (

Channel Culture

"{channel.culture_summary}"

)} {channel.recent_messages.length > 0 && (

Recent Messages

{channel.recent_messages.slice(0, 5).map((msg) => (
{msg.username} {new Date(msg.created_at).toLocaleString()}

{msg.content}

))}
)}
); } // ── Shared Components ─────────────────────────── function DetailStat({ label, value, variant, suffix, }: { label: string; value: number; variant?: "default" | "danger" | "success"; suffix?: string; }) { return (

{label}

{formatNumber(value)} {suffix}

); } // ── Helpers ───────────────────────────────────── function formatNumber(n: number): string { return n.toLocaleString(); }