diff --git a/frontend/package.json b/frontend/package.json index 1545f79a5..3a5a24832 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,7 +27,7 @@ "cmdk": "^0.2.0", "jotai": "^2.4.1", "lightweight-charts": "^4.0.1", - "lucide-react": "^0.274.0", + "lucide-react": "^0.285.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-minimal-pie-chart": "^8.4.0", diff --git a/frontend/src/components/config/util.tsx b/frontend/src/components/config/util.tsx index 4c688a2f5..41ffcf5b2 100644 --- a/frontend/src/components/config/util.tsx +++ b/frontend/src/components/config/util.tsx @@ -155,10 +155,11 @@ export const ResourceSelector = ({ onSelect: (id: string) => void; }) => { const resources = useRead(`List${type}s`, {}).data; + const value = resources?.find((r) => r.id === selected)?.name; return ( - - + {value ?? `Select ${type}`} @@ -187,9 +188,7 @@ export const AccountSelector = ({ onSelect: (id: string) => void; }) => { const request = - type === "Server" - ? "GetServerAvailableAccounts" - : "GetBuilderAvailableAccounts"; + type === "Server" ? "GetAvailableAccounts" : "GetBuilderAvailableAccounts"; const accounts = useRead(request, { id: id! }, { enabled: !!id }).data; return ( diff --git a/frontend/src/components/permissions.tsx b/frontend/src/components/permissions.tsx index d7c6c7d17..e66ac159b 100644 --- a/frontend/src/components/permissions.tsx +++ b/frontend/src/components/permissions.tsx @@ -26,8 +26,6 @@ import { ResourceTarget } from "@monitor/client/dist/types"; const Username = ({ user_id }: { user_id: string }) => { const username = useRead("GetUsername", { user_id }).data?.username; - - console.log(user_id, useRead("GetUsername", { user_id }).data); return <>{username}; }; @@ -138,8 +136,6 @@ export const ResourcePermissions = ({ .filter((id) => id != me) .filter((id) => !users?.find((u) => u._id?.$oid === id)?.admin); - console.log(users, display); - if (!admin || !display.length) return null; return ( diff --git a/frontend/src/components/resources/index.tsx b/frontend/src/components/resources/index.tsx index 614d53dea..4fe6c7eaf 100644 --- a/frontend/src/components/resources/index.tsx +++ b/frontend/src/components/resources/index.tsx @@ -5,6 +5,7 @@ import { Builder } from "./builder"; import { Deployment } from "./deployment"; import { Repo } from "./repo"; import { Server } from "./server"; +import { Procedure } from "./procedure"; export const ResourceComponents: { [key in UsableResource]: RequiredResourceComponents; @@ -15,4 +16,5 @@ export const ResourceComponents: { Deployment, Repo, Server, + Procedure, }; diff --git a/frontend/src/components/resources/procedure/index.tsx b/frontend/src/components/resources/procedure/index.tsx new file mode 100644 index 000000000..677600474 --- /dev/null +++ b/frontend/src/components/resources/procedure/index.tsx @@ -0,0 +1,403 @@ +import { ResourceSelector } from "@components/config/util"; +import { NewResource } from "@components/layouts"; +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 { RequiredResourceComponents, UsableResource } from "@types"; +import { Button } from "@ui/button"; +import { Input } from "@ui/input"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@ui/select"; +import { Loader2, Route, Save } from "lucide-react"; +import React, { useState } from "react"; + +const useProcedure = (id?: string) => + useRead("ListProcedures", {}).data?.find((d) => d.id === id); + +const get_default_data = ( + type: T +): string[] | Types.Execution => { + if (type === "Execution") return { type: "None", params: {} }; + return [] as string[]; +}; + +const NewProcedure = ({ parent }: { parent?: Types.Procedure }) => { + const [name, setName] = useState(""); + const [type, setType] = useState("Execution"); + + const update_parent = useWrite("UpdateProcedure").mutate; + + const { mutateAsync } = useWrite("CreateProcedure", { + onSuccess: ({ _id }) => { + if (!parent) return; + if ( + parent.config.type === "Sequence" || + parent.config.type === "Parallel" + ) { + update_parent({ + id: parent._id?.$oid!, + config: { + ...parent.config, + data: [...parent.config.data, _id?.$oid!], + }, + }); + } + }, + }); + + return ( + + mutateAsync({ + name, + config: { + type, + data: get_default_data(type), + } as Types.ProcedureConfig, + }) + } + enabled={!!name} + > +
+ Procedure Name + setName(e.target.value)} + /> +
+
+ Procedure Type + +
+
+ ); +}; + +type ExecutionType = Extract< + Types.ProcedureConfig, + { type: "Execution" } +>["data"]["type"]; + +const TypeSelector = ({ + type, + selected, + onSelect, +}: { + type: UsableResource; + selected: string; + onSelect: (value: string) => void; +}) => ( +
+ {type} + +
+); + +// const EXECUTION_TYPES: ExecutionType[] = [ +// "None", +// "CloneRepo", +// "Deploy", +// "PruneDockerContainers", +// "PruneDockerImages", +// "PruneDockerNetworks", +// "PullRepo", +// "RemoveContainer", +// "RunBuild", +// "RunProcedure", +// "StartContainer", +// "StopAllContainers", +// "StopContainer", +// ]; + +type ExecutionConfigComponent< + T extends ExecutionType, + P = Extract["params"] +> = React.FC<{ + params: P; + setParams: React.Dispatch>; +}>; + +type ExecutionConfigParams = Extract< + Execution, + { type: T } +>["params"]; + +type ExecutionConfigs = { + [ExType in ExecutionType]: { + component: ExecutionConfigComponent; + params: ExecutionConfigParams; + }; +}; + +const EXEC_TYPES: ExecutionConfigs = { + None: { + params: {}, + component: () => <>, + }, + CloneRepo: { + params: { id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, id }))} + /> + ), + }, + Deploy: { + params: { deployment_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, deployment_id: id }))} + /> + ), + }, + PruneDockerContainers: { + params: { server_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, server_id }))} + /> + ), + }, + PruneDockerImages: { + params: { server_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, id }))} + /> + ), + }, + PruneDockerNetworks: { + params: { server_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, id }))} + /> + ), + }, + PullRepo: { + params: { id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, id }))} + /> + ), + }, + RemoveContainer: { + params: { deployment_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, deployment_id: id }))} + /> + ), + }, + RunBuild: { + params: { build_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, build_id }))} + /> + ), + }, + RunProcedure: { + params: { procedure_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, procedure_id: id }))} + /> + ), + }, + StartContainer: { + params: { deployment_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, deployment_id: id }))} + /> + ), + }, + StopAllContainers: { + params: { server_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, server_id: id }))} + /> + ), + }, + StopContainer: { + params: { deployment_id: "" }, + component: ({ params, setParams }) => ( + setParams((p) => ({ ...p, deployment_id: id }))} + /> + ), + }, +}; + +const UpdateProcedure = ({ + id, + procedure, +}: { + id: string; + procedure: Types.ProcedureConfig; +}) => { + const { mutate } = useWrite("UpdateProcedure"); + + return ( + + ); +}; + +const ExecutionConfig = ({ id }: { id: string }) => { + const procedure = useRead("GetProcedure", { id }).data; + if (procedure?.config.type !== "Execution") return null; + + const [type, setType] = useState(procedure.config.data.type); + const [params, setParams] = useState( + procedure.config.data ?? EXEC_TYPES[type].params + ); + + const Component = EXEC_TYPES[type].component; + + console.log(params); + + return ( +
+
+ {procedure.name} + +
+
+ Execution Type + +
+
+ +
+
+
{JSON.stringify(procedure?.config, null, 2)}
+
+
+ ); +}; + +const SequenceConfig = ({ id }: { id: string }) => { + const procedure = useRead("GetProcedure", { id }).data; + if (procedure?.config.type !== "Sequence") return null; + + return ( +
+
+ {procedure?.name} + +
+
{JSON.stringify(procedure?.config, null, 2)}
+
+ ); +}; + +export const ProcedureConfig = ({ id }: { id: string }) => { + const procedure = useRead("GetProcedure", { id }).data; + if (procedure?.config.type === "Sequence") return ; + if (procedure?.config.type === "Execution") + return ; +}; + +export const Procedure: RequiredResourceComponents = { + Name: ({ id }) => <>{useProcedure(id)?.name}, + Description: ({ id }) => <>{useProcedure(id)?.info.procedure_type}, + Info: ({ id }) => <>{id}, + Icon: () => , + Page: { + Config: ({ id }) => , + }, + Actions: ({ id }) => { + const running = useRead("GetProcedureActionState", { id }).data?.running; + const { mutate, isLoading } = useExecute("RunProcedure"); + return ( + + ) : ( + + ) + } + onClick={() => mutate({ procedure_id: id })} + disabled={running || isLoading} + /> + ); + }, + New: () => , +}; diff --git a/frontend/src/lib/hooks.ts b/frontend/src/lib/hooks.ts index 023027494..d9fb552e0 100644 --- a/frontend/src/lib/hooks.ts +++ b/frontend/src/lib/hooks.ts @@ -34,11 +34,11 @@ export const useRead = < (T | P)[] >, "queryFn" | "queryKey" - >, + > >( type: T, params: P, - config?: C, + config?: C ) => useQuery([type, params], () => client.read({ type, params } as R), config); export const useWrite = < @@ -48,10 +48,10 @@ export const useWrite = < C extends Omit< UseMutationOptions, "mutationKey" | "mutationFn" - >, + > >( type: T, - config?: C, + config?: C ) => useMutation([type], (params: P) => client.write({ type, params } as R), { ...config, @@ -67,10 +67,10 @@ export const useExecute = < C extends Omit< UseMutationOptions, "mutationKey" | "mutationFn" - >, + > >( type: T, - config?: C, + config?: C ) => useMutation([type], (params: P) => client.execute({ type, params } as R), { ...config, @@ -85,14 +85,10 @@ export const useInvalidate = () => { return < T extends Types.ReadRequest["type"], - P = Extract["params"], + P = Extract["params"] >( ...keys: Array<[T] | [T, P]> - ) => - keys.forEach((k) => { - console.log("invalidating", k); - qc.invalidateQueries([...k]); - }); + ) => keys.forEach((k) => qc.invalidateQueries([...k])); }; export const usePushRecentlyViewed = ({ type, id }: Types.ResourceTarget) => { diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index b5a024068..64e5bae1c 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -17,6 +17,7 @@ export const RESOURCE_TARGETS: UsableResource[] = [ "Deployment", "Repo", "Server", + "Procedure", ]; export const fmt_update_date = (d: Date) => diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 3e98b94ee..42d25af04 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,4 +1,5 @@ import "globals.css"; +import React from "react"; import ReactDOM from "react-dom/client"; import { MonitorClient } from "@monitor/client"; import { ThemeProvider } from "@ui/theme"; @@ -21,14 +22,14 @@ const query_client = new QueryClient({ }); ReactDOM.createRoot(document.getElementById("root")!).render( - // - - - - - - - - - // + + + + + + + + + + ); diff --git a/frontend/src/types.d.ts b/frontend/src/types.d.ts index 84cfcb8b6..d225c7e59 100644 --- a/frontend/src/types.d.ts +++ b/frontend/src/types.d.ts @@ -2,19 +2,6 @@ import { Types } from "@monitor/client"; export type UsableResource = Exclude; -// export type RequiredComponents = -// | "Name" -// | "Description" -// | "Icon" -// | "Info" -// | "Actions"; - -// export type RequiredResourceComponents = { -// [key in RequiredComponents]: React.FC<{ id: string }>; -// } & { Page: { [key: string]: React.FC<{ id: string }> } } & { -// New: () => React.ReactNode; -// }; - type IdComponent = React.FC<{ id: string }>; type OptionalIdComponent = React.FC<{ id?: string }>; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0590d6f85..4f4665808 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1968,10 +1968,10 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lucide-react@^0.274.0: - version "0.274.0" - resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.274.0.tgz#d3b54dcb972b12f1292061448d61d422ef2e269d" - integrity sha512-qiWcojRXEwDiSimMX1+arnxha+ROJzZjJaVvCC0rsG6a9pUPjZePXSq7em4ZKMp0NDm1hyzPNkM7UaWC3LU2AA== +lucide-react@^0.285.0: + version "0.285.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.285.0.tgz#2929460f74aeb6b709ef5c1f9fad7cd208cd6507" + integrity sha512-TvWtS0Zc2lT0wTMyD+sEB7x9TM/38MQMJfJbQMMWJOsPx+lEaWBk1aKalqhCZj/Vbl2r00Uqln7xTTY2T7R63g== merge2@^1.3.0, merge2@^1.4.1: version "1.4.1"