import { useState } from "react"; import { useDatabaseInfo, useRoles, useDropDatabase, useDropRole, } from "@/hooks/use-management"; import { useAppStore } from "@/stores/app-store"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { CreateDatabaseDialog } from "./CreateDatabaseDialog"; import { CreateRoleDialog } from "./CreateRoleDialog"; import { AlterRoleDialog } from "./AlterRoleDialog"; import { toast } from "sonner"; import { Plus, Pencil, Trash2, ChevronDown, ChevronRight, HardDrive, Users, Activity, Loader2, } from "lucide-react"; import { DockerContainersList } from "@/components/docker/DockerContainersList"; import type { Tab, RoleInfo } from "@/types"; export function AdminPanel() { const { activeConnectionId, currentDatabase, readOnlyMap, addTab } = useAppStore(); if (!activeConnectionId) { return (
Connect to a database to manage it.
); } const isReadOnly = readOnlyMap[activeConnectionId] ?? true; return (
{ const tab: Tab = { id: crypto.randomUUID(), type: "roles", title: "Roles & Users", connectionId: activeConnectionId, database: currentDatabase ?? undefined, }; addTab(tab); }} /> { const tab: Tab = { id: crypto.randomUUID(), type: "sessions", title: "Active Sessions", connectionId: activeConnectionId, database: currentDatabase ?? undefined, }; addTab(tab); }} />
); } function DatabasesSection({ connectionId, currentDatabase, isReadOnly, }: { connectionId: string; currentDatabase: string | null; isReadOnly: boolean; }) { const [expanded, setExpanded] = useState(true); const [createOpen, setCreateOpen] = useState(false); const { data: databases, isLoading } = useDatabaseInfo(connectionId); const dropMutation = useDropDatabase(); const handleDrop = (name: string) => { if (name === currentDatabase) { toast.error("Cannot drop the active database"); return; } if (!confirm(`Drop database "${name}"? This will terminate all connections and cannot be undone.`)) { return; } dropMutation.mutate( { connectionId, name }, { onSuccess: () => toast.success(`Database "${name}" dropped`), onError: (err) => toast.error("Failed to drop database", { description: String(err) }), } ); }; return (
setExpanded(!expanded)} > {expanded ? ( ) : ( )} Databases {!isReadOnly && ( )}
{expanded && (
{isLoading && (
Loading...
)} {databases?.map((db) => (
{db.name} {db.size} {db.name === currentDatabase && ( active )} {!isReadOnly && db.name !== currentDatabase && ( )}
))}
)}
); } function RolesSection({ connectionId, isReadOnly, onOpenRoleManager, }: { connectionId: string; isReadOnly: boolean; onOpenRoleManager: () => void; }) { const [expanded, setExpanded] = useState(true); const [createOpen, setCreateOpen] = useState(false); const [alterRole, setAlterRole] = useState(null); const { data: roles, isLoading } = useRoles(connectionId); const dropMutation = useDropRole(); const handleDrop = (name: string) => { if (!confirm(`Drop role "${name}"? This cannot be undone.`)) return; dropMutation.mutate( { connectionId, name }, { onSuccess: () => toast.success(`Role "${name}" dropped`), onError: (err) => toast.error("Failed to drop role", { description: String(err) }), } ); }; return (
setExpanded(!expanded)} > {expanded ? ( ) : ( )} Roles
e.stopPropagation()}> {!isReadOnly && ( )}
{expanded && (
{isLoading && (
Loading...
)} {roles?.map((role) => (
{role.name}
{role.can_login && ( LOGIN )} {role.is_superuser && ( SUPER )}
{!isReadOnly && ( <> )}
))}
)} !open && setAlterRole(null)} connectionId={connectionId} role={alterRole} />
); } function SessionsSection({ connectionId, onOpenSessions, }: { connectionId: string; onOpenSessions: () => void; }) { return (
Sessions
Monitor active database connections and running queries. {/* The connectionId is used by parent to open the sessions tab */} {connectionId}
); }