From 0f53056648c9f1cfc279e5ae811db59437ed308a Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Fri, 14 Feb 2025 08:36:53 +0000 Subject: [PATCH 1/7] add support for presets to the coder frontend --- site/src/api/api.ts | 9 ++ site/src/api/queries/templates.ts | 8 ++ .../CreateWorkspacePage.tsx | 7 ++ .../CreateWorkspacePageView.stories.tsx | 32 +++++++ .../CreateWorkspacePageView.tsx | 94 ++++++++++++++++++- 5 files changed, 149 insertions(+), 1 deletion(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 43051961fa7e7..13db8b841d969 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1145,6 +1145,15 @@ class ApiMethods { return response.data; }; + getTemplateVersionPresets = async ( + templateVersionId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templateversions/${templateVersionId}/presets`, + ); + return response.data; + }; + startWorkspace = ( workspaceId: string, templateVersionId: string, diff --git a/site/src/api/queries/templates.ts b/site/src/api/queries/templates.ts index 8f6399cc4b354..2cd2d7693cfda 100644 --- a/site/src/api/queries/templates.ts +++ b/site/src/api/queries/templates.ts @@ -2,6 +2,7 @@ import { API, type GetTemplatesOptions, type GetTemplatesQuery } from "api/api"; import type { CreateTemplateRequest, CreateTemplateVersionRequest, + Preset, ProvisionerJob, ProvisionerJobStatus, Template, @@ -305,6 +306,13 @@ export const previousTemplateVersion = ( }; }; +export const templateVersionPresets = (versionId: string) => { + return { + queryKey: ["templateVersion", versionId, "presets"], + queryFn: () => API.getTemplateVersionPresets(versionId), + }; +}; + const waitBuildToBeFinished = async ( version: TemplateVersion, onRequest?: (data: TemplateVersion) => void, diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 56bd0da8a0516..a04c46a609711 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -5,6 +5,7 @@ import { richParameters, templateByName, templateVersionExternalAuth, + templateVersionPresets, } from "api/queries/templates"; import { autoCreateWorkspace, createWorkspace } from "api/queries/workspaces"; import type { @@ -56,6 +57,11 @@ const CreateWorkspacePage: FC = () => { const templateQuery = useQuery( templateByName(organizationName, templateName), ); + const templateVersionPresetsQuery = useQuery( + templateQuery.data + ? templateVersionPresets(templateQuery.data.active_version_id) + : { enabled: false }, + ); const permissionsQuery = useQuery( templateQuery.data ? checkAuthorization({ @@ -203,6 +209,7 @@ const CreateWorkspacePage: FC = () => { hasAllRequiredExternalAuth={hasAllRequiredExternalAuth} permissions={permissionsQuery.data as CreateWSPermissions} parameters={realizedParameters as TemplateVersionParameter[]} + presets={templateVersionPresetsQuery.data ?? []} creatingWorkspace={createWorkspaceMutation.isLoading} onCancel={() => { navigate(-1); diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 46f1f87e8a50f..e25bd47d466f5 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -116,6 +116,38 @@ export const Parameters: Story = { }, }; +export const Presets: Story = { + args: { + presets: [ + { + ID: "preset-1", + Name: "Preset 1", + Parameters: [ + { + Name: MockTemplateVersionParameter1.name, + Value: "preset 1 override", + }, + ], + }, + { + ID: "preset-2", + Name: "Preset 2", + Parameters: [ + { + Name: MockTemplateVersionParameter2.name, + Value: "42", + }, + ], + }, + ], + parameters: [ + MockTemplateVersionParameter1, + MockTemplateVersionParameter2, + MockTemplateVersionParameter3, + ], + }, +}; + export const ExternalAuth: Story = { args: { externalAuth: [ diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index cc912e1f6facf..0d770b6cc0673 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -6,6 +6,7 @@ import { Alert } from "components/Alert/Alert"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Avatar } from "components/Avatar/Avatar"; import { Button } from "components/Button/Button"; +import { SelectFilter } from "components/Filter/SelectFilter"; import { FormFields, FormFooter, @@ -64,6 +65,7 @@ export interface CreateWorkspacePageViewProps { hasAllRequiredExternalAuth: boolean; parameters: TypesGen.TemplateVersionParameter[]; autofillParameters: AutofillBuildParameter[]; + presets: TypesGen.Preset[]; permissions: CreateWSPermissions; creatingWorkspace: boolean; onCancel: () => void; @@ -88,6 +90,7 @@ export const CreateWorkspacePageView: FC = ({ hasAllRequiredExternalAuth, parameters, autofillParameters, + presets = [], permissions, creatingWorkspace, onSubmit, @@ -145,6 +148,68 @@ export const CreateWorkspacePageView: FC = ({ [autofillParameters], ); + const presetOptions = useMemo(() => { + return [ + { label: "None", value: "" }, + ...presets.map((preset) => ({ + label: preset.Name, + value: preset.ID, + })), + ]; + }, [presets]); + + const [selectedPresetIndex, setSelectedPresetIndex] = useState(0); + const [presetParameterNames, setPresetParameterNames] = useState( + [], + ); + + useEffect(() => { + // TODO (sasswart): test case: what if immutable parameters are used in the preset? + // TODO (sasswart): test case: what if presets are defined for a template version with no params? + // TODO (sasswart): test case: what if a non active version is selected? + // TODO (sasswart): test case: what if a preset is selected that has no parameters? + // TODO (sasswart): what if we have preset params and autofill params on the same param? + // TODO (sasswart): test case: if we move from preset to no preset, do we reset the params? + // If so, how should it behave? Reset to initial value? reset to last set value? + // TODO (sasswart): test case: rich parameters + + const selectedPresetOption = presetOptions[selectedPresetIndex]; + let selectedPreset: TypesGen.Preset | undefined; + for (const preset of presets) { + if (preset.ID === selectedPresetOption.value) { + selectedPreset = preset; + break; + } + } + + if (!selectedPreset || !selectedPreset.Parameters) { + setPresetParameterNames([]); + return; + } + + setPresetParameterNames(selectedPreset.Parameters.map((p) => p.Name)); + + for (const presetParameter of selectedPreset.Parameters) { + const parameterIndex = parameters.findIndex( + (p) => p.name === presetParameter.Name, + ); + if (parameterIndex === -1) continue; + + const parameterField = `rich_parameter_values.${parameterIndex}`; + + form.setFieldValue(parameterField, { + name: presetParameter.Name, + value: presetParameter.Value, + }); + } + }, [ + presetOptions, + selectedPresetIndex, + presets, + parameters, + form.setFieldValue, + ]); + return ( = ({ )} + {presets.length > 0 && ( + + + + { + setSelectedPresetIndex( + presetOptions.findIndex( + (preset) => preset.value === option?.value, + ), + ); + }} + placeholder="Select a preset" + selectedOption={presetOptions[selectedPresetIndex]} + /> + + + + )} + {/* General info */} = ({ const isDisabled = disabledParams?.includes( parameter.name.toLowerCase().replace(/ /g, "_"), - ) || creatingWorkspace; + ) || + creatingWorkspace || + presetParameterNames.includes(parameter.name); return ( Date: Fri, 14 Feb 2025 08:42:05 +0000 Subject: [PATCH 2/7] Collect all todo testcases in a single file --- coderd/presets_test.go | 8 ++++++++ .../CreateWorkspacePage/CreateWorkspacePageView.tsx | 9 --------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/coderd/presets_test.go b/coderd/presets_test.go index ffe51787d5f5c..2d048abf69d35 100644 --- a/coderd/presets_test.go +++ b/coderd/presets_test.go @@ -15,6 +15,14 @@ import ( ) func TestTemplateVersionPresets(t *testing.T) { + // TODO (sasswart): test case: what if immutable parameters are used in the preset? + // TODO (sasswart): test case: what if presets are defined for a template version with no params? + // TODO (sasswart): test case: what if a non active version is selected? + // TODO (sasswart): test case: what if a preset is selected that has no parameters? + // TODO (sasswart): what if we have preset params and autofill params on the same param? + // TODO (sasswart): test case: if we move from preset to no preset, do we reset the params? + // If so, how should it behave? Reset to initial value? reset to last set value? + // TODO (sasswart): test case: rich parameters // TODO (sasswart): Test case: what if a user tries to read presets or preset parameters from a different org? t.Parallel() diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 0d770b6cc0673..fccea627e4c76 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -164,15 +164,6 @@ export const CreateWorkspacePageView: FC = ({ ); useEffect(() => { - // TODO (sasswart): test case: what if immutable parameters are used in the preset? - // TODO (sasswart): test case: what if presets are defined for a template version with no params? - // TODO (sasswart): test case: what if a non active version is selected? - // TODO (sasswart): test case: what if a preset is selected that has no parameters? - // TODO (sasswart): what if we have preset params and autofill params on the same param? - // TODO (sasswart): test case: if we move from preset to no preset, do we reset the params? - // If so, how should it behave? Reset to initial value? reset to last set value? - // TODO (sasswart): test case: rich parameters - const selectedPresetOption = presetOptions[selectedPresetIndex]; let selectedPreset: TypesGen.Preset | undefined; for (const preset of presets) { From 8d08a644fd0d37f6d3f3a41762b0a2eaa994ee99 Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Fri, 14 Feb 2025 09:22:12 +0000 Subject: [PATCH 3/7] remove todos --- coderd/presets_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/coderd/presets_test.go b/coderd/presets_test.go index 2d048abf69d35..96d1a03e94b1f 100644 --- a/coderd/presets_test.go +++ b/coderd/presets_test.go @@ -15,16 +15,6 @@ import ( ) func TestTemplateVersionPresets(t *testing.T) { - // TODO (sasswart): test case: what if immutable parameters are used in the preset? - // TODO (sasswart): test case: what if presets are defined for a template version with no params? - // TODO (sasswart): test case: what if a non active version is selected? - // TODO (sasswart): test case: what if a preset is selected that has no parameters? - // TODO (sasswart): what if we have preset params and autofill params on the same param? - // TODO (sasswart): test case: if we move from preset to no preset, do we reset the params? - // If so, how should it behave? Reset to initial value? reset to last set value? - // TODO (sasswart): test case: rich parameters - // TODO (sasswart): Test case: what if a user tries to read presets or preset parameters from a different org? - t.Parallel() givenPreset := codersdk.Preset{ From da9239e23578bbef12e49fd8e8c0b0e6768d7165 Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Fri, 14 Feb 2025 09:33:33 +0000 Subject: [PATCH 4/7] add a story to test when a preset has been selected --- .../CreateWorkspacePageView.stories.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index e25bd47d466f5..e3d706afc7707 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -10,6 +10,8 @@ import { mockApiError, } from "testHelpers/entities"; import { CreateWorkspacePageView } from "./CreateWorkspacePageView"; +import { within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; const meta: Meta = { title: "pages/CreateWorkspacePage", @@ -116,7 +118,7 @@ export const Parameters: Story = { }, }; -export const Presets: Story = { +export const PresetsButNoneSelected: Story = { args: { presets: [ { @@ -148,6 +150,15 @@ export const Presets: Story = { }, }; +export const PresetSelected: Story = { + args: PresetsButNoneSelected.args, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getByLabelText("Preset")); + await userEvent.click(canvas.getByText("Preset 1")); + }, +}; + export const ExternalAuth: Story = { args: { externalAuth: [ From e47296c351e26a198440f37a105451b4639a747b Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Fri, 14 Feb 2025 09:43:33 +0000 Subject: [PATCH 5/7] make -B fmt --- .../CreateWorkspacePage/CreateWorkspacePageView.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index e3d706afc7707..6f0647c9f28e8 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -1,5 +1,7 @@ import { action } from "@storybook/addon-actions"; import type { Meta, StoryObj } from "@storybook/react"; +import { within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { chromatic } from "testHelpers/chromatic"; import { MockTemplate, @@ -10,8 +12,6 @@ import { mockApiError, } from "testHelpers/entities"; import { CreateWorkspacePageView } from "./CreateWorkspacePageView"; -import { within } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; const meta: Meta = { title: "pages/CreateWorkspacePage", From 6787f107b45a5fa4e699d7ee67e34d0a05ae9faf Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Tue, 18 Feb 2025 07:59:06 +0000 Subject: [PATCH 6/7] review notes --- .../CreateWorkspacePage.tsx | 9 ++-- .../CreateWorkspacePageView.tsx | 54 +++++++++---------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index a04c46a609711..b2481b4729915 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -57,11 +57,10 @@ const CreateWorkspacePage: FC = () => { const templateQuery = useQuery( templateByName(organizationName, templateName), ); - const templateVersionPresetsQuery = useQuery( - templateQuery.data - ? templateVersionPresets(templateQuery.data.active_version_id) - : { enabled: false }, - ); + const templateVersionPresetsQuery = useQuery({ + ...templateVersionPresets(templateQuery.data?.active_version_id ?? ""), + enabled: templateQuery.data !== undefined, + }); const permissionsQuery = useQuery( templateQuery.data ? checkAuthorization({ diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index fccea627e4c76..ab97493c30b79 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -148,14 +148,17 @@ export const CreateWorkspacePageView: FC = ({ [autofillParameters], ); - const presetOptions = useMemo(() => { - return [ + const [presetOptions, setPresetOptions] = useState([ + { label: "None", value: "" }, + ]); + useEffect(() => { + setPresetOptions([ { label: "None", value: "" }, ...presets.map((preset) => ({ label: preset.Name, value: preset.ID, })), - ]; + ]); }, [presets]); const [selectedPresetIndex, setSelectedPresetIndex] = useState(0); @@ -245,31 +248,6 @@ export const CreateWorkspacePageView: FC = ({ )} - {presets.length > 0 && ( - - - - { - setSelectedPresetIndex( - presetOptions.findIndex( - (preset) => preset.value === option?.value, - ), - ); - }} - placeholder="Select a preset" - selectedOption={presetOptions[selectedPresetIndex]} - /> - - - - )} - {/* General info */} = ({ )} + {presets.length > 0 && ( + + { + setSelectedPresetIndex( + presetOptions.findIndex( + (preset) => preset.value === option?.value, + ), + ); + }} + placeholder="Select a preset" + selectedOption={presetOptions[selectedPresetIndex]} + /> + + Select a preset to get you started. + + + )}
Date: Tue, 18 Feb 2025 11:53:29 +0000 Subject: [PATCH 7/7] review notes --- .../CreateWorkspacePageView.tsx | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index ab97493c30b79..de72a79e456ef 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -273,23 +273,25 @@ export const CreateWorkspacePageView: FC = ({ )} {presets.length > 0 && ( - - { - setSelectedPresetIndex( - presetOptions.findIndex( - (preset) => preset.value === option?.value, - ), - ); - }} - placeholder="Select a preset" - selectedOption={presetOptions[selectedPresetIndex]} - /> + - Select a preset to get you started. + Select a preset to get started + + { + setSelectedPresetIndex( + presetOptions.findIndex( + (preset) => preset.value === option?.value, + ), + ); + }} + placeholder="Select a preset" + selectedOption={presetOptions[selectedPresetIndex]} + /> + )}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy