diff --git a/frontend/src/components/resources/alerter/index.tsx b/frontend/src/components/resources/alerter/index.tsx index 280b9e8c4..1ac7709a9 100644 --- a/frontend/src/components/resources/alerter/index.tsx +++ b/frontend/src/components/resources/alerter/index.tsx @@ -11,9 +11,11 @@ import { } from "@ui/select"; import { RequiredResourceComponents } from "@types"; import { Input } from "@ui/input"; -import { AlarmClock } from "lucide-react"; +import { AlarmClock, Link } from "lucide-react"; import { useState } from "react"; import { ConfigInner } from "@components/config"; +import { DataTable } from "@ui/data-table"; +import { Icon } from "@radix-ui/react-select"; const useAlerter = (id?: string) => useRead("ListAlerters", {}).data?.find((d) => d.id === id); @@ -106,8 +108,10 @@ const CustomAlerterConfig = ({ id }: { id: string }) => { ); }; +const Name = ({ id }: { id: string }) => <>{useAlerter(id)?.name}; + export const Alerter: RequiredResourceComponents = { - Name: ({ id }) => <>{useAlerter(id)?.name}, + Name, Description: ({ id }) => <>{useAlerter(id)?.info.alerter_type} alerter, Info: ({ id }) => <>{id}, Icon: () => , @@ -119,5 +123,29 @@ export const Alerter: RequiredResourceComponents = { }, }, Actions: () => null, + Table: () => { + const alerters = useRead("ListAlerters", {}).data; + return ( + { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + ]} + /> + ); + }, New: () => , }; diff --git a/frontend/src/components/resources/build/index.tsx b/frontend/src/components/resources/build/index.tsx index a728f1e90..66dcd4960 100644 --- a/frontend/src/components/resources/build/index.tsx +++ b/frontend/src/components/resources/build/index.tsx @@ -3,12 +3,14 @@ import { ResourceSelector, AccountSelector } from "@components/config/util"; import { NewResource } from "@components/layouts"; import { ConfirmButton } from "@components/util"; import { useExecute, useRead, useWrite } from "@lib/hooks"; -import { fmt_verison } from "@lib/utils"; +import { fmt_date_with_minutes, fmt_version } from "@lib/utils"; import { Types } from "@monitor/client"; import { RequiredResourceComponents } from "@types"; +import { DataTable } from "@ui/data-table"; import { Input } from "@ui/input"; import { Hammer, History, Loader2 } from "lucide-react"; import { useState } from "react"; +import { Link } from "react-router-dom"; const useBuild = (id?: string) => useRead("ListBuilds", {}).data?.find((d) => d.id === id); @@ -95,9 +97,20 @@ export const BuildConfig = ({ id }: { id: string }) => { ); }; +const Name = ({ id }: { id: string }) => <>{useBuild(id)?.name}; +const Icon = ({ id }: { id?: string }) => { + if (!id) return ; + const building = useRead("GetBuildActionState", { id }).data?.building; + const className = building + ? "w-4 h-4 animate-spin fill-green-500" + : "w-4 h-4"; + return ; +}; + export const BuildComponents: RequiredResourceComponents = { - Name: ({ id }) => <>{useBuild(id)?.name}, - Description: ({ id }) => <>{fmt_verison(useBuild(id)?.info.version)}, + Name, + Icon, + Description: ({ id }) => <>{fmt_version(useBuild(id)?.info.version)}, Info: ({ id }) => { const ts = useBuild(id)?.info.last_built_at; return ( @@ -107,7 +120,6 @@ export const BuildComponents: RequiredResourceComponents = { ); }, - Icon: () => , Page: { Config: ({ id }) => , }, @@ -129,5 +141,55 @@ export const BuildComponents: RequiredResourceComponents = { /> ); }, + Table: () => { + const builds = useRead("ListBuilds", {}).data; + return ( + { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { + header: "Deployments", + cell: ({ row }) => { + const deps = useRead("ListDeployments", { + query: { specific: { build_ids: [row.original.id] } }, + })?.data?.map((d) => ( + {d.name} + )); + return
{deps}
; + }, + }, + { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + { + 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: "Created", + accessorFn: ({ created_at }) => + fmt_date_with_minutes(new Date(created_at)), + }, + ]} + /> + ); + }, New: () => , }; diff --git a/frontend/src/components/resources/builder/index.tsx b/frontend/src/components/resources/builder/index.tsx index 0696beb43..419b69998 100644 --- a/frontend/src/components/resources/builder/index.tsx +++ b/frontend/src/components/resources/builder/index.tsx @@ -3,7 +3,9 @@ import { InputList, ResourceSelector } from "@components/config/util"; import { NewResource } from "@components/layouts"; import { useRead, useWrite } from "@lib/hooks"; import { Types } from "@monitor/client"; +import { Icon } from "@radix-ui/react-select"; import { RequiredResourceComponents } from "@types"; +import { DataTable } from "@ui/data-table"; import { Input } from "@ui/input"; import { Select, @@ -13,7 +15,7 @@ import { SelectTrigger, SelectValue, } from "@ui/select"; -import { Cloud, Bot, Factory } from "lucide-react"; +import { Cloud, Bot, Factory, Link } from "lucide-react"; import { useState } from "react"; const useBuilder = (id?: string) => @@ -138,9 +140,10 @@ const NewBuilder = () => { ); }; +const Name = ({ id }: { id: string }) => <>{useBuilder(id)?.name}; export const Builder: RequiredResourceComponents = { - Name: ({ id }) => <>{useBuilder(id)?.name}, + Name, Description: ({ id }) => <>{id}, Info: ({ id }) => ( <> @@ -162,6 +165,33 @@ export const Builder: RequiredResourceComponents = { if (config?.type === "Server") return ; }, }, + Table: () => { + const alerters = useRead("ListAlerters", {}).data; + return ( + { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + ]} + /> + ); + }, Actions: () => null, New: () => , }; diff --git a/frontend/src/components/resources/deployment/config/components/image.tsx b/frontend/src/components/resources/deployment/config/components/image.tsx index bb03ffba8..949f5b306 100644 --- a/frontend/src/components/resources/deployment/config/components/image.tsx +++ b/frontend/src/components/resources/deployment/config/components/image.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { ConfigItem, ResourceSelector } from "@components/config/util"; import { useRead } from "@lib/hooks"; -import { fmt_verison } from "@lib/utils"; +import { fmt_version } from "@lib/utils"; import { Types } from "@monitor/client"; import { Input } from "@ui/input"; import { @@ -40,7 +40,7 @@ const BuildVersionSelector = ({ key={JSON.stringify(v.version) + v.ts} value={JSON.stringify(v.version)} > - {fmt_verison(v.version)} + {fmt_version(v.version)} ))} diff --git a/frontend/src/components/resources/deployment/index.tsx b/frontend/src/components/resources/deployment/index.tsx index c014fc846..9b4a68bd0 100644 --- a/frontend/src/components/resources/deployment/index.tsx +++ b/frontend/src/components/resources/deployment/index.tsx @@ -2,7 +2,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 { cn } from "@lib/utils"; +import { cn, fmt_date_with_minutes } from "@lib/utils"; import { useState } from "react"; import { NewResource, Section } from "@components/layouts"; @@ -26,18 +26,27 @@ export const useDeployment = (id?: string) => (d) => d.id === id ); +const deployment_state_color = (state: Types.DockerContainerState) => { + if (state === Types.DockerContainerState.Running) return "green-500"; + if (state === Types.DockerContainerState.Paused) return "orange-500"; + if (state === Types.DockerContainerState.NotDeployed) return "blue-500"; + return "red-500"; +}; + +const deployment_state_fill_color = (state: Types.DockerContainerState) => { + return `fill-${deployment_state_color(state)}` +}; + +const deployment_state_text_color = (state: Types.DockerContainerState) => { + return `text-${deployment_state_color(state)}`; +}; + const Icon = ({ id }: { id?: string }) => { const state = useDeployment(id)?.info.state; - const color = () => { - if (state === Types.DockerContainerState.Running) return "fill-green-500"; - if (state === Types.DockerContainerState.Paused) return "fill-orange-500"; - if (state === Types.DockerContainerState.NotDeployed) - return "fill-blue-500"; - return "fill-red-500"; - }; - - return ; + return ( + + ); }; const Name = ({ id }: { id: string }) => <>{useDeployment(id)?.name}; @@ -124,12 +133,17 @@ export const Deployment: RequiredResourceComponents = { to={`/deployments/${id}`} className="flex items-center gap-2" > - - + + ); }, }, + // { + // header: "Description", + // accessorKey: "description", + // }, + { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, { header: "Server", cell: ({ row }) => { @@ -159,7 +173,21 @@ export const Deployment: RequiredResourceComponents = { accessorKey: "info.image", header: "Image", }, - { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + { + header: "Status", + cell: ({ row }) => { + const status = row.original.info.status; + if (!status) return null; + const state = row.original.info.state; + const color = deployment_state_text_color(state); + return
{status}
; + }, + }, + { + header: "Created", + accessorFn: ({ created_at }) => + fmt_date_with_minutes(new Date(created_at)), + }, ]} /> ); diff --git a/frontend/src/components/resources/procedure/index.tsx b/frontend/src/components/resources/procedure/index.tsx index 62c00e304..2a9e26146 100644 --- a/frontend/src/components/resources/procedure/index.tsx +++ b/frontend/src/components/resources/procedure/index.tsx @@ -4,8 +4,10 @@ import { ConfirmButton } from "@components/util"; import { useExecute, useRead, useWrite } from "@lib/hooks"; import { Types } from "@monitor/client"; import { Execution } from "@monitor/client/dist/types"; +import { Icon } from "@radix-ui/react-select"; import { RequiredResourceComponents, UsableResource } from "@types"; import { Button } from "@ui/button"; +import { DataTable } from "@ui/data-table"; import { Input } from "@ui/input"; import { Select, @@ -15,12 +17,14 @@ import { SelectTrigger, SelectValue, } from "@ui/select"; -import { Loader2, Route, Save } from "lucide-react"; +import { Link, Loader2, Route, Save } from "lucide-react"; import React, { useEffect, useState } from "react"; const useProcedure = (id?: string) => useRead("ListProcedures", {}).data?.find((d) => d.id === id); +const Name = ({ id }: { id: string }) => <>{useProcedure(id)?.name}; + const get_default_data = ( type: T ): string[] | Types.Execution => { @@ -396,5 +400,32 @@ export const Procedure: RequiredResourceComponents = { /> ); }, + Table: () => { + const alerters = useRead("ListAlerters", {}).data; + return ( + { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + ]} + /> + ); + }, New: () => , }; diff --git a/frontend/src/components/resources/repo/index.tsx b/frontend/src/components/resources/repo/index.tsx index 90d8a6d24..f364ea917 100644 --- a/frontend/src/components/resources/repo/index.tsx +++ b/frontend/src/components/resources/repo/index.tsx @@ -2,15 +2,19 @@ import { ConfigInner } from "@components/config"; import { AccountSelector, ResourceSelector } from "@components/config/util"; import { useRead, useWrite } from "@lib/hooks"; import { Types } from "@monitor/client"; +import { Icon } from "@radix-ui/react-select"; import { RequiredResourceComponents } from "@types"; -import { GitBranch } from "lucide-react"; +import { DataTable } from "@ui/data-table"; +import { GitBranch, Link } from "lucide-react"; import { useState } from "react"; const useRepo = (id?: string) => useRead("ListRepos", {}).data?.find((d) => d.id === id); +const Name = ({ id }: { id: string }) => <>{useRepo(id)?.name}; + export const Repo: RequiredResourceComponents = { - Name: ({ id }) => <>{useRepo(id)?.name}, + Name, Description: ({ id }) => <>{id}, Info: ({ id }) => <>{id}, Icon: () => , @@ -56,6 +60,33 @@ export const Repo: RequiredResourceComponents = { ); }, }, + Table: () => { + const alerters = useRead("ListAlerters", {}).data; + return ( + { + const id = row.original.id; + return ( + + + + + ); + }, + }, + { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + ]} + /> + ); + }, Actions: () => null, New: () => null, }; diff --git a/frontend/src/components/resources/server/index.tsx b/frontend/src/components/resources/server/index.tsx index 268813618..9f98d2115 100644 --- a/frontend/src/components/resources/server/index.tsx +++ b/frontend/src/components/resources/server/index.tsx @@ -1,5 +1,5 @@ import { useRead, useWrite } from "@lib/hooks"; -import { cn } from "@lib/utils"; +import { cn, fmt_date_with_minutes } from "@lib/utils"; import { Types } from "@monitor/client"; import { RequiredResourceComponents } from "@types"; import { MapPin, Cpu, MemoryStick, Database, ServerIcon } from "lucide-react"; @@ -122,8 +122,10 @@ const NewServer = () => { ); }; +const Name = ({ id }: { id: string }) => <>{useServer(id)?.name}; + export const ServerComponents: RequiredResourceComponents = { - Name: ({ id }) => <>{useServer(id)?.name}, + Name, Description: ({ id }) => <>{useServer(id)?.info.status}, Info: ({ id }) => , Actions: () => null, @@ -153,8 +155,30 @@ export const ServerComponents: RequiredResourceComponents = { ); }, }, - { header: "Region", accessorKey: "info.region" }, + // { + // header: "Description", + // accessorKey: "description", + // }, { header: "Tags", accessorFn: ({ tags }) => tags.join(", ") }, + { + header: "Deployments", + cell: ({ row: { original: { id } } }) => { + const count = useRead("ListDeployments", { + query: { specific: { server_ids: [id] } }, + }).data?.length; + if (count) { + return <>{count} + } else { + return <>0 + } + }, + }, + { header: "Region", accessorKey: "info.region" }, + { + header: "Created", + accessorFn: ({ created_at }) => + fmt_date_with_minutes(new Date(created_at)), + }, ]} /> ); diff --git a/frontend/src/components/updates/details.tsx b/frontend/src/components/updates/details.tsx index bdc76240b..a590e06d2 100644 --- a/frontend/src/components/updates/details.tsx +++ b/frontend/src/components/updates/details.tsx @@ -16,7 +16,7 @@ import { } from "@ui/card"; import { ReactNode } from "react"; import { useRead } from "@lib/hooks"; -import { fmt_duration, fmt_verison } from "@lib/utils"; +import { fmt_duration, fmt_version } from "@lib/utils"; import { ResourceComponents } from "@components/resources"; export const UpdateUser = ({ user_id }: { user_id: string }) => { @@ -53,7 +53,7 @@ export const UpdateDetails = ({ .split("_") .map((s) => s[0].toUpperCase() + s.slice(1)) .join(" ")}{" "} - {fmt_verison(update.version)} + {fmt_version(update.version)}
@@ -68,7 +68,7 @@ export const UpdateDetails = ({ {update.version && (
- {fmt_verison(update.version)} + {fmt_version(update.version)}
)}
diff --git a/frontend/src/components/updates/header.tsx b/frontend/src/components/updates/header.tsx index 6f2672de9..aa457e526 100644 --- a/frontend/src/components/updates/header.tsx +++ b/frontend/src/components/updates/header.tsx @@ -10,7 +10,7 @@ import { Button } from "@ui/button"; import { Calendar, User } from "lucide-react"; import { UpdateDetails, UpdateUser } from "./details"; import { ResourceComponents } from "@components/resources"; -import { cn, fmt_verison } from "@lib/utils"; +import { cn, fmt_version } from "@lib/utils"; import { Types } from "@monitor/client"; const fmt_date = (d: Date) => @@ -38,7 +38,7 @@ export const SingleUpdate = ({ update }: { update: Types.UpdateListItem }) => { {update.operation.match(/[A-Z][a-z]+|[0-9]+/g)?.join(" ")}
- {fmt_verison(update.version)} + {fmt_version(update.version)}
diff --git a/frontend/src/components/updates/resource.tsx b/frontend/src/components/updates/resource.tsx index 6bd89026d..6bc9e9938 100644 --- a/frontend/src/components/updates/resource.tsx +++ b/frontend/src/components/updates/resource.tsx @@ -20,7 +20,7 @@ import { import { Link } from "react-router-dom"; import { Types } from "@monitor/client"; import { Section } from "@components/layouts"; -import { fmt_update_date, fmt_verison } from "@lib/utils"; +import { fmt_update_date, fmt_version } from "@lib/utils"; import { UpdateDetails, UpdateUser } from "./details"; import { UpdateStatus } from "@monitor/client/dist/types"; @@ -56,7 +56,7 @@ const UpdateCard = ({ update }: { update: Types.UpdateListItem }) => { {update.operation} - {fmt_verison(update.version)} + {fmt_version(update.version)}
diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 64e5bae1c..56f344ab3 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -20,10 +20,16 @@ export const RESOURCE_TARGETS: UsableResource[] = [ "Procedure", ]; -export const fmt_update_date = (d: Date) => - `${d.getDate()}/${d.getMonth() + 1} @ ${d.getHours()}:${d.getMinutes()}`; +export const fmt_update_date = (d: Date) => { + return `${d.getDate()}/${d.getMonth() + 1} @ ${d.getHours()}:${d.getMinutes()}`; +}; -export const fmt_verison = (version: Types.Version | undefined) => { +export const fmt_date_with_minutes = (d: Date) => { + // return `${d.toLocaleDateString()} ${d.toLocaleTimeString()}`; + return d.toLocaleString() +} + +export const fmt_version = (version: Types.Version | undefined) => { if (!version) return "..."; const { major, minor, patch } = version; if (major === 0 && minor === 0 && patch === 0) return "latest";