From 41a6e0a65ac5dee5cf8df5922edfc24ef3746d71 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Sat, 18 May 2024 02:34:47 -0700 Subject: [PATCH] improve dashboard --- client/core/rs/src/entities/mod.rs | 2 +- frontend/src/components/resources/common.tsx | 26 --- frontend/src/components/updates/table.tsx | 82 ++++++---- frontend/src/pages/home/dashboard.tsx | 164 +++++++------------ frontend/src/pages/home/dashboard2.tsx | 58 ------- frontend/src/pages/home/index.tsx | 2 +- frontend/src/pages/updates.tsx | 153 ++++++++++++++++- 7 files changed, 252 insertions(+), 235 deletions(-) delete mode 100644 frontend/src/pages/home/dashboard2.tsx diff --git a/client/core/rs/src/entities/mod.rs b/client/core/rs/src/entities/mod.rs index 8a37721a8..79e8c74b6 100644 --- a/client/core/rs/src/entities/mod.rs +++ b/client/core/rs/src/entities/mod.rs @@ -386,6 +386,7 @@ pub enum Operation { PruneNetworksServer, CreateNetwork, DeleteNetwork, + StopAllContainers, // build CreateBuild, @@ -405,7 +406,6 @@ pub enum Operation { DeleteDeployment, DeployContainer, StopContainer, - StopAllContainers, StartContainer, RemoveContainer, RenameDeployment, diff --git a/frontend/src/components/resources/common.tsx b/frontend/src/components/resources/common.tsx index ef7dbc0ec..6cfb35209 100644 --- a/frontend/src/components/resources/common.tsx +++ b/frontend/src/components/resources/common.tsx @@ -35,8 +35,6 @@ import { NewLayout } from "@components/layouts"; import { Types } from "@monitor/client"; import { ConfigItem, DoubleInput } from "@components/config/util"; import { usableResourcePath } from "@lib/utils"; -import { Card } from "@ui/card"; -import { TagsWithBadge } from "@components/tags"; export const ResourceDescription = ({ type, @@ -345,27 +343,3 @@ export const ServerSelector = ({ /> ); - -export const RecentCard = ({ - type, - id, -}: { - type: UsableResource; - id: string; -}) => { - const Components = ResourceComponents[type]; - const tags = Components.list_item(id)?.tags; - return ( - - -
- - -
-
- -
-
- - ); -}; diff --git a/frontend/src/components/updates/table.tsx b/frontend/src/components/updates/table.tsx index c1611758c..dd3175602 100644 --- a/frontend/src/components/updates/table.tsx +++ b/frontend/src/components/updates/table.tsx @@ -1,9 +1,12 @@ import { fmt_date_with_minutes } from "@lib/formatting"; import { Types } from "@monitor/client"; -import { ColumnDef } from "@tanstack/react-table"; import { DataTable } from "@ui/data-table"; import { useState } from "react"; import { UpdateDetailsInner, UpdateUser } from "./details"; +import { bg_color_class_by_intention } from "@lib/color"; +import { Card, CardHeader } from "@ui/card"; +import { cn } from "@lib/utils"; +import { ResourceLink } from "@components/resources/common"; export const UpdatesTable = ({ updates, @@ -12,46 +15,55 @@ export const UpdatesTable = ({ updates: Types.UpdateListItem[]; showTarget?: boolean; }) => { - let data: ColumnDef[] = [ - { - header: "Operation", - accessorKey: "operation", - }, - { - header: "Status", - accessorKey: "status", - }, - { - header: "Success", - accessorFn: ({ success }) => (success ? "Ok" : "Fail"), - }, - { - header: "Start Time", - accessorFn: ({ start_ts }) => fmt_date_with_minutes(new Date(start_ts)), - }, - { - header: "Operator", - accessorKey: "operator", - cell: ({ row }) => , - }, - ]; - // attach the target column on front - if (showTarget) { - data = [ - { - header: "Target", - accessorKey: "target.id", - }, - ...data, - ]; - } const [id, setId] = useState(""); return ( <> + row.original.target.type === "System" ? ( + "N/A" + ) : ( + + ), + }, + { + header: "Status", + cell: ({ row }) => { + const color = bg_color_class_by_intention( + row.original.success ? "Good" : "Critical" + ); + return ( + + + {row.original.success ? "Success" : "Fail"} + + + ); + }, + }, + { + header: "Start Time", + accessorFn: ({ start_ts }) => + fmt_date_with_minutes(new Date(start_ts)), + }, + { + header: "Operator", + accessorKey: "operator", + cell: ({ row }) => , + }, + ]} onRowClick={(row) => setId(row.id)} /> setId("")} /> diff --git a/frontend/src/pages/home/dashboard.tsx b/frontend/src/pages/home/dashboard.tsx index af47a0662..4d183797f 100644 --- a/frontend/src/pages/home/dashboard.tsx +++ b/frontend/src/pages/home/dashboard.tsx @@ -1,130 +1,84 @@ -import { Page, Section } from "@components/layouts"; -import { Box, Key, Tag } from "lucide-react"; -import { Link } from "react-router-dom"; -import { - Card, - CardDescription, - CardHeader, - CardTitle, -} from "@ui/card"; -import { ResourceComponents } from "@components/resources"; import { OpenAlerts } from "@components/alert"; -import { useRead } from "@lib/hooks"; +import { Page } from "@components/layouts"; +import { ResourceComponents } from "@components/resources"; +import { TagsWithBadge } from "@components/tags"; import { AllUpdates } from "@components/updates/resource"; +import { useRead, useUser } from "@lib/hooks"; +import { usableResourcePath } from "@lib/utils"; +import { UsableResource } from "@types"; +import { Card } from "@ui/card"; +import { Separator } from "@ui/separator"; +import { History } from "lucide-react"; +import { Link } from "react-router-dom"; export const Dashboard = () => { return ( - {/* */} - + + + + + + ); }; -const Resources = () => { +const ResourceRow = ({ type }: { type: UsableResource }) => { + const recents = useUser().data?.[`recent_${type.toLowerCase()}s`]?.slice( + 0, + 6 + ) as string[] | undefined; + const resources = useRead(`List${type}s`, {}) + .data?.filter((r) => !recents?.includes(r.id)) + .map((r) => r.id); + const ids = [ + ...(recents ?? []), + ...(resources?.slice(0, 6 - (recents?.length || 0)) ?? []), + ]; + if (ids.length === 0) return; + const Components = ResourceComponents[type]; return ( -
} actions=""> -
-
- - +
+ +
+ +
+
+
+ +

Recent {type}s

-
- +
+ {ids.map((id: string) => ( + + ))}
-
- - -
-
-
- - -
-
- - -
-
-
+ ); }; -// const RecentlyViewed = () => { -// const nav = useNavigate(); -// const recently_viewed = useUser().data?.recently_viewed; -// const checkResourceExists = useCheckResourceExists(); -// return ( -//
} -// actions="" -// > -//
-// {recently_viewed -// ?.filter(checkResourceExists) -// .slice(0, 8) -// .map(({ type, id }, i) => ( -// -// {type !== "System" && ( -// nav(`/${usableResourcePath(type)}/${id}`)} -// className={cn( -// "px-3 py-2 h-fit hover:bg-accent/50 group-focus:bg-accent/50 transition-colors cursor-pointer", -// i > 5 && "hidden 2xl:flex" -// )} -// > -// -// -// {type} -// -// -// )} -// -// ))} -//
-//
-// ); -// }; - -const TagsSummary = () => { - const tags_count = useRead("ListTags", {}).data?.length; - +const RecentCard = ({ type, id }: { type: UsableResource; id: string }) => { + const Components = ResourceComponents[type]; + const tags = Components.list_item(id)?.tags; return ( - - - -
-
- Tags - {tags_count} Total -
- + + +
+
+ +
- - - - ); -}; - -const ApiKeysSummary = () => { - const keys_count = useRead("ListApiKeys", {}).data?.length; - - return ( - - - -
-
- Api Keys - {keys_count} Total -
- +
+
- +
+
+ +
); diff --git a/frontend/src/pages/home/dashboard2.tsx b/frontend/src/pages/home/dashboard2.tsx deleted file mode 100644 index e86d4df6f..000000000 --- a/frontend/src/pages/home/dashboard2.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { OpenAlerts } from "@components/alert"; -import { Page } from "@components/layouts"; -import { ResourceComponents } from "@components/resources"; -import { RecentCard } from "@components/resources/common"; -import { AllUpdates } from "@components/updates/resource"; -import { useRead, useUser } from "@lib/hooks"; -import { UsableResource } from "@types"; -import { Separator } from "@ui/separator"; -import { History } from "lucide-react"; - -export const Dashboard = () => { - return ( - - - - - - - - - - - ); -}; - -const ResourceRow = ({ type }: { type: UsableResource }) => { - const recents = useUser().data?.[`recent_${type.toLowerCase()}s`]?.slice( - 0, - 6 - ) as string[] | undefined; - const resources = useRead(`List${type}s`, {}) - .data?.filter((r) => !recents?.includes(r.id)) - .map((r) => r.id); - const ids = [ - ...(recents ?? []), - ...(resources?.slice(0, 6 - (recents?.length || 0)) ?? []), - ]; - const Components = ResourceComponents[type]; - return ( -
- -
- -
-
-
- -

Recent {type}s

-
-
- {ids.map((id: string) => ( - - ))} -
-
-
- ); -}; diff --git a/frontend/src/pages/home/index.tsx b/frontend/src/pages/home/index.tsx index 1a4f56cb4..f36de2eda 100644 --- a/frontend/src/pages/home/index.tsx +++ b/frontend/src/pages/home/index.tsx @@ -1,6 +1,6 @@ import { homeViewAtom } from "@main"; import { useAtom } from "jotai"; -import { Dashboard } from "./dashboard2"; +import { Dashboard } from "./dashboard"; import { AllResources } from "./all_resources"; import { Tree } from "./tree"; import { useSetTitle } from "@lib/hooks"; diff --git a/frontend/src/pages/updates.tsx b/frontend/src/pages/updates.tsx index 56df36bc6..ec3148b27 100644 --- a/frontend/src/pages/updates.tsx +++ b/frontend/src/pages/updates.tsx @@ -1,9 +1,21 @@ import { Page } from "@components/layouts"; import { ResourceComponents } from "@components/resources"; -import { AddTags, ResourceTags } from "@components/tags"; import { UpdatesTable } from "@components/updates/table"; import { useRead, useResourceParamType, useSetTitle } from "@lib/hooks"; +import { Types } from "@monitor/client"; +import { CaretSortIcon } from "@radix-ui/react-icons"; import { UsableResource } from "@types"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@ui/command"; +import { Popover, PopoverContent, PopoverTrigger } from "@ui/popover"; +import { SearchX } from "lucide-react"; +import { useState } from "react"; import { useParams } from "react-router-dom"; export const Updates = () => { @@ -18,10 +30,16 @@ export const Updates = () => { const AllUpdates = () => { useSetTitle("Updates"); - const updates = useRead("ListUpdates", {}).data; + const [operation, setOperation] = useState(); + const updates = useRead("ListUpdates", { query: { operation } }).data; return ( - - + + } + > + ); }; @@ -35,24 +53,141 @@ const ResourceUpdates = ({ }) => { const name = useRead(`List${type}s`, {}).data?.find((r) => r.id === id)?.name; useSetTitle(name && `${name} | Updates`); + const [operation, setOperation] = useState(); const updates = useRead("ListUpdates", { query: { "target.type": type, "target.id": id, + operation, }, }).data; const Components = ResourceComponents[type]; return ( } - titleRight={ -
- - -
+ titleRight={

Updates

} + icon={} + actions={ + } >
); }; + +const OPERATIONS_BY_RESOURCE: { [key: string]: Types.Operation[] } = { + Server: [ + Types.Operation.CreateServer, + Types.Operation.UpdateServer, + Types.Operation.RenameServer, + Types.Operation.PruneImagesServer, + Types.Operation.PruneContainersServer, + Types.Operation.PruneNetworksServer, + Types.Operation.CreateNetwork, + Types.Operation.DeleteNetwork, + Types.Operation.StopAllContainers, + ], + Deployment: [ + Types.Operation.CreateDeployment, + Types.Operation.UpdateDeployment, + Types.Operation.RenameDeployment, + Types.Operation.DeployContainer, + Types.Operation.StopContainer, + Types.Operation.StartContainer, + Types.Operation.RemoveContainer, + ], + Build: [ + Types.Operation.CreateBuild, + Types.Operation.UpdateBuild, + Types.Operation.RunBuild, + Types.Operation.CancelBuild, + ], + Repo: [ + Types.Operation.CreateRepo, + Types.Operation.UpdateRepo, + Types.Operation.CloneRepo, + Types.Operation.PullRepo, + ], + Procedure: [ + Types.Operation.CreateProcedure, + Types.Operation.UpdateProcedure, + Types.Operation.RunProcedure, + ], + Builder: [Types.Operation.CreateBuilder, Types.Operation.UpdateBuilder], + Alerter: [Types.Operation.CreateAlerter, Types.Operation.UpdateAlerter], + ServerTemplate: [ + Types.Operation.CreateServerTemplate, + Types.Operation.UpdateServerTemplate, + Types.Operation.LaunchServer, + ], +}; + +const OperationSelector = ({ + selected, + onSelect, + options = Object.values(Types.Operation).filter( + (o) => o !== Types.Operation.None + ), +}: { + selected: Types.Operation | undefined; + onSelect: (operation: Types.Operation | undefined) => void; + options?: Types.Operation[]; +}) => { + const [open, setOpen] = useState(false); + const [input, setInput] = useState(""); + return ( + + +
+ {selected ?? "Select Operation"} + +
+
+ + + + + + No Operations Found + + + + + { + onSelect(undefined); + setOpen(false); + }} + > +
All
+
+ + {options.map((operation) => ( + { + onSelect(operation); + setOpen(false); + }} + > + {operation} + + ))} +
+
+
+
+
+ ); +};