mirror of
https://github.com/moghtech/komodo.git
synced 2026-09-11 16:01:27 +00:00
improve execute hooks, cleanup a bit etc
This commit is contained in:
+15
-3
@@ -4,7 +4,10 @@ import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { WriteResponses } from "@monitor/client/dist/responses";
|
||||
import {
|
||||
ExecuteResponses,
|
||||
WriteResponses,
|
||||
} from "@monitor/client/dist/responses";
|
||||
|
||||
export const useRead = <T extends Types.ReadRequest>(req: T) =>
|
||||
useQuery([req], () => client.read(req));
|
||||
@@ -21,8 +24,17 @@ export const useWrite = <
|
||||
(await client.write({ type, params } as any)) as WriteResponses[T]
|
||||
);
|
||||
|
||||
export const useExecute = <T extends Types.ExecuteRequest>() =>
|
||||
useMutation((req: T) => client.execute(req));
|
||||
export const useExecute = <
|
||||
T extends Types.ExecuteRequest["type"],
|
||||
P = Extract<Types.ExecuteRequest, { type: T }>["params"]
|
||||
>(
|
||||
type: T
|
||||
) =>
|
||||
useMutation(
|
||||
[type],
|
||||
async (params: P) =>
|
||||
(await client.execute({ type, params } as any)) as ExecuteResponses[T]
|
||||
);
|
||||
|
||||
export const useUser = () => useRead({ type: "GetUser", params: {} });
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { Button } from "@ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@ui/card";
|
||||
import { RefreshCcw, Save } from "lucide-react";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
export const Config = ({
|
||||
children,
|
||||
loading,
|
||||
save,
|
||||
reset,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
loading: boolean;
|
||||
save: { disabled?: boolean; onClick: () => void };
|
||||
reset: { disabled?: boolean; onClick: () => void };
|
||||
}) => (
|
||||
<Card>
|
||||
<CardHeader className="flex-row justify-between">
|
||||
<CardTitle className="text-xl"> Config </CardTitle>
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={loading || reset.disabled}
|
||||
intent="warning"
|
||||
className="flex gap-2"
|
||||
onClick={reset.onClick}
|
||||
>
|
||||
Reset <RefreshCcw className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={loading || save.disabled}
|
||||
intent="success"
|
||||
className="flex gap-2"
|
||||
onClick={save.onClick}
|
||||
>
|
||||
{loading ? "Saving..." : "Save"}
|
||||
<Save className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4 max-h-[50vh] overflow-y-auto">
|
||||
{children}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Header } from "@components/header";
|
||||
import { useUser } from "@hooks";
|
||||
import { Login } from "@pages/auth/login";
|
||||
import { Toaster } from "@ui/toast";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { useRead } from "@hooks";
|
||||
import { DeploymentCard } from "@resources/deployment/card";
|
||||
import { Button } from "@ui/button";
|
||||
import { Input } from "@ui/input";
|
||||
import { PlusCircle } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export const Deployments = () => {
|
||||
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
|
||||
const [search, set] = useState("");
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-12">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-3xl">Deployments</h1>
|
||||
<div className="flex gap-4">
|
||||
<Input
|
||||
className="w-[300px]"
|
||||
placeholder="Search"
|
||||
value={search}
|
||||
onChange={(e) => set(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
className="w-[200px] flex items-center gap-2"
|
||||
variant="outline"
|
||||
intent="success"
|
||||
>
|
||||
<PlusCircle className="w-4 h-4 text-green-500" />
|
||||
New Deployment
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-8">
|
||||
{deployments?.map(
|
||||
({ id, name }) =>
|
||||
(search.includes(name) || name.includes(search)) && (
|
||||
<DeploymentCard key={id} id={id} />
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
@@ -7,7 +6,6 @@ import {
|
||||
CardContent,
|
||||
} from "@ui/card";
|
||||
import { WithLoading } from "@components/util";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { PieChart } from "react-minimal-pie-chart";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useRead } from "@hooks";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useGetRecentlyViewed } from "@hooks";
|
||||
import { BuildCard, ServerCard } from "..";
|
||||
import { BuildCard } from "..";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -14,6 +14,8 @@ import { Button } from "@ui/button";
|
||||
import { ChevronDown, PlusCircle } from "lucide-react";
|
||||
import { DeploymentCard } from "@resources/deployment/card";
|
||||
import { NewDeployment } from "@resources/deployment/new";
|
||||
import { ServerCard } from "@resources/server/util";
|
||||
import { NewBuild } from "@resources/build/new";
|
||||
|
||||
const NewButton = () => {
|
||||
const [open, set] = useState<"deployment" | "build" | "server" | boolean>(
|
||||
@@ -59,7 +61,7 @@ const NewButton = () => {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<NewDeployment open={open === "deployment"} set={set} />
|
||||
{/* <NewBuild open={open === "build"} set={set} /> */}
|
||||
<NewBuild open={open === "build"} set={set} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
@@ -7,7 +6,6 @@ import {
|
||||
CardContent,
|
||||
} from "@ui/card";
|
||||
import { WithLoading } from "@components/util";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { PieChart } from "react-minimal-pie-chart";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useRead } from "@hooks";
|
||||
|
||||
@@ -1,95 +1,11 @@
|
||||
import { useRead, useUser, useWrite } from "@hooks";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@ui/card";
|
||||
import { useRead, useUser } from "@hooks";
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
|
||||
import { version_to_string } from "@util/helpers";
|
||||
import { ServersChart } from "./components/servers-chart";
|
||||
import { DeploymentsChart } from "./components/deployments-chart";
|
||||
import { Input } from "@ui/input";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@ui/dialog";
|
||||
import { useState } from "react";
|
||||
import { RecentlyViewed } from "./components/recently-viewed";
|
||||
import { ServerStats, ServerStatusIcon } from "@resources/server/util";
|
||||
|
||||
const NewBuild = ({
|
||||
open,
|
||||
set,
|
||||
}: {
|
||||
open: boolean;
|
||||
set: (b: boolean) => void;
|
||||
}) => {
|
||||
const { mutate } = useWrite();
|
||||
const [name, setName] = useState("");
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={set}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>New Build</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>Build Name</div>
|
||||
<Input
|
||||
className="max-w-[50%]"
|
||||
placeholder="Build Name"
|
||||
name={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
intent="success"
|
||||
onClick={() => {
|
||||
mutate({
|
||||
type: "CreateBuild",
|
||||
params: { name, config: {} },
|
||||
});
|
||||
set(false);
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const ServerCard = ({ id }: { id: string }) => {
|
||||
const servers = useRead({ type: "ListServers", params: {} }).data;
|
||||
const server = servers?.find((server) => server.id === id);
|
||||
if (!server) return null;
|
||||
|
||||
return (
|
||||
<Link to={`/servers/${server.id}`} key={server.id}>
|
||||
<Card hoverable>
|
||||
<CardHeader className="flex flex-row justify-between">
|
||||
<div>
|
||||
<CardTitle>{server.name}</CardTitle>
|
||||
<CardDescription>{server.status}</CardDescription>
|
||||
</div>
|
||||
<ServerStatusIcon serverId={server.id} />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ServerStats serverId={server.id} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
import { ServerStatusIcon } from "@resources/server/util";
|
||||
|
||||
export const BuildCard = ({ id }: { id: string }) => {
|
||||
const builds = useRead({ type: "ListBuilds", params: {} }).data;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRead } from "@hooks";
|
||||
import { ServerCard } from "@pages/dashboard";
|
||||
import { ServerCard } from "@resources/server/util";
|
||||
import { Button } from "@ui/button";
|
||||
import { Input } from "@ui/input";
|
||||
import { PlusCircle } from "lucide-react";
|
||||
|
||||
@@ -3,15 +3,13 @@ import { useExecute } from "@hooks";
|
||||
import { Hammer } from "lucide-react";
|
||||
|
||||
export const RebuildBuild = ({ buildId }: { buildId: string }) => {
|
||||
const { mutate, isLoading } = useExecute();
|
||||
const { mutate, isLoading } = useExecute("RunBuild");
|
||||
return (
|
||||
<ActionButton
|
||||
intent="success"
|
||||
title="Build"
|
||||
icon={<Hammer className="h-4 w-4" />}
|
||||
onClick={() =>
|
||||
mutate({ type: "RunBuild", params: { build_id: buildId } })
|
||||
}
|
||||
onClick={() => mutate({ build_id: buildId })}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useState } from "react";
|
||||
import { useWrite } from "@hooks";
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@ui/dialog";
|
||||
import { Input } from "@ui/input";
|
||||
|
||||
export const NewBuild = ({
|
||||
open,
|
||||
set,
|
||||
}: {
|
||||
open: boolean;
|
||||
set: (b: boolean) => void;
|
||||
}) => {
|
||||
const { mutate } = useWrite("CreateBuild");
|
||||
const [name, setName] = useState("");
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={set}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>New Build</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>Build Name</div>
|
||||
<Input
|
||||
className="max-w-[50%]"
|
||||
placeholder="Build Name"
|
||||
name={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
intent="success"
|
||||
onClick={() => {
|
||||
mutate({ name, config: {} });
|
||||
set(false);
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -3,89 +3,66 @@ import { RefreshCw, Play, Trash, Pause } from "lucide-react";
|
||||
import { useExecute, useRead } from "@hooks";
|
||||
import { DockerContainerState } from "@monitor/client/dist/types";
|
||||
|
||||
export const RedeployContainer = ({
|
||||
deploymentId,
|
||||
}: {
|
||||
deploymentId: string;
|
||||
}) => {
|
||||
const { mutate, isLoading } = useExecute();
|
||||
interface DeploymentId {
|
||||
deployment_id: string;
|
||||
}
|
||||
|
||||
export const RedeployContainer = ({ deployment_id }: DeploymentId) => {
|
||||
const { mutate, isLoading } = useExecute("Deploy");
|
||||
return (
|
||||
<ActionButton
|
||||
title="Redeploy"
|
||||
intent="success"
|
||||
icon={<RefreshCw className="h-4 w-4" />}
|
||||
onClick={() =>
|
||||
mutate({ type: "Deploy", params: { deployment_id: deploymentId } })
|
||||
}
|
||||
onClick={() => mutate({ deployment_id })}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const StartContainer = ({ deploymentId }: { deploymentId: string }) => {
|
||||
const { mutate, isLoading } = useExecute();
|
||||
|
||||
const StartContainer = ({ deployment_id }: DeploymentId) => {
|
||||
const { mutate, isLoading } = useExecute("StartContainer");
|
||||
return (
|
||||
<ActionButton
|
||||
title="Start"
|
||||
intent="success"
|
||||
icon={<Play className="h-4 w-4" />}
|
||||
onClick={() =>
|
||||
mutate({
|
||||
type: "StartContainer",
|
||||
params: { deployment_id: deploymentId },
|
||||
})
|
||||
}
|
||||
onClick={() => mutate({ deployment_id })}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const StopContainer = ({ deploymentId }: { deploymentId: string }) => {
|
||||
const { mutate, isLoading } = useExecute();
|
||||
const StopContainer = ({ deployment_id }: DeploymentId) => {
|
||||
const { mutate, isLoading } = useExecute("StopContainer");
|
||||
|
||||
return (
|
||||
<ActionButton
|
||||
title="Stop"
|
||||
intent="warning"
|
||||
icon={<Pause className="h-4 w-4" />}
|
||||
onClick={() =>
|
||||
mutate({
|
||||
type: "StopContainer",
|
||||
params: { deployment_id: deploymentId },
|
||||
})
|
||||
}
|
||||
onClick={() => mutate({ deployment_id })}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const StartOrStopContainer = ({
|
||||
deploymentId,
|
||||
}: {
|
||||
deploymentId: string;
|
||||
}) => {
|
||||
export const StartOrStopContainer = ({ deployment_id }: DeploymentId) => {
|
||||
const { data } = useRead({ type: "ListDeployments", params: {} });
|
||||
const deployment = data?.find((d) => d.id == deploymentId);
|
||||
const deployment = data?.find((d) => d.id == deployment_id);
|
||||
if (deployment?.state === DockerContainerState.Running)
|
||||
return <StopContainer deploymentId={deploymentId} />;
|
||||
return <StartContainer deploymentId={deploymentId} />;
|
||||
return <StopContainer deployment_id={deployment_id} />;
|
||||
return <StartContainer deployment_id={deployment_id} />;
|
||||
};
|
||||
|
||||
export const RemoveContainer = ({ deploymentId }: { deploymentId: string }) => {
|
||||
const { mutate, isLoading } = useExecute();
|
||||
|
||||
export const RemoveContainer = ({ deployment_id }: DeploymentId) => {
|
||||
const { mutate, isLoading } = useExecute("RemoveContainer");
|
||||
return (
|
||||
<ActionButton
|
||||
title="Remove"
|
||||
intent="warning"
|
||||
icon={<Trash className="h-4 w-4" />}
|
||||
onClick={() =>
|
||||
mutate({
|
||||
type: "RemoveContainer",
|
||||
params: { deployment_id: deploymentId },
|
||||
})
|
||||
}
|
||||
onClick={() => mutate({ deployment_id })}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { useRead } from "@hooks";
|
||||
import { ServerStatus } from "@monitor/client/dist/types";
|
||||
import { CardDescription } from "@ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@ui/card";
|
||||
import { cn } from "@util/helpers";
|
||||
import { Circle, Cpu, Database, MemoryStick } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export const ServerName = ({ serverId }: { serverId: string | undefined }) => {
|
||||
const servers = useRead({ type: "ListServers", params: {} }).data;
|
||||
@@ -78,3 +85,26 @@ export const ServerStatusIcon = ({
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ServerCard = ({ id }: { id: string }) => {
|
||||
const servers = useRead({ type: "ListServers", params: {} }).data;
|
||||
const server = servers?.find((server) => server.id === id);
|
||||
if (!server) return null;
|
||||
|
||||
return (
|
||||
<Link to={`/servers/${server.id}`} key={server.id}>
|
||||
<Card hoverable>
|
||||
<CardHeader className="flex flex-row justify-between">
|
||||
<div>
|
||||
<CardTitle>{server.name}</CardTitle>
|
||||
<CardDescription>{server.status}</CardDescription>
|
||||
</div>
|
||||
<ServerStatusIcon serverId={server.id} />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ServerStats serverId={server.id} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user