From 99fb38629743e68de85c76ba102f4b60e8520c41 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Tue, 14 Mar 2023 19:35:55 +0000 Subject: [PATCH 1/9] Add base for settings page --- site/src/AppRouter.tsx | 4 + site/src/api/api.ts | 7 ++ site/src/components/Form/Form.tsx | 2 +- .../CreateWorkspacePageView.tsx | 11 +-- .../WorkspaceBuildParametersPageView.tsx | 6 +- .../WorkspaceSettingsForm.tsx | 94 +++++++++++++++++++ .../WorkspaceSettingsPage.tsx | 62 ++++++++++++ site/src/pages/WorkspaceSettingsPage/data.ts | 73 ++++++++++++++ site/src/util/richParameters.ts | 10 ++ 9 files changed, 256 insertions(+), 13 deletions(-) create mode 100644 site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx create mode 100644 site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx create mode 100644 site/src/pages/WorkspaceSettingsPage/data.ts diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index e2a184cc599cf..666ace61c556f 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -126,6 +126,9 @@ const CreateTemplatePage = lazy( const TemplateVariablesPage = lazy( () => import("./pages/TemplateVariablesPage/TemplateVariablesPage"), ) +const WorkspaceSettingsPage = lazy( + () => import("./pages/WorkspaceSettingsPage/WorkspaceSettingsPage"), +) export const AppRouter: FC = () => { return ( @@ -234,6 +237,7 @@ export const AppRouter: FC = () => { path="build-parameters" element={} /> + } /> diff --git a/site/src/api/api.ts b/site/src/api/api.ts index b28d9e0379c12..4adf1d55be38a 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -506,6 +506,13 @@ export const createWorkspace = async ( return response.data } +export const patchWorkspace = async ( + workspaceId: string, + data: TypesGen.UpdateWorkspaceRequest, +) => { + await axios.patch(`/api/v2/workspaces/${workspaceId}`, data) +} + export const getBuildInfo = async (): Promise => { const response = await axios.get("/api/v2/buildinfo") return response.data diff --git a/site/src/components/Form/Form.tsx b/site/src/components/Form/Form.tsx index 1c7e06c25513e..88a67acf1212d 100644 --- a/site/src/components/Form/Form.tsx +++ b/site/src/components/Form/Form.tsx @@ -24,7 +24,7 @@ type FormProps = HTMLProps & { } export const Form: FC = ({ direction, className, ...formProps }) => { - const styles = useStyles() + const styles = useStyles({ direction }) return ( diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index f3e6766a48e46..a8540be3012f3 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -24,6 +24,7 @@ import { makeStyles } from "@material-ui/core/styles" import { selectInitialRichParametersValues, useValidationSchemaForRichParameters, + workspaceBuildParameterValue, } from "util/richParameters" export enum CreateWorkspaceErrors { @@ -401,13 +402,3 @@ const useStyles = makeStyles((theme) => ({ marginRight: -theme.spacing(10), }, })) - -export const workspaceBuildParameterValue = ( - workspaceBuildParameters: TypesGen.WorkspaceBuildParameter[], - parameter: TypesGen.TemplateVersionParameter, -): string => { - const buildParameter = workspaceBuildParameters.find((buildParameter) => { - return buildParameter.name === parameter.name - }) - return (buildParameter && buildParameter.value) || "" -} diff --git a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx b/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx index 8aeaaf52efea3..6f0cf7739a6ef 100644 --- a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx +++ b/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx @@ -8,12 +8,14 @@ import { makeStyles } from "@material-ui/core/styles" import { getFormHelpers } from "util/formUtils" import { FormikContextType, FormikTouched, useFormik } from "formik" import { RichParameterInput } from "components/RichParameterInput/RichParameterInput" -import { workspaceBuildParameterValue } from "pages/CreateWorkspacePage/CreateWorkspacePageView" import { FormFooter } from "components/FormFooter/FormFooter" import * as Yup from "yup" import { Maybe } from "components/Conditionals/Maybe" import { GoBackButton } from "components/GoBackButton/GoBackButton" -import { useValidationSchemaForRichParameters } from "util/richParameters" +import { + useValidationSchemaForRichParameters, + workspaceBuildParameterValue, +} from "util/richParameters" export enum UpdateWorkspaceErrors { GET_WORKSPACE_ERROR = "getWorkspaceError", diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx new file mode 100644 index 0000000000000..bd87f891718cc --- /dev/null +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -0,0 +1,94 @@ +import { + FormFields, + FormFooter, + FormSection, + HorizontalForm, +} from "components/Form/Form" +import { RichParameterInput } from "components/RichParameterInput/RichParameterInput" +import { useFormik } from "formik" +import { FC } from "react" +import { useTranslation } from "react-i18next" +import { + useValidationSchemaForRichParameters, + workspaceBuildParameterValue, +} from "util/richParameters" +import { WorkspaceSettings, WorkspaceSettingsFormValue } from "./data" +import * as Yup from "yup" +import { nameValidator, getFormHelpers, onChangeTrimmed } from "util/formUtils" +import TextField from "@material-ui/core/TextField" + +export const WorkspaceSettingsForm: FC<{ + isSubmitting: boolean + settings: WorkspaceSettings + error: unknown + onCancel: () => void + onSubmit: (values: WorkspaceSettingsFormValue) => void +}> = ({ onCancel, onSubmit, settings, error, isSubmitting }) => { + const { t } = useTranslation("createWorkspacePage") + const form = useFormik({ + onSubmit, + initialValues: { + name: settings.workspace.name, + rich_parameter_values: settings.buildParameters, + }, + validationSchema: Yup.object({ + name: nameValidator(t("nameLabel", { ns: "createWorkspacePage" })), + rich_parameter_values: useValidationSchemaForRichParameters( + "createWorkspacePage", + settings.templateVersionParameters, + ), + }), + }) + const getFieldHelpers = getFormHelpers( + form, + error, + ) + + return ( + + + + + + + + + {settings.templateVersionParameters.map((parameter, index) => ( + { + await form.setFieldValue("rich_parameter_values." + index, { + name: parameter.name, + value: value, + }) + }} + parameter={parameter} + initialValue={workspaceBuildParameterValue( + settings.buildParameters, + parameter, + )} + /> + ))} + + + + + ) +} diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx new file mode 100644 index 0000000000000..744abb293d013 --- /dev/null +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -0,0 +1,62 @@ +import { getErrorMessage } from "api/errors" +import { AlertBanner } from "components/AlertBanner/AlertBanner" +import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizontalForm" +import { displayError } from "components/GlobalSnackbar/utils" +import { Loader } from "components/Loader/Loader" +import { Helmet } from "react-helmet-async" +import { useNavigate, useParams } from "react-router-dom" +import { pageTitle } from "util/page" +import { useUpdateWorkspaceSettings, useWorkspaceSettings } from "./data" +import { WorkspaceSettingsForm } from "./WorkspaceSettingsForm" + +const WorkspaceSettingsPage = () => { + const { username, workspace: workspaceName } = useParams() as { + username: string + workspace: string + } + const { + data: settings, + error, + isLoading, + } = useWorkspaceSettings(username, workspaceName) + const navigate = useNavigate() + const updateSettings = useUpdateWorkspaceSettings(settings?.workspace.id, { + onSuccess: ({ name }) => { + navigate(`/@${username}/${name}`) + }, + onError: (error) => + displayError( + getErrorMessage(error, "Error on update workspace settings"), + ), + }) + + const onCancel = () => navigate(-1) + + return ( + <> + + Codestin Search App + + + + <> + {error && } + {isLoading && } + {settings && ( + { + updateSettings.mutate(formValues) + }} + /> + )} + + + + ) +} + +export default WorkspaceSettingsPage diff --git a/site/src/pages/WorkspaceSettingsPage/data.ts b/site/src/pages/WorkspaceSettingsPage/data.ts new file mode 100644 index 0000000000000..cd5da49af1053 --- /dev/null +++ b/site/src/pages/WorkspaceSettingsPage/data.ts @@ -0,0 +1,73 @@ +import { useMutation, useQuery } from "@tanstack/react-query" +import { + getWorkspaceByOwnerAndName, + getWorkspaceBuildParameters, + getTemplateVersionRichParameters, + patchWorkspace, + postWorkspaceBuild, +} from "api/api" +import { WorkspaceBuildParameter } from "api/typesGenerated" + +const getWorkspaceSettings = async (owner: string, name: string) => { + const workspace = await getWorkspaceByOwnerAndName(owner, name) + const latestBuild = workspace.latest_build + const [templateVersionParameters, buildParameters] = await Promise.all([ + getTemplateVersionRichParameters(latestBuild.template_version_id), + getWorkspaceBuildParameters(latestBuild.id), + ]) + return { + workspace, + templateVersionParameters, + buildParameters, + } +} + +export const useWorkspaceSettings = (owner: string, workspace: string) => { + return useQuery({ + queryKey: ["workspaceSettings", owner, workspace], + queryFn: () => getWorkspaceSettings(owner, workspace), + }) +} + +export type WorkspaceSettings = Awaited> + +export type WorkspaceSettingsFormValue = { + name: string + rich_parameter_values: WorkspaceBuildParameter[] +} + +const updateWorkspaceSettings = async ( + workspaceId: string, + formValues: WorkspaceSettingsFormValue, +) => { + await Promise.all([ + patchWorkspace(workspaceId, { name: formValues.name }), + postWorkspaceBuild(workspaceId, { + transition: "start", + rich_parameter_values: formValues.rich_parameter_values, + }), + ]) + + return formValues // So we can get then on the onSuccess callback +} + +export const useUpdateWorkspaceSettings = ( + workspaceId?: string, + options?: { + onSuccess?: ( + result: Awaited>, + ) => void + onError?: (error: unknown) => void + }, +) => { + return useMutation({ + mutationFn: (formValues: WorkspaceSettingsFormValue) => { + if (!workspaceId) { + throw new Error("No workspace id") + } + return updateWorkspaceSettings(workspaceId, formValues) + }, + onSuccess: options?.onSuccess, + onError: options?.onError, + }) +} diff --git a/site/src/util/richParameters.ts b/site/src/util/richParameters.ts index 7bb036ebac337..cc239f1249dee 100644 --- a/site/src/util/richParameters.ts +++ b/site/src/util/richParameters.ts @@ -146,3 +146,13 @@ export const useValidationSchemaForRichParameters = ( ) .required() } + +export const workspaceBuildParameterValue = ( + workspaceBuildParameters: WorkspaceBuildParameter[], + parameter: TemplateVersionParameter, +): string => { + const buildParameter = workspaceBuildParameters.find((buildParameter) => { + return buildParameter.name === parameter.name + }) + return (buildParameter && buildParameter.value) || "" +} From 139ea5b5ba7f0fd59fcb883dc1901b7a6274c843 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Mar 2023 14:13:21 +0000 Subject: [PATCH 2/9] Refactor form --- .../WorkspaceSettingsForm.tsx | 69 +++++++++++-------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index bd87f891718cc..6cd5042444088 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -25,11 +25,22 @@ export const WorkspaceSettingsForm: FC<{ onSubmit: (values: WorkspaceSettingsFormValue) => void }> = ({ onCancel, onSubmit, settings, error, isSubmitting }) => { const { t } = useTranslation("createWorkspacePage") + const mutableParameters = settings.templateVersionParameters.filter( + (param) => param.mutable, + ) const form = useFormik({ onSubmit, initialValues: { name: settings.workspace.name, - rich_parameter_values: settings.buildParameters, + rich_parameter_values: mutableParameters.map((parameter) => { + const buildParameter = settings.buildParameters.find( + (p) => p.name === parameter.name, + ) + if (!buildParameter) { + throw new Error("Missing build parameter for " + parameter.name) + } + return buildParameter + }), }, validationSchema: Yup.object({ name: nameValidator(t("nameLabel", { ns: "createWorkspacePage" })), @@ -62,32 +73,36 @@ export const WorkspaceSettingsForm: FC<{ /> - - - {settings.templateVersionParameters.map((parameter, index) => ( - { - await form.setFieldValue("rich_parameter_values." + index, { - name: parameter.name, - value: value, - }) - }} - parameter={parameter} - initialValue={workspaceBuildParameterValue( - settings.buildParameters, - parameter, - )} - /> - ))} - - + {mutableParameters.length > 0 && ( + + + {settings.templateVersionParameters.map((parameter, index) => ( + { + await form.setFieldValue("rich_parameter_values." + index, { + name: parameter.name, + value: value, + }) + }} + parameter={parameter} + initialValue={workspaceBuildParameterValue( + settings.buildParameters, + parameter, + )} + /> + ))} + + + )} ) From 0e6e65155676d639055e14386de1ea1a7b9386a9 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Mar 2023 14:16:11 +0000 Subject: [PATCH 3/9] Update nav --- site/src/components/DropdownButton/ActionCtas.tsx | 8 ++++---- site/src/components/Workspace/Workspace.tsx | 6 +++--- .../components/WorkspaceActions/WorkspaceActions.test.tsx | 4 ++-- site/src/components/WorkspaceActions/WorkspaceActions.tsx | 8 ++++---- site/src/i18n/en/workspacePage.json | 2 +- site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/site/src/components/DropdownButton/ActionCtas.tsx b/site/src/components/DropdownButton/ActionCtas.tsx index da83675c710d3..bc505db375c5c 100644 --- a/site/src/components/DropdownButton/ActionCtas.tsx +++ b/site/src/components/DropdownButton/ActionCtas.tsx @@ -54,9 +54,9 @@ export const ChangeVersionButton: FC< ) } -export const BuildParametersButton: FC< - React.PropsWithChildren -> = ({ handleAction }) => { +export const SettingsButton: FC> = ({ + handleAction, +}) => { const styles = useStyles() const { t } = useTranslation("workspacePage") @@ -67,7 +67,7 @@ export const BuildParametersButton: FC< startIcon={} onClick={handleAction} > - {t("actionButton.buildParameters")} + {t("actionButton.settings")} ) } diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index 19b8514ed154c..1dad5817f11ca 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -43,7 +43,7 @@ export interface WorkspaceProps { handleUpdate: () => void handleCancel: () => void handleChangeVersion: () => void - handleBuildParameters: () => void + handleSettings: () => void isUpdating: boolean workspace: TypesGen.Workspace resources?: TypesGen.WorkspaceResource[] @@ -70,7 +70,7 @@ export const Workspace: FC> = ({ handleUpdate, handleCancel, handleChangeVersion, - handleBuildParameters, + handleSettings, workspace, isUpdating, resources, @@ -136,7 +136,7 @@ export const Workspace: FC> = ({ handleUpdate={handleUpdate} handleCancel={handleCancel} handleChangeVersion={handleChangeVersion} - handleBuildParameters={handleBuildParameters} + handleSettings={handleSettings} isUpdating={isUpdating} /> diff --git a/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx b/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx index a83aa41cb1144..e83139086de37 100644 --- a/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx +++ b/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx @@ -20,7 +20,7 @@ const renderComponent = async (props: Partial = {}) => { handleUpdate={jest.fn()} handleCancel={jest.fn()} handleChangeVersion={jest.fn()} - handleBuildParameters={jest.fn()} + handleSettings={jest.fn()} isUpdating={false} />, ) @@ -40,7 +40,7 @@ const renderAndClick = async (props: Partial = {}) => { handleUpdate={jest.fn()} handleCancel={jest.fn()} handleChangeVersion={jest.fn()} - handleBuildParameters={jest.fn()} + handleSettings={jest.fn()} isUpdating={false} />, ) diff --git a/site/src/components/WorkspaceActions/WorkspaceActions.tsx b/site/src/components/WorkspaceActions/WorkspaceActions.tsx index fad29ebc7a209..cfcbc92231b43 100644 --- a/site/src/components/WorkspaceActions/WorkspaceActions.tsx +++ b/site/src/components/WorkspaceActions/WorkspaceActions.tsx @@ -7,7 +7,7 @@ import { ChangeVersionButton, DeleteButton, DisabledButton, - BuildParametersButton, + SettingsButton, StartButton, StopButton, UpdateButton, @@ -24,7 +24,7 @@ export interface WorkspaceActionsProps { handleUpdate: () => void handleCancel: () => void handleChangeVersion: () => void - handleBuildParameters: () => void + handleSettings: () => void isUpdating: boolean children?: ReactNode } @@ -39,7 +39,7 @@ export const WorkspaceActions: FC = ({ handleUpdate, handleCancel, handleChangeVersion, - handleBuildParameters, + handleSettings, isUpdating, }) => { const { t } = useTranslation("workspacePage") @@ -59,7 +59,7 @@ export const WorkspaceActions: FC = ({ ), [ButtonTypesEnum.buildParameters]: ( - + ), [ButtonTypesEnum.start]: , [ButtonTypesEnum.starting]: ( diff --git a/site/src/i18n/en/workspacePage.json b/site/src/i18n/en/workspacePage.json index 02d40b8dff42b..3c4de459f9fef 100644 --- a/site/src/i18n/en/workspacePage.json +++ b/site/src/i18n/en/workspacePage.json @@ -29,7 +29,7 @@ "stopping": "Stopping...", "deleting": "Deleting...", "changeVersion": "Change version", - "buildParameters": "Build parameters" + "settings": "Settings" }, "disabledButton": { "canceling": "Canceling", diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 0174745194daf..2214fda5c96ca 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -113,7 +113,7 @@ export const WorkspaceReadyPage = ({ handleUpdate={() => workspaceSend({ type: "UPDATE" })} handleCancel={() => workspaceSend({ type: "CANCEL" })} handleChangeVersion={() => navigate("change-version")} - handleBuildParameters={() => navigate("build-parameters")} + handleSettings={() => navigate("settings")} resources={workspace.latest_build.resources} builds={builds} canUpdateWorkspace={canUpdateWorkspace} From 5d31e8119bdb511f44aa6173f0bb55e43e2f1eff Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Mar 2023 14:24:51 +0000 Subject: [PATCH 4/9] Add translation --- site/src/i18n/en/index.ts | 2 ++ site/src/i18n/en/workspaceSettingsPage.json | 9 +++++++++ .../WorkspaceSettingsPage/WorkspaceSettingsForm.tsx | 12 ++++++------ .../WorkspaceSettingsPage/WorkspaceSettingsPage.tsx | 10 +++++----- 4 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 site/src/i18n/en/workspaceSettingsPage.json diff --git a/site/src/i18n/en/index.ts b/site/src/i18n/en/index.ts index 58f6530cab049..badd2e4c5ab1f 100644 --- a/site/src/i18n/en/index.ts +++ b/site/src/i18n/en/index.ts @@ -21,6 +21,7 @@ import starterTemplatePage from "./starterTemplatePage.json" import createTemplatePage from "./createTemplatePage.json" import userSettingsPage from "./userSettingsPage.json" import tokensPage from "./tokensPage.json" +import workspaceSettingsPage from "./workspaceSettingsPage.json" export const en = { common, @@ -46,4 +47,5 @@ export const en = { createTemplatePage, userSettingsPage, tokensPage, + workspaceSettingsPage, } diff --git a/site/src/i18n/en/workspaceSettingsPage.json b/site/src/i18n/en/workspaceSettingsPage.json new file mode 100644 index 0000000000000..94d8bc4d126d9 --- /dev/null +++ b/site/src/i18n/en/workspaceSettingsPage.json @@ -0,0 +1,9 @@ +{ + "title": "Workspace Settings", + "defaultErrorMessage": "Error on update workspace settings", + "nameLabel": "Name", + "generalInfo": "General info", + "generalInfoDescription": "The name of your new workspace.", + "parameters": "Parameters", + "parametersDescription": "Those values are provided by your template's Terraform configuration. Values can be changed after creating the workspace." +} diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index 6cd5042444088..6265b34691298 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -24,7 +24,7 @@ export const WorkspaceSettingsForm: FC<{ onCancel: () => void onSubmit: (values: WorkspaceSettingsFormValue) => void }> = ({ onCancel, onSubmit, settings, error, isSubmitting }) => { - const { t } = useTranslation("createWorkspacePage") + const { t } = useTranslation("workspaceSettingsPage") const mutableParameters = settings.templateVersionParameters.filter( (param) => param.mutable, ) @@ -43,7 +43,7 @@ export const WorkspaceSettingsForm: FC<{ }), }, validationSchema: Yup.object({ - name: nameValidator(t("nameLabel", { ns: "createWorkspacePage" })), + name: nameValidator(t("nameLabel")), rich_parameter_values: useValidationSchemaForRichParameters( "createWorkspacePage", settings.templateVersionParameters, @@ -58,8 +58,8 @@ export const WorkspaceSettingsForm: FC<{ return ( {mutableParameters.length > 0 && ( {settings.templateVersionParameters.map((parameter, index) => ( diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx index 744abb293d013..dc7e58e6f78d9 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -4,12 +4,14 @@ import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizont import { displayError } from "components/GlobalSnackbar/utils" import { Loader } from "components/Loader/Loader" import { Helmet } from "react-helmet-async" +import { useTranslation } from "react-i18next" import { useNavigate, useParams } from "react-router-dom" import { pageTitle } from "util/page" import { useUpdateWorkspaceSettings, useWorkspaceSettings } from "./data" import { WorkspaceSettingsForm } from "./WorkspaceSettingsForm" const WorkspaceSettingsPage = () => { + const { t } = useTranslation("workspaceSettingsPage") const { username, workspace: workspaceName } = useParams() as { username: string workspace: string @@ -25,9 +27,7 @@ const WorkspaceSettingsPage = () => { navigate(`/@${username}/${name}`) }, onError: (error) => - displayError( - getErrorMessage(error, "Error on update workspace settings"), - ), + displayError(getErrorMessage(error, t("defaultErrorMessage"))), }) const onCancel = () => navigate(-1) @@ -35,10 +35,10 @@ const WorkspaceSettingsPage = () => { return ( <> - Codestin Search App + Codestin Search App - + <> {error && } {isLoading && } From 0876598d9ffcbd00d70d41f2e75d6bbe9048f3c3 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Mar 2023 14:43:12 +0000 Subject: [PATCH 5/9] Add storybook --- site/src/components/Form/Form.tsx | 1 + .../WorkspaceSettingsForm.tsx | 6 +-- .../WorkspaceSettingsPage.tsx | 33 ++++--------- .../WorkspaceSettingsPageView.stories.tsx | 41 ++++++++++++++++ .../WorkspaceSettingsPageView.tsx | 47 +++++++++++++++++++ site/src/pages/WorkspaceSettingsPage/data.ts | 4 +- 6 files changed, 104 insertions(+), 28 deletions(-) create mode 100644 site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx create mode 100644 site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx diff --git a/site/src/components/Form/Form.tsx b/site/src/components/Form/Form.tsx index 88a67acf1212d..62d7de3817620 100644 --- a/site/src/components/Form/Form.tsx +++ b/site/src/components/Form/Form.tsx @@ -136,6 +136,7 @@ const useStyles = makeStyles((theme) => ({ }, formSectionInfo: { + width: "100%", maxWidth: ({ direction }: FormContextValue = {}) => direction === "horizontal" ? 312 : undefined, flexShrink: 0, diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index 6265b34691298..f66ad2bc605c9 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -25,7 +25,7 @@ export const WorkspaceSettingsForm: FC<{ onSubmit: (values: WorkspaceSettingsFormValue) => void }> = ({ onCancel, onSubmit, settings, error, isSubmitting }) => { const { t } = useTranslation("workspaceSettingsPage") - const mutableParameters = settings.templateVersionParameters.filter( + const mutableParameters = settings.templateVersionRichParameters.filter( (param) => param.mutable, ) const form = useFormik({ @@ -46,7 +46,7 @@ export const WorkspaceSettingsForm: FC<{ name: nameValidator(t("nameLabel")), rich_parameter_values: useValidationSchemaForRichParameters( "createWorkspacePage", - settings.templateVersionParameters, + settings.templateVersionRichParameters, ), }), }) @@ -79,7 +79,7 @@ export const WorkspaceSettingsForm: FC<{ description={t("parametersDescription")} > - {settings.templateVersionParameters.map((parameter, index) => ( + {settings.templateVersionRichParameters.map((parameter, index) => ( { const { t } = useTranslation("workspaceSettingsPage") @@ -30,31 +27,21 @@ const WorkspaceSettingsPage = () => { displayError(getErrorMessage(error, t("defaultErrorMessage"))), }) - const onCancel = () => navigate(-1) - return ( <> Codestin Search App - - <> - {error && } - {isLoading && } - {settings && ( - { - updateSettings.mutate(formValues) - }} - /> - )} - - + navigate(-1)} + onSubmit={updateSettings.mutate} + /> ) } diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx new file mode 100644 index 0000000000000..cc32594a7c759 --- /dev/null +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx @@ -0,0 +1,41 @@ +import { ComponentMeta, Story } from "@storybook/react" +import { + MockTemplateVersionParameter1, + MockTemplateVersionParameter2, + MockWorkspace, + MockWorkspaceBuildParameter1, + MockWorkspaceBuildParameter2, +} from "testHelpers/entities" +import { + WorkspaceSettingsPageView, + WorkspaceSettingsPageViewProps, +} from "./WorkspaceSettingsPageView" + +export default { + title: "pages/WorkspaceSettingsPageView", + component: WorkspaceSettingsPageView, + args: { + formError: undefined, + loadingError: undefined, + isLoading: false, + isSubmitting: false, + settings: { + workspace: MockWorkspace, + buildParameters: [ + MockWorkspaceBuildParameter1, + MockWorkspaceBuildParameter2, + ], + templateVersionRichParameters: [ + MockTemplateVersionParameter1, + MockTemplateVersionParameter2, + ], + }, + }, +} as ComponentMeta + +const Template: Story = (args) => ( + +) + +export const Example = Template.bind({}) +Example.args = {} diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx new file mode 100644 index 0000000000000..ab94200fa9f09 --- /dev/null +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.tsx @@ -0,0 +1,47 @@ +import { AlertBanner } from "components/AlertBanner/AlertBanner" +import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizontalForm" +import { Loader } from "components/Loader/Loader" +import { FC } from "react" +import { useTranslation } from "react-i18next" +import { WorkspaceSettings, WorkspaceSettingsFormValue } from "./data" +import { WorkspaceSettingsForm } from "./WorkspaceSettingsForm" + +export type WorkspaceSettingsPageViewProps = { + formError: unknown + loadingError: unknown + isLoading: boolean + isSubmitting: boolean + settings: WorkspaceSettings | undefined + onCancel: () => void + onSubmit: (formValues: WorkspaceSettingsFormValue) => void +} + +export const WorkspaceSettingsPageView: FC = ({ + onCancel, + onSubmit, + isLoading, + isSubmitting, + settings, + formError, + loadingError, +}) => { + const { t } = useTranslation("workspaceSettingsPage") + + return ( + + <> + {loadingError && } + {isLoading && } + {settings && ( + + )} + + + ) +} diff --git a/site/src/pages/WorkspaceSettingsPage/data.ts b/site/src/pages/WorkspaceSettingsPage/data.ts index cd5da49af1053..cf64abc9a189e 100644 --- a/site/src/pages/WorkspaceSettingsPage/data.ts +++ b/site/src/pages/WorkspaceSettingsPage/data.ts @@ -11,13 +11,13 @@ import { WorkspaceBuildParameter } from "api/typesGenerated" const getWorkspaceSettings = async (owner: string, name: string) => { const workspace = await getWorkspaceByOwnerAndName(owner, name) const latestBuild = workspace.latest_build - const [templateVersionParameters, buildParameters] = await Promise.all([ + const [templateVersionRichParameters, buildParameters] = await Promise.all([ getTemplateVersionRichParameters(latestBuild.template_version_id), getWorkspaceBuildParameters(latestBuild.id), ]) return { workspace, - templateVersionParameters, + templateVersionRichParameters, buildParameters, } } From 7b4233ad43bed9a42616ac68f7f834d9215d1cdd Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Mar 2023 15:11:08 +0000 Subject: [PATCH 6/9] Add tests --- .../WorkspaceSettingsForm.tsx | 2 +- .../WorkspaceSettingsPage.test.tsx | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.test.tsx diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx index f66ad2bc605c9..7d6fc9fbfc608 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsForm.tsx @@ -56,7 +56,7 @@ export const WorkspaceSettingsForm: FC<{ ) return ( - + { + // Mock the API calls that loads data + jest + .spyOn(api, "getWorkspaceByOwnerAndName") + .mockResolvedValueOnce(MockWorkspace) + jest + .spyOn(api, "getTemplateVersionRichParameters") + .mockResolvedValueOnce([ + MockTemplateVersionParameter1, + MockTemplateVersionParameter2, + ]) + jest + .spyOn(api, "getWorkspaceBuildParameters") + .mockResolvedValueOnce([ + MockWorkspaceBuildParameter1, + MockWorkspaceBuildParameter2, + ]) + // Mock the API calls that submit data + const patchWorkspaceSpy = jest + .spyOn(api, "patchWorkspace") + .mockResolvedValue() + const postWorkspaceBuildSpy = jest + .spyOn(api, "postWorkspaceBuild") + .mockResolvedValue(MockWorkspaceBuild) + // Setup event and rendering + const user = userEvent.setup() + renderWithAuth(, { + route: "/@test-user/test-workspace/settings", + path: "/@:username/:workspace/settings", + // Need this because after submit the user is redirected + extraRoutes: [{ path: "/@:username/:workspace", element:
}], + }) + await waitForLoaderToBeRemoved() + // Fill the form and submit + const form = screen.getByTestId("form") + const name = within(form).getByLabelText("Name") + await user.clear(name) + await user.type(within(form).getByLabelText("Name"), "new-name") + const parameter1 = within(form).getByLabelText( + MockWorkspaceBuildParameter1.name, + { exact: false }, + ) + await user.clear(parameter1) + await user.type(parameter1, "new-value") + const parameter2 = within(form).getByLabelText( + MockWorkspaceBuildParameter2.name, + { exact: false }, + ) + await user.clear(parameter2) + await user.type(parameter2, "1") + await user.click(within(form).getByRole("button", { name: "Submit" })) + // Assert that the API calls were made with the correct data + await waitFor(() => { + expect(patchWorkspaceSpy).toHaveBeenCalledWith(MockWorkspace.id, { + name: "new-name", + }) + expect(postWorkspaceBuildSpy).toHaveBeenCalledWith(MockWorkspace.id, { + transition: "start", + rich_parameter_values: [ + { name: MockTemplateVersionParameter1.name, value: "new-value" }, + { name: MockTemplateVersionParameter2.name, value: "1" }, + ], + }) + }) +}) From c17543a5ef32ac1b6b83a87d7c9d79c25651164b Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Mar 2023 15:12:18 +0000 Subject: [PATCH 7/9] Remove not used workspace build parameters --- site/src/AppRouter.tsx | 5 - .../WorkspaceBuildParametersPage.test.tsx | 210 ---------- .../WorkspaceBuildParametersPage.tsx | 82 ---- ...rkspaceBuildParametersPageView.stories.tsx | 48 --- .../WorkspaceBuildParametersPageView.tsx | 369 ------------------ 5 files changed, 714 deletions(-) delete mode 100644 site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage.test.tsx delete mode 100644 site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage.tsx delete mode 100644 site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.stories.tsx delete mode 100644 site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index 666ace61c556f..d8cd4aab5036e 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -7,7 +7,6 @@ import GroupsPage from "pages/GroupsPage/GroupsPage" import LoginPage from "pages/LoginPage/LoginPage" import { SetupPage } from "pages/SetupPage/SetupPage" import { TemplateSettingsPage } from "pages/TemplateSettingsPage/TemplateSettingsPage" -import { WorkspaceBuildParametersPage } from "pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage" import TemplatesPage from "pages/TemplatesPage/TemplatesPage" import UsersPage from "pages/UsersPage/UsersPage" import WorkspacesPage from "pages/WorkspacesPage/WorkspacesPage" @@ -233,10 +232,6 @@ export const AppRouter: FC = () => { path="change-version" element={} /> - } - /> } /> diff --git a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage.test.tsx b/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage.test.tsx deleted file mode 100644 index 57d1de52439c3..0000000000000 --- a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage.test.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { fireEvent, screen } from "@testing-library/react" -import { - MockTemplateVersionParameter1, - MockTemplateVersionParameter2, - MockTemplateVersionParameter5, - MockWorkspace, - MockWorkspaceBuildParameter1, - MockWorkspaceBuildParameter2, - MockWorkspaceBuildParameter5, - renderWithAuth, -} from "testHelpers/renderHelpers" -import * as API from "api/api" -import i18next from "i18next" -import { WorkspaceBuildParametersPage } from "./WorkspaceBuildParametersPage" - -const { t } = i18next - -const pageTitleText = t("title", { ns: "workspaceBuildParametersPage" }) -const validationNumberNotInRangeText = t("validationNumberNotInRange", { - ns: "workspaceBuildParametersPage", - min: "1", - max: "3", -}) -const validationNumberNotIncreasing = t("validationNumberNotIncreasing", { - ns: "workspaceBuildParametersPage", - last: "3", -}) -const validationNumberNotDecreasing = t("validationNumberNotDecreasing", { - ns: "workspaceBuildParametersPage", - last: "5", -}) - -const renderWorkspaceBuildParametersPage = () => { - return renderWithAuth(, { - route: `/@${MockWorkspace.owner_name}/${MockWorkspace.name}/build-parameters`, - path: `/@:ownerName/:workspaceName/build-parameters`, - }) -} - -describe("WorkspaceBuildParametersPage", () => { - it("renders without rich parameters", async () => { - jest.spyOn(API, "getWorkspace").mockResolvedValueOnce(MockWorkspace) - jest - .spyOn(API, "getTemplateVersionRichParameters") - .mockResolvedValueOnce([]) - jest - .spyOn(API, "getWorkspaceBuildParameters") - .mockResolvedValueOnce([ - MockWorkspaceBuildParameter1, - MockWorkspaceBuildParameter2, - ]) - renderWorkspaceBuildParametersPage() - - const element = await screen.findByText(pageTitleText) - expect(element).toBeDefined() - - const goBackButton = await screen.findByText("Go back") - expect(goBackButton).toBeDefined() - }) - - it("renders with rich parameter", async () => { - jest.spyOn(API, "getWorkspace").mockResolvedValueOnce(MockWorkspace) - jest - .spyOn(API, "getTemplateVersionRichParameters") - .mockResolvedValueOnce([ - MockTemplateVersionParameter1, - MockTemplateVersionParameter2, - ]) - jest - .spyOn(API, "getWorkspaceBuildParameters") - .mockResolvedValueOnce([ - MockWorkspaceBuildParameter1, - MockWorkspaceBuildParameter2, - ]) - - renderWorkspaceBuildParametersPage() - - const element = await screen.findByText(pageTitleText) - expect(element).toBeDefined() - - const firstParameter = await screen.findByLabelText( - MockTemplateVersionParameter1.name, - { exact: false }, - ) - expect(firstParameter).toBeDefined() - - const secondParameter = await screen.findByLabelText( - MockTemplateVersionParameter2.name, - { exact: false }, - ) - expect(secondParameter).toBeDefined() - }) - - it("rich parameter: number validation fails", async () => { - jest - .spyOn(API, "getTemplateVersionRichParameters") - .mockResolvedValueOnce([ - MockTemplateVersionParameter1, - MockTemplateVersionParameter2, - ]) - jest - .spyOn(API, "getWorkspaceBuildParameters") - .mockResolvedValueOnce([ - MockWorkspaceBuildParameter1, - MockWorkspaceBuildParameter2, - ]) - renderWorkspaceBuildParametersPage() - - const element = await screen.findByText(pageTitleText) - expect(element).toBeDefined() - const secondParameter = await screen.findByText( - MockTemplateVersionParameter2.description, - ) - expect(secondParameter).toBeDefined() - - const secondParameterField = await screen.findByLabelText( - MockTemplateVersionParameter2.name, - { exact: false }, - ) - expect(secondParameterField).toBeDefined() - - fireEvent.change(secondParameterField, { - target: { value: "4" }, - }) - fireEvent.submit(secondParameter) - - const validationError = await screen.findByText( - validationNumberNotInRangeText, - ) - expect(validationError).toBeDefined() - }) - - it("rich parameter: number is not monotonically increasing", async () => { - jest - .spyOn(API, "getTemplateVersionRichParameters") - .mockResolvedValueOnce([ - MockTemplateVersionParameter1, - MockTemplateVersionParameter2, - ]) - jest - .spyOn(API, "getWorkspaceBuildParameters") - .mockResolvedValueOnce([ - MockWorkspaceBuildParameter1, - MockWorkspaceBuildParameter2, - ]) - renderWorkspaceBuildParametersPage() - - const element = await screen.findByText(pageTitleText) - expect(element).toBeDefined() - const secondParameter = await screen.findByText( - MockTemplateVersionParameter2.description, - ) - expect(secondParameter).toBeDefined() - - const secondParameterField = await screen.findByLabelText( - MockTemplateVersionParameter2.name, - { exact: false }, - ) - expect(secondParameterField).toBeDefined() - - fireEvent.change(secondParameterField, { - target: { value: "1" }, - }) - fireEvent.submit(secondParameter) - - const validationError = await screen.findByText( - validationNumberNotIncreasing, - ) - expect(validationError).toBeDefined() - }) - - it("rich parameter: number is not monotonically decreasing", async () => { - jest - .spyOn(API, "getTemplateVersionRichParameters") - .mockResolvedValueOnce([ - MockTemplateVersionParameter1, - MockTemplateVersionParameter5, - ]) - jest - .spyOn(API, "getWorkspaceBuildParameters") - .mockResolvedValueOnce([ - MockWorkspaceBuildParameter1, - MockWorkspaceBuildParameter5, - ]) - renderWorkspaceBuildParametersPage() - - const element = await screen.findByText(pageTitleText) - expect(element).toBeDefined() - const secondParameter = await screen.findByText( - MockTemplateVersionParameter5.description, - ) - expect(secondParameter).toBeDefined() - - const secondParameterField = await screen.findByLabelText( - MockTemplateVersionParameter5.name, - { exact: false }, - ) - expect(secondParameterField).toBeDefined() - - fireEvent.change(secondParameterField, { - target: { value: "6" }, - }) - fireEvent.submit(secondParameter) - - const validationError = await screen.findByText( - validationNumberNotDecreasing, - ) - expect(validationError).toBeDefined() - }) -}) diff --git a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage.tsx b/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage.tsx deleted file mode 100644 index 391b0d4b7a206..0000000000000 --- a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPage.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { FC } from "react" -import { Helmet } from "react-helmet-async" -import { useTranslation } from "react-i18next" -import { pageTitle } from "util/page" -import { useMachine } from "@xstate/react" -import { useNavigate, useParams } from "react-router-dom" -import { workspaceBuildParametersMachine } from "xServices/workspace/workspaceBuildParametersXService" -import { - UpdateWorkspaceErrors, - WorkspaceBuildParametersPageView, -} from "./WorkspaceBuildParametersPageView" -import { orderedTemplateParameters } from "pages/CreateWorkspacePage/CreateWorkspacePage" - -export const WorkspaceBuildParametersPage: FC = () => { - const { t } = useTranslation("workspaceBuildParametersPage") - - const navigate = useNavigate() - const { owner: workspaceOwner, workspace: workspaceName } = useParams() as { - owner: string - workspace: string - } - const [state, send] = useMachine(workspaceBuildParametersMachine, { - context: { - workspaceOwner, - workspaceName, - }, - actions: { - onUpdateWorkspace: (_, event) => { - navigate( - `/@${event.data.workspace_owner_name}/${event.data.workspace_name}`, - ) - }, - }, - }) - const { - selectedWorkspace, - templateParameters, - workspaceBuildParameters, - getWorkspaceError, - getTemplateParametersError, - getWorkspaceBuildParametersError, - updateWorkspaceError, - } = state.context - - return ( - <> - - Codestin Search App - - { - // Go back - navigate(-1) - }} - onSubmit={(request) => { - send({ - type: "UPDATE_WORKSPACE", - request, - }) - }} - /> - - ) -} diff --git a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.stories.tsx b/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.stories.tsx deleted file mode 100644 index aec6fc73a1f08..0000000000000 --- a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.stories.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { ComponentMeta, Story } from "@storybook/react" -import { - MockTemplateVersionParameter1, - MockTemplateVersionParameter2, - MockTemplateVersionParameter3, - MockTemplateVersionParameter4, - MockWorkspace, -} from "testHelpers/entities" -import { - WorkspaceBuildParametersPageView, - WorkspaceBuildParametersPageViewProps, -} from "./WorkspaceBuildParametersPageView" - -export default { - title: "pages/WorkspaceBuildParametersPageView", - component: WorkspaceBuildParametersPageView, -} as ComponentMeta - -const Template: Story = (args) => ( - -) - -export const NoRichParametersDefined = Template.bind({}) -NoRichParametersDefined.args = { - workspace: MockWorkspace, - templateParameters: [], - workspaceBuildParameters: [], - updateWorkspaceErrors: {}, - initialTouched: { - name: true, - }, -} - -export const RichParametersDefined = Template.bind({}) -RichParametersDefined.args = { - workspace: MockWorkspace, - templateParameters: [ - MockTemplateVersionParameter1, - MockTemplateVersionParameter2, - MockTemplateVersionParameter3, - MockTemplateVersionParameter4, - ], - workspaceBuildParameters: [], - updateWorkspaceErrors: {}, - initialTouched: { - name: true, - }, -} diff --git a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx b/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx deleted file mode 100644 index 6f0cf7739a6ef..0000000000000 --- a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx +++ /dev/null @@ -1,369 +0,0 @@ -import { FC } from "react" -import { FullPageForm } from "components/FullPageForm/FullPageForm" -import { useTranslation } from "react-i18next" -import * as TypesGen from "api/typesGenerated" -import { AlertBanner } from "components/AlertBanner/AlertBanner" -import { Stack } from "components/Stack/Stack" -import { makeStyles } from "@material-ui/core/styles" -import { getFormHelpers } from "util/formUtils" -import { FormikContextType, FormikTouched, useFormik } from "formik" -import { RichParameterInput } from "components/RichParameterInput/RichParameterInput" -import { FormFooter } from "components/FormFooter/FormFooter" -import * as Yup from "yup" -import { Maybe } from "components/Conditionals/Maybe" -import { GoBackButton } from "components/GoBackButton/GoBackButton" -import { - useValidationSchemaForRichParameters, - workspaceBuildParameterValue, -} from "util/richParameters" - -export enum UpdateWorkspaceErrors { - GET_WORKSPACE_ERROR = "getWorkspaceError", - GET_TEMPLATE_PARAMETERS_ERROR = "getTemplateParametersError", - GET_WORKSPACE_BUILD_PARAMETERS_ERROR = "getWorkspaceBuildParametersError", - UPDATE_WORKSPACE_ERROR = "updateWorkspaceError", -} - -export interface WorkspaceBuildParametersPageViewProps { - workspace?: TypesGen.Workspace - templateParameters?: TypesGen.TemplateVersionParameter[] - workspaceBuildParameters?: TypesGen.WorkspaceBuildParameter[] - - isLoading: boolean - initialTouched?: FormikTouched - updatingWorkspace: boolean - onCancel: () => void - onSubmit: (req: TypesGen.CreateWorkspaceBuildRequest) => void - - hasErrors: boolean - updateWorkspaceErrors: Partial> -} - -export const WorkspaceBuildParametersPageView: FC< - React.PropsWithChildren -> = (props) => { - const { t } = useTranslation("workspaceBuildParametersPage") - const styles = useStyles() - const formFooterStyles = useFormFooterStyles() - - const initialRichParameterValues = selectInitialRichParametersValues( - props.templateParameters, - props.workspaceBuildParameters, - ) - - const form: FormikContextType = - useFormik({ - initialValues: { - template_version_id: props.workspace - ? props.workspace.latest_build.template_version_id - : "", - transition: "start", - rich_parameter_values: initialRichParameterValues, - }, - validationSchema: Yup.object({ - rich_parameter_values: useValidationSchemaForRichParameters( - "workspaceBuildParametersPage", - props.templateParameters, - initialRichParameterValues, - ), - }), - enableReinitialize: true, - initialTouched: props.initialTouched, - onSubmit: (request) => { - props.onSubmit( - stripImmutableParameters(request, props.templateParameters), - ) - form.setSubmitting(false) - }, - }) - - const getFieldHelpers = getFormHelpers( - form, - props.updateWorkspaceErrors[UpdateWorkspaceErrors.UPDATE_WORKSPACE_ERROR], - ) - - { - props.hasErrors && ( - - {Boolean( - props.updateWorkspaceErrors[ - UpdateWorkspaceErrors.GET_WORKSPACE_ERROR - ], - ) && ( - - )} - {Boolean( - props.updateWorkspaceErrors[ - UpdateWorkspaceErrors.GET_TEMPLATE_PARAMETERS_ERROR - ], - ) && ( - - )} - {Boolean( - props.updateWorkspaceErrors[ - UpdateWorkspaceErrors.GET_WORKSPACE_BUILD_PARAMETERS_ERROR - ], - ) && ( - - )} - - ) - } - - return ( - - - - - - -
- -
- -
-
-
- - {!props.isLoading && - props.templateParameters && - props.templateParameters.length > 0 && - props.workspaceBuildParameters && ( -
-
- - {props.templateParameters.filter((p) => !p.mutable).length > - 0 && ( -
- Immutable parameters -
- )} - {props.templateParameters.map( - (parameter, index) => - !parameter.mutable && ( - { - form.setFieldValue("rich_parameter_values." + index, { - name: parameter.name, - value: value, - }) - }} - parameter={parameter} - initialValue={workspaceBuildParameterValue( - initialRichParameterValues, - parameter, - )} - /> - ), - )} - - {props.templateParameters.filter((p) => p.mutable).length > - 0 && ( -
- Mutable parameters -
- )} - {props.templateParameters.map( - (parameter, index) => - parameter.mutable && ( - { - form.setFieldValue("rich_parameter_values." + index, { - name: parameter.name, - value: value, - }) - }} - parameter={parameter} - initialValue={workspaceBuildParameterValue( - initialRichParameterValues, - parameter, - )} - /> - ), - )} - -
-
-
- )} -
- ) -} - -const selectInitialRichParametersValues = ( - templateParameters?: TypesGen.TemplateVersionParameter[], - workspaceBuildParameters?: TypesGen.WorkspaceBuildParameter[], -): TypesGen.WorkspaceBuildParameter[] => { - const defaults: TypesGen.WorkspaceBuildParameter[] = [] - if (!templateParameters) { - return defaults - } - - templateParameters.forEach((parameter) => { - if (parameter.options.length > 0) { - let parameterValue = parameter.options[0].value - if (workspaceBuildParameters) { - const foundBuildParameter = workspaceBuildParameters.find( - (buildParameter) => { - return buildParameter.name === parameter.name - }, - ) - if (foundBuildParameter) { - parameterValue = foundBuildParameter.value - } - } - - const buildParameter: TypesGen.WorkspaceBuildParameter = { - name: parameter.name, - value: parameterValue, - } - defaults.push(buildParameter) - return - } - - let parameterValue = parameter.default_value - if (workspaceBuildParameters) { - const foundBuildParameter = workspaceBuildParameters.find( - (buildParameter) => { - return buildParameter.name === parameter.name - }, - ) - if (foundBuildParameter) { - parameterValue = foundBuildParameter.value - } - } - - const buildParameter: TypesGen.WorkspaceBuildParameter = { - name: parameter.name, - value: parameterValue || "", - } - defaults.push(buildParameter) - }) - return defaults -} - -const stripImmutableParameters = ( - request: TypesGen.CreateWorkspaceBuildRequest, - templateParameters?: TypesGen.TemplateVersionParameter[], -): TypesGen.CreateWorkspaceBuildRequest => { - if (!templateParameters || !request.rich_parameter_values) { - return request - } - - const mutableBuildParameters = request.rich_parameter_values.filter( - (buildParameter) => - templateParameters.find( - (templateParameter) => templateParameter.name === buildParameter.name, - )?.mutable, - ) - - return { - ...request, - rich_parameter_values: mutableBuildParameters, - } -} - -const useStyles = makeStyles((theme) => ({ - goBackSection: { - display: "flex", - width: "100%", - marginTop: 32, - }, - formSection: { - marginTop: 20, - }, - - formSectionFields: { - width: "100%", - }, - formSectionParameterTitle: { - fontSize: 20, - color: theme.palette.text.primary, - fontWeight: 400, - margin: 0, - marginBottom: theme.spacing(1), - }, -})) - -const useFormFooterStyles = makeStyles((theme) => ({ - button: { - minWidth: theme.spacing(23), - - [theme.breakpoints.down("sm")]: { - width: "100%", - }, - }, - footer: { - display: "flex", - alignItems: "center", - justifyContent: "flex-start", - flexDirection: "row-reverse", - gap: theme.spacing(2), - - [theme.breakpoints.down("sm")]: { - flexDirection: "column", - gap: theme.spacing(1), - }, - }, -})) From bf1c920520a8e45bc3f215a84478576b9586b311 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Mar 2023 15:22:26 +0000 Subject: [PATCH 8/9] Remvoe unecessary test --- .../WorkspaceActions/WorkspaceActions.test.tsx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx b/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx index e83139086de37..901378f80e6b4 100644 --- a/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx +++ b/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx @@ -91,20 +91,6 @@ describe("WorkspaceActions", () => { ) }) }) - describe("when the workspace with rich parameters is started", () => { - it("primary is stop; secondary is build parameters", async () => { - await renderAndClick({ - workspaceStatus: Mocks.MockWorkspace.latest_build.status, - hasTemplateParameters: true, - }) - expect(screen.getByTestId("primary-cta")).toHaveTextContent( - t("actionButton.stop", { ns: "workspacePage" }), - ) - expect(screen.getByTestId("secondary-ctas")).toHaveTextContent( - t("actionButton.buildParameters", { ns: "workspacePage" }), - ) - }) - }) describe("when the workspace is stopping", () => { it("primary is stopping; cancel is available; no secondary", async () => { await renderComponent({ From e9b1147de71c06101092a4bd1e19c6b787d88bd4 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Mar 2023 17:56:05 +0000 Subject: [PATCH 9/9] Remove workspace build parameters --- site/src/components/Workspace/Workspace.tsx | 5 - .../WorkspaceActions.test.tsx | 2 - .../WorkspaceActions/WorkspaceActions.tsx | 9 +- .../components/WorkspaceActions/constants.ts | 23 +- site/src/i18n/en/index.ts | 2 - .../i18n/en/workspaceBuildParametersPage.json | 10 - .../WorkspacePage/WorkspaceReadyPage.tsx | 2 - .../workspaceBuildParametersXService.ts | 223 ------------------ .../xServices/workspace/workspaceXService.ts | 49 +--- 9 files changed, 9 insertions(+), 316 deletions(-) delete mode 100644 site/src/i18n/en/workspaceBuildParametersPage.json delete mode 100644 site/src/xServices/workspace/workspaceBuildParametersXService.ts diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index 1dad5817f11ca..22be3c6f18abe 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -55,7 +55,6 @@ export interface WorkspaceProps { buildInfo?: TypesGen.BuildInfoResponse applicationsHost?: string template?: TypesGen.Template - templateParameters?: TypesGen.TemplateVersionParameter[] quota_budget?: number } @@ -82,7 +81,6 @@ export const Workspace: FC> = ({ buildInfo, applicationsHost, template, - templateParameters, quota_budget, }) => { const styles = useStyles() @@ -126,9 +124,6 @@ export const Workspace: FC> = ({ /> 0 : false - } isOutdated={workspace.outdated} handleStart={handleStart} handleStop={handleStop} diff --git a/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx b/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx index 901378f80e6b4..6220f0a987bbb 100644 --- a/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx +++ b/site/src/components/WorkspaceActions/WorkspaceActions.test.tsx @@ -12,7 +12,6 @@ const renderComponent = async (props: Partial = {}) => { workspaceStatus={ props.workspaceStatus ?? Mocks.MockWorkspace.latest_build.status } - hasTemplateParameters={props.hasTemplateParameters ?? false} isOutdated={props.isOutdated ?? false} handleStart={jest.fn()} handleStop={jest.fn()} @@ -32,7 +31,6 @@ const renderAndClick = async (props: Partial = {}) => { workspaceStatus={ props.workspaceStatus ?? Mocks.MockWorkspace.latest_build.status } - hasTemplateParameters={props.hasTemplateParameters ?? false} isOutdated={props.isOutdated ?? false} handleStart={jest.fn()} handleStop={jest.fn()} diff --git a/site/src/components/WorkspaceActions/WorkspaceActions.tsx b/site/src/components/WorkspaceActions/WorkspaceActions.tsx index cfcbc92231b43..9a65d0b5e5f81 100644 --- a/site/src/components/WorkspaceActions/WorkspaceActions.tsx +++ b/site/src/components/WorkspaceActions/WorkspaceActions.tsx @@ -16,7 +16,6 @@ import { ButtonMapping, ButtonTypesEnum, buttonAbilities } from "./constants" export interface WorkspaceActionsProps { workspaceStatus: WorkspaceStatus - hasTemplateParameters: boolean isOutdated: boolean handleStart: () => void handleStop: () => void @@ -31,7 +30,6 @@ export interface WorkspaceActionsProps { export const WorkspaceActions: FC = ({ workspaceStatus, - hasTemplateParameters, isOutdated, handleStart, handleStop, @@ -43,10 +41,7 @@ export const WorkspaceActions: FC = ({ isUpdating, }) => { const { t } = useTranslation("workspacePage") - const { canCancel, canAcceptJobs, actions } = buttonAbilities( - workspaceStatus, - hasTemplateParameters, - ) + const { canCancel, canAcceptJobs, actions } = buttonAbilities(workspaceStatus) const canBeUpdated = isOutdated && canAcceptJobs // A mapping of button type to the corresponding React component @@ -58,7 +53,7 @@ export const WorkspaceActions: FC = ({ [ButtonTypesEnum.changeVersion]: ( ), - [ButtonTypesEnum.buildParameters]: ( + [ButtonTypesEnum.settings]: ( ), [ButtonTypesEnum.start]: , diff --git a/site/src/components/WorkspaceActions/constants.ts b/site/src/components/WorkspaceActions/constants.ts index d019721f6a852..e64946d638428 100644 --- a/site/src/components/WorkspaceActions/constants.ts +++ b/site/src/components/WorkspaceActions/constants.ts @@ -12,7 +12,7 @@ export enum ButtonTypesEnum { update = "update", updating = "updating", changeVersion = "changeVersion", - buildParameters = "buildParameters", + settings = "settings", // disabled buttons canceling = "canceling", deleted = "deleted", @@ -31,19 +31,8 @@ interface WorkspaceAbilities { export const buttonAbilities = ( status: WorkspaceStatus, - hasTemplateParameters: boolean, ): WorkspaceAbilities => { - if (hasTemplateParameters) { - return statusToAbilities[status] - } - - const all = statusToAbilities[status] - return { - ...all, - actions: all.actions.filter( - (action) => action !== ButtonTypesEnum.buildParameters, - ), - } + return statusToAbilities[status] } const statusToAbilities: Record = { @@ -55,7 +44,7 @@ const statusToAbilities: Record = { running: { actions: [ ButtonTypesEnum.stop, - ButtonTypesEnum.buildParameters, + ButtonTypesEnum.settings, ButtonTypesEnum.changeVersion, ButtonTypesEnum.delete, ], @@ -70,7 +59,7 @@ const statusToAbilities: Record = { stopped: { actions: [ ButtonTypesEnum.start, - ButtonTypesEnum.buildParameters, + ButtonTypesEnum.settings, ButtonTypesEnum.changeVersion, ButtonTypesEnum.delete, ], @@ -81,7 +70,7 @@ const statusToAbilities: Record = { actions: [ ButtonTypesEnum.start, ButtonTypesEnum.stop, - ButtonTypesEnum.buildParameters, + ButtonTypesEnum.settings, ButtonTypesEnum.changeVersion, ButtonTypesEnum.delete, ], @@ -92,7 +81,7 @@ const statusToAbilities: Record = { failed: { actions: [ ButtonTypesEnum.start, - ButtonTypesEnum.buildParameters, + ButtonTypesEnum.settings, ButtonTypesEnum.changeVersion, ButtonTypesEnum.delete, ], diff --git a/site/src/i18n/en/index.ts b/site/src/i18n/en/index.ts index badd2e4c5ab1f..8176c9d34b9bb 100644 --- a/site/src/i18n/en/index.ts +++ b/site/src/i18n/en/index.ts @@ -12,7 +12,6 @@ import templateSettingsPage from "./templateSettingsPage.json" import templateVariablesPage from "./templateVariablesPage.json" import templateVersionPage from "./templateVersionPage.json" import loginPage from "./loginPage.json" -import workspaceBuildParametersPage from "./workspaceBuildParametersPage.json" import workspaceChangeVersionPage from "./workspaceChangeVersionPage.json" import workspaceSchedulePage from "./workspaceSchedulePage.json" import appearanceSettings from "./appearanceSettings.json" @@ -38,7 +37,6 @@ export const en = { templateVariablesPage, templateVersionPage, loginPage, - workspaceBuildParametersPage, workspaceChangeVersionPage, workspaceSchedulePage, appearanceSettings, diff --git a/site/src/i18n/en/workspaceBuildParametersPage.json b/site/src/i18n/en/workspaceBuildParametersPage.json deleted file mode 100644 index 1a61012633803..0000000000000 --- a/site/src/i18n/en/workspaceBuildParametersPage.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Workspace build parameters", - "detail": "Those values were provided by the workspace owner.", - "noParametersDefined": "This template does not use any rich parameters.", - "validationNumberNotInRange": "Value must be between {{min}} and {{max}}.", - "validationPatternNotMatched": "{{error}} (value does not match the pattern {{pattern}}).", - "updateWorkspace": "Update workspace", - "validationNumberNotIncreasing": "The value must be equal or greater than the previous one {{last}}.", - "validationNumberNotDecreasing": "The value must be equal or lower than the previous one {{last}}." -} diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 2214fda5c96ca..323b685c3a449 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -46,7 +46,6 @@ export const WorkspaceReadyPage = ({ const { workspace, template, - templateParameters, builds, getBuildsError, buildError, @@ -127,7 +126,6 @@ export const WorkspaceReadyPage = ({ buildInfo={buildInfo} applicationsHost={applicationsHost} template={template} - templateParameters={templateParameters} quota_budget={quotaState.context.quota?.budget} /> { - const { workspaceOwner, workspaceName } = context - return getWorkspaceByOwnerAndName(workspaceOwner, workspaceName) - }, - getTemplateParameters: (context) => { - const { selectedWorkspace } = context - - if (!selectedWorkspace) { - throw new Error("No workspace selected") - } - - return getTemplateVersionRichParameters( - selectedWorkspace.latest_build.template_version_id, - ) - }, - getWorkspaceBuildParameters: (context) => { - const { selectedWorkspace } = context - - if (!selectedWorkspace) { - throw new Error("No workspace selected") - } - - return getWorkspaceBuildParameters(selectedWorkspace.latest_build.id) - }, - updateWorkspace: (context) => { - const { selectedWorkspace, createWorkspaceBuildRequest } = context - - if (!selectedWorkspace) { - throw new Error("No workspace selected") - } - - if (!createWorkspaceBuildRequest) { - throw new Error("No workspace build request") - } - - return postWorkspaceBuild( - selectedWorkspace.id, - createWorkspaceBuildRequest, - ) - }, - }, - actions: { - assignWorkspace: assign({ - selectedWorkspace: (_, event) => event.data, - }), - assignTemplateParameters: assign({ - templateParameters: (_, event) => event.data, - }), - assignWorkspaceBuildParameters: assign({ - workspaceBuildParameters: (_, event) => event.data, - }), - - assignCreateWorkspaceBuildRequest: assign({ - createWorkspaceBuildRequest: (_, event) => event.request, - }), - assignGetWorkspaceError: assign({ - getWorkspaceError: (_, event) => event.data, - }), - clearGetWorkspaceError: assign({ - getWorkspaceError: (_) => undefined, - }), - assignGetTemplateParametersError: assign({ - getTemplateParametersError: (_, event) => event.data, - }), - clearGetTemplateParametersError: assign({ - getTemplateParametersError: (_) => undefined, - }), - clearGetWorkspaceBuildParametersError: assign({ - getWorkspaceBuildParametersError: (_) => undefined, - }), - assignGetWorkspaceBuildParametersError: assign({ - getWorkspaceBuildParametersError: (_, event) => event.data, - }), - clearUpdateWorkspaceError: assign({ - updateWorkspaceError: (_) => undefined, - }), - assignUpdateWorkspaceError: assign({ - updateWorkspaceError: (_, event) => event.data, - }), - }, - }, -) diff --git a/site/src/xServices/workspace/workspaceXService.ts b/site/src/xServices/workspace/workspaceXService.ts index cdd7089076d17..5787156821379 100644 --- a/site/src/xServices/workspace/workspaceXService.ts +++ b/site/src/xServices/workspace/workspaceXService.ts @@ -55,7 +55,6 @@ export interface WorkspaceContext { eventSource?: EventSource workspace?: TypesGen.Workspace template?: TypesGen.Template - templateParameters?: TypesGen.TemplateVersionParameter[] build?: TypesGen.WorkspaceBuild getWorkspaceError?: Error | unknown getTemplateWarning: Error | unknown @@ -203,7 +202,7 @@ export const workspaceMachine = createMachine( onDone: [ { actions: ["assignTemplate", "clearGetTemplateWarning"], - target: "gettingTemplateParameters", + target: "gettingPermissions", }, ], onError: [ @@ -218,31 +217,6 @@ export const workspaceMachine = createMachine( }, tags: "loading", }, - gettingTemplateParameters: { - invoke: { - src: "getTemplateParameters", - id: "getTemplateParameters", - onDone: [ - { - actions: [ - "assignTemplateParameters", - "clearGetTemplateParametersWarning", - ], - target: "gettingPermissions", - }, - ], - onError: [ - { - actions: [ - "assignGetTemplateParametersWarning", - "displayGetTemplateParametersWarning", - ], - target: "error", - }, - ], - }, - tags: "loading", - }, gettingPermissions: { invoke: { src: "checkPermissions", @@ -524,9 +498,6 @@ export const workspaceMachine = createMachine( assignTemplate: assign({ template: (_, event) => event.data, }), - assignTemplateParameters: assign({ - templateParameters: (_, event) => event.data, - }), assignPermissions: assign({ // Setting event.data as Permissions to be more stricted. So we know // what permissions we asked for. @@ -587,15 +558,6 @@ export const workspaceMachine = createMachine( clearGetTemplateWarning: assign({ getTemplateWarning: (_) => undefined, }), - assignGetTemplateParametersWarning: assign({ - getTemplateParametersWarning: (_, event) => event.data, - }), - displayGetTemplateParametersWarning: () => { - displayError(Language.getTemplateParametersWarning) - }, - clearGetTemplateParametersWarning: assign({ - getTemplateParametersWarning: (_) => undefined, - }), // Timeline assignBuilds: assign({ builds: (_, event) => event.data, @@ -667,15 +629,6 @@ export const workspaceMachine = createMachine( throw Error("Cannot get template without workspace") } }, - getTemplateParameters: async (context) => { - if (context.workspace) { - return await API.getTemplateVersionRichParameters( - context.workspace.latest_build.template_version_id, - ) - } else { - throw Error("Cannot get template parameters without workspace") - } - }, updateWorkspace: ({ workspace }, { buildParameters }) => async (send) => {