"use client"; import { AlertCircle, RefreshCw } from "lucide-react"; import { useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Skeleton } from "@/components/ui/skeleton"; import { DashboardPanel } from "@/features/dashboard/dashboard-panel"; import { LivePanel } from "@/features/live/live-panel"; import { MessagesPanel } from "@/features/messages/messages-panel"; import { voiceApi } from "@/lib/api"; import { useAppConfig } from "@/lib/hooks/use-config"; import type { Guild } from "@/lib/types"; export default function DashboardPage() { const searchParams = useSearchParams(); const tab = searchParams.get("tab") ?? "messages"; const urlGuildId = searchParams.get("guildId"); const { config, loading: configLoading } = useAppConfig(); const [guilds, setGuilds] = useState([]); const [guildsLoading, setGuildsLoading] = useState(true); const [guildsError, setGuildsError] = useState(null); const [selectedGuildId, setSelectedGuildId] = useState(""); // Resolve the active guild ID from: // 1. URL param (?guildId=xxx) // 2. Config monitorGuildId // 3. First available guild from /api/guilds // 4. Empty (user needs to select) const resolveGuild = useCallback(() => { if (urlGuildId) return urlGuildId; if (config?.monitorGuildId) return config.monitorGuildId; if (guilds.length > 0) return guilds[0].id; return ""; }, [urlGuildId, config?.monitorGuildId, guilds]); // Fetch guilds list from backend useEffect(() => { let cancelled = false; setGuildsLoading(true); setGuildsError(null); voiceApi .getGuilds() .then((g) => { if (!cancelled) setGuilds(g); }) .catch((err) => { if (!cancelled) setGuildsError( err instanceof Error ? err.message : "Failed to load guilds", ); }) .finally(() => { if (!cancelled) setGuildsLoading(false); }); return () => { cancelled = true; }; }, []); // Resolve guild ID once config and guilds are loaded useEffect(() => { if (configLoading || guildsLoading) return; const resolved = resolveGuild(); if (resolved && resolved !== selectedGuildId) { setSelectedGuildId(resolved); } }, [configLoading, guildsLoading, resolveGuild, selectedGuildId]); const handleGuildChange = useCallback((guildId: string | null) => { if (guildId) setSelectedGuildId(guildId); }, []); const handleRetry = useCallback(() => { setGuildsLoading(true); setGuildsError(null); voiceApi .getGuilds() .then(setGuilds) .catch((err) => setGuildsError( err instanceof Error ? err.message : "Failed to load guilds", ), ) .finally(() => setGuildsLoading(false)); }, []); const isReady = !configLoading && !guildsLoading; return (
{/* Guild selector bar */} {/* Main panel */} {isReady ? (
{tab === "live" && } {tab === "dashboard" && } {tab === "messages" && }
) : (

Loading dashboard…

)}
); } // ── Guild Bar ──────────────────────────────────── function GuildBar({ guilds, loading, error, selectedGuildId, onChange, onRetry, }: { guilds: Guild[]; loading: boolean; error: string | null; selectedGuildId: string; onChange: (id: string | null) => void; onRetry: () => void; }) { // No guild bar if there's only one guild and it's already selected if (guilds.length <= 1 && !loading && !error) return null; if (loading) { return (
); } if (error) { return (

Could not load guilds: {error}

); } if (guilds.length === 0) { return (

No guilds available. Make sure the Discord gateway is connected.

); } return (
Guild
); }