"use client" import { AppSidebar } from "@/components/app-sidebar" import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb" import { Separator } from "@/components/ui/separator" import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar" import { Button } from "@/components/ui/button" import { Plus, Link, MonitorIcon as MonitorCog, FileDigit, Trash2, LayoutGrid, List, Pencil, Cpu, MicroscopeIcon as Microchip, MemoryStick, HardDrive, LucideServer, } from "lucide-react" import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" import Cookies from "js-cookie" import { useState, useEffect } from "react" import axios from "axios" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Checkbox } from "@/components/ui/checkbox" import { ScrollArea } from "@/components/ui/scroll-area" import { DynamicIcon } from "lucide-react/dynamic" interface Server { id: number name: string icon: string host: boolean hostServer: number | null os?: string ip?: string url?: string cpu?: string gpu?: string ram?: string disk?: string hostedVMs?: Server[] isVM?: boolean monitoring?: boolean monitoringURL?: string online?: boolean cpuUsage?: number ramUsage?: number diskUsage?: number } interface GetServersResponse { servers: Server[] maxPage: number } export default function Dashboard() { const [host, setHost] = useState(false) const [hostServer, setHostServer] = useState(0) const [name, setName] = useState("") const [icon, setIcon] = useState("") const [os, setOs] = useState("") const [ip, setIp] = useState("") const [url, setUrl] = useState("") const [cpu, setCpu] = useState("") const [gpu, setGpu] = useState("") const [ram, setRam] = useState("") const [disk, setDisk] = useState("") const [monitoring, setMonitoring] = useState(false) const [monitoringURL, setMonitoringURL] = useState("") const [online, setOnline] = useState(false) const [cpuUsage, setCpuUsage] = useState(0) const [ramUsage, setRamUsage] = useState(0) const [diskUsage, setDiskUsage] = useState(0) const [currentPage, setCurrentPage] = useState(1) const [maxPage, setMaxPage] = useState(1) const [itemsPerPage, setItemsPerPage] = useState(4) const [servers, setServers] = useState([]) const [isGridLayout, setIsGridLayout] = useState(false) const [loading, setLoading] = useState(true) const [editId, setEditId] = useState(null) const [editHost, setEditHost] = useState(false) const [editHostServer, setEditHostServer] = useState(0) const [editName, setEditName] = useState("") const [editIcon, setEditIcon] = useState("") const [editOs, setEditOs] = useState("") const [editIp, setEditIp] = useState("") const [editUrl, setEditUrl] = useState("") const [editCpu, setEditCpu] = useState("") const [editGpu, setEditGpu] = useState("") const [editRam, setEditRam] = useState("") const [editDisk, setEditDisk] = useState("") const [editMonitoring, setEditMonitoring] = useState(false) const [editMonitoringURL, setEditMonitoringURL] = useState("") const [searchTerm, setSearchTerm] = useState("") const [isSearching, setIsSearching] = useState(false) const [hostServers, setHostServers] = useState([]) const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) useEffect(() => { const savedLayout = Cookies.get("layoutPreference-servers") const layout_bool = savedLayout === "grid" setIsGridLayout(layout_bool) setItemsPerPage(layout_bool ? 6 : 4) }, []) const toggleLayout = () => { const newLayout = !isGridLayout setIsGridLayout(newLayout) Cookies.set("layoutPreference-servers", newLayout ? "grid" : "standard", { expires: 365, path: "/", sameSite: "strict", }) setItemsPerPage(newLayout ? 6 : 4) } const add = async () => { try { await axios.post("/api/servers/add", { host, hostServer, name, icon, os, ip, url, cpu, gpu, ram, disk, monitoring, monitoringURL, }) setIsAddDialogOpen(false) setHost(false) setHostServer(0) setIcon("") setName("") setOs("") setIp("") setUrl("") setCpu("") setGpu("") setRam("") setDisk("") setMonitoring(false) setMonitoringURL("") getServers() } catch (error: any) { console.log(error.response.data) } } const getServers = async () => { try { setLoading(true) const response = await axios.post("/api/servers/get", { page: currentPage, ITEMS_PER_PAGE: itemsPerPage, }) for (const server of response.data.servers) { console.log("Host Server:" + server.hostServer) console.log("ID:" + server.id) } setServers(response.data.servers) setMaxPage(response.data.maxPage) setLoading(false) } catch (error: any) { console.log(error.response) } } useEffect(() => { getServers() }, [currentPage, itemsPerPage]) const handlePrevious = () => { setCurrentPage((prev) => Math.max(1, prev - 1)) } const handleNext = () => { setCurrentPage((prev) => Math.min(maxPage, prev + 1)) } const deleteApplication = async (id: number) => { try { await axios.post("/api/servers/delete", { id }) getServers() } catch (error: any) { console.log(error.response.data) } } const openEditDialog = (server: Server) => { setEditId(server.id) setEditHost(server.host) setEditHostServer(server.hostServer || null) setEditName(server.name) setEditIcon(server.icon || "") setEditOs(server.os || "") setEditIp(server.ip || "") setEditUrl(server.url || "") setEditCpu(server.cpu || "") setEditGpu(server.gpu || "") setEditRam(server.ram || "") setEditDisk(server.disk || "") setEditMonitoring(server.monitoring || false) setEditMonitoringURL(server.monitoringURL || "") } const edit = async () => { if (!editId) return try { await axios.put("/api/servers/edit", { id: editId, host: editHost, hostServer: editHostServer, name: editName, icon: editIcon, os: editOs, ip: editIp, url: editUrl, cpu: editCpu, gpu: editGpu, ram: editRam, disk: editDisk, monitoring: editMonitoring, monitoringURL: editMonitoringURL, }) getServers() setEditId(null) } catch (error: any) { console.log(error.response.data) } } const searchServers = async () => { try { setIsSearching(true) const response = await axios.post<{ results: Server[] }>("/api/servers/search", { searchterm: searchTerm }) setServers(response.data.results) setMaxPage(1) setIsSearching(false) } catch (error: any) { console.error("Search error:", error.response?.data) setIsSearching(false) } } useEffect(() => { const delayDebounce = setTimeout(() => { if (searchTerm.trim() === "") { getServers() } else { searchServers() } }, 300) return () => clearTimeout(delayDebounce) }, [searchTerm]) useEffect(() => { const fetchHostServers = async () => { try { const response = await axios.get<{ servers: Server[] }>("/api/servers/hosts") setHostServers(response.data.servers) } catch (error) { console.error("Error fetching host servers:", error) } } if (isAddDialogOpen || editId !== null) { fetchHostServers() } }, [isAddDialogOpen, editId]) // Add this function to get the host server name for a VM const getHostServerName = (hostServerId: number | null) => { if (!hostServerId) return "" const hostServer = servers.find((server) => server.id === hostServerId) return hostServer ? hostServer.name : "" } const iconCategories = { Infrastructure: ["server", "network", "database", "cloud", "hard-drive", "router", "wifi", "antenna"], Computing: ["cpu", "microchip", "memory-stick", "terminal", "code", "binary", "command"], Monitoring: ["activity", "monitor", "gauge", "bar-chart", "line-chart", "pie-chart"], Security: ["shield", "lock", "key", "fingerprint", "scan-face"], Status: ["check-circle", "x-octagon", "alert-triangle", "alarm-check", "life-buoy"], Other: [ "settings", "power", "folder", "file-code", "clipboard-list", "git-branch", "git-commit", "git-merge", "git-pull-request", "github", "bug", ], } // Flatten icons for search const allIcons = Object.values(iconCategories).flat() return (
/ My Infrastructure Servers
Your Servers
{isGridLayout ? "Switch to list view" : "Switch to grid view"} Add an server General Hardware Virtualization Monitoring
{ const iconElements = document.querySelectorAll("[data-icon-item]") const searchTerm = e.target.value.toLowerCase() iconElements.forEach((el) => { const iconName = el.getAttribute("data-icon-name")?.toLowerCase() || "" if (iconName.includes(searchTerm)) { ;(el as HTMLElement).style.display = "flex" } else { ;(el as HTMLElement).style.display = "none" } }) }} /> {Object.entries(iconCategories).map(([category, categoryIcons]) => (
{category}
{categoryIcons.map((iconName) => (
{iconName}
))}
))}
{icon && }
setName(e.target.value)} />
setIp(e.target.value)} />
Link to a web interface (e.g. Proxmox or Portainer) with which the server can be managed setUrl(e.target.value)} />
setCpu(e.target.value)} />
setGpu(e.target.value)} />
setRam(e.target.value)} />
setDisk(e.target.value)} />
setHost(checked === true)} />
{!host && (
)}
setMonitoring(checked === true)} />
{monitoring && (
setMonitoringURL(e.target.value)} />
)}
Cancel Add
setSearchTerm(e.target.value)} />

{!loading ? (
{servers .filter((server) => (searchTerm ? true : server.hostServer === 0)) .map((server) => (
{server.icon && } {server.icon && "・"} {server.name}
{server.isVM && ( VM )} {server.monitoring && (
)}
OS: {server.os || "-"}
IP: {server.ip || "Not set"}
{server.isVM && server.hostServer && (
Host: {getHostServerName(server.hostServer)}
)}
CPU: {server.cpu || "-"}
GPU: {server.gpu || "-"}
RAM: {server.ram || "-"}
Disk: {server.disk || "-"}
{server.monitoring && server.hostServer === 0 && ( <>

Resource Usage

CPU
{server.cpuUsage || 0}%
80 ? "bg-destructive" : server.cpuUsage && server.cpuUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${server.cpuUsage || 0}%` }} />
RAM
{server.ramUsage || 0}%
80 ? "bg-destructive" : server.ramUsage && server.ramUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${server.ramUsage || 0}%` }} />
Disk
{server.diskUsage || 0}%
80 ? "bg-destructive" : server.diskUsage && server.diskUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${server.diskUsage || 0}%` }} />
)}
{server.url && ( )} Edit Server General Hardware Virtualization Monitoring
{ const iconElements = document.querySelectorAll("[data-edit-icon-item]") const searchTerm = e.target.value.toLowerCase() iconElements.forEach((el) => { const iconName = el.getAttribute("data-icon-name")?.toLowerCase() || "" if (iconName.includes(searchTerm)) { ;(el as HTMLElement).style.display = "flex" } else { ;(el as HTMLElement).style.display = "none" } }) }} /> {Object.entries(iconCategories).map( ([category, categoryIcons]) => (
{category}
{categoryIcons.map((iconName) => (
{iconName}
))}
), )}
{editIcon && ( )}
setEditName(e.target.value)} />
setEditIp(e.target.value)} />
setEditUrl(e.target.value)} />
setEditCpu(e.target.value)} />
setEditGpu(e.target.value)} />
setEditRam(e.target.value)} />
setEditDisk(e.target.value)} />
setEditHost(checked === true)} disabled={server.hostedVMs && server.hostedVMs.length > 0} />
{!editHost && (
)}
setEditMonitoring(checked === true)} />
{editMonitoring && (
setEditMonitoringURL(e.target.value)} />
)}
Cancel
{server.hostedVMs && server.hostedVMs.length > 0 && ( Hosted VMs {server.host && (
{server.hostedVMs?.map((hostedVM) => (
{hostedVM.icon && ( )}
{hostedVM.icon && "・ "} {hostedVM.name}
Edit VM General Hardware Virtualization
{ const iconElements = document.querySelectorAll( "[data-vm-edit-icon-item]", ) const searchTerm = e.target.value.toLowerCase() iconElements.forEach((el) => { const iconName = el .getAttribute( "data-icon-name", ) ?.toLowerCase() || "" if ( iconName.includes(searchTerm) ) { ;( el as HTMLElement ).style.display = "flex" } else { ;( el as HTMLElement ).style.display = "none" } }) }} /> {Object.entries(iconCategories).map( ([category, categoryIcons]) => (
{category}
{categoryIcons.map((iconName) => (
{iconName}
))}
), )}
{editIcon && ( )}
setEditName(e.target.value)} />
setEditIp(e.target.value)} />
setEditUrl(e.target.value)} />
setEditCpu(e.target.value)} />
setEditGpu(e.target.value)} />
setEditRam(e.target.value)} />
setEditDisk(e.target.value)} />
setEditHost(checked === true) } disabled={ server.hostedVMs && server.hostedVMs.length > 0 } />
{!editHost && (
)}
Cancel
OS: {hostedVM.os || "-"}
IP: {hostedVM.ip || "Not set"}
CPU: {hostedVM.cpu || "-"}
GPU: {hostedVM.gpu || "-"}
RAM: {hostedVM.ram || "-"}
Disk: {hostedVM.disk || "-"}
{hostedVM.monitoring && ( <>
CPU
{hostedVM.cpuUsage || 0}%
80 ? "bg-destructive" : hostedVM.cpuUsage && hostedVM.cpuUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${hostedVM.cpuUsage || 0}%` }} />
RAM
{hostedVM.ramUsage || 0}%
80 ? "bg-destructive" : hostedVM.ramUsage && hostedVM.ramUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${hostedVM.ramUsage || 0}%` }} />
Disk
{hostedVM.diskUsage || 0}%
80 ? "bg-destructive" : hostedVM.diskUsage && hostedVM.diskUsage > 60 ? "bg-amber-500" : "bg-emerald-500"}`} style={{ width: `${hostedVM.diskUsage || 0}%` }} />
)}
))}
)} Close )}
))}
) : (
Loading...
)}
1} style={{ cursor: currentPage === 1 ? "not-allowed" : "pointer", }} /> {currentPage}
) }