diff --git a/frontend/src/components/layouts.tsx b/frontend/src/components/layouts.tsx index c7c5da39b..74ca2b084 100644 --- a/frontend/src/components/layouts.tsx +++ b/frontend/src/components/layouts.tsx @@ -102,10 +102,7 @@ export const NewResource = ({ return ( - @@ -158,7 +155,9 @@ export const ResourceCard = ({ - + {Components.Info.map((Info) => ( + + ))} @@ -185,7 +184,9 @@ export const ResourceRow = ({ - + {Components.Info.map((Info) => ( + + ))}
diff --git a/frontend/src/components/resources/alerter/index.tsx b/frontend/src/components/resources/alerter/index.tsx index 5db70f554..52b3b7c41 100644 --- a/frontend/src/components/resources/alerter/index.tsx +++ b/frontend/src/components/resources/alerter/index.tsx @@ -22,101 +22,98 @@ import { AlerterConfig } from "./config"; const useAlerter = (id?: string) => useRead("ListAlerters", {}).data?.find((d) => d.id === id); -const NewAlerter = () => { - const { mutateAsync } = useWrite("CreateAlerter"); - const [name, setName] = useState(""); - const [type, setType] = useState(); - - return ( - - !!type && mutateAsync({ name, config: { type, params: {} } }) - } - enabled={!!name && !!type} - > -
- Name - setName(e.target.value)} - /> -
-
- Alerter Type - -
-
- ); -}; - -const AlerterTable = () => { - const alerters = useRead("ListAlerters", {}).data; - return ( - { - const id = row.original.id; - return ( - - - - - ); - }, - }, - { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, - ]} - /> - ); -}; - -export const AlerterDashboard = () => { - const alerters_count = useRead("ListAlerters", {}).data?.length; - return ( - - - -
- Alerters - {alerters_count} Total -
- -
-
- - ); -}; - export const AlerterComponents: RequiredResourceComponents = { Name: ({ id }: { id: string }) => <>{useAlerter(id)?.name}, Icon: () => , Description: ({ id }) => <>{useAlerter(id)?.info.alerter_type} alerter, - Info: ({ id }) => <>{id}, + Info: [], Status: () => <>, Page: { Config: AlerterConfig, }, Actions: () => null, - Table: AlerterTable, - New: () => , - Dashboard: AlerterDashboard, + Table: () => { + const alerters = useRead("ListAlerters", {}).data; + return ( + { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + ]} + /> + ); + }, + Dashboard: () => { + const alerters_count = useRead("ListAlerters", {}).data?.length; + return ( + + + +
+ Alerters + {alerters_count} Total +
+ +
+
+ + ); + }, + New: () => { + const { mutateAsync } = useWrite("CreateAlerter"); + const [name, setName] = useState(""); + const [type, setType] = useState(); + + return ( + + !!type && mutateAsync({ name, config: { type, params: {} } }) + } + enabled={!!name && !!type} + > +
+ Name + setName(e.target.value)} + /> +
+
+ Alerter Type + +
+
+ ); + }, }; diff --git a/frontend/src/components/dashboard/builds-chart.tsx b/frontend/src/components/resources/build/dashboard.tsx similarity index 100% rename from frontend/src/components/dashboard/builds-chart.tsx rename to frontend/src/components/resources/build/dashboard.tsx diff --git a/frontend/src/components/resources/build/index.tsx b/frontend/src/components/resources/build/index.tsx index 1cf2f37ba..81a4be437 100644 --- a/frontend/src/components/resources/build/index.tsx +++ b/frontend/src/components/resources/build/index.tsx @@ -2,45 +2,19 @@ import { NewResource } from "@components/layouts"; import { ConfirmButton } from "@components/util"; import { useExecute, useRead, useWrite } from "@lib/hooks"; import { RequiredResourceComponents } from "@types"; -import { DataTable } from "@ui/data-table"; import { Input } from "@ui/input"; import { Ban, Hammer, History, Loader2 } from "lucide-react"; import { useState } from "react"; -import { Link } from "react-router-dom"; -import { ResourceComponents } from ".."; -import { BuildChart } from "@components/dashboard/builds-chart"; -import { TagsWithBadge, useTagsFilter } from "@components/tags"; import { useToast } from "@ui/use-toast"; import { BuildConfig } from "./config"; -import { fmt_date_with_minutes, fmt_version } from "@lib/formatting"; import { fill_color_class_by_intention } from "@lib/color"; +import { BuildChart } from "./dashboard"; +import { BuildTable } from "./table"; +import { fmt_version } from "@lib/formatting"; const useBuild = (id?: string) => useRead("ListBuilds", {}).data?.find((d) => d.id === id); -const NewBuild = () => { - const { mutateAsync } = useWrite("CreateBuild"); - const [name, setName] = useState(""); - return ( - mutateAsync({ name, config: {} })} - enabled={!!name} - > -
- Build Name - setName(e.target.value)} - /> -
-
- ); -}; - -const Name = ({ id }: { id: string }) => <>{useBuild(id)?.name}; - const Icon = ({ id }: { id: string }) => { const building = useRead("GetBuildActionState", { build: id }).data?.building; const className = building @@ -49,75 +23,22 @@ const Icon = ({ id }: { id: string }) => { return ; }; -const BuildTable = () => { - const builds = useRead("ListBuilds", {}).data; - const tags = useTagsFilter(); - return ( - - tags.every((tag) => build.tags.includes(tag)) - ) ?? [] - } - columns={[ - { - accessorKey: "id", - header: "Name", - cell: ({ row }) => { - const id = row.original.id; - return ( - - - - - ); - }, - }, - { - header: "Repo", - accessorKey: "info.repo", - }, - { - header: "Version", - accessorFn: ({ info }) => fmt_version(info.version), - }, - { - header: "Last Built", - accessorFn: ({ info: { last_built_at } }) => { - if (last_built_at > 0) { - return fmt_date_with_minutes(new Date(last_built_at)); - } else { - return "never"; - } - }, - }, - { - header: "Tags", - cell: ({ row }) => { - return ( -
- -
- ); - }, - }, - ]} - /> - ); -}; - export const BuildComponents: RequiredResourceComponents = { - Name, + Table: BuildTable, + Dashboard: BuildChart, + Name: ({ id }: { id: string }) => <>{useBuild(id)?.name}, Description: ({ id }) => <>{fmt_version(useBuild(id)?.info.version)}, - Info: ({ id }) => { - const ts = useBuild(id)?.info.last_built_at; - return ( -
- - {ts ? new Date(ts).toLocaleString() : "Never Built"} -
- ); - }, + Info: [ + ({ id }) => { + const ts = useBuild(id)?.info.last_built_at; + return ( +
+ + {ts ? new Date(ts).toLocaleString() : "Never Built"} +
+ ); + }, + ], Status: () => <>Build, Page: { Config: ({ id }) => , @@ -176,7 +97,24 @@ export const BuildComponents: RequiredResourceComponents = { ); } }, - Table: BuildTable, - New: NewBuild, - Dashboard: BuildChart, + New: () => { + const { mutateAsync } = useWrite("CreateBuild"); + const [name, setName] = useState(""); + return ( + mutateAsync({ name, config: {} })} + enabled={!!name} + > +
+ Build Name + setName(e.target.value)} + /> +
+
+ ); + }, }; diff --git a/frontend/src/components/resources/build/table.tsx b/frontend/src/components/resources/build/table.tsx new file mode 100644 index 000000000..fc0107447 --- /dev/null +++ b/frontend/src/components/resources/build/table.tsx @@ -0,0 +1,63 @@ +import { TagsWithBadge, useTagsFilter } from "@components/tags"; +import { useRead } from "@lib/hooks"; +import { DataTable } from "@ui/data-table"; +import { Link } from "react-router-dom"; +import { ResourceComponents } from ".."; +import { fmt_date_with_minutes, fmt_version } from "@lib/formatting"; + +export const BuildTable = () => { + const builds = useRead("ListBuilds", {}).data; + const tags = useTagsFilter(); + return ( + + tags.every((tag) => build.tags.includes(tag)) + ) ?? [] + } + columns={[ + { + accessorKey: "id", + header: "Name", + cell: ({ row }) => { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { + header: "Repo", + accessorKey: "info.repo", + }, + { + header: "Version", + accessorFn: ({ info }) => fmt_version(info.version), + }, + { + header: "Last Built", + accessorFn: ({ info: { last_built_at } }) => { + if (last_built_at > 0) { + return fmt_date_with_minutes(new Date(last_built_at)); + } else { + return "never"; + } + }, + }, + { + header: "Tags", + cell: ({ row }) => { + return ( +
+ +
+ ); + }, + }, + ]} + /> + ); +}; diff --git a/frontend/src/components/resources/builder/index.tsx b/frontend/src/components/resources/builder/index.tsx index 01c56d89e..2b22fae68 100644 --- a/frontend/src/components/resources/builder/index.tsx +++ b/frontend/src/components/resources/builder/index.tsx @@ -22,126 +22,124 @@ import { BuilderConfig } from "./config"; const useBuilder = (id?: string) => useRead("ListBuilders", {}).data?.find((d) => d.id === id); -const NewBuilder = () => { - const { mutateAsync } = useWrite("CreateBuilder"); - const [name, setName] = useState(""); - const [type, setType] = useState(); - - return ( - - !!type && mutateAsync({ name, config: { type, params: {} } }) - } - enabled={!!name && !!type} - > -
- Name - setName(e.target.value)} - /> -
-
- Builder Type - -
-
- ); -}; -const Name = ({ id }: { id: string }) => <>{useBuilder(id)?.name}; - -const BuilderDashboard = () => { - const builders_count = useRead("ListBuilders", {}).data?.length; - return ( - - - -
- Builders - {builders_count} Total -
- -
-
- - ); -}; - -const BuilderTable = () => { - const tags = useTagsFilter(); - const builders = useRead("ListBuilders", {}).data; - return ( - - tags.every((tag) => builder.tags.includes(tag)) - ) ?? [] - } - columns={[ - { - accessorKey: "id", - header: "Name", - cell: ({ row }) => { - const id = row.original.id; - return ( - - - - - ); - }, - }, - { - header: "Provider", - accessorKey: "info.provider", - }, - { - header: "Instance Type", - accessorKey: "info.instance_type", - }, - { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, - ]} - /> - ); -}; - export const BuilderComponents: RequiredResourceComponents = { - Name, - Description: ({ id }) => <>{id}, - Info: ({ id }) => ( - <> + Name: ({ id }: { id: string }) => <>{useBuilder(id)?.name}, + Description: () => <>, + Info: [ + ({ id }) => (
{useBuilder(id)?.info.provider}
+ ), + ({ id }) => (
{useBuilder(id)?.info.instance_type ?? "N/A"}
- - ), + ), + ], Icon: () => , - Status: () => <>, + Status: () => <>Builder, + Actions: () => <>, Page: { Config: BuilderConfig, }, - Table: BuilderTable, - Actions: () => null, - New: () => , - Dashboard: BuilderDashboard, + Table: () => { + const tags = useTagsFilter(); + const builders = useRead("ListBuilders", {}).data; + return ( + + tags.every((tag) => builder.tags.includes(tag)) + ) ?? [] + } + columns={[ + { + accessorKey: "id", + header: "Name", + cell: ({ row }) => { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { + header: "Provider", + accessorKey: "info.provider", + }, + { + header: "Instance Type", + accessorKey: "info.instance_type", + }, + { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + ]} + /> + ); + }, + New: () => { + const { mutateAsync } = useWrite("CreateBuilder"); + const [name, setName] = useState(""); + const [type, setType] = useState(); + + return ( + + !!type && mutateAsync({ name, config: { type, params: {} } }) + } + enabled={!!name && !!type} + > +
+ Name + setName(e.target.value)} + /> +
+
+ Builder Type + +
+
+ ); + }, + Dashboard: () => { + const builders_count = useRead("ListBuilders", {}).data?.length; + return ( + + + +
+ Builders + {builders_count} Total +
+ +
+
+ + ); + }, }; diff --git a/frontend/src/components/dashboard/deployments-chart.tsx b/frontend/src/components/resources/deployment/dashboard.tsx similarity index 100% rename from frontend/src/components/dashboard/deployments-chart.tsx rename to frontend/src/components/resources/deployment/dashboard.tsx diff --git a/frontend/src/components/resources/deployment/index.tsx b/frontend/src/components/resources/deployment/index.tsx index 9f6f57cd2..442932a7c 100644 --- a/frontend/src/components/resources/deployment/index.tsx +++ b/frontend/src/components/resources/deployment/index.tsx @@ -1,7 +1,7 @@ import { useRead, useWrite } from "@lib/hooks"; import { Types } from "@monitor/client"; import { RequiredResourceComponents } from "@types"; -import { AlertTriangle, HardDrive, Rocket, Server } from "lucide-react"; +import { AlertTriangle, HardDrive, Rocket } from "lucide-react"; import { cn } from "@lib/utils"; import { useState } from "react"; import { NewResource, Section } from "@components/layouts"; @@ -17,174 +17,53 @@ import { } from "./actions"; import { Input } from "@ui/input"; import { DeploymentLogs } from "./logs"; -import { Link } from "react-router-dom"; -import { DataTable } from "@ui/data-table"; -import { ResourceComponents } from ".."; -import { TagsWithBadge, useTagsFilter } from "@components/tags"; -import { DeploymentsChart } from "@components/dashboard/deployments-chart"; -import { Button } from "@ui/button"; import { snake_case_to_upper_space_case } from "@lib/formatting"; import { deployment_state_intention, fill_color_class_by_intention, text_color_class_by_intention, } from "@lib/color"; +import { DeploymentTable } from "./table"; +import { ResourceLink } from "@components/util"; +import { DeploymentsChart } from "./dashboard"; export const useDeployment = (id?: string) => useRead("ListDeployments", {}, { refetchInterval: 5000 }).data?.find( (d) => d.id === id ); -const Icon = ({ id }: { id?: string }) => { - const state = useDeployment(id)?.info.state; - - return ( - - ); -}; - -const Name = ({ id }: { id: string }) => <>{useDeployment(id)?.name}; -const Description = ({ id }: { id: string }) => ( - <>{useDeployment(id)?.info.status} -); - -const Info = ({ id }: { id: string }) => { - const info = useDeployment(id)?.info; - const server = useServer(info?.server_id); - - return ( - <> - - - {useDeployment(id)?.info.image || "N/A"} - - - - - - ); -}; - -export const DeploymentTable = ({ - deployments, -}: { - deployments: Types.DeploymentListItem[] | undefined; -}) => { - const tags = useTagsFilter(); - return ( - - tags.every((tag) => deployment.tags.includes(tag)) - ) ?? [] - } - columns={[ - { - header: "Name", - cell: ({ row }) => { - const id = row.original.id; - return ( - - - - ); - }, - }, - { - header: "Image", - cell: ({ - row: { - original: { - info: { build_id, image }, - }, - }, - }) => { - const builds = useRead("ListBuilds", {}).data; - if (build_id) { - const build = builds?.find((build) => build.id === build_id); - if (build) { - return ( - - - - ); - } else { - return undefined; - } - } else { - const [img, _] = image.split(":"); - return img; - } - }, - }, - { - header: "Server", - cell: ({ row }) => { - const id = row.original.info.server_id; - return ( - - - - ); - }, - }, - { - header: "State", - cell: ({ row }) => { - const state = row.original.info.state; - const color = text_color_class_by_intention( - deployment_state_intention(state) - ); - return ( -
- {snake_case_to_upper_space_case(state)} -
- ); - }, - }, - { - header: "Tags", - cell: ({ row }) => { - return ( -
- -
- ); - }, - }, - ]} - /> - ); -}; - export const DeploymentComponents: RequiredResourceComponents = { - Name, - Description, - Info, - Icon, + Name: ({ id }) => <>{useDeployment(id)?.name}, + Description: ({ id }) => <>{useDeployment(id)?.info.status}, + Info: [ + ({ id }) => { + const info = useDeployment(id)?.info; + return info?.build_id ? ( + + ) : ( +
+ + {info?.image || "N/A"} +
+ ); + }, + ({ id }) => { + const info = useDeployment(id)?.info; + const server = useServer(info?.server_id); + return server?.id ? ( + + ) : ( + "Unknown Server" + ); + }, + ], + Icon: ({ id }) => { + const state = useDeployment(id)?.info.state; + const color = fill_color_class_by_intention( + deployment_state_intention(state) + ); + return ; + }, Status: ({ id }) => { const state = useDeployment(id)?.info.state ?? Types.DockerContainerState.Unknown; diff --git a/frontend/src/components/resources/deployment/table.tsx b/frontend/src/components/resources/deployment/table.tsx new file mode 100644 index 000000000..0d396580d --- /dev/null +++ b/frontend/src/components/resources/deployment/table.tsx @@ -0,0 +1,116 @@ +import { TagsWithBadge, useTagsFilter } from "@components/tags"; +import { Types } from "@monitor/client"; +import { Button } from "@ui/button"; +import { DataTable } from "@ui/data-table"; +import { Link } from "react-router-dom"; +import { useRead } from "@lib/hooks"; +import { ResourceComponents } from ".."; +import { + deployment_state_intention, + text_color_class_by_intention, +} from "@lib/color"; +import { snake_case_to_upper_space_case } from "@lib/formatting"; + +export const DeploymentTable = ({ + deployments, +}: { + deployments: Types.DeploymentListItem[] | undefined; +}) => { + const tags = useTagsFilter(); + return ( + + tags.every((tag) => deployment.tags.includes(tag)) + ) ?? [] + } + columns={[ + { + header: "Name", + cell: ({ row }) => { + const id = row.original.id; + return ( + + + + ); + }, + }, + { + header: "Image", + cell: ({ + row: { + original: { + info: { build_id, image }, + }, + }, + }) => { + const builds = useRead("ListBuilds", {}).data; + if (build_id) { + const build = builds?.find((build) => build.id === build_id); + if (build) { + return ( + + + + ); + } else { + return undefined; + } + } else { + const [img, _] = image.split(":"); + return img; + } + }, + }, + { + header: "Server", + cell: ({ row }) => { + const id = row.original.info.server_id; + return ( + + + + ); + }, + }, + { + header: "State", + cell: ({ row }) => { + const state = row.original.info.state; + const color = text_color_class_by_intention( + deployment_state_intention(state) + ); + return ( +
+ {snake_case_to_upper_space_case(state)} +
+ ); + }, + }, + { + header: "Tags", + cell: ({ row }) => { + return ( +
+ +
+ ); + }, + }, + ]} + /> + ); +}; diff --git a/frontend/src/components/resources/procedure/config.tsx b/frontend/src/components/resources/procedure/config.tsx index fb297d070..1c1c276b0 100644 --- a/frontend/src/components/resources/procedure/config.tsx +++ b/frontend/src/components/resources/procedure/config.tsx @@ -99,10 +99,6 @@ const ProcedureConfigInner = ({ } columns={[ - { - header: "Stage", - cell: ({ row: { index } }) => <>{index + 1}, - }, { header: "Enabled", cell: ({ diff --git a/frontend/src/components/resources/procedure/index.tsx b/frontend/src/components/resources/procedure/index.tsx index 4dedf6771..0707702fd 100644 --- a/frontend/src/components/resources/procedure/index.tsx +++ b/frontend/src/components/resources/procedure/index.tsx @@ -1,11 +1,9 @@ import { NewResource } from "@components/layouts"; -import { TagsWithBadge } from "@components/tags"; import { ConfirmButton } from "@components/util"; import { useExecute, useRead, useWrite } from "@lib/hooks"; import { Types } from "@monitor/client"; import { RequiredResourceComponents } from "@types"; import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card"; -import { DataTable } from "@ui/data-table"; import { Input } from "@ui/input"; import { Select, @@ -18,6 +16,7 @@ import { Loader2, Route } from "lucide-react"; import { useState } from "react"; import { Link } from "react-router-dom"; import { ProcedureConfig } from "./config"; +import { ProcedureTable } from "./table"; const useProcedure = (id?: string) => useRead("ListProcedures", {}).data?.find((d) => d.id === id); @@ -25,9 +24,9 @@ const useProcedure = (id?: string) => export const ProcedureComponents: RequiredResourceComponents = { Name: ({ id }) => <>{useProcedure(id)?.name}, Description: ({ id }) => <>{useProcedure(id)?.info.procedure_type}, - Info: () => <>, + Info: [({ id }) => <>{useProcedure(id)?.info.procedure_type}], Icon: () => , - Status: () => <>, + Status: () => <>Procedure, Page: { Config: ProcedureConfig, }, @@ -50,46 +49,7 @@ export const ProcedureComponents: RequiredResourceComponents = { /> ); }, - Table: () => { - const procedures = useRead("ListProcedures", {}).data; - return ( - { - const id = row.original.id; - return ( - - - - - ); - }, - }, - { - header: "Type", - accessorKey: "info.procedure_type", - }, - { - header: "Tags", - cell: ({ row }) => { - return ( -
- -
- ); - }, - }, - ]} - /> - ); - }, + Table: ProcedureTable, New: () => { const { mutateAsync } = useWrite("CreateProcedure"); const [name, setName] = useState(""); diff --git a/frontend/src/components/resources/procedure/table.tsx b/frontend/src/components/resources/procedure/table.tsx new file mode 100644 index 000000000..f9655dc14 --- /dev/null +++ b/frontend/src/components/resources/procedure/table.tsx @@ -0,0 +1,46 @@ +import { useRead } from "@lib/hooks"; +import { DataTable } from "@ui/data-table"; +import { Link } from "react-router-dom"; +import { ProcedureComponents } from "."; +import { TagsWithBadge } from "@components/tags"; + +export const ProcedureTable = () => { + const procedures = useRead("ListProcedures", {}).data; + return ( + { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { + header: "Type", + accessorKey: "info.procedure_type", + }, + { + header: "Tags", + cell: ({ row }) => { + return ( +
+ +
+ ); + }, + }, + ]} + /> + ); +}; \ No newline at end of file diff --git a/frontend/src/components/resources/repo/index.tsx b/frontend/src/components/resources/repo/index.tsx index 21cfeb54d..b122c4703 100644 --- a/frontend/src/components/resources/repo/index.tsx +++ b/frontend/src/components/resources/repo/index.tsx @@ -1,6 +1,5 @@ import { TagsWithBadge } from "@components/tags"; import { useRead } from "@lib/hooks"; -import { Icon } from "@radix-ui/react-select"; import { RequiredResourceComponents } from "@types"; import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card"; import { DataTable } from "@ui/data-table"; @@ -11,31 +10,14 @@ import { RepoConfig } from "./config"; const useRepo = (id?: string) => useRead("ListRepos", {}).data?.find((d) => d.id === id); -const Name = ({ id }: { id: string }) => <>{useRepo(id)?.name}; - -export const RepoDashboard = () => { - const repo_count = useRead("ListRepos", {}).data?.length; - return ( - - - -
- Repos - {repo_count} Total -
- -
-
- - ); -}; - export const RepoComponents: RequiredResourceComponents = { - Name, + Name: ({ id }: { id: string }) => <>{useRepo(id)?.name}, Description: ({ id }) => <>{id}, - Info: ({ id }) => <>{id}, Icon: () => , + Info: [], Status: () => <>, + Actions: () => <>, + New: () => <>, Page: { Config: RepoConfig, }, @@ -52,8 +34,8 @@ export const RepoComponents: RequiredResourceComponents = { const id = row.original.id; return ( - - + + ); }, @@ -72,7 +54,20 @@ export const RepoComponents: RequiredResourceComponents = { /> ); }, - Actions: () => null, - New: () => null, - Dashboard: RepoDashboard, + Dashboard: () => { + const repo_count = useRead("ListRepos", {}).data?.length; + return ( + + + +
+ Repos + {repo_count} Total +
+ +
+
+ + ); + }, }; diff --git a/frontend/src/components/dashboard/servers-chart.tsx b/frontend/src/components/resources/server/dashboard.tsx similarity index 100% rename from frontend/src/components/dashboard/servers-chart.tsx rename to frontend/src/components/resources/server/dashboard.tsx diff --git a/frontend/src/components/resources/server/index.tsx b/frontend/src/components/resources/server/index.tsx index 3063ce106..8662fc1b8 100644 --- a/frontend/src/components/resources/server/index.tsx +++ b/frontend/src/components/resources/server/index.tsx @@ -2,191 +2,41 @@ import { useRead, useWrite } from "@lib/hooks"; import { cn } from "@lib/utils"; import { Types } from "@monitor/client"; import { RequiredResourceComponents } from "@types"; -import { - MapPin, - Cpu, - MemoryStick, - Database, - ServerIcon, - AlertTriangle, - Rocket, -} from "lucide-react"; +import { ServerIcon, AlertTriangle, Rocket } from "lucide-react"; import { ServerStats } from "./stats"; import { useState } from "react"; import { NewResource, Section } from "@components/layouts"; import { Input } from "@ui/input"; -import { DataTable } from "@ui/data-table"; -import { Link } from "react-router-dom"; -import { ResourceComponents } from ".."; -import { TagsWithBadge, useTagsFilter } from "@components/tags"; import { DeleteServer, RenameServer } from "./actions"; -import { ServersChart } from "@components/dashboard/servers-chart"; -import { DeploymentTable } from "../deployment"; import { fill_color_class_by_intention, server_status_intention, text_color_class_by_intention, } from "@lib/color"; import { ServerConfig } from "./config"; +import { DeploymentTable } from "../deployment/table"; +import { ServerTable } from "./table"; +import { ServerInfo } from "./info"; +import { ServersChart } from "./dashboard"; export const useServer = (id?: string) => useRead("ListServers", {}).data?.find((d) => d.id === id); -export const ServerInfo = ({ - id, - showRegion = true, -}: { - id: string; - showRegion?: boolean; -}) => { - const server = useServer(id); - const stats = useRead( - "GetBasicSystemStats", - { server: id }, - { enabled: server ? server.info.status !== "Disabled" : false } - ).data; - const info = useRead( - "GetSystemInformation", - { server: id }, - { enabled: server ? server.info.status !== "Disabled" : false } - ).data; - return ( - <> - {showRegion && ( - <> -
- - {useServer(id)?.info.region} -
- | - - )} -
- - {info?.core_count ?? "N/A"} Core(s) -
- | -
- - {stats?.mem_total_gb.toFixed(2) ?? "N/A"} GB -
- | -
- - {stats?.disk_total_gb.toFixed(2) ?? "N/A"} GB -
- - ); -}; - -export const ServerIconComponent = ({ id }: { id?: string }) => { - const status = useServer(id)?.info.status; - return ( - - ); -}; - -const NewServer = () => { - const { mutateAsync } = useWrite("CreateServer"); - const [name, setName] = useState(""); - return ( - mutateAsync({ name, config: {} })} - enabled={!!name} - > -
- Server Name - setName(e.target.value)} - /> -
-
- ); -}; - -const DeploymentCountOnServer = ({ id }: { id: string }) => { - const { data } = useRead("ListDeployments", { - query: { specific: { server_ids: [id] } }, - }); - - return <>{data?.length ?? 0}; -}; - -const ServerTable = () => { - const servers = useRead("ListServers", {}).data; - const tags = useTagsFilter(); - return ( - nav(`/servers/${id}`)} - data={ - servers?.filter((server) => - tags.every((tag) => server.tags.includes(tag)) - ) ?? [] - } - columns={[ - { - header: "Name", - accessorKey: "id", - cell: ({ - row: { - original: { id }, - }, - }) => { - return ( - - - - - ); - }, - }, - // { - // header: "Description", - // accessorKey: "description", - // }, - - { - header: "Deployments", - cell: ({ row }) => , - }, - { header: "Region", accessorKey: "info.region" }, - { - header: "State", - cell: ({ - row: { - original: { id }, - }, - }) => , - }, - { - header: "Tags", - cell: ({ row }) => { - return ( -
- -
- ); - }, - }, - ]} - /> - ); -}; - export const ServerComponents: RequiredResourceComponents = { Name: ({ id }: { id: string }) => <>{useServer(id)?.name}, Description: ({ id }) => <>{useServer(id)?.info.status}, - Info: ({ id }) => , - Actions: () => null, - Icon: ServerIconComponent, + Info: [({ id }) => ], + Icon: ({ id }) => { + const status = useServer(id)?.info.status; + return ( + + ); + }, Status: ({ id }) => { const status = useServer(id)?.info.status; const stateClass = text_color_class_by_intention( @@ -198,6 +48,7 @@ export const ServerComponents: RequiredResourceComponents = {
); }, + Actions: () => null, Page: { Stats: ({ id }) => , Deployments: ({ id }) => { @@ -218,7 +69,26 @@ export const ServerComponents: RequiredResourceComponents = { ), }, - New: () => , + New: () => { + const { mutateAsync } = useWrite("CreateServer"); + const [name, setName] = useState(""); + return ( + mutateAsync({ name, config: {} })} + enabled={!!name} + > +
+ Server Name + setName(e.target.value)} + /> +
+
+ ); + }, Table: ServerTable, Dashboard: ServersChart, }; diff --git a/frontend/src/components/resources/server/info.tsx b/frontend/src/components/resources/server/info.tsx new file mode 100644 index 000000000..c0d8fafe5 --- /dev/null +++ b/frontend/src/components/resources/server/info.tsx @@ -0,0 +1,50 @@ +import { useRead } from "@lib/hooks"; +import { useServer } from "."; +import { Cpu, Database, MapPin, MemoryStick } from "lucide-react"; + +export const ServerInfo = ({ + id, + showRegion = true, +}: { + id: string; + showRegion?: boolean; +}) => { + const server = useServer(id); + const stats = useRead( + "GetBasicSystemStats", + { server: id }, + { enabled: server ? server.info.status !== "Disabled" : false } + ).data; + const info = useRead( + "GetSystemInformation", + { server: id }, + { enabled: server ? server.info.status !== "Disabled" : false } + ).data; + return ( + <> + {showRegion && ( + <> +
+ + {useServer(id)?.info.region} +
+ | + + )} +
+ + {info?.core_count ?? "N/A"} Core(s) +
+ | +
+ + {stats?.mem_total_gb.toFixed(2) ?? "N/A"} GB +
+ | +
+ + {stats?.disk_total_gb.toFixed(2) ?? "N/A"} GB +
+ + ); +}; diff --git a/frontend/src/components/resources/server/table.tsx b/frontend/src/components/resources/server/table.tsx new file mode 100644 index 000000000..13b0d989c --- /dev/null +++ b/frontend/src/components/resources/server/table.tsx @@ -0,0 +1,74 @@ +import { TagsWithBadge, useTagsFilter } from "@components/tags"; +import { useRead } from "@lib/hooks"; +import { DataTable } from "@ui/data-table"; +import { Link } from "react-router-dom"; +import { ServerComponents } from "."; + +export const ServerTable = () => { + const servers = useRead("ListServers", {}).data; + const tags = useTagsFilter(); + return ( + nav(`/servers/${id}`)} + data={ + servers?.filter((server) => + tags.every((tag) => server.tags.includes(tag)) + ) ?? [] + } + columns={[ + { + header: "Name", + accessorKey: "id", + cell: ({ + row: { + original: { id }, + }, + }) => { + return ( + + + + + ); + }, + }, + // { + // header: "Description", + // accessorKey: "description", + // }, + + { + header: "Deployments", + cell: ({ row }) => , + }, + { header: "Region", accessorKey: "info.region" }, + { + header: "State", + cell: ({ + row: { + original: { id }, + }, + }) => , + }, + { + header: "Tags", + cell: ({ row }) => { + return ( +
+ +
+ ); + }, + }, + ]} + /> + ); +}; + +const DeploymentCountOnServer = ({ id }: { id: string }) => { + const { data } = useRead("ListDeployments", { + query: { specific: { server_ids: [id] } }, + }); + + return <>{data?.length ?? 0}; +}; diff --git a/frontend/src/lib/color.ts b/frontend/src/lib/color.ts index df38220da..181bd841c 100644 --- a/frontend/src/lib/color.ts +++ b/frontend/src/lib/color.ts @@ -5,7 +5,8 @@ export type ColorIntention = | "Neutral" | "Warning" | "Critical" - | "Unknown"; + | "Unknown" + | "None"; export const hex_color_by_intention = (intention: ColorIntention) => { switch (intention) { @@ -19,6 +20,8 @@ export const hex_color_by_intention = (intention: ColorIntention) => { return "#EF0044"; case "Unknown": return "#A855F7"; + case "None": + return ""; } }; @@ -34,21 +37,38 @@ export const color_class_by_intention = (intention: ColorIntention) => { return "red-500"; case "Unknown": return "purple-500"; + case "None": + return ""; } }; -export const text_color_class_by_intention = (intention: ColorIntention) => { - return `text-${color_class_by_intention(intention)}`; -}; - export const fill_color_class_by_intention = (intention: ColorIntention) => { + if (intention === "None") return ""; return `fill-${color_class_by_intention(intention)}`; }; export const stroke_color_class_by_intention = (intention: ColorIntention) => { + if (intention === "None") return ""; return `stroke-${color_class_by_intention(intention)}`; }; +export const text_color_class_by_intention = (intention: ColorIntention) => { + switch (intention) { + case "Good": + return "text-green-500"; + case "Neutral": + return "text-blue-500"; + case "Warning": + return "text-orange-500"; + case "Critical": + return "text-red-500"; + case "Unknown": + return "text-purple-500"; + case "None": + return ""; + } +}; + export const server_status_intention: ( status?: Types.ServerStatus ) => ColorIntention = (status) => { @@ -59,8 +79,8 @@ export const server_status_intention: ( return "Critical"; case Types.ServerStatus.Disabled: return "Neutral"; - default: - return "Unknown"; + case undefined: + return "None"; } }; @@ -68,11 +88,13 @@ export const deployment_state_intention: ( state?: Types.DockerContainerState ) => ColorIntention = (state) => { switch (state) { + case undefined: + return "None"; case Types.DockerContainerState.Running: return "Good"; case Types.DockerContainerState.NotDeployed: return "Neutral"; - case Types.DockerContainerState.Unknown || undefined: + case Types.DockerContainerState.Unknown: return "Unknown"; default: return "Critical"; diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index 718bd3d7c..114174b45 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -3,21 +3,15 @@ import { Page, Section } from "@components/layouts"; import { AlertTriangle, Box, FolderTree } from "lucide-react"; import { DataTable } from "@ui/data-table"; import { Link } from "react-router-dom"; -import { ServerComponents } from "@components/resources/server"; import { AlertLevel } from "@components/util"; import { Button } from "@ui/button"; import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card"; -import { DeploymentComponents } from "@components/resources/deployment"; -import { BuildComponents } from "@components/resources/build"; -import { RepoComponents } from "@components/resources/repo"; -import { BuilderComponents } from "@components/resources/builder"; -import { AlerterComponents } from "@components/resources/alerter"; -import { ProcedureComponents } from "@components/resources/procedure/index"; import { TagsSummary } from "@components/dashboard/tags"; import { ApiKeysSummary } from "@components/dashboard/api-keys"; import { atomWithStorage } from "jotai/utils"; import { useAtom } from "jotai"; import { fmt_date_with_minutes } from "@lib/formatting"; +import { ResourceComponents } from "@components/resources"; export const Dashboard = () => { return ( @@ -57,7 +51,9 @@ const OpenAlerts = () => { case "Server": return ( - + ); default: @@ -88,11 +84,11 @@ const Resources = () => (
} actions="">
- - + +
- +
@@ -112,16 +108,16 @@ const Resources = () => (
- - + + {/* */}
- - + +
diff --git a/frontend/src/pages/resource.tsx b/frontend/src/pages/resource.tsx index ea86a80c2..7c67c522c 100644 --- a/frontend/src/pages/resource.tsx +++ b/frontend/src/pages/resource.tsx @@ -30,8 +30,11 @@ export const Resource = () => {
- | - + {Components.Info.map((Info) => ( + <> + | + + ))}
} diff --git a/frontend/src/pages/tree.tsx b/frontend/src/pages/tree.tsx index 726b1ebed..8e8fd9e00 100644 --- a/frontend/src/pages/tree.tsx +++ b/frontend/src/pages/tree.tsx @@ -1,6 +1,7 @@ import { Page, Section } from "@components/layouts"; -import { DeploymentTable } from "@components/resources/deployment"; -import { ServerIconComponent, ServerInfo } from "@components/resources/server"; +import { ResourceComponents } from "@components/resources"; +import { DeploymentTable } from "@components/resources/deployment/table"; +import { ServerInfo } from "@components/resources/server/info"; import { TagsFilter, TagsWithBadge, useTagsFilter } from "@components/tags"; import { useRead } from "@lib/hooks"; import { Button } from "@ui/button"; @@ -45,7 +46,7 @@ const Server = ({ id }: { id: string }) => { {server?.id && } diff --git a/frontend/src/types.d.ts b/frontend/src/types.d.ts index 95fbd7036..4002dafd4 100644 --- a/frontend/src/types.d.ts +++ b/frontend/src/types.d.ts @@ -1,4 +1,5 @@ import { Types } from "@monitor/client"; +import { ReactNode } from "react"; export type UsableResource = Exclude; @@ -15,7 +16,7 @@ export interface RequiredResourceComponents { Name: IdComponent; Description: IdComponent; - Info: IdComponent; + Info: IdComponent[]; Status: IdComponent; Actions: IdComponent;