refactor component layout

This commit is contained in:
karamvir
2023-07-24 09:07:32 -07:00
parent 438a172e77
commit 9206e00a99
15 changed files with 262 additions and 199 deletions

View File

@@ -5,7 +5,7 @@ import { ChevronRight, Circle, LogOut } from "lucide-react";
import { Link, useLocation, useParams } from "react-router-dom";
import { useUser } from "@hooks";
import { ServerName } from "@pages/server";
import { ServerName } from "@resources/server/util";
import { DeploymentName } from "@resources/deployment/util";
export const Paths = () => {

View File

@@ -1,15 +1,25 @@
import { Types } from "@monitor/client";
import { client } from "./main";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
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";
export const useRead = <T extends Types.ReadRequest>(req: T) =>
useQuery([req], () => client.read(req));
export const useWrite = <T extends Types.WriteRequest>() =>
useMutation((req: T) => client.write(req));
export const useWrite = <
T extends Types.WriteRequest["type"],
P = Extract<Types.WriteRequest, { type: T }>["params"]
>(
type: T
) =>
useMutation(
[type],
async (params: P) =>
(await client.write({ type, params } as any)) as WriteResponses[T]
);
export const useExecute = <T extends Types.ExecuteRequest>() =>
useMutation((req: T) => client.execute(req));

View File

@@ -1,24 +1,43 @@
import { Card, CardContent, CardHeader, CardTitle } from "@ui/card";
import { ReactNode } from "react";
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("");
export const Resources = ({
title,
newButton,
children,
}: {
title: string;
newButton: ReactNode;
children: ReactNode;
}) => {
return (
<Card>
<CardHeader className="flex flex-row justify-between">
<CardTitle className="text-3xl">{title}</CardTitle>
{newButton}
</CardHeader>
<CardContent className="h-fit min-h-[50vh] max-h-[70vh] overflow-auto">
<div className="grid gap-4 md:grid-cols-2">{children}</div>
</CardContent>
</Card>
<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>
);
};

View File

@@ -1,63 +0,0 @@
import { Resource } from "@layouts/resource";
import { useParams } from "react-router-dom";
import { useExecute, useRead, useSetRecentlyViewed } from "@hooks";
import { ActionButton } from "@components/util";
import { Hammer } from "lucide-react";
import { version_to_string } from "@util/helpers";
export const BuildName = ({ id }: { id: string }) => {
const builds = useRead({ type: "ListBuilds", params: {} }).data;
const build = builds?.find((b) => b.id === id);
return <>{build?.name ?? "..."}</>;
};
export const BuildVersion = ({ id }: { id: string }) => {
const builds = useRead({ type: "ListBuilds", params: {} }).data;
const build = builds?.find((b) => b.id === id);
return <>{version_to_string(build?.version) ?? "..."}</>;
};
export const RebuildBuild = ({ buildId }: { buildId: string }) => {
const { mutate, isLoading } = useExecute();
return (
<ActionButton
title="Build"
intent="success"
icon={<Hammer className="h-4 w-4" />}
onClick={() =>
mutate({ type: "RunBuild", params: { build_id: buildId } })
}
disabled={isLoading}
/>
);
};
export const Build = () => {
const { buildId } = useParams();
const push = useSetRecentlyViewed();
if (!buildId) return null;
push("Build", buildId);
return (
<Resource
title={<BuildName id={buildId} />}
info={<BuildVersion id={buildId} />}
actions={<RebuildBuild buildId={buildId} />}
tabs={[
{
title: "Config",
component: "config",
},
{
title: "Builder",
component: "builder",
},
{
title: "Updates",
component: "updates",
},
]}
/>
);
};

View File

@@ -1,5 +1,5 @@
import { useGetRecentlyViewed } from "@hooks";
import { BuildCard, DeploymentCard, NewDeployment, ServerCard } from "..";
import { BuildCard, ServerCard } from "..";
import { useState } from "react";
import {
DropdownMenu,
@@ -12,6 +12,8 @@ import {
} from "@ui/dropdown";
import { Button } from "@ui/button";
import { ChevronDown, PlusCircle } from "lucide-react";
import { DeploymentCard } from "@resources/deployment/card";
import { NewDeployment } from "@resources/deployment/new";
const NewButton = () => {
const [open, set] = useState<"deployment" | "build" | "server" | boolean>(
@@ -38,13 +40,21 @@ const NewButton = () => {
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem onClick={() => set("deployment")}>
<DropdownMenuItem
className="cursor-pointer"
onClick={() => set("deployment")}
>
Deployment
</DropdownMenuItem>
<DropdownMenuItem onClick={() => set("build")}>
<DropdownMenuItem
className="cursor-pointer"
onClick={() => set("build")}
>
Build
</DropdownMenuItem>
<DropdownMenuItem> Server </DropdownMenuItem>
<DropdownMenuItem className="cursor-pointer">
Server
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -21,52 +21,7 @@ import {
} from "@ui/dialog";
import { useState } from "react";
import { RecentlyViewed } from "./components/recently-viewed";
import { ServerStats, ServerStatusIcon } from "@pages/server";
export const NewDeployment = ({
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 Deployment</DialogTitle>
</DialogHeader>
<div className="flex items-center justify-between">
<div>Deployment Name</div>
<Input
className="max-w-[50%]"
placeholder="Deployment Name"
name={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<DialogFooter>
<Button
variant="outline"
intent="success"
onClick={() => {
mutate({
type: "CreateDeployment",
params: { name, config: {} },
});
set(false);
}}
>
Create
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
import { ServerStats, ServerStatusIcon } from "@resources/server/util";
const NewBuild = ({
open,
@@ -113,24 +68,6 @@ const NewBuild = ({
);
};
export const DeploymentCard = ({ id }: { id: string }) => {
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
const deployment = deployments?.find((d) => d.id === id);
if (!deployment) return null;
return (
<Link to={`/deployments/${deployment.id}`}>
<Card hoverable>
<CardHeader>
<CardTitle>{deployment.name}</CardTitle>
<CardDescription>
{deployment.status ?? "not deployed"}
</CardDescription>
</CardHeader>
</Card>
</Link>
);
};
export const ServerCard = ({ id }: { id: string }) => {
const servers = useRead({ type: "ListServers", params: {} }).data;
const server = servers?.find((server) => server.id === id);

View File

@@ -1,5 +1,6 @@
import { useRead } from "@hooks";
import { DeploymentCard } from "@pages/dashboard";
import { DeploymentCard } from "@resources/deployment/card";
import { NewDeployment } from "@resources/deployment/new";
import { Button } from "@ui/button";
import { Input } from "@ui/input";
import { PlusCircle } from "lucide-react";
@@ -7,7 +8,8 @@ import { useState } from "react";
export const Deployments = () => {
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
const [search, set] = useState("");
const [search, setSearch] = useState("");
const [open, setOpen] = useState(false);
return (
<div className="flex flex-col gap-12">
@@ -18,16 +20,18 @@ export const Deployments = () => {
className="w-[300px]"
placeholder="Search"
value={search}
onChange={(e) => set(e.target.value)}
onChange={(e) => setSearch(e.target.value)}
/>
<Button
className="w-[200px] flex items-center gap-2"
variant="outline"
intent="success"
onClick={() => setOpen(true)}
>
<PlusCircle className="w-4 h-4 text-green-500" />
New Deployment
</Button>
<NewDeployment open={open} set={setOpen} />
</div>
</div>
<div className="grid grid-cols-3 gap-8">

View File

@@ -0,0 +1,18 @@
import { ActionButton } from "@components/util";
import { useExecute } from "@hooks";
import { Hammer } from "lucide-react";
export const RebuildBuild = ({ buildId }: { buildId: string }) => {
const { mutate, isLoading } = useExecute();
return (
<ActionButton
intent="success"
title="Build"
icon={<Hammer className="h-4 w-4" />}
onClick={() =>
mutate({ type: "RunBuild", params: { build_id: buildId } })
}
disabled={isLoading}
/>
);
};

View File

@@ -0,0 +1,35 @@
import { useSetRecentlyViewed } from "@hooks";
import { Resource } from "@layouts/resource";
import { BuildName, BuildVersion } from "./util";
import { useParams } from "react-router-dom";
import { RebuildBuild } from "./components/actions";
export const Build = () => {
const { buildId } = useParams();
const push = useSetRecentlyViewed();
if (!buildId) return null;
push("Build", buildId);
return (
<Resource
title={<BuildName id={buildId} />}
info={<BuildVersion id={buildId} />}
actions={<RebuildBuild buildId={buildId} />}
tabs={[
{
title: "Config",
component: "config",
},
{
title: "Builder",
component: "builder",
},
{
title: "Updates",
component: "updates",
},
]}
/>
);
};

View File

@@ -0,0 +1,14 @@
import { useRead } from "@hooks";
import { version_to_string } from "@util/helpers";
export const BuildName = ({ id }: { id: string }) => {
const builds = useRead({ type: "ListBuilds", params: {} }).data;
const build = builds?.find((b) => b.id === id);
return <>{build?.name ?? "..."}</>;
};
export const BuildVersion = ({ id }: { id: string }) => {
const builds = useRead({ type: "ListBuilds", params: {} }).data;
const build = builds?.find((b) => b.id === id);
return <>{version_to_string(build?.version) ?? "..."}</>;
};

View File

@@ -0,0 +1,21 @@
import { useRead } from "@hooks";
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
import { Link } from "react-router-dom";
export const DeploymentCard = ({ id }: { id: string }) => {
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
const deployment = deployments?.find((d) => d.id === id);
if (!deployment) return null;
return (
<Link to={`/deployments/${deployment.id}`}>
<Card hoverable>
<CardHeader>
<CardTitle>{deployment.name}</CardTitle>
<CardDescription>
{deployment.status ?? "not deployed"}
</CardDescription>
</CardHeader>
</Card>
</Link>
);
};

View File

@@ -0,0 +1,56 @@
import { useState } from "react";
import { Button } from "@ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@ui/dialog";
import { Input } from "@ui/input";
import { useWrite } from "@hooks";
import { useNavigate } from "react-router-dom";
export const NewDeployment = ({
open,
set,
}: {
open: boolean;
set: (b: boolean) => void;
}) => {
const nav = useNavigate();
const { mutateAsync } = useWrite("CreateDeployment");
const [name, setName] = useState("");
return (
<Dialog open={open} onOpenChange={set}>
<DialogContent>
<DialogHeader>
<DialogTitle>New Deployment</DialogTitle>
</DialogHeader>
<div className="flex items-center justify-between">
<div>Deployment Name</div>
<Input
className="max-w-[50%]"
placeholder="Deployment Name"
name={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<DialogFooter>
<Button
variant="outline"
intent="success"
onClick={async () => {
const { _id } = await mutateAsync({ name, config: {} });
nav(`/deployments/${_id?.$oid}`);
set(false);
}}
>
Create
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,40 @@
import { useSetRecentlyViewed } from "@hooks";
import { Resource } from "@layouts/resource";
import { useParams } from "react-router-dom";
import { ServerInfo, ServerName } from "./util";
export const Server = () => {
const { serverId } = useParams();
const push = useSetRecentlyViewed();
// if (!serverId) return null;
// push("Server", serverId!);
if (!serverId) return null;
push("Server", serverId);
return (
<Resource
title={<ServerName serverId={serverId} />}
info={<ServerInfo serverId={serverId} />}
actions=""
tabs={[
{
title: "Config",
component: <>config</>,
},
{
title: "Deployments",
component: <>server deployments</>,
},
{
title: "Stats",
component: "server stats",
},
{
title: "Updates",
component: <>updates</>,
},
]}
/>
);
};

View File

@@ -1,11 +1,9 @@
import { useRead, useSetRecentlyViewed } from "@hooks";
import { Resource } from "@layouts/resource";
import { useRead } from "@hooks";
import { ServerStatus } from "@monitor/client/dist/types";
import { CardDescription } from "@ui/card";
import { cn } from "@util/helpers";
import { Circle, Cpu, Database, MemoryStick } from "lucide-react";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
export const ServerName = ({ serverId }: { serverId: string | undefined }) => {
const servers = useRead({ type: "ListServers", params: {} }).data;
@@ -80,39 +78,3 @@ export const ServerStatusIcon = ({
/>
);
};
export const Server = () => {
const { serverId } = useParams();
const push = useSetRecentlyViewed();
// if (!serverId) return null;
// push("Server", serverId!);
if (!serverId) return null;
push("Server", serverId);
return (
<Resource
title={<ServerName serverId={serverId} />}
info={<ServerInfo serverId={serverId} />}
actions=""
tabs={[
{
title: "Config",
component: <>config</>,
},
{
title: "Deployments",
component: <>server deployments</>,
},
{
title: "Stats",
component: "server stats",
},
{
title: "Updates",
component: <>updates</>,
},
]}
/>
);
};

View File

@@ -3,12 +3,12 @@ import { Layout } from "@layouts/layout";
import { Login } from "@pages/auth/login";
import { Signup } from "@pages/auth/signup";
import { Dashboard } from "@pages/dashboard";
import { Server } from "@pages/server";
import { Servers } from "@pages/servers";
import { Deployments } from "@pages/deployments";
import { Builds } from "@pages/builds";
import { Build } from "@pages/build";
import { Deployment } from "@resources/deployment/page";
import { Server } from "@resources/server/page";
import { Build } from "@resources/build/page";
const router = createBrowserRouter([
{