mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user