mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-09 21:45:17 +00:00
feat(servers): enhance server management UI with button options
- Added `asButton` prop to `HandleServers`, `SetupServer`, `ShowServerActions`, and `TerminalModal` components to allow rendering as buttons for improved UI flexibility. - Updated the server management interface to use buttons for actions like editing and setting up servers, enhancing user experience. - Introduced new icons for better visual representation of actions in the server management dashboard.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { Activity } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -6,6 +7,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||||
import { ShowStorageActions } from "./show-storage-actions";
|
import { ShowStorageActions } from "./show-storage-actions";
|
||||||
import { ShowTraefikActions } from "./show-traefik-actions";
|
import { ShowTraefikActions } from "./show-traefik-actions";
|
||||||
@@ -13,20 +15,30 @@ import { ToggleDockerCleanup } from "./toggle-docker-cleanup";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
serverId: string;
|
serverId: string;
|
||||||
|
asButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShowServerActions = ({ serverId }: Props) => {
|
export const ShowServerActions = ({ serverId, asButton = false }: Props) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger asChild>
|
{asButton ? (
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline" size="icon" className="h-9 w-9">
|
||||||
|
<Activity className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
) : (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="w-full cursor-pointer"
|
className="w-full cursor-pointer"
|
||||||
onSelect={(e) => e.preventDefault()}
|
onSelect={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsOpen(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
View Actions
|
View Actions
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DialogTrigger>
|
)}
|
||||||
<DialogContent className="sm:max-w-xl">
|
<DialogContent className="sm:max-w-xl">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<DialogTitle className="text-xl">Web server settings</DialogTitle>
|
<DialogTitle className="text-xl">Web server settings</DialogTitle>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { PlusIcon } from "lucide-react";
|
import { PlusIcon, Pencil } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -59,9 +59,10 @@ type Schema = z.infer<typeof Schema>;
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
serverId?: string;
|
serverId?: string;
|
||||||
|
asButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HandleServers = ({ serverId }: Props) => {
|
export const HandleServers = ({ serverId, asButton = false }: Props) => {
|
||||||
const { t } = useTranslation("settings");
|
const { t } = useTranslation("settings");
|
||||||
|
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
@@ -137,21 +138,32 @@ export const HandleServers = ({ serverId }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger asChild>
|
{serverId ? (
|
||||||
{serverId ? (
|
asButton ? (
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline" size="icon" className="h-9 w-9">
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
) : (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="w-full cursor-pointer "
|
className="w-full cursor-pointer "
|
||||||
onSelect={(e) => e.preventDefault()}
|
onSelect={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsOpen(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Edit Server
|
Edit Server
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
) : (
|
)
|
||||||
|
) : (
|
||||||
|
<DialogTrigger asChild>
|
||||||
<Button className="cursor-pointer space-x-3">
|
<Button className="cursor-pointer space-x-3">
|
||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
Create Server
|
Create Server
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</DialogTrigger>
|
||||||
</DialogTrigger>
|
)}
|
||||||
<DialogContent className="sm:max-w-3xl ">
|
<DialogContent className="sm:max-w-3xl ">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{serverId ? "Edit" : "Create"} Server</DialogTitle>
|
<DialogTitle>{serverId ? "Edit" : "Create"} Server</DialogTitle>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import copy from "copy-to-clipboard";
|
import copy from "copy-to-clipboard";
|
||||||
import { CopyIcon, ExternalLinkIcon, ServerIcon } from "lucide-react";
|
import { CopyIcon, ExternalLinkIcon, ServerIcon, Settings } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -36,9 +36,10 @@ import { ValidateServer } from "./validate-server";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
serverId: string;
|
serverId: string;
|
||||||
|
asButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SetupServer = ({ serverId }: Props) => {
|
export const SetupServer = ({ serverId, asButton = false }: Props) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { data: server } = api.server.one.useQuery(
|
const { data: server } = api.server.one.useQuery(
|
||||||
{
|
{
|
||||||
@@ -81,14 +82,23 @@ export const SetupServer = ({ serverId }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger asChild>
|
{asButton ? (
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline" size="icon" className="h-9 w-9">
|
||||||
|
<Settings className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
) : (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="w-full cursor-pointer "
|
className="w-full cursor-pointer "
|
||||||
onSelect={(e) => e.preventDefault()}
|
onSelect={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsOpen(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Setup Server
|
Setup Server
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DialogTrigger>
|
)}
|
||||||
<DialogContent className="sm:max-w-4xl ">
|
<DialogContent className="sm:max-w-4xl ">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { KeyIcon, Loader2, MoreHorizontal, ServerIcon } from "lucide-react";
|
import { KeyIcon, Loader2, MoreHorizontal, ServerIcon, Clock, User, Key, Network, Terminal, Settings, Pencil, Trash2 } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
@@ -24,14 +24,11 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
Table,
|
Tooltip,
|
||||||
TableBody,
|
TooltipContent,
|
||||||
TableCaption,
|
TooltipProvider,
|
||||||
TableCell,
|
TooltipTrigger,
|
||||||
TableHead,
|
} from "@/components/ui/tooltip";
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { ShowNodesModal } from "../cluster/nodes/show-nodes-modal";
|
import { ShowNodesModal } from "../cluster/nodes/show-nodes-modal";
|
||||||
import { TerminalModal } from "../web-server/terminal-modal";
|
import { TerminalModal } from "../web-server/terminal-modal";
|
||||||
@@ -59,7 +56,7 @@ export const ShowServers = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{query?.success && isCloud && <WelcomeSuscription />}
|
{query?.success && isCloud && <WelcomeSuscription />}
|
||||||
<Card className="h-full bg-sidebar p-2.5 rounded-xl max-w-5xl mx-auto">
|
<Card className="h-full p-2.5 rounded-xl max-w-5xl mx-auto">
|
||||||
<div className="rounded-xl bg-background shadow-md ">
|
<div className="rounded-xl bg-background shadow-md ">
|
||||||
<CardHeader className="">
|
<CardHeader className="">
|
||||||
<CardTitle className="text-xl flex flex-row gap-2">
|
<CardTitle className="text-xl flex flex-row gap-2">
|
||||||
@@ -114,57 +111,70 @@ export const ShowServers = () => {
|
|||||||
<HandleServers />
|
<HandleServers />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-4 min-h-[25vh]">
|
<div className="flex flex-col gap-4 min-h-[25vh]">
|
||||||
<Table>
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
<TableCaption>
|
{data?.map((server) => {
|
||||||
<div className="flex flex-col gap-4">
|
const canDelete = server.totalSum === 0;
|
||||||
See all servers
|
const isActive = server.serverStatus === "active";
|
||||||
</div>
|
const isBuildServer =
|
||||||
</TableCaption>
|
server.serverType === "build";
|
||||||
<TableHeader>
|
return (
|
||||||
<TableRow>
|
<Card key={server.serverId} className="relative hover:shadow-lg transition-shadow flex flex-col bg-transparent">
|
||||||
<TableHead className="text-left">Name</TableHead>
|
<CardHeader className="pb-3">
|
||||||
{isCloud && (
|
<div className="flex items-start justify-between">
|
||||||
<TableHead className="text-center">
|
<div className="flex items-center gap-2">
|
||||||
Status
|
<ServerIcon className="size-5 text-muted-foreground" />
|
||||||
</TableHead>
|
<CardTitle className="text-lg">
|
||||||
)}
|
{server.name}
|
||||||
<TableHead className="text-center">
|
</CardTitle>
|
||||||
Type
|
</div>
|
||||||
</TableHead>
|
{isActive && server.sshKeyId && !isBuildServer && (
|
||||||
<TableHead className="text-center">
|
<DropdownMenu>
|
||||||
IP Address
|
<DropdownMenuTrigger asChild>
|
||||||
</TableHead>
|
<Button
|
||||||
<TableHead className="text-center">
|
variant="ghost"
|
||||||
Port
|
className="h-8 w-8 p-0"
|
||||||
</TableHead>
|
>
|
||||||
<TableHead className="text-center">
|
<span className="sr-only">
|
||||||
Username
|
More options
|
||||||
</TableHead>
|
</span>
|
||||||
<TableHead className="text-center">
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
SSH Key
|
</Button>
|
||||||
</TableHead>
|
</DropdownMenuTrigger>
|
||||||
<TableHead className="text-center">
|
<DropdownMenuContent align="end">
|
||||||
Created
|
<DropdownMenuLabel>
|
||||||
</TableHead>
|
Advanced
|
||||||
<TableHead className="text-right">
|
</DropdownMenuLabel>
|
||||||
Actions
|
<ShowTraefikFileSystemModal
|
||||||
</TableHead>
|
serverId={server.serverId}
|
||||||
</TableRow>
|
/>
|
||||||
</TableHeader>
|
<ShowDockerContainersModal
|
||||||
<TableBody>
|
serverId={server.serverId}
|
||||||
{data?.map((server) => {
|
/>
|
||||||
const canDelete = server.totalSum === 0;
|
{isCloud && (
|
||||||
const isActive = server.serverStatus === "active";
|
<ShowMonitoringModal
|
||||||
const isBuildServer =
|
url={`http://${server.ipAddress}:${server?.metricsConfig?.server?.port}/metrics`}
|
||||||
server.serverType === "build";
|
token={
|
||||||
return (
|
server?.metricsConfig
|
||||||
<TableRow key={server.serverId}>
|
?.server?.token
|
||||||
<TableCell className="text-left">
|
}
|
||||||
{server.name}
|
/>
|
||||||
</TableCell>
|
)}
|
||||||
{isCloud && (
|
<ShowSwarmOverviewModal
|
||||||
<TableHead className="text-center">
|
serverId={server.serverId}
|
||||||
|
/>
|
||||||
|
<ShowNodesModal
|
||||||
|
serverId={server.serverId}
|
||||||
|
/>
|
||||||
|
<ShowSchedulesModal
|
||||||
|
serverId={server.serverId}
|
||||||
|
/>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 mt-2 flex-wrap">
|
||||||
|
{isCloud && (
|
||||||
<Badge
|
<Badge
|
||||||
variant={
|
variant={
|
||||||
server.serverStatus === "active"
|
server.serverStatus === "active"
|
||||||
@@ -174,9 +184,7 @@ export const ShowServers = () => {
|
|||||||
>
|
>
|
||||||
{server.serverStatus}
|
{server.serverStatus}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableHead>
|
)}
|
||||||
)}
|
|
||||||
<TableCell className="text-center">
|
|
||||||
<Badge
|
<Badge
|
||||||
variant={
|
variant={
|
||||||
isBuildServer ? "secondary" : "default"
|
isBuildServer ? "secondary" : "default"
|
||||||
@@ -184,170 +192,173 @@ export const ShowServers = () => {
|
|||||||
>
|
>
|
||||||
{server.serverType}
|
{server.serverType}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</div>
|
||||||
<TableCell className="text-center">
|
</CardHeader>
|
||||||
<Badge>{server.ipAddress}</Badge>
|
<CardContent className="space-y-3 flex-1 flex flex-col">
|
||||||
</TableCell>
|
<div className="flex items-center gap-2 text-sm">
|
||||||
<TableCell className="text-center">
|
<Network className="size-4 text-muted-foreground" />
|
||||||
{server.port}
|
<span className="text-muted-foreground">IP:</span>
|
||||||
</TableCell>
|
<Badge variant="outline">{server.ipAddress}</Badge>
|
||||||
<TableCell className="text-center">
|
<span className="text-muted-foreground">Port:</span>
|
||||||
{server.username}
|
<span className="font-medium">{server.port}</span>
|
||||||
</TableCell>
|
</div>
|
||||||
<TableCell className="text-right">
|
<div className="flex items-center gap-2 text-sm">
|
||||||
<span className="text-sm text-muted-foreground">
|
<User className="size-4 text-muted-foreground" />
|
||||||
|
<span className="text-muted-foreground">User:</span>
|
||||||
|
<span className="font-medium">{server.username}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-sm">
|
||||||
|
<Key className="size-4 text-muted-foreground" />
|
||||||
|
<span className="text-muted-foreground">SSH Key:</span>
|
||||||
|
<span className="font-medium">
|
||||||
{server.sshKeyId ? "Yes" : "No"}
|
{server.sshKeyId ? "Yes" : "No"}
|
||||||
</span>
|
</span>
|
||||||
</TableCell>
|
</div>
|
||||||
<TableCell className="text-right">
|
<div className="flex items-center gap-2 text-sm pt-2 border-t">
|
||||||
<span className="text-sm text-muted-foreground">
|
<Clock className="size-4 text-muted-foreground" />
|
||||||
{format(
|
<span className="text-xs text-muted-foreground">
|
||||||
new Date(server.createdAt),
|
Created {format(new Date(server.createdAt), "PPp")}
|
||||||
"PPpp",
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
</TableCell>
|
</div>
|
||||||
|
|
||||||
<TableCell className="text-right flex justify-end">
|
{/* Compact Actions */}
|
||||||
<DropdownMenu>
|
{isActive && (
|
||||||
<DropdownMenuTrigger asChild>
|
<div className="flex items-center gap-2 pt-3 border-t mt-auto">
|
||||||
<Button
|
<TooltipProvider>
|
||||||
variant="ghost"
|
{server.sshKeyId && (
|
||||||
className="h-8 w-8 p-0"
|
<Tooltip>
|
||||||
>
|
<TooltipTrigger asChild>
|
||||||
<span className="sr-only">
|
<div>
|
||||||
Open menu
|
<TerminalModal
|
||||||
</span>
|
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end">
|
|
||||||
<DropdownMenuLabel>
|
|
||||||
Actions
|
|
||||||
</DropdownMenuLabel>
|
|
||||||
|
|
||||||
{isActive && (
|
|
||||||
<>
|
|
||||||
{server.sshKeyId && (
|
|
||||||
<TerminalModal
|
|
||||||
serverId={server.serverId}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{t(
|
|
||||||
"settings.common.enterTerminal",
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</TerminalModal>
|
|
||||||
)}
|
|
||||||
<SetupServer
|
|
||||||
serverId={server.serverId}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<HandleServers
|
|
||||||
serverId={server.serverId}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{server.sshKeyId &&
|
|
||||||
!isBuildServer && (
|
|
||||||
<ShowServerActions
|
|
||||||
serverId={server.serverId}
|
serverId={server.serverId}
|
||||||
/>
|
asButton={true}
|
||||||
)}
|
>
|
||||||
</>
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="h-9 w-9"
|
||||||
|
>
|
||||||
|
<Terminal className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TerminalModal>
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Terminal</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DialogAction
|
<Tooltip>
|
||||||
disabled={!canDelete}
|
<TooltipTrigger asChild>
|
||||||
title={
|
<div>
|
||||||
canDelete
|
<SetupServer
|
||||||
? "Delete Server"
|
|
||||||
: "Server has active services"
|
|
||||||
}
|
|
||||||
description={
|
|
||||||
canDelete ? (
|
|
||||||
"This will delete the server and all associated data"
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
You can not delete this server
|
|
||||||
because it has active services.
|
|
||||||
<AlertBlock type="warning">
|
|
||||||
You have active services
|
|
||||||
associated with this server,
|
|
||||||
please delete them first.
|
|
||||||
</AlertBlock>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={async () => {
|
|
||||||
await mutateAsync({
|
|
||||||
serverId: server.serverId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
refetch();
|
|
||||||
toast.success(
|
|
||||||
`Server ${server.name} deleted successfully`,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.error(err.message);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="w-full cursor-pointer text-red-500 hover:!text-red-600"
|
|
||||||
onSelect={(e) => e.preventDefault()}
|
|
||||||
>
|
|
||||||
Delete Server
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DialogAction>
|
|
||||||
|
|
||||||
{isActive &&
|
|
||||||
server.sshKeyId &&
|
|
||||||
!isBuildServer && (
|
|
||||||
<>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuLabel>
|
|
||||||
Extra
|
|
||||||
</DropdownMenuLabel>
|
|
||||||
|
|
||||||
<ShowTraefikFileSystemModal
|
|
||||||
serverId={server.serverId}
|
serverId={server.serverId}
|
||||||
|
asButton={true}
|
||||||
/>
|
/>
|
||||||
<ShowDockerContainersModal
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Setup Server</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div>
|
||||||
|
<HandleServers
|
||||||
serverId={server.serverId}
|
serverId={server.serverId}
|
||||||
|
asButton={true}
|
||||||
/>
|
/>
|
||||||
{isCloud && (
|
</div>
|
||||||
<ShowMonitoringModal
|
</TooltipTrigger>
|
||||||
url={`http://${server.ipAddress}:${server?.metricsConfig?.server?.port}/metrics`}
|
<TooltipContent>
|
||||||
token={
|
<p>Edit Server</p>
|
||||||
server?.metricsConfig
|
</TooltipContent>
|
||||||
?.server?.token
|
</Tooltip>
|
||||||
}
|
|
||||||
|
{server.sshKeyId && !isBuildServer && (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div>
|
||||||
|
<ShowServerActions
|
||||||
|
serverId={server.serverId}
|
||||||
|
asButton={true}
|
||||||
/>
|
/>
|
||||||
)}
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Web Server Actions</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
<ShowSwarmOverviewModal
|
<div className="flex-1" />
|
||||||
serverId={server.serverId}
|
|
||||||
/>
|
|
||||||
<ShowNodesModal
|
|
||||||
serverId={server.serverId}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ShowSchedulesModal
|
<Tooltip>
|
||||||
serverId={server.serverId}
|
<TooltipTrigger asChild>
|
||||||
/>
|
<div>
|
||||||
</>
|
<DialogAction
|
||||||
)}
|
disabled={!canDelete}
|
||||||
</DropdownMenuContent>
|
title={
|
||||||
</DropdownMenu>
|
canDelete
|
||||||
</TableCell>
|
? "Delete Server"
|
||||||
</TableRow>
|
: "Server has active services"
|
||||||
);
|
}
|
||||||
})}
|
description={
|
||||||
</TableBody>
|
canDelete ? (
|
||||||
</Table>
|
"This will delete the server and all associated data"
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
You can not delete this server
|
||||||
|
because it has active services.
|
||||||
|
<AlertBlock type="warning">
|
||||||
|
You have active services
|
||||||
|
associated with this server,
|
||||||
|
please delete them first.
|
||||||
|
</AlertBlock>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={async () => {
|
||||||
|
await mutateAsync({
|
||||||
|
serverId: server.serverId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
refetch();
|
||||||
|
toast.success(
|
||||||
|
`Server ${server.name} deleted successfully`,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
toast.error(err.message);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className={`h-9 w-9 ${canDelete ? 'text-destructive hover:text-destructive hover:bg-destructive/10' : 'text-muted-foreground hover:bg-muted'}`}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{canDelete ? 'Delete Server' : 'Cannot delete - has active services'}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row gap-2 flex-wrap w-full justify-end mr-4">
|
<div className="flex flex-row gap-2 flex-wrap w-full justify-end mt-4">
|
||||||
{data && data?.length > 0 && (
|
{data && data?.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<HandleServers />
|
<HandleServers />
|
||||||
|
|||||||
@@ -24,10 +24,12 @@ const getTerminalKey = () => {
|
|||||||
interface Props {
|
interface Props {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
serverId: string;
|
serverId: string;
|
||||||
|
asButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TerminalModal = ({ children, serverId }: Props) => {
|
export const TerminalModal = ({ children, serverId, asButton = false }: Props) => {
|
||||||
const [terminalKey, setTerminalKey] = useState<string>(getTerminalKey());
|
const [terminalKey, setTerminalKey] = useState<string>(getTerminalKey());
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const isLocalServer = serverId === "local";
|
const isLocalServer = serverId === "local";
|
||||||
|
|
||||||
const { data } = api.server.one.useQuery(
|
const { data } = api.server.one.useQuery(
|
||||||
@@ -43,15 +45,22 @@ export const TerminalModal = ({ children, serverId }: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger asChild>
|
{asButton ? (
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
{children}
|
||||||
|
</DialogTrigger>
|
||||||
|
) : (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="w-full cursor-pointer space-x-3"
|
className="w-full cursor-pointer space-x-3"
|
||||||
onSelect={(e) => e.preventDefault()}
|
onSelect={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsOpen(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DialogTrigger>
|
)}
|
||||||
<DialogContent
|
<DialogContent
|
||||||
className="sm:max-w-7xl"
|
className="sm:max-w-7xl"
|
||||||
onEscapeKeyDown={(event) => event.preventDefault()}
|
onEscapeKeyDown={(event) => event.preventDefault()}
|
||||||
|
|||||||
Reference in New Issue
Block a user