From cc63118868754686685701ad54e58104a347ef8e Mon Sep 17 00:00:00 2001 From: Kacper Wojciechowski <39823706+jog1t@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:01:35 +0100 Subject: [PATCH] feat: add more options to create actor form --- .../dialogs/create-actor-dialog.tsx | 13 +- .../project/components/region-select.tsx | 54 ++++++++ .../project/forms/actor-create-form.tsx | 121 +++++++++++++++++- .../domains/project/forms/build-tags-form.tsx | 2 +- .../project/queries/actors/mutations.ts | 7 +- .../project/queries/actors/query-options.ts | 2 +- .../$environmentNameId/actors.tsx | 2 +- .../packages/components/src/ui/button.tsx | 36 ++---- 8 files changed, 205 insertions(+), 32 deletions(-) create mode 100644 frontend/apps/hub/src/domains/project/components/region-select.tsx diff --git a/frontend/apps/hub/src/domains/project/components/dialogs/create-actor-dialog.tsx b/frontend/apps/hub/src/domains/project/components/dialogs/create-actor-dialog.tsx index e3e42d4125..527de21500 100644 --- a/frontend/apps/hub/src/domains/project/components/dialogs/create-actor-dialog.tsx +++ b/frontend/apps/hub/src/domains/project/components/dialogs/create-actor-dialog.tsx @@ -31,9 +31,11 @@ export default function CreateActorDialog({ projectNameId, environmentNameId, buildId: values.buildId, + region: values.regionId, + parameters: values.parameters, }); }} - defaultValues={{ buildId: "" }} + defaultValues={{ buildId: "", regionId: "" }} > Create Actor @@ -47,6 +49,15 @@ export default function CreateActorDialog({ projectNameId={projectNameId} environmentNameId={environmentNameId} /> + + + diff --git a/frontend/apps/hub/src/domains/project/components/region-select.tsx b/frontend/apps/hub/src/domains/project/components/region-select.tsx new file mode 100644 index 0000000000..0a734b879c --- /dev/null +++ b/frontend/apps/hub/src/domains/project/components/region-select.tsx @@ -0,0 +1,54 @@ +import { Combobox } from "@rivet-gg/components"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import { actorRegionsQueryOptions } from "../queries"; +import { ActorRegion } from "./actors/actor-region"; + +interface RegionSelectProps { + projectNameId: string; + environmentNameId: string; + onValueChange: (value: string) => void; + value: string; +} + +export function RegionSelect({ + projectNameId, + environmentNameId, + onValueChange, + value, +}: RegionSelectProps) { + const { data } = useSuspenseQuery( + actorRegionsQueryOptions({ + projectNameId, + environmentNameId, + }), + ); + + const regions = data.map((region) => { + return { + label: ( + + ), + value: region.id, + region, + }; + }); + + return ( + + option.region.id.includes(search) || + option.region.name.includes(search) + } + className="w-full" + /> + ); +} diff --git a/frontend/apps/hub/src/domains/project/forms/actor-create-form.tsx b/frontend/apps/hub/src/domains/project/forms/actor-create-form.tsx index 55fee5e8c2..3a66a1ce16 100644 --- a/frontend/apps/hub/src/domains/project/forms/actor-create-form.tsx +++ b/frontend/apps/hub/src/domains/project/forms/actor-create-form.tsx @@ -4,14 +4,37 @@ import { FormItem, FormLabel, FormMessage, + Label, createSchemaForm, } from "@rivet-gg/components"; +import { JsonCode } from "@rivet-gg/components/code-mirror"; import { type UseFormReturn, useFormContext } from "react-hook-form"; import z from "zod"; import { BuildSelect } from "../components/build-select"; +import { RegionSelect } from "../components/region-select"; + +import { + Tags as TagsInput, + formSchema as tagsFormSchema, +} from "@/domains/project/forms/build-tags-form"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import { useState } from "react"; +import { actorBuildsQueryOptions } from "../queries"; + +const jsonValid = z.custom((value) => { + try { + JSON.parse(value); + return true; + } catch { + return false; + } +}); export const formSchema = z.object({ - buildId: z.string(), + buildId: z.string().nonempty(), + regionId: z.string().nonempty(), + parameters: jsonValid.optional(), + tags: tagsFormSchema.shape.tags, }); export type FormValues = z.infer; @@ -49,3 +72,99 @@ export const Build = ({ /> ); }; + +export const Region = ({ + projectNameId, + environmentNameId, +}: { projectNameId: string; environmentNameId: string }) => { + const { control } = useFormContext(); + return ( + ( + + Region + + + + + + )} + /> + ); +}; + +export const Parameters = () => { + const { control } = useFormContext(); + return ( + ( + + Parameters + + + + + + )} + /> + ); +}; + +export const Tags = ({ + projectNameId, + environmentNameId, +}: { projectNameId: string; environmentNameId: string }) => { + const { data: builds } = useSuspenseQuery( + actorBuildsQueryOptions({ projectNameId, environmentNameId }), + ); + + const [tagKeys, setTagKeys] = useState(() => + Array.from( + new Set( + builds?.flatMap((build) => Object.keys(build.tags)), + ).values(), + ).map((key) => ({ + label: key, + value: key, + })), + ); + + const [tagValues, setTagValues] = useState(() => + Array.from( + new Set( + builds?.flatMap((build) => Object.values(build.tags)), + ).values(), + ).map((key) => ({ label: key, value: key })), + ); + + return ( +
+ + + setTagKeys((opts) => [ + ...opts, + { label: option, value: option }, + ]) + } + onCreateValueOption={(option) => + setTagValues((opts) => [ + ...opts, + { label: option, value: option }, + ]) + } + /> +
+ ); +}; diff --git a/frontend/apps/hub/src/domains/project/forms/build-tags-form.tsx b/frontend/apps/hub/src/domains/project/forms/build-tags-form.tsx index 78df25dbd6..ba19167987 100644 --- a/frontend/apps/hub/src/domains/project/forms/build-tags-form.tsx +++ b/frontend/apps/hub/src/domains/project/forms/build-tags-form.tsx @@ -6,7 +6,7 @@ import { FormItem, FormLabel, FormMessage, - type Option, + type ComboboxOption as Option, Text, createSchemaForm, } from "@rivet-gg/components"; diff --git a/frontend/apps/hub/src/domains/project/queries/actors/mutations.ts b/frontend/apps/hub/src/domains/project/queries/actors/mutations.ts index c1630e0b1e..1857c18764 100644 --- a/frontend/apps/hub/src/domains/project/queries/actors/mutations.ts +++ b/frontend/apps/hub/src/domains/project/queries/actors/mutations.ts @@ -135,10 +135,14 @@ export function useCreateActorFromSdkMutation({ projectNameId, environmentNameId, buildId, + region, + parameters, }: { projectNameId: string; environmentNameId: string; buildId: string; + region: string; + parameters: unknown; }) => { const managerUrl = await queryClient.fetchQuery( actorManagerUrlQueryOptions({ @@ -158,7 +162,8 @@ export function useCreateActorFromSdkMutation({ const cl = new Client(managerUrl); await cl.create({ - create: { tags: { name: build.tags.name || build.id } }, + parameters, + create: { tags: { name: build.tags.name || build.id }, region }, }); }, onSuccess: async () => { diff --git a/frontend/apps/hub/src/domains/project/queries/actors/query-options.ts b/frontend/apps/hub/src/domains/project/queries/actors/query-options.ts index bf29876e20..735a81c692 100644 --- a/frontend/apps/hub/src/domains/project/queries/actors/query-options.ts +++ b/frontend/apps/hub/src/domains/project/queries/actors/query-options.ts @@ -264,7 +264,7 @@ export const actorBuildsQueryOptions = ({ "actor-builds", tags, ] as const, - refetchInterval: 5000, + refetchInterval: 2000, queryFn: ({ queryKey: [ // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/frontend/apps/hub/src/routes/_authenticated/_layout/projects/$projectNameId/environments/$environmentNameId/actors.tsx b/frontend/apps/hub/src/routes/_authenticated/_layout/projects/$projectNameId/environments/$environmentNameId/actors.tsx index da5641d4b6..d97dfb3eaf 100644 --- a/frontend/apps/hub/src/routes/_authenticated/_layout/projects/$projectNameId/environments/$environmentNameId/actors.tsx +++ b/frontend/apps/hub/src/routes/_authenticated/_layout/projects/$projectNameId/environments/$environmentNameId/actors.tsx @@ -22,7 +22,7 @@ function ProjectActorsRoute() { projectNameId, environmentNameId, tags: tagsRecord, - includeDestroyed: showDestroyed, + includeDestroyed: showDestroyed ?? true, }), ); diff --git a/frontend/packages/components/src/ui/button.tsx b/frontend/packages/components/src/ui/button.tsx index 6a015713cb..46273aff3a 100644 --- a/frontend/packages/components/src/ui/button.tsx +++ b/frontend/packages/components/src/ui/button.tsx @@ -30,13 +30,13 @@ const buttonVariants = cva( link: "text-primary underline-offset-4 hover:underline", }, size: { - default: "h-10 px-4 py-2", - xs: "h-5 rounded-md px-2 text-xs", - sm: "h-7 rounded-md px-2 text-xs [&_svg]:size-3", - lg: "h-11 rounded-md px-8", - icon: "h-10 w-10", - "icon-sm": "h-7 w-7 text-xs [&_svg]:size-3", - "icon-xs": "h-5 w-5 text-xs [&_svg]:size-2", + default: "h-10 px-4 py-2 gap-1.5", + xs: "h-5 rounded-md px-2 text-xs gap-0.5", + sm: "h-7 rounded-md px-2 text-xs [&_svg]:size-3 gap-1.5", + lg: "h-11 rounded-md px-8 gap-2", + icon: "h-10 w-10 gap-1.5", + "icon-sm": "h-7 w-7 text-xs [&_svg]:size-3 gap-1.5", + "icon-xs": "h-5 w-5 text-xs [&_svg]:size-2 gap-0.5", }, }, defaultVariants: { @@ -88,31 +88,15 @@ const Button = React.forwardRef( {isLoading ? ( ) : startIcon ? ( - React.cloneElement(startIcon, { - className: cn( - "mr-2", - { - "mr-1.5": size === "sm", - }, - startIcon.props.className, - ), - }) + React.cloneElement(startIcon, startIcon.props) ) : null} {!size?.includes("icon") && isLoading ? null : ( {children} )} - {endIcon - ? React.cloneElement(endIcon, { - ...endIcon.props, - className: cn("ml-2", endIcon.props.className), - }) - : null} + {endIcon ? React.cloneElement(endIcon, endIcon.props) : null} ); },