feat(site): add support for presets to the create workspace page (#16567)

This pull request adds support for presets to the create workspace page.
This behaviour can be seen in the storybook. This will not be visible in
dogfood until we merge support for presets in the provisioners.

There is more frontend work to be done before this is ready for a
general release, but this should be sufficient for dogfood testing
This commit is contained in:
Sas Swart
2025-02-18 15:26:55 +02:00
committed by GitHub
parent 7fd04d4c54
commit a17cf03980
5 changed files with 150 additions and 1 deletions
+9
View File
@@ -1145,6 +1145,15 @@ class ApiMethods {
return response.data;
};
getTemplateVersionPresets = async (
templateVersionId: string,
): Promise<TypesGen.Preset[]> => {
const response = await this.axios.get<TypesGen.Preset[]>(
`/api/v2/templateversions/${templateVersionId}/presets`,
);
return response.data;
};
startWorkspace = (
workspaceId: string,
templateVersionId: string,
+8
View File
@@ -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,
@@ -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,10 @@ const CreateWorkspacePage: FC = () => {
const templateQuery = useQuery(
templateByName(organizationName, templateName),
);
const templateVersionPresetsQuery = useQuery({
...templateVersionPresets(templateQuery.data?.active_version_id ?? ""),
enabled: templateQuery.data !== undefined,
});
const permissionsQuery = useQuery(
templateQuery.data
? checkAuthorization({
@@ -203,6 +208,7 @@ const CreateWorkspacePage: FC = () => {
hasAllRequiredExternalAuth={hasAllRequiredExternalAuth}
permissions={permissionsQuery.data as CreateWSPermissions}
parameters={realizedParameters as TemplateVersionParameter[]}
presets={templateVersionPresetsQuery.data ?? []}
creatingWorkspace={createWorkspaceMutation.isLoading}
onCancel={() => {
navigate(-1);
@@ -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,
@@ -116,6 +118,47 @@ export const Parameters: Story = {
},
};
export const PresetsButNoneSelected: 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 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: [
@@ -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<CreateWorkspacePageViewProps> = ({
hasAllRequiredExternalAuth,
parameters,
autofillParameters,
presets = [],
permissions,
creatingWorkspace,
onSubmit,
@@ -145,6 +148,62 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
[autofillParameters],
);
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);
const [presetParameterNames, setPresetParameterNames] = useState<string[]>(
[],
);
useEffect(() => {
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 (
<Margins size="medium">
<PageHeader
@@ -213,6 +272,28 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
</Stack>
)}
{presets.length > 0 && (
<Stack direction="column" spacing={2}>
<span css={styles.description}>
Select a preset to get started
</span>
<Stack direction="row" spacing={2}>
<SelectFilter
label="Preset"
options={presetOptions}
onSelect={(option) => {
setSelectedPresetIndex(
presetOptions.findIndex(
(preset) => preset.value === option?.value,
),
);
}}
placeholder="Select a preset"
selectedOption={presetOptions[selectedPresetIndex]}
/>
</Stack>
</Stack>
)}
<div>
<TextField
{...getFieldHelpers("name")}
@@ -292,7 +373,9 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
const isDisabled =
disabledParams?.includes(
parameter.name.toLowerCase().replace(/ /g, "_"),
) || creatingWorkspace;
) ||
creatingWorkspace ||
presetParameterNames.includes(parameter.name);
return (
<RichParameterInput