add some basic dashboard components, add server page

This commit is contained in:
karamvir
2023-07-23 16:08:02 -07:00
parent 9c120118e9
commit b57688033e
6 changed files with 384 additions and 9 deletions

View File

@@ -8,7 +8,7 @@ export const Layout = () => {
const { data, isError } = useUser();
const navigate = useNavigate();
const path = useLocation().pathname;
// if (isError && !path.includes("login")) navigate("/login");
// if (isError && (!path.includes("login") || !path.includes("signup"))) navigate("/login");
console.log(data);

View File

@@ -0,0 +1,81 @@
import { Button } from "@ui/button";
import {
Card,
CardHeader,
CardTitle,
CardDescription,
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";
export const DeploymentsChart = () => {
const { data, isLoading, isError } = useRead({
type: "ListDeployments",
params: {},
});
const running = data?.filter((d) => d.state === "running").length;
const stopped = data?.filter((d) => d.state === "exited").length;
const not_deployed = data?.filter((d) => d.state === "not_deployed").length;
return (
<Card className="pb-4 w-full">
<CardHeader className="flex-row justify-between items-center">
<CardTitle>Deployments</CardTitle>
<Link to="/deployments">
<Button variant="outline" size="sm">
<CardDescription>{data?.length} Total</CardDescription>
<ChevronRight className="w-3 h-3" />
</Button>
</Link>
</CardHeader>
<CardContent className="flex gap-4 items-center w-full">
<WithLoading {...{ isLoading, isError }}>
<div className="flex flex-col gap-2 text-muted-foreground w-full">
<CardDescription>
<span className="text-green-500 font-bold">{running} </span>
Running
</CardDescription>
<CardDescription>
<span className="text-red-500 font-bold">{stopped} </span>
Stopped
</CardDescription>
<CardDescription>
<span className="text-blue-500 font-bold">{not_deployed} </span>
Not Deployed
</CardDescription>
</div>
<div className="flex justify-end items-center w-full">
<PieChart
className="w-20 h-20"
data={[
{
color: "#22C55E",
value: running ?? 0,
title: "deployed",
key: "deployed",
},
{
color: "#EF0044",
value: stopped ?? 0,
title: "stopped",
key: "stopped",
},
{
color: "#3B82F6",
value: not_deployed ?? 0,
title: "not-deployed",
key: "not-deployed",
},
]}
/>
</div>
</WithLoading>
</CardContent>
</Card>
);
};

View File

@@ -0,0 +1,84 @@
import { Button } from "@ui/button";
import {
Card,
CardHeader,
CardTitle,
CardDescription,
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";
import { ServerStatus } from "@monitor/client/dist/types";
export const ServersChart = () => {
const { data, isLoading, isError } = useRead({
type: "ListServers",
params: {},
});
const running = data?.filter((d) => d.status === ServerStatus.Ok).length;
const stopped = data?.filter((d) => d.status === ServerStatus.NotOk).length;
const not_deployed = data?.filter(
(d) => d.status === ServerStatus.Disabled
).length;
return (
<Card className="w-full pb-4">
<CardHeader className="flex-row justify-between items-center">
<CardTitle>Servers</CardTitle>
<Link to="/servers">
<Button variant="outline" size="sm">
<CardDescription>{data?.length} Total</CardDescription>
<ChevronRight className="w-3 h-3" />
</Button>
</Link>
</CardHeader>
<CardContent className="flex gap-4 items-center w-full">
<WithLoading {...{ isLoading, isError }}>
<div className="flex flex-col gap-2 text-muted-foreground w-full">
<CardDescription>
<span className="text-green-500 font-bold">{running} </span>
Healthy
</CardDescription>
<CardDescription>
<span className="text-red-500 font-bold">{stopped} </span>
Unhealthy
</CardDescription>
<CardDescription>
<span className="text-blue-500 font-bold">{not_deployed} </span>
Disabled
</CardDescription>
</div>
<div className="flex justify-end items-center w-full">
<PieChart
className="w-20 h-20"
data={[
{
color: "#22C55E",
value: running ?? 0,
title: "deployed",
key: "deployed",
},
{
color: "#EF0044",
value: stopped ?? 0,
title: "stopped",
key: "stopped",
},
{
color: "#3B82F6",
value: not_deployed ?? 0,
title: "not-deployed",
key: "not-deployed",
},
]}
/>
</div>
</WithLoading>
</CardContent>
</Card>
);
};

View File

@@ -0,0 +1,95 @@
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 { Button } from "@ui/button";
import { PlusCircle } from "lucide-react";
import { Link } from "react-router-dom";
const DeploymentsList = () => {
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
return (
<div className="flex flex-col gap-2 w-full border-r pr-4">
<h2 className="text-lg">Deployments</h2>
{deployments?.map((deployment) => (
<Card>
<CardHeader>
<CardTitle>{deployment.name}</CardTitle>
<CardDescription>{deployment.version}</CardDescription>
</CardHeader>
</Card>
))}
</div>
);
};
const ServersList = () => {
const servers = useRead({ type: "ListServers", params: {} }).data;
return (
<div className="flex flex-col gap-2 w-full border-r pr-4">
<h2 className="text-lg">Servers</h2>
{servers?.map((server) => (
<Link to={`/servers/${server.id}`} key={server.id}>
<Card>
<CardHeader>
<CardTitle>{server.name}</CardTitle>
<CardDescription>{server.status}</CardDescription>
</CardHeader>
</Card>
</Link>
))}
</div>
);
};
const BuildsList = () => {
const builds = useRead({ type: "ListBuilds", params: {} }).data;
return (
<div className="flex flex-col gap-2 w-full">
<h2 className="text-lg">Builds</h2>
{builds?.map((build) => (
<Card>
<CardHeader key={build.id}>
<CardTitle>{build.name}</CardTitle>
<CardDescription>
{version_to_string(build.version)}
</CardDescription>
</CardHeader>
</Card>
))}
</div>
);
};
export const Dashboard = () => {
const user = useUser().data;
return (
<>
<div className="flex items-center justify-between">
<h1 className="text-xl"> Hello, {user?.username}.</h1>
<div className="flex gap-4">
<Input className="w-[300px]" placeholder="Search" />
<Button className="w-[120px]" variant="outline" intent="success">
<PlusCircle className="w-4 h-4 mr-2 text-green-500" />
Add New
</Button>
</div>
</div>
<div className="flex gap-4">
<DeploymentsChart />
<ServersChart />
</div>
<div className="flex gap-4">
<DeploymentsList />
<ServersList />
<BuildsList />
</div>
</>
);
};

View File

@@ -0,0 +1,113 @@
import { useRead } from "@hooks";
import { Resource } from "@layouts/resource";
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;
const server = servers?.find((s) => s.id === serverId);
return <>{server?.name ?? "..."}</>;
};
export const ServerInfo = ({ serverId }: { serverId: string | undefined }) => {
const servers = useRead({ type: "ListServers", params: {} }).data;
const server = servers?.find((s) => s.id === serverId);
return (
<div className="flex items-center gap-4">
{serverId && <ServerStats serverId={serverId} />}
<CardDescription>|</CardDescription>
<div className="flex items-center gap-2">
<CardDescription> Status: {server?.status}</CardDescription>
<ServerStatusIcon serverId={serverId} />
</div>
</div>
);
};
export const ServerStats = ({ serverId }: { serverId: string }) => {
const { data, refetch } = useRead({
type: "GetBasicSystemStats",
params: { server_id: serverId },
});
useEffect(() => {
const handle = setInterval(() => refetch(), 30000);
return () => {
clearInterval(handle);
};
}, [refetch]);
return (
<div className="flex gap-4">
<div className="flex gap-2 items-center">
<Cpu className="w-4 h-4" />
<CardDescription>{data?.cpu_perc.toFixed(2)}%</CardDescription>
</div>
<div className="flex gap-2 items-center">
<MemoryStick className="w-4 h-4" />
<CardDescription>{data?.mem_total_gb.toFixed(2)} GB</CardDescription>
</div>
<div className="flex gap-2 items-center">
<Database className="w-4 h-4" />
<CardDescription>{data?.disk_total_gb.toFixed(2)} GB</CardDescription>
</div>
</div>
);
};
export const ServerStatusIcon = ({
serverId,
sm,
}: {
serverId: string | undefined;
sm?: boolean;
}) => {
const servers = useRead({ type: "ListServers", params: {} }).data;
const server = servers?.find((s) => s.id === serverId);
return (
<Circle
className={cn(
"w-4 h-4 stroke-none",
server?.status === ServerStatus.Ok && "fill-green-500",
server?.status === ServerStatus.NotOk && "fill-red-500",
server?.status === ServerStatus.Disabled && "fill-blue-500",
sm && "w-3 h-3"
)}
/>
);
};
export const Server = () => {
const { serverId } = useParams();
// const { data } = useRead({ type: "GetServer", params: { id: 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

@@ -6,13 +6,15 @@ import { RouterProvider, createBrowserRouter } from "react-router-dom";
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";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{ path: "", element: <>hello</> },
{ path: "", element: <Dashboard /> },
{ path: "login", element: <Login /> },
{ path: "signup", element: <Signup /> },
@@ -30,13 +32,13 @@ const router = createBrowserRouter([
// { path: ":buildId", element: <Build /> },
// ],
// },
// {
// path: "servers",
// children: [
// { path: "", element: <Servers /> },
// { path: ":serverId", element: <Server /> },
// ],
// },
{
path: "servers",
children: [
{ path: "", element: "servers" },
{ path: ":serverId", element: <Server /> },
],
},
],
},
]);