improve execute hooks, cleanup a bit etc

This commit is contained in:
karamvir
2023-07-24 09:24:02 -07:00
parent 9206e00a99
commit ec20b288be
13 changed files with 129 additions and 235 deletions
+15 -3
View File
@@ -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: {} });
-46
View File
@@ -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
View File
@@ -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";
-43
View File
@@ -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";
+3 -87
View File
@@ -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 -1
View File
@@ -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}
/>
);
+53
View File
@@ -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}
/>
);
+31 -1
View File
@@ -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>
);
};