improve layout standardisation, dashboard

This commit is contained in:
karamvir
2023-08-03 10:50:52 -07:00
parent 3c5a218152
commit 9d95750092
10 changed files with 212 additions and 104 deletions

View File

@@ -73,7 +73,7 @@ export const ResourceUpdates = ({ type, id }: ResourceTarget) => {
</Link>
}
>
<div className="grid md:grid-cols-3 mt-2 gap-4">
<div className="grid md:grid-cols-3 gap-4">
{isLoading && <UpdatePlaceHolder />}
{data?.updates.slice(0, 3).map((update) => (
<UpdateCard update={update} key={update.id} />

View File

@@ -4,19 +4,19 @@ interface PageProps {
title: ReactNode;
subtitle: ReactNode;
actions: ReactNode;
content: ReactNode;
children: ReactNode;
}
export const Page = ({ title, subtitle, actions, content }: PageProps) => (
export const Page = ({ title, subtitle, actions, children }: PageProps) => (
<div className="flex flex-col gap-12">
<div className="flex flex-col gap-6 lg:flex-row lg:gap-0 justify-between">
<div className="flex flex-col">
{title}
<h1 className="text-4xl">{title}</h1>
{subtitle}
</div>
{actions}
</div>
{content}
{children}
</div>
);
@@ -28,7 +28,7 @@ interface SectionProps {
}
export const Section = ({ title, icon, actions, children }: SectionProps) => (
<div className="flex flex-col">
<div className="flex flex-col gap-2">
<div className="flex justify-between">
<div className="flex items-center gap-2 text-muted-foreground">
{icon}

View File

@@ -13,6 +13,7 @@ export const Resource = ({ title, info, actions, children }: ResourceProps) => (
title={<h1 className="text-4xl">{title}</h1>}
subtitle={<h2 className="text-md">{info}</h2>}
actions={actions}
content={children}
/>
>
{children}
</Page>
);

View File

@@ -47,11 +47,10 @@ export const Resources = ({
<NewDeployment open={open} set={setOpen} />
</div>
}
content={
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
{components(search)}
</div>
}
/>
>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
{components(search)}
</div>
</Page>
);
};

View File

@@ -0,0 +1,68 @@
import {
ColorType,
IChartApi,
ISeriesApi,
Time,
createChart,
} from "lightweight-charts";
import { useEffect, useRef } from "react";
import { useRead } from "@hooks";
export const BuildChart = () => {
const container_ref = useRef<HTMLDivElement>(null);
const line_ref = useRef<IChartApi>();
const series_ref = useRef<ISeriesApi<"Histogram">>();
const { data } = useRead("GetBuildMonthlyStats", {});
const handleResize = () =>
line_ref.current?.applyOptions({
width: container_ref.current?.clientWidth,
});
useEffect(() => {
if (!data) return;
if (line_ref.current) line_ref.current.remove();
const init = () => {
if (!container_ref.current) return;
line_ref.current = createChart(container_ref.current, {
width: container_ref.current.clientWidth,
height: container_ref.current.clientHeight,
layout: {
background: { type: ColorType.Solid, color: "transparent" },
textColor: "grey",
fontSize: 12,
},
grid: {
horzLines: { color: "transparent" },
vertLines: { color: "transparent" },
},
handleScale: false,
handleScroll: false,
});
line_ref.current.timeScale().fitContent();
series_ref.current = line_ref.current.addHistogramSeries({
priceLineVisible: false,
});
const max = data.days.reduce((m, c) => Math.max(m, c.time), 0);
series_ref.current.setData(
data.days.map((d) => ({
time: (d.ts / 1000) as Time,
value: d.count,
color:
d.time > max * 0.7
? "darkred"
: d.time > max * 0.35
? "darkorange"
: "darkgreen",
})) ?? []
);
};
init();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [data]);
return <div className="w-full max-w-full h-full" ref={container_ref} />;
};

View File

@@ -14,11 +14,11 @@ export const DeploymentsChart = () => {
return (
<Link to="/deployments" className="w-full">
<Card className="pb-4" hoverable>
<Card hoverable>
<CardHeader className="flex-row justify-between items-center">
<CardTitle>Deployments</CardTitle>
</CardHeader>
<CardContent className="flex gap-4 items-center w-full">
<CardContent className="flex gap-4 items-center w-full h-[200px]">
<div className="flex flex-col gap-2 text-muted-foreground w-full">
<CardDescription>
<span className="text-green-500 font-bold">
@@ -47,7 +47,9 @@ export const DeploymentsChart = () => {
</div>
<div className="flex justify-end items-center w-full">
<PieChart
className="w-20 h-20"
className="w-32"
segmentsShift={0.5}
lineWidth={35}
data={[
{
color: "#22C55E",

View File

@@ -20,6 +20,7 @@ import { Types } from "@monitor/client";
import { NewBuilder } from "@resources/builder/new";
import { ResourceTarget } from "@monitor/client/dist/types";
import { BuilderCard } from "@resources/builder";
import { Section } from "@layouts/page";
const NewResource = () => {
const [open, set] = useState<Types.ResourceTarget["type"] | false>(false);
@@ -68,6 +69,23 @@ export const RecentlyViewed = () => {
const user = useUser().data;
const recents = useRead("GetUser", {}).data?.recently_viewed;
return (
<Section
title="Recently Viewed"
icon={<History className="w-4 h-4" />}
actions=""
>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
{recents?.map(({ type, id }) => {
if (type === "Deployment") return <DeploymentCard key={id} id={id} />;
if (type === "Build") return <BuildCard key={id} id={id} />;
if (type === "Server") return <ServerCard key={id} id={id} />;
if (type === "Builder") return <BuilderCard key={id} id={id} />;
})}
</div>
</Section>
);
return (
<div className="w-full flex flex-col gap-12">
<div className="flex justify-between">
@@ -82,14 +100,6 @@ export const RecentlyViewed = () => {
</div>
<NewResource />
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
{recents?.map(({ type, id }) => {
if (type === "Deployment") return <DeploymentCard key={id} id={id} />;
if (type === "Build") return <BuildCard key={id} id={id} />;
if (type === "Server") return <ServerCard key={id} id={id} />;
if (type === "Builder") return <BuilderCard key={id} id={id} />;
})}
</div>
</div>
);
};

View File

@@ -5,14 +5,13 @@ import {
CardDescription,
CardContent,
} from "@ui/card";
import { WithLoading } from "@components/util";
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("ListServers", {});
const { data } = useRead("ListServers", {});
const running = data?.filter((d) => d.status === ServerStatus.Ok).length;
const stopped = data?.filter((d) => d.status === ServerStatus.NotOk).length;
@@ -22,52 +21,52 @@ export const ServersChart = () => {
return (
<Link to="/servers" className="w-full">
<Card className="pb-4" hoverable>
<Card hoverable>
<CardHeader className="flex-row justify-between items-center">
<CardTitle>Servers</CardTitle>
</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 className="flex gap-4 items-center w-full h-[200px]">
<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-32"
segmentsShift={0.15}
lineWidth={30}
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>
</CardContent>
</Card>
</Link>

View File

@@ -1,42 +1,71 @@
import { Card, CardHeader, CardTitle } from "@ui/card";
import { Card, CardContent, CardHeader, CardTitle } from "@ui/card";
import { ServersChart } from "./components/servers-chart";
import { DeploymentsChart } from "./components/deployments-chart";
import { Link } from "react-router-dom";
import { RecentlyViewed } from "./components/recently-viewed";
import { Box } from "lucide-react";
import { BuildChart } from "./components/builds-chart";
import { Page, Section } from "@layouts/page";
import { useUser } from "@hooks";
export const Dashboard = () => {
return (
<div className="flex flex-col gap-24">
<RecentlyViewed />
<div className="flex flex-col gap-6 w-full">
<div className="flex items-center gap-2 text-muted-foreground">
<Box className="w-4 h-4" />
<h2 className="text-xl">My Resources</h2>
</div>
<div className="flex flex-col md:flex-row gap-4">
<div className="flex flex-col md:flex-row gap-4 w-full h-fit">
<DeploymentsChart />
<ServersChart />
</div>
<div className="flex gap-4 w-full h-fit">
<Link to="/builds" className="w-full max-w-[50%] h-full">
<Card hoverable>
<CardHeader>
<CardTitle>Builds</CardTitle>
</CardHeader>
</Card>
</Link>
<Link to="/builders" className="w-full max-w-[50%] h-full">
<Card hoverable>
<CardHeader>
<CardTitle>Builders</CardTitle>
</CardHeader>
</Card>
</Link>
</div>
const DashboardTitle = () => {
const user = useUser().data;
return <>Hello, {user?.username}.</>;
};
const Builds = () => (
<Link to="/builds" className="w-full">
<Card hoverable>
<CardHeader>
<CardTitle>Builds</CardTitle>
</CardHeader>
<CardContent className="h-[200px]">
<BuildChart />
</CardContent>
</Card>
</Link>
);
const MyResources = () => (
<Section title="My Resources" icon={<Box className="w-4 h-4" />} actions="">
<div className="flex flex-col gap-4">
<div className="flex flex-col lg:flex-row gap-4">
<div className="flex flex-col md:flex-row gap-4 w-full">
<DeploymentsChart />
<ServersChart />
</div>
<Builds />
</div>
<div className="flex gap-4">
<Link to="/builders" className="w-full h-full">
<Card hoverable>
<CardHeader>
<CardTitle>Builders</CardTitle>
</CardHeader>
</Card>
</Link>
<Link to="/alerters" className="w-full h-full">
<Card hoverable>
<CardHeader>
<CardTitle>alerters</CardTitle>
</CardHeader>
</Card>
</Link>
<Link to="/repos" className="w-full h-full">
<Card hoverable>
<CardHeader>
<CardTitle>repos</CardTitle>
</CardHeader>
</Card>
</Link>
</div>
</div>
);
};
</Section>
);
export const Dashboard = () => (
<Page title={<DashboardTitle />} subtitle="" actions="">
<RecentlyViewed />
<MyResources />
</Page>
);

View File

@@ -41,7 +41,7 @@ const TabsContent = React.forwardRef<
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
"ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}