diff --git a/app/dashboard/servers/Servers.tsx b/app/dashboard/servers/Servers.tsx index 75b9deb..704d0cd 100644 --- a/app/dashboard/servers/Servers.tsx +++ b/app/dashboard/servers/Servers.tsx @@ -1,42 +1,32 @@ -"use client"; +"use client" -import { AppSidebar } from "@/components/app-sidebar"; +import { AppSidebar } from "@/components/app-sidebar" import { Breadcrumb, BreadcrumbItem, - BreadcrumbLink, 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"; +} 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, - MonitorCog, + MonitorIcon as MonitorCog, FileDigit, Trash2, LayoutGrid, List, Pencil, Cpu, - Microchip, + MicroscopeIcon as Microchip, MemoryStick, HardDrive, Server, -} from "lucide-react"; -import { - Card, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; +} from "lucide-react" +import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Pagination, PaginationContent, @@ -44,7 +34,7 @@ import { PaginationLink, PaginationNext, PaginationPrevious, -} from "@/components/ui/pagination"; +} from "@/components/ui/pagination" import { AlertDialog, AlertDialogAction, @@ -55,108 +45,97 @@ import { 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'; +} 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; + 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 } interface GetServersResponse { - servers: Server[]; - maxPage: number; + 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 [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 [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 [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 [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 [searchTerm, setSearchTerm] = useState(""); - const [isSearching, setIsSearching] = useState(false); + const [searchTerm, setSearchTerm] = useState("") + const [isSearching, setIsSearching] = useState(false) - const [hostServers, setHostServers] = useState([]); - const [isAddDialogOpen, setIsAddDialogOpen] = 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 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); + const newLayout = !isGridLayout + setIsGridLayout(newLayout) Cookies.set("layoutPreference-servers", newLayout ? "grid" : "standard", { expires: 365, path: "/", sameSite: "strict", - }); - setItemsPerPage(newLayout ? 6 : 4); - }; + }) + setItemsPerPage(newLayout ? 6 : 4) + } const add = async () => { try { @@ -172,85 +151,82 @@ export default function Dashboard() { gpu, ram, disk, - }); - setIsAddDialogOpen(false); - setHost(false); - setHostServer(0); - setIcon(""); - setName(""); - setOs(""); - setIp(""); - setUrl(""); - setCpu(""); - setGpu(""); - setRam(""); - setDisk(""); - getServers(); + }) + setIsAddDialogOpen(false) + setHost(false) + setHostServer(0) + setIcon("") + setName("") + setOs("") + setIp("") + setUrl("") + setCpu("") + setGpu("") + setRam("") + setDisk("") + getServers() } catch (error: any) { - console.log(error.response.data); + 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, - } - ); + 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); + console.log("Host Server:" + server.hostServer) + console.log("ID:" + server.id) } - setServers(response.data.servers); - setMaxPage(response.data.maxPage); - setLoading(false); + setServers(response.data.servers) + setMaxPage(response.data.maxPage) + setLoading(false) } catch (error: any) { - console.log(error.response); + console.log(error.response) } - }; + } useEffect(() => { - getServers(); - }, [currentPage, itemsPerPage]); + getServers() + }, [currentPage, itemsPerPage]) const handlePrevious = () => { - setCurrentPage((prev) => Math.max(1, prev - 1)); - }; + setCurrentPage((prev) => Math.max(1, prev - 1)) + } const handleNext = () => { - setCurrentPage((prev) => Math.min(maxPage, prev + 1)); - }; + setCurrentPage((prev) => Math.min(maxPage, prev + 1)) + } const deleteApplication = async (id: number) => { try { - await axios.post("/api/servers/delete", { id }); - getServers(); + await axios.post("/api/servers/delete", { id }) + getServers() } catch (error: any) { - console.log(error.response.data); + 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 || ""); - }; + 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 || "") + } const edit = async () => { - if (!editId) return; + if (!editId) return try { await axios.put("/api/servers/edit", { @@ -266,76 +242,84 @@ export default function Dashboard() { gpu: editGpu, ram: editRam, disk: editDisk, - }); - getServers(); - setEditId(null); + }) + getServers() + setEditId(null) } catch (error: any) { - console.log(error.response.data); + 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); + 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); + console.error("Search error:", error.response?.data) + setIsSearching(false) } - }; + } useEffect(() => { const delayDebounce = setTimeout(() => { if (searchTerm.trim() === "") { - getServers(); + getServers() } else { - searchServers(); + searchServers() } - }, 300); + }, 300) - return () => clearTimeout(delayDebounce); - }, [searchTerm]); + return () => clearTimeout(delayDebounce) + }, [searchTerm]) useEffect(() => { const fetchHostServers = async () => { try { - const response = await axios.get<{ servers: Server[] }>( - "/api/servers/hosts" - ); - setHostServers(response.data.servers); + const response = await axios.get<{ servers: Server[] }>("/api/servers/hosts") + setHostServers(response.data.servers) } catch (error) { - console.error("Error fetching host servers:", error); + console.error("Error fetching host servers:", error) } - }; + } if (isAddDialogOpen || editId !== null) { - fetchHostServers(); + fetchHostServers() } - }, [isAddDialogOpen, editId]); + }, [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 : ""; - }; + if (!hostServerId) return "" + const hostServer = servers.find((server) => server.id === hostServerId) + return hostServer ? hostServer.name : "" + } - const icons = [ - "server", "network", "database", "cloud", - "terminal", "code", "cpu", "hard-drive", "router", - "activity", "monitor", "bug", "settings", "shield", - "lock", "key", "wifi", "antenna", "power", - "folder", "file-code", "clipboard-list", "binary", "command", - "git-branch", "git-commit", "git-merge", "git-pull-request", "github", - "life-buoy", "alarm-check", "alert-triangle", "check-circle", "x-octagon" - ]; - + 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 ( @@ -369,23 +353,11 @@ export default function Dashboard() { - - - {isGridLayout - ? "Switch to list view" - : "Switch to grid view"} - + {isGridLayout ? "Switch to list view" : "Switch to grid view"} @@ -402,40 +374,71 @@ export default function Dashboard() { General Hardware - - Virtualization - + Virtualization
- setIcon(value)}> + + + {icon && ( +
+ + {icon} +
+ )} +
+
+ + { + 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 && } + {icon && }
@@ -450,19 +453,14 @@ export default function Dashboard() {
- Link to a web interface (e.g. Proxmox or - Portainer) with which the server can be + Link to a web interface (e.g. Proxmox or Portainer) with which the server can be managed @@ -513,10 +504,7 @@ export default function Dashboard() {
- setHost(checked === true) - } + onCheckedChange={(checked) => setHost(checked === true)} /> - +
{!host && (
setEditIcon(value)}> - - - {editIcon &&
- - {editIcon} -
} -
-
- - {icons.map((icon) => ( - -
- - {icon} -
-
- ))} -
- -
-
- -
- {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 - - - - - - - {server.hostedVMs && server.hostedVMs.length > 0 && ( +
+ - - - Hosted VMs - + Edit Server + + + + General + Hardware + Virtualization + + +
+
+
+ +
+ { + 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 && ( +
+ + +
+ )} +
+
+
+
+
+ + Cancel + + +
+
+
+ + {server.hostedVMs && server.hostedVMs.length > 0 && ( + + + + + + + Hosted VMs {server.host && (
- {server.hostedVMs?.map( - (hostedVM) => ( -
-
-
- {hostedVM.icon && } -
- {hostedVM.name} -
-
-
- + {server.hostedVMs?.map((hostedVM) => ( +
+
+
+ {hostedVM.icon && ( + + )} +
{hostedVM.name}
+
+
+ - + - - Edit VM - + Edit VM - + - - General - - - Hardware - + General + Hardware Virtualization @@ -1097,68 +1014,121 @@ export default function Dashboard() {
- +
+ { + 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 && } + {editIcon && ( + + )}
- + - setEditName( - e - .target - .value - ) - } + value={editName} + onChange={(e) => setEditName(e.target.value)} />
- +
- + - setEditIp( - e - .target - .value - ) - } + value={editIp} + onChange={(e) => setEditIp(e.target.value)} />
- + - setEditUrl( - e - .target - .value - ) - } + value={editUrl} + onChange={(e) => setEditUrl(e.target.value)} />
@@ -1228,83 +1168,35 @@ export default function Dashboard() {
- + - setEditCpu( - e - .target - .value - ) - } + value={editCpu} + onChange={(e) => setEditCpu(e.target.value)} />
- + - setEditGpu( - e - .target - .value - ) - } + value={editGpu} + onChange={(e) => setEditGpu(e.target.value)} />
- + - setEditRam( - e - .target - .value - ) - } + value={editRam} + onChange={(e) => setEditRam(e.target.value)} />
- + - setEditDisk( - e - .target - .value - ) - } + value={editDisk} + onChange={(e) => setEditDisk(e.target.value)} />
@@ -1314,46 +1206,32 @@ export default function Dashboard() {
+ setEditHost(checked === true) } - onCheckedChange={( - checked - ) => - setEditHost( - checked === - true - ) + disabled={ + server.hostedVMs && + server.hostedVMs.length > 0 } - disabled={server.hostedVMs && server.hostedVMs.length > 0} />
{!editHost && (
- +