From 8259563c33eda7254ce3b73775bf178b4262e3a0 Mon Sep 17 00:00:00 2001 From: headlessdev Date: Sat, 19 Apr 2025 15:58:48 +0200 Subject: [PATCH] Refactor Servers component to improve code readability and structure. Removed unused imports, standardized import statements, and updated state variable declarations for consistency. Enhanced icon selection functionality in the server editing interface. --- app/dashboard/servers/Servers.tsx | 1579 +++++++++++++---------------- 1 file changed, 721 insertions(+), 858 deletions(-) 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 && (
- +