forked from github-starred/komodo
work on dashboard 2
This commit is contained in:
@@ -363,9 +363,11 @@ export const SystemCommand = ({
|
|||||||
export const AddExtraArgMenu = ({
|
export const AddExtraArgMenu = ({
|
||||||
onSelect,
|
onSelect,
|
||||||
type,
|
type,
|
||||||
|
disabled,
|
||||||
}: {
|
}: {
|
||||||
onSelect: (suggestion: string) => void;
|
onSelect: (suggestion: string) => void;
|
||||||
type: "Deployment" | "Build";
|
type: "Deployment" | "Build";
|
||||||
|
disabled?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
@@ -376,6 +378,7 @@ export const AddExtraArgMenu = ({
|
|||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="flex items-center gap-2 w-[200px]"
|
className="flex items-center gap-2 w-[200px]"
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<PlusCircle className="w-4 h-4" /> Add Extra Arg
|
<PlusCircle className="w-4 h-4" /> Add Extra Arg
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ const useAlerter = (id?: string) =>
|
|||||||
useRead("ListAlerters", {}).data?.find((d) => d.id === id);
|
useRead("ListAlerters", {}).data?.find((d) => d.id === id);
|
||||||
|
|
||||||
export const AlerterComponents: RequiredResourceComponents = {
|
export const AlerterComponents: RequiredResourceComponents = {
|
||||||
|
list_item: (id) => useAlerter(id),
|
||||||
|
|
||||||
Dashboard: () => {
|
Dashboard: () => {
|
||||||
const alerters_count = useRead("ListAlerters", {}).data?.length;
|
const alerters_count = useRead("ListAlerters", {}).data?.length;
|
||||||
return (
|
return (
|
||||||
@@ -91,7 +93,6 @@ export const AlerterComponents: RequiredResourceComponents = {
|
|||||||
Table: AlerterTable,
|
Table: AlerterTable,
|
||||||
|
|
||||||
Name: ({ id }: { id: string }) => <>{useAlerter(id)?.name}</>,
|
Name: ({ id }: { id: string }) => <>{useAlerter(id)?.name}</>,
|
||||||
name: (id) => useAlerter(id)?.name,
|
|
||||||
|
|
||||||
Icon: () => <AlarmClock className="w-4 h-4" />,
|
Icon: () => <AlarmClock className="w-4 h-4" />,
|
||||||
BigIcon: () => <AlarmClock className="w-8 h-8" />,
|
BigIcon: () => <AlarmClock className="w-8 h-8" />,
|
||||||
|
|||||||
105
frontend/src/components/resources/build/chart.tsx
Normal file
105
frontend/src/components/resources/build/chart.tsx
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import {
|
||||||
|
ColorType,
|
||||||
|
IChartApi,
|
||||||
|
ISeriesApi,
|
||||||
|
Time,
|
||||||
|
createChart,
|
||||||
|
} from "lightweight-charts";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import { useRead } from "@lib/hooks";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@ui/card";
|
||||||
|
import { Hammer } from "lucide-react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { convertTsMsToLocalUnixTsInSecs } from "@lib/utils";
|
||||||
|
|
||||||
|
export const BuildChart = () => {
|
||||||
|
const container_ref = useRef<HTMLDivElement>(null);
|
||||||
|
const line_ref = useRef<IChartApi>();
|
||||||
|
const series_ref = useRef<ISeriesApi<"Histogram">>();
|
||||||
|
const build_stats = useRead("GetBuildMonthlyStats", {}).data;
|
||||||
|
const summary = useRead("GetBuildsSummary", {}).data;
|
||||||
|
|
||||||
|
const handleResize = () =>
|
||||||
|
line_ref.current?.applyOptions({
|
||||||
|
width: container_ref.current?.clientWidth,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!build_stats) return;
|
||||||
|
if (line_ref.current) line_ref.current.remove();
|
||||||
|
const init = () => {
|
||||||
|
if (!container_ref.current) return;
|
||||||
|
|
||||||
|
// INIT LINE
|
||||||
|
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();
|
||||||
|
|
||||||
|
// INIT SERIES
|
||||||
|
series_ref.current = line_ref.current.addHistogramSeries({
|
||||||
|
priceLineVisible: false,
|
||||||
|
});
|
||||||
|
const max = build_stats.days.reduce((m, c) => Math.max(m, c.time), 0);
|
||||||
|
series_ref.current.setData(
|
||||||
|
build_stats.days.map((d) => ({
|
||||||
|
time: convertTsMsToLocalUnixTsInSecs(d.ts) as Time,
|
||||||
|
value: d.count,
|
||||||
|
color:
|
||||||
|
d.time > max * 0.7
|
||||||
|
? "darkred"
|
||||||
|
: d.time > max * 0.35
|
||||||
|
? "darkorange"
|
||||||
|
: "darkgreen",
|
||||||
|
})) ?? []
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run the effect
|
||||||
|
init();
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
|
};
|
||||||
|
}, [build_stats]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to="/builds" className="w-full">
|
||||||
|
<Card className="hover:bg-accent/50 transition-colors cursor-pointer">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<div>
|
||||||
|
<CardTitle>Builds</CardTitle>
|
||||||
|
<CardDescription className="flex gap-2">
|
||||||
|
<div>{summary?.total} Total</div> |{" "}
|
||||||
|
<div>{build_stats?.total_time.toFixed(2)} Hours</div>
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<Hammer className="w-4 h-4" />
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="hidden xl:flex h-[200px]">
|
||||||
|
<div className="w-full max-w-full h-full" ref={container_ref} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -166,6 +166,7 @@ export const BuildConfig = ({ id, titleOther }: { id: string; titleOther: ReactN
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
import {
|
|
||||||
ColorType,
|
|
||||||
IChartApi,
|
|
||||||
ISeriesApi,
|
|
||||||
Time,
|
|
||||||
createChart,
|
|
||||||
} from "lightweight-charts";
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { useRead } from "@lib/hooks";
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -14,90 +5,111 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@ui/card";
|
} from "@ui/card";
|
||||||
|
import { PieChart } from "react-minimal-pie-chart";
|
||||||
|
import { useRead } from "@lib/hooks";
|
||||||
import { Hammer } from "lucide-react";
|
import { Hammer } from "lucide-react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { convertTsMsToLocalUnixTsInSecs } from "@lib/utils";
|
import { cn } from "@lib/utils";
|
||||||
|
import {
|
||||||
|
hex_color_by_intention,
|
||||||
|
text_color_class_by_intention,
|
||||||
|
} from "@lib/color";
|
||||||
|
|
||||||
export const BuildChart = () => {
|
export const BuildDashboard = () => {
|
||||||
const container_ref = useRef<HTMLDivElement>(null);
|
|
||||||
const line_ref = useRef<IChartApi>();
|
|
||||||
const series_ref = useRef<ISeriesApi<"Histogram">>();
|
|
||||||
const build_stats = useRead("GetBuildMonthlyStats", {}).data;
|
|
||||||
const summary = useRead("GetBuildsSummary", {}).data;
|
const summary = useRead("GetBuildsSummary", {}).data;
|
||||||
|
|
||||||
const handleResize = () =>
|
|
||||||
line_ref.current?.applyOptions({
|
|
||||||
width: container_ref.current?.clientWidth,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!build_stats) return;
|
|
||||||
if (line_ref.current) line_ref.current.remove();
|
|
||||||
const init = () => {
|
|
||||||
if (!container_ref.current) return;
|
|
||||||
|
|
||||||
// INIT LINE
|
|
||||||
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();
|
|
||||||
|
|
||||||
// INIT SERIES
|
|
||||||
series_ref.current = line_ref.current.addHistogramSeries({
|
|
||||||
priceLineVisible: false,
|
|
||||||
});
|
|
||||||
const max = build_stats.days.reduce((m, c) => Math.max(m, c.time), 0);
|
|
||||||
series_ref.current.setData(
|
|
||||||
build_stats.days.map((d) => ({
|
|
||||||
time: convertTsMsToLocalUnixTsInSecs(d.ts) as Time,
|
|
||||||
value: d.count,
|
|
||||||
color:
|
|
||||||
d.time > max * 0.7
|
|
||||||
? "darkred"
|
|
||||||
: d.time > max * 0.35
|
|
||||||
? "darkorange"
|
|
||||||
: "darkgreen",
|
|
||||||
})) ?? []
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run the effect
|
|
||||||
init();
|
|
||||||
window.addEventListener("resize", handleResize);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("resize", handleResize);
|
|
||||||
};
|
|
||||||
}, [build_stats]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to="/builds" className="w-full">
|
<Link to="/builds">
|
||||||
<Card className="hover:bg-accent/50 transition-colors cursor-pointer">
|
<Card className="hover:bg-accent/50 transition-colors cursor-pointer w-fit">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
<CardTitle>Builds</CardTitle>
|
<CardTitle>Builds</CardTitle>
|
||||||
<CardDescription className="flex gap-2">
|
<CardDescription>{summary?.total} Total</CardDescription>
|
||||||
<div>{summary?.total} Total</div> |{" "}
|
|
||||||
<div>{build_stats?.total_time.toFixed(2)} Hours</div>
|
|
||||||
</CardDescription>
|
|
||||||
</div>
|
</div>
|
||||||
<Hammer className="w-4 h-4" />
|
<Hammer className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="hidden xl:flex h-[200px]">
|
<CardContent className="hidden xl:flex h-[200px] items-center justify-between gap-4">
|
||||||
<div className="w-full max-w-full h-full" ref={container_ref} />
|
<div className="flex flex-col gap-2 text-muted-foreground w-full text-nowrap">
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Good"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.ok}{" "}
|
||||||
|
</span>
|
||||||
|
Ok
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Warning"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.building}{" "}
|
||||||
|
</span>
|
||||||
|
Building
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Critical"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.failed}{" "}
|
||||||
|
</span>
|
||||||
|
Failed
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Unknown"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.unknown}{" "}
|
||||||
|
</span>
|
||||||
|
Unknown
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end items-center w-full">
|
||||||
|
<PieChart
|
||||||
|
className="w-32 h-32"
|
||||||
|
radius={42}
|
||||||
|
lineWidth={30}
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Good"),
|
||||||
|
value: summary?.ok ?? 0,
|
||||||
|
title: "ok",
|
||||||
|
key: "ok",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Warning"),
|
||||||
|
value: summary?.building ?? 0,
|
||||||
|
title: "building",
|
||||||
|
key: "building",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Critical"),
|
||||||
|
value: summary?.failed ?? 0,
|
||||||
|
title: "failed",
|
||||||
|
key: "failed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Unknown"),
|
||||||
|
value: summary?.unknown ?? 0,
|
||||||
|
title: "unknown",
|
||||||
|
key: "unknown",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useRead } from "@lib/hooks";
|
|||||||
import { RequiredResourceComponents } from "@types";
|
import { RequiredResourceComponents } from "@types";
|
||||||
import { FolderGit, Hammer } from "lucide-react";
|
import { FolderGit, Hammer } from "lucide-react";
|
||||||
import { BuildConfig } from "./config";
|
import { BuildConfig } from "./config";
|
||||||
import { BuildChart } from "./dashboard";
|
import { BuildDashboard } from "./dashboard";
|
||||||
import { BuildTable } from "./table";
|
import { BuildTable } from "./table";
|
||||||
import { DeleteResource, NewResource } from "../common";
|
import { DeleteResource, NewResource } from "../common";
|
||||||
import { DeploymentTable } from "../deployment/table";
|
import { DeploymentTable } from "../deployment/table";
|
||||||
@@ -83,14 +83,15 @@ const ConfigOrDeployments = ({ id }: { id: string }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const BuildComponents: RequiredResourceComponents = {
|
export const BuildComponents: RequiredResourceComponents = {
|
||||||
Dashboard: BuildChart,
|
list_item: (id) => useBuild(id),
|
||||||
|
|
||||||
|
Dashboard: BuildDashboard,
|
||||||
|
|
||||||
New: () => <NewResource type="Build" />,
|
New: () => <NewResource type="Build" />,
|
||||||
|
|
||||||
Table: BuildTable,
|
Table: BuildTable,
|
||||||
|
|
||||||
Name: ({ id }) => <>{useBuild(id)?.name}</>,
|
Name: ({ id }) => <>{useBuild(id)?.name}</>,
|
||||||
name: (id) => useBuild(id)?.name,
|
|
||||||
|
|
||||||
Icon: ({ id }) => <BuildIcon id={id} size={4} />,
|
Icon: ({ id }) => <BuildIcon id={id} size={4} />,
|
||||||
BigIcon: ({ id }) => <BuildIcon id={id} size={8} />,
|
BigIcon: ({ id }) => <BuildIcon id={id} size={8} />,
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ export const BuilderInstanceType = ({ id }: { id: string }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const BuilderComponents: RequiredResourceComponents = {
|
export const BuilderComponents: RequiredResourceComponents = {
|
||||||
|
list_item: (id) => useBuilder(id),
|
||||||
|
|
||||||
Dashboard: () => {
|
Dashboard: () => {
|
||||||
const builders_count = useRead("ListBuilders", {}).data?.length;
|
const builders_count = useRead("ListBuilders", {}).data?.length;
|
||||||
return (
|
return (
|
||||||
@@ -65,8 +67,9 @@ export const BuilderComponents: RequiredResourceComponents = {
|
|||||||
<NewLayout
|
<NewLayout
|
||||||
entityType="Builder"
|
entityType="Builder"
|
||||||
onSuccess={async () => {
|
onSuccess={async () => {
|
||||||
if (!type) return
|
if (!type) return;
|
||||||
const id = (await mutateAsync({ name, config: { type, params: {} } }))._id?.$oid!;
|
const id = (await mutateAsync({ name, config: { type, params: {} } }))
|
||||||
|
._id?.$oid!;
|
||||||
nav(`/builders/${id}`);
|
nav(`/builders/${id}`);
|
||||||
}}
|
}}
|
||||||
enabled={!!name && !!type}
|
enabled={!!name && !!type}
|
||||||
@@ -103,7 +106,6 @@ export const BuilderComponents: RequiredResourceComponents = {
|
|||||||
Table: BuilderTable,
|
Table: BuilderTable,
|
||||||
|
|
||||||
Name: ({ id }: { id: string }) => <>{useBuilder(id)?.name}</>,
|
Name: ({ id }: { id: string }) => <>{useBuilder(id)?.name}</>,
|
||||||
name: (id) => useBuilder(id)?.name,
|
|
||||||
|
|
||||||
Icon: () => <Factory className="w-4 h-4" />,
|
Icon: () => <Factory className="w-4 h-4" />,
|
||||||
BigIcon: () => <Factory className="w-8 h-8" />,
|
BigIcon: () => <Factory className="w-8 h-8" />,
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ import { NewLayout } from "@components/layouts";
|
|||||||
import { Types } from "@monitor/client";
|
import { Types } from "@monitor/client";
|
||||||
import { ConfigItem, DoubleInput } from "@components/config/util";
|
import { ConfigItem, DoubleInput } from "@components/config/util";
|
||||||
import { usableResourcePath } from "@lib/utils";
|
import { usableResourcePath } from "@lib/utils";
|
||||||
|
import { Card } from "@ui/card";
|
||||||
|
import { TagsWithBadge } from "@components/tags";
|
||||||
|
|
||||||
export const ResourceDescription = ({
|
export const ResourceDescription = ({
|
||||||
type,
|
type,
|
||||||
@@ -87,7 +89,7 @@ export const ResourceSelector = ({
|
|||||||
selected: string | undefined;
|
selected: string | undefined;
|
||||||
onSelect?: (id: string) => void;
|
onSelect?: (id: string) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
align?: "start" | "center" | "end"
|
align?: "start" | "center" | "end";
|
||||||
}) => {
|
}) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
@@ -311,11 +313,7 @@ export const LabelsConfig = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const CopyGithubWebhook = ({
|
export const CopyGithubWebhook = ({ path }: { path: string }) => {
|
||||||
path,
|
|
||||||
}: {
|
|
||||||
path: string;
|
|
||||||
}) => {
|
|
||||||
const base_url = useRead("GetCoreInfo", {}).data?.github_webhook_base_url;
|
const base_url = useRead("GetCoreInfo", {}).data?.github_webhook_base_url;
|
||||||
const url = base_url + "/listener/github" + path;
|
const url = base_url + "/listener/github" + path;
|
||||||
return (
|
return (
|
||||||
@@ -335,7 +333,7 @@ export const ServerSelector = ({
|
|||||||
selected: string | undefined;
|
selected: string | undefined;
|
||||||
set: (input: Partial<Types.DeploymentConfig>) => void;
|
set: (input: Partial<Types.DeploymentConfig>) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
align?: "start" | "center" | "end"
|
align?: "start" | "center" | "end";
|
||||||
}) => (
|
}) => (
|
||||||
<ConfigItem label="Server">
|
<ConfigItem label="Server">
|
||||||
<ResourceSelector
|
<ResourceSelector
|
||||||
@@ -346,4 +344,28 @@ export const ServerSelector = ({
|
|||||||
align={align}
|
align={align}
|
||||||
/>
|
/>
|
||||||
</ConfigItem>
|
</ConfigItem>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const RecentCard = ({
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
}: {
|
||||||
|
type: UsableResource;
|
||||||
|
id: string;
|
||||||
|
}) => {
|
||||||
|
const Components = ResourceComponents[type];
|
||||||
|
const tags = Components.list_item(id)?.tags;
|
||||||
|
return (
|
||||||
|
<Link to={`${usableResourcePath(type)}/${id}`} className="h-full">
|
||||||
|
<Card className="h-full px-6 py-4 flex flex-col justify-between hover:bg-accent/50 transition-colors cursor-pointer">
|
||||||
|
<div className="flex items-center justify-between w-full">
|
||||||
|
<Components.Name id={id} />
|
||||||
|
<Components.Icon id={id} />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-end justify-end gap-2 w-full">
|
||||||
|
<TagsWithBadge tag_ids={tags} />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ export const DeploymentConfig = ({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ export const DeploymentsChart = () => {
|
|||||||
const summary = useRead("GetDeploymentsSummary", {}).data;
|
const summary = useRead("GetDeploymentsSummary", {}).data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to="/deployments" className="w-full">
|
<Link to="/deployments">
|
||||||
<Card className="hover:bg-accent/50 transition-colors cursor-pointer">
|
<Card className="hover:bg-accent/50 transition-colors cursor-pointer w-fit">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -30,9 +30,9 @@ export const DeploymentsChart = () => {
|
|||||||
<Rocket className="w-4 h-4" />
|
<Rocket className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="hidden xl:flex h-[200px] items-center justify-between">
|
<CardContent className="hidden xl:flex h-[200px] items-center justify-between gap-4">
|
||||||
<div className="flex flex-col gap-2 text-muted-foreground w-full">
|
<div className="flex flex-col gap-2 text-muted-foreground w-full text-nowrap">
|
||||||
<CardDescription>
|
<CardDescription className="flex items-center gap-2">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
text_color_class_by_intention("Good"),
|
text_color_class_by_intention("Good"),
|
||||||
@@ -43,7 +43,7 @@ export const DeploymentsChart = () => {
|
|||||||
</span>
|
</span>
|
||||||
Running
|
Running
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
<CardDescription>
|
<CardDescription className="flex items-center gap-2">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
text_color_class_by_intention("Critical"),
|
text_color_class_by_intention("Critical"),
|
||||||
@@ -54,7 +54,7 @@ export const DeploymentsChart = () => {
|
|||||||
</span>
|
</span>
|
||||||
Stopped
|
Stopped
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
<CardDescription>
|
<CardDescription className="flex items-center gap-2">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
text_color_class_by_intention("Neutral"),
|
text_color_class_by_intention("Neutral"),
|
||||||
@@ -65,7 +65,7 @@ export const DeploymentsChart = () => {
|
|||||||
</span>
|
</span>
|
||||||
Not Deployed
|
Not Deployed
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
<CardDescription>
|
<CardDescription className="flex items-center gap-2">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
text_color_class_by_intention("Unknown"),
|
text_color_class_by_intention("Unknown"),
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ const DeploymentIcon = ({ id, size }: { id?: string; size: number }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DeploymentComponents: RequiredResourceComponents = {
|
export const DeploymentComponents: RequiredResourceComponents = {
|
||||||
|
list_item: (id) => useDeployment(id),
|
||||||
|
|
||||||
Dashboard: DeploymentsChart,
|
Dashboard: DeploymentsChart,
|
||||||
|
|
||||||
New: () => <NewResource type="Deployment" />,
|
New: () => <NewResource type="Deployment" />,
|
||||||
@@ -109,7 +111,6 @@ export const DeploymentComponents: RequiredResourceComponents = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Name: ({ id }) => <>{useDeployment(id)?.name}</>,
|
Name: ({ id }) => <>{useDeployment(id)?.name}</>,
|
||||||
name: (id) => useDeployment(id)?.name,
|
|
||||||
|
|
||||||
Icon: ({ id }) => <DeploymentIcon id={id} size={4} />,
|
Icon: ({ id }) => <DeploymentIcon id={id} size={4} />,
|
||||||
BigIcon: ({ id }) => <DeploymentIcon id={id} size={8} />,
|
BigIcon: ({ id }) => <DeploymentIcon id={id} size={8} />,
|
||||||
|
|||||||
117
frontend/src/components/resources/procedure/dashboard.tsx
Normal file
117
frontend/src/components/resources/procedure/dashboard.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@ui/card";
|
||||||
|
import { PieChart } from "react-minimal-pie-chart";
|
||||||
|
import { useRead } from "@lib/hooks";
|
||||||
|
import { Route } from "lucide-react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { cn } from "@lib/utils";
|
||||||
|
import {
|
||||||
|
hex_color_by_intention,
|
||||||
|
text_color_class_by_intention,
|
||||||
|
} from "@lib/color";
|
||||||
|
|
||||||
|
export const ProcedureDashboard = () => {
|
||||||
|
const summary = useRead("GetProceduresSummary", {}).data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to="/procedures">
|
||||||
|
<Card className="hover:bg-accent/50 transition-colors cursor-pointer w-fit">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<div>
|
||||||
|
<CardTitle>Procedures</CardTitle>
|
||||||
|
<CardDescription>{summary?.total} Total</CardDescription>
|
||||||
|
</div>
|
||||||
|
<Route className="w-4 h-4" />
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="hidden xl:flex h-[200px] items-center justify-between gap-4">
|
||||||
|
<div className="flex flex-col gap-2 text-muted-foreground w-full text-nowrap">
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Good"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.ok}{" "}
|
||||||
|
</span>
|
||||||
|
Ok
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Warning"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.running}{" "}
|
||||||
|
</span>
|
||||||
|
Running
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Critical"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.failed}{" "}
|
||||||
|
</span>
|
||||||
|
Failed
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Unknown"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.unknown}{" "}
|
||||||
|
</span>
|
||||||
|
Unknown
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end items-center w-full">
|
||||||
|
<PieChart
|
||||||
|
className="w-32 h-32"
|
||||||
|
radius={42}
|
||||||
|
lineWidth={30}
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Good"),
|
||||||
|
value: summary?.ok ?? 0,
|
||||||
|
title: "ok",
|
||||||
|
key: "ok",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Warning"),
|
||||||
|
value: summary?.running ?? 0,
|
||||||
|
title: "running",
|
||||||
|
key: "running",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Critical"),
|
||||||
|
value: summary?.failed ?? 0,
|
||||||
|
title: "failed",
|
||||||
|
key: "failed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Unknown"),
|
||||||
|
value: summary?.unknown ?? 0,
|
||||||
|
title: "unknown",
|
||||||
|
key: "unknown",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -7,13 +7,18 @@ import { Link } from "react-router-dom";
|
|||||||
import { ProcedureConfig } from "./config";
|
import { ProcedureConfig } from "./config";
|
||||||
import { ProcedureTable } from "./table";
|
import { ProcedureTable } from "./table";
|
||||||
import { DeleteResource, NewResource } from "../common";
|
import { DeleteResource, NewResource } from "../common";
|
||||||
import { bg_color_class_by_intention, procedure_state_intention } from "@lib/color";
|
import {
|
||||||
|
bg_color_class_by_intention,
|
||||||
|
procedure_state_intention,
|
||||||
|
} from "@lib/color";
|
||||||
import { cn } from "@lib/utils";
|
import { cn } from "@lib/utils";
|
||||||
|
|
||||||
const useProcedure = (id?: string) =>
|
const useProcedure = (id?: string) =>
|
||||||
useRead("ListProcedures", {}).data?.find((d) => d.id === id);
|
useRead("ListProcedures", {}).data?.find((d) => d.id === id);
|
||||||
|
|
||||||
export const ProcedureComponents: RequiredResourceComponents = {
|
export const ProcedureComponents: RequiredResourceComponents = {
|
||||||
|
list_item: (id) => useProcedure(id),
|
||||||
|
|
||||||
Dashboard: () => {
|
Dashboard: () => {
|
||||||
const procedure_count = useRead("ListProcedures", {}).data?.length;
|
const procedure_count = useRead("ListProcedures", {}).data?.length;
|
||||||
return (
|
return (
|
||||||
@@ -38,7 +43,6 @@ export const ProcedureComponents: RequiredResourceComponents = {
|
|||||||
Table: ProcedureTable,
|
Table: ProcedureTable,
|
||||||
|
|
||||||
Name: ({ id }) => <>{useProcedure(id)?.name}</>,
|
Name: ({ id }) => <>{useProcedure(id)?.name}</>,
|
||||||
name: (id) => useProcedure(id)?.name,
|
|
||||||
|
|
||||||
Icon: () => <Route className="w-4" />,
|
Icon: () => <Route className="w-4" />,
|
||||||
BigIcon: () => <Route className="w-8" />,
|
BigIcon: () => <Route className="w-8" />,
|
||||||
@@ -46,7 +50,9 @@ export const ProcedureComponents: RequiredResourceComponents = {
|
|||||||
Status: {
|
Status: {
|
||||||
State: ({ id }) => {
|
State: ({ id }) => {
|
||||||
let state = useProcedure(id)?.info.state;
|
let state = useProcedure(id)?.info.state;
|
||||||
const color = bg_color_class_by_intention(procedure_state_intention(state));
|
const color = bg_color_class_by_intention(
|
||||||
|
procedure_state_intention(state)
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Card className={cn("w-fit", color)}>
|
<Card className={cn("w-fit", color)}>
|
||||||
<CardHeader className="py-0 px-2">{state}</CardHeader>
|
<CardHeader className="py-0 px-2">{state}</CardHeader>
|
||||||
|
|||||||
117
frontend/src/components/resources/repo/dashboard.tsx
Normal file
117
frontend/src/components/resources/repo/dashboard.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@ui/card";
|
||||||
|
import { PieChart } from "react-minimal-pie-chart";
|
||||||
|
import { useRead } from "@lib/hooks";
|
||||||
|
import { GitBranch } from "lucide-react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { cn } from "@lib/utils";
|
||||||
|
import {
|
||||||
|
hex_color_by_intention,
|
||||||
|
text_color_class_by_intention,
|
||||||
|
} from "@lib/color";
|
||||||
|
|
||||||
|
export const RepoDashboard = () => {
|
||||||
|
const summary = useRead("GetReposSummary", {}).data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to="/repos">
|
||||||
|
<Card className="hover:bg-accent/50 transition-colors cursor-pointer w-fit">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<div>
|
||||||
|
<CardTitle>Repos</CardTitle>
|
||||||
|
<CardDescription>{summary?.total} Total</CardDescription>
|
||||||
|
</div>
|
||||||
|
<GitBranch className="w-4 h-4" />
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="hidden xl:flex h-[200px] items-center justify-between gap-4">
|
||||||
|
<div className="flex flex-col gap-2 text-muted-foreground w-full text-nowrap">
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Good"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.ok}{" "}
|
||||||
|
</span>
|
||||||
|
Ok
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Warning"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{(summary?.cloning ?? 0) + (summary?.pulling ?? 0)}{" "}
|
||||||
|
</span>
|
||||||
|
Pulling
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Critical"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.failed}{" "}
|
||||||
|
</span>
|
||||||
|
Failed
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
text_color_class_by_intention("Unknown"),
|
||||||
|
"font-bold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{summary?.unknown}{" "}
|
||||||
|
</span>
|
||||||
|
Unknown
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end items-center w-full">
|
||||||
|
<PieChart
|
||||||
|
className="w-32 h-32"
|
||||||
|
radius={42}
|
||||||
|
lineWidth={30}
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Good"),
|
||||||
|
value: summary?.ok ?? 0,
|
||||||
|
title: "ok",
|
||||||
|
key: "ok",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Warning"),
|
||||||
|
value: (summary?.cloning ?? 0) + (summary?.pulling ?? 0),
|
||||||
|
title: "pulling",
|
||||||
|
key: "pulling",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Critical"),
|
||||||
|
value: summary?.failed ?? 0,
|
||||||
|
title: "failed",
|
||||||
|
key: "failed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: hex_color_by_intention("Unknown"),
|
||||||
|
value: summary?.unknown ?? 0,
|
||||||
|
title: "unknown",
|
||||||
|
key: "unknown",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -27,6 +27,8 @@ const RepoIcon = ({ id, size }: { id?: string; size: number }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RepoComponents: RequiredResourceComponents = {
|
export const RepoComponents: RequiredResourceComponents = {
|
||||||
|
list_item: (id) => useRepo(id),
|
||||||
|
|
||||||
Dashboard: () => {
|
Dashboard: () => {
|
||||||
const repo_count = useRead("ListRepos", {}).data?.length;
|
const repo_count = useRead("ListRepos", {}).data?.length;
|
||||||
return (
|
return (
|
||||||
@@ -51,7 +53,6 @@ export const RepoComponents: RequiredResourceComponents = {
|
|||||||
Table: RepoTable,
|
Table: RepoTable,
|
||||||
|
|
||||||
Name: ({ id }) => <>{useRepo(id)?.name}</>,
|
Name: ({ id }) => <>{useRepo(id)?.name}</>,
|
||||||
name: (id) => useRepo(id)?.name,
|
|
||||||
|
|
||||||
Icon: ({ id }) => <RepoIcon id={id} size={4} />,
|
Icon: ({ id }) => <RepoIcon id={id} size={4} />,
|
||||||
BigIcon: ({ id }) => <RepoIcon id={id} size={8} />,
|
BigIcon: ({ id }) => <RepoIcon id={id} size={8} />,
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ export const useServerTemplate = (id?: string) =>
|
|||||||
useRead("ListServerTemplates", {}).data?.find((d) => d.id === id);
|
useRead("ListServerTemplates", {}).data?.find((d) => d.id === id);
|
||||||
|
|
||||||
export const ServerTemplateComponents: RequiredResourceComponents = {
|
export const ServerTemplateComponents: RequiredResourceComponents = {
|
||||||
|
list_item: (id) => useServerTemplate(id),
|
||||||
|
|
||||||
Dashboard: () => {
|
Dashboard: () => {
|
||||||
const count = useRead("ListServerTemplates", {}).data?.length;
|
const count = useRead("ListServerTemplates", {}).data?.length;
|
||||||
return (
|
return (
|
||||||
@@ -91,7 +93,6 @@ export const ServerTemplateComponents: RequiredResourceComponents = {
|
|||||||
Table: ServerTemplateTable,
|
Table: ServerTemplateTable,
|
||||||
|
|
||||||
Name: ({ id }) => <>{useServerTemplate(id)?.name}</>,
|
Name: ({ id }) => <>{useServerTemplate(id)?.name}</>,
|
||||||
name: (id) => useServerTemplate(id)?.name,
|
|
||||||
|
|
||||||
Icon: () => <ServerCog className="w-4 h-4" />,
|
Icon: () => <ServerCog className="w-4 h-4" />,
|
||||||
BigIcon: () => <ServerCog className="w-8 h-8" />,
|
BigIcon: () => <ServerCog className="w-8 h-8" />,
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ const ConfigOrDeployments = ({ id }: { id: string }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ServerComponents: RequiredResourceComponents = {
|
export const ServerComponents: RequiredResourceComponents = {
|
||||||
|
list_item: (id) => useServer(id),
|
||||||
|
|
||||||
Dashboard: ServersChart,
|
Dashboard: ServersChart,
|
||||||
|
|
||||||
New: () => <NewResource type="Server" />,
|
New: () => <NewResource type="Server" />,
|
||||||
@@ -111,7 +113,6 @@ export const ServerComponents: RequiredResourceComponents = {
|
|||||||
Table: ServerTable,
|
Table: ServerTable,
|
||||||
|
|
||||||
Name: ({ id }: { id: string }) => <>{useServer(id)?.name}</>,
|
Name: ({ id }: { id: string }) => <>{useServer(id)?.name}</>,
|
||||||
name: (id) => useServer(id)?.name,
|
|
||||||
|
|
||||||
Icon: ({ id }) => <_ServerIcon id={id} size={4} />,
|
Icon: ({ id }) => <_ServerIcon id={id} size={4} />,
|
||||||
BigIcon: ({ id }) => <_ServerIcon id={id} size={8} />,
|
BigIcon: ({ id }) => <_ServerIcon id={id} size={8} />,
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function version_is_none({ major, minor, patch }: Types.Version) {
|
|||||||
|
|
||||||
export function resource_name(type: UsableResource, id: string) {
|
export function resource_name(type: UsableResource, id: string) {
|
||||||
const Components = ResourceComponents[type];
|
const Components = ResourceComponents[type];
|
||||||
return Components.name(id);
|
return Components.list_item(id)?.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const level_to_number = (level: Types.PermissionLevel | undefined) => {
|
export const level_to_number = (level: Types.PermissionLevel | undefined) => {
|
||||||
|
|||||||
@@ -1,12 +1,34 @@
|
|||||||
import { OpenAlerts } from "@components/alert";
|
import { OpenAlerts } from "@components/alert";
|
||||||
import { Page } from "@components/layouts";
|
import { Page } from "@components/layouts";
|
||||||
|
import { ResourceComponents } from "@components/resources";
|
||||||
|
import { RecentCard } from "@components/resources/common";
|
||||||
import { AllUpdates } from "@components/updates/resource";
|
import { AllUpdates } from "@components/updates/resource";
|
||||||
|
import { useUser } from "@lib/hooks";
|
||||||
|
import { UsableResource } from "@types";
|
||||||
|
import { Separator } from "@ui/separator";
|
||||||
|
|
||||||
export const Dashboard = () => {
|
export const Dashboard = () => {
|
||||||
return (
|
const user = useUser().data;
|
||||||
|
return (
|
||||||
<Page title="">
|
<Page title="">
|
||||||
<OpenAlerts />
|
<OpenAlerts />
|
||||||
<AllUpdates />
|
<AllUpdates />
|
||||||
|
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<ResourceComponents.Deployment.Dashboard />
|
||||||
|
<div className="py-2">
|
||||||
|
<Separator orientation="vertical" />
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-3 gap-4 w-full">
|
||||||
|
{user?.recent_deployments?.slice(0, 6).map((id) => (
|
||||||
|
<RecentCard type="Deployment" id={id} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// const ResourceRow = ({ type }: { type: UsableResource }) => {
|
||||||
|
// const Components =
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { homeViewAtom } from "@main";
|
import { homeViewAtom } from "@main";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { Dashboard } from "./dashboard";
|
import { Dashboard } from "./dashboard2";
|
||||||
import { AllResources } from "./all_resources";
|
import { AllResources } from "./all_resources";
|
||||||
import { Tree } from "./tree";
|
import { Tree } from "./tree";
|
||||||
import { useSetTitle } from "@lib/hooks";
|
import { useSetTitle } from "@lib/hooks";
|
||||||
|
|||||||
3
frontend/src/types.d.ts
vendored
3
frontend/src/types.d.ts
vendored
@@ -6,6 +6,8 @@ type IdComponent = React.FC<{ id: string }>;
|
|||||||
type OptionalIdComponent = React.FC<{ id?: string }>;
|
type OptionalIdComponent = React.FC<{ id?: string }>;
|
||||||
|
|
||||||
export interface RequiredResourceComponents {
|
export interface RequiredResourceComponents {
|
||||||
|
list_item: (id: string) => Types.ResourceListItem<unknown> | undefined;
|
||||||
|
|
||||||
/** Summary card for use in dashboard */
|
/** Summary card for use in dashboard */
|
||||||
Dashboard: React.FC;
|
Dashboard: React.FC;
|
||||||
|
|
||||||
@@ -17,7 +19,6 @@ export interface RequiredResourceComponents {
|
|||||||
|
|
||||||
/** Name of the resource */
|
/** Name of the resource */
|
||||||
Name: IdComponent;
|
Name: IdComponent;
|
||||||
name: (id: string) => string | undefined;
|
|
||||||
|
|
||||||
/** Icon for the component */
|
/** Icon for the component */
|
||||||
Icon: OptionalIdComponent;
|
Icon: OptionalIdComponent;
|
||||||
|
|||||||
Reference in New Issue
Block a user