diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx
index e2a184cc599cf..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"
@@ -126,6 +125,9 @@ const CreateTemplatePage = lazy(
const TemplateVariablesPage = lazy(
() => import("./pages/TemplateVariablesPage/TemplateVariablesPage"),
)
+const WorkspaceSettingsPage = lazy(
+ () => import("./pages/WorkspaceSettingsPage/WorkspaceSettingsPage"),
+)
export const AppRouter: FC = () => {
return (
@@ -230,10 +232,7 @@ export const AppRouter: FC = () => {
path="change-version"
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/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/Form/Form.tsx b/site/src/components/Form/Form.tsx
index 1c7e06c25513e..62d7de3817620 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 (
@@ -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/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx
index 19b8514ed154c..22be3c6f18abe 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[]
@@ -55,7 +55,6 @@ export interface WorkspaceProps {
buildInfo?: TypesGen.BuildInfoResponse
applicationsHost?: string
template?: TypesGen.Template
- templateParameters?: TypesGen.TemplateVersionParameter[]
quota_budget?: number
}
@@ -70,7 +69,7 @@ export const Workspace: FC> = ({
handleUpdate,
handleCancel,
handleChangeVersion,
- handleBuildParameters,
+ handleSettings,
workspace,
isUpdating,
resources,
@@ -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}
@@ -136,7 +131,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..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()}
@@ -20,7 +19,7 @@ const renderComponent = async (props: Partial = {}) => {
handleUpdate={jest.fn()}
handleCancel={jest.fn()}
handleChangeVersion={jest.fn()}
- handleBuildParameters={jest.fn()}
+ handleSettings={jest.fn()}
isUpdating={false}
/>,
)
@@ -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()}
@@ -40,7 +38,7 @@ const renderAndClick = async (props: Partial = {}) => {
handleUpdate={jest.fn()}
handleCancel={jest.fn()}
handleChangeVersion={jest.fn()}
- handleBuildParameters={jest.fn()}
+ handleSettings={jest.fn()}
isUpdating={false}
/>,
)
@@ -91,20 +89,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({
diff --git a/site/src/components/WorkspaceActions/WorkspaceActions.tsx b/site/src/components/WorkspaceActions/WorkspaceActions.tsx
index fad29ebc7a209..9a65d0b5e5f81 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,
@@ -16,7 +16,6 @@ import { ButtonMapping, ButtonTypesEnum, buttonAbilities } from "./constants"
export interface WorkspaceActionsProps {
workspaceStatus: WorkspaceStatus
- hasTemplateParameters: boolean
isOutdated: boolean
handleStart: () => void
handleStop: () => void
@@ -24,14 +23,13 @@ export interface WorkspaceActionsProps {
handleUpdate: () => void
handleCancel: () => void
handleChangeVersion: () => void
- handleBuildParameters: () => void
+ handleSettings: () => void
isUpdating: boolean
children?: ReactNode
}
export const WorkspaceActions: FC = ({
workspaceStatus,
- hasTemplateParameters,
isOutdated,
handleStart,
handleStop,
@@ -39,14 +37,11 @@ export const WorkspaceActions: FC = ({
handleUpdate,
handleCancel,
handleChangeVersion,
- handleBuildParameters,
+ handleSettings,
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,8 +53,8 @@ export const WorkspaceActions: FC = ({
[ButtonTypesEnum.changeVersion]: (
),
- [ButtonTypesEnum.buildParameters]: (
-
+ [ButtonTypesEnum.settings]: (
+
),
[ButtonTypesEnum.start]: ,
[ButtonTypesEnum.starting]: (
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 58f6530cab049..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"
@@ -21,6 +20,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,
@@ -37,7 +37,6 @@ export const en = {
templateVariablesPage,
templateVersionPage,
loginPage,
- workspaceBuildParametersPage,
workspaceChangeVersionPage,
workspaceSchedulePage,
appearanceSettings,
@@ -46,4 +45,5 @@ export const en = {
createTemplatePage,
userSettingsPage,
tokensPage,
+ workspaceSettingsPage,
}
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/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/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/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/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 8aeaaf52efea3..0000000000000
--- a/site/src/pages/WorkspaceBuildParametersPage/WorkspaceBuildParametersPageView.tsx
+++ /dev/null
@@ -1,367 +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 { 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"
-
-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 && (
-
- )}
-
- )
-}
-
-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),
- },
- },
-}))
diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx
index 0174745194daf..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,
@@ -113,7 +112,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}
@@ -127,7 +126,6 @@ export const WorkspaceReadyPage = ({
buildInfo={buildInfo}
applicationsHost={applicationsHost}
template={template}
- templateParameters={templateParameters}
quota_budget={quotaState.context.quota?.budget}
/>
void
+ onSubmit: (values: WorkspaceSettingsFormValue) => void
+}> = ({ onCancel, onSubmit, settings, error, isSubmitting }) => {
+ const { t } = useTranslation("workspaceSettingsPage")
+ const mutableParameters = settings.templateVersionRichParameters.filter(
+ (param) => param.mutable,
+ )
+ const form = useFormik({
+ onSubmit,
+ initialValues: {
+ name: settings.workspace.name,
+ 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")),
+ rich_parameter_values: useValidationSchemaForRichParameters(
+ "createWorkspacePage",
+ settings.templateVersionRichParameters,
+ ),
+ }),
+ })
+ const getFieldHelpers = getFormHelpers(
+ form,
+ error,
+ )
+
+ return (
+
+
+
+
+
+
+ {mutableParameters.length > 0 && (
+
+
+ {settings.templateVersionRichParameters.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.test.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.test.tsx
new file mode 100644
index 0000000000000..02838be215db2
--- /dev/null
+++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.test.tsx
@@ -0,0 +1,80 @@
+import userEvent from "@testing-library/user-event"
+import {
+ MockTemplateVersionParameter1,
+ MockTemplateVersionParameter2,
+ MockWorkspace,
+ MockWorkspaceBuild,
+ MockWorkspaceBuildParameter1,
+ MockWorkspaceBuildParameter2,
+ renderWithAuth,
+ waitForLoaderToBeRemoved,
+} from "testHelpers/renderHelpers"
+import WorkspaceSettingsPage from "./WorkspaceSettingsPage"
+import { screen, waitFor, within } from "@testing-library/react"
+import * as api from "api/api"
+
+test("Submit the workspace settings page successfully", async () => {
+ // 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" },
+ ],
+ })
+ })
+})
diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx
new file mode 100644
index 0000000000000..87d8cff54e5e3
--- /dev/null
+++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx
@@ -0,0 +1,49 @@
+import { getErrorMessage } from "api/errors"
+import { displayError } from "components/GlobalSnackbar/utils"
+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 { WorkspaceSettingsPageView } from "./WorkspaceSettingsPageView"
+
+const WorkspaceSettingsPage = () => {
+ const { t } = useTranslation("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, t("defaultErrorMessage"))),
+ })
+
+ return (
+ <>
+
+ Codestin Search App
+
+
+ navigate(-1)}
+ onSubmit={updateSettings.mutate}
+ />
+ >
+ )
+}
+
+export default WorkspaceSettingsPage
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
new file mode 100644
index 0000000000000..cf64abc9a189e
--- /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 [templateVersionRichParameters, buildParameters] = await Promise.all([
+ getTemplateVersionRichParameters(latestBuild.template_version_id),
+ getWorkspaceBuildParameters(latestBuild.id),
+ ])
+ return {
+ workspace,
+ templateVersionRichParameters,
+ 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) || ""
+}
diff --git a/site/src/xServices/workspace/workspaceBuildParametersXService.ts b/site/src/xServices/workspace/workspaceBuildParametersXService.ts
deleted file mode 100644
index b4b2e7fad42f1..0000000000000
--- a/site/src/xServices/workspace/workspaceBuildParametersXService.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-import {
- getTemplateVersionRichParameters,
- getWorkspaceByOwnerAndName,
- getWorkspaceBuildParameters,
- postWorkspaceBuild,
-} from "api/api"
-import {
- CreateWorkspaceBuildRequest,
- Template,
- TemplateVersionParameter,
- Workspace,
- WorkspaceBuild,
- WorkspaceBuildParameter,
-} from "api/typesGenerated"
-import { assign, createMachine } from "xstate"
-
-type WorkspaceBuildParametersContext = {
- workspaceOwner: string
- workspaceName: string
-
- selectedWorkspace?: Workspace
- selectedTemplate?: Template
- templateParameters?: TemplateVersionParameter[]
- workspaceBuildParameters?: WorkspaceBuildParameter[]
-
- createWorkspaceBuildRequest?: CreateWorkspaceBuildRequest
-
- getWorkspaceError?: Error | unknown
- getTemplateParametersError?: Error | unknown
- getWorkspaceBuildParametersError?: Error | unknown
- updateWorkspaceError?: Error | unknown
-}
-
-type UpdateWorkspaceEvent = {
- type: "UPDATE_WORKSPACE"
- request: CreateWorkspaceBuildRequest
-}
-
-export const workspaceBuildParametersMachine = createMachine(
- {
- id: "workspaceBuildParametersState",
- predictableActionArguments: true,
- tsTypes:
- {} as import("./workspaceBuildParametersXService.typegen").Typegen0,
- schema: {
- context: {} as WorkspaceBuildParametersContext,
- events: {} as UpdateWorkspaceEvent,
- services: {} as {
- getWorkspace: {
- data: Workspace
- }
- getTemplateParameters: {
- data: TemplateVersionParameter[]
- }
- getWorkspaceBuildParameters: {
- data: WorkspaceBuildParameter[]
- }
- updateWorkspace: {
- data: WorkspaceBuild
- }
- },
- },
- initial: "gettingWorkspace",
- states: {
- gettingWorkspace: {
- entry: "clearGetWorkspaceError",
- invoke: {
- src: "getWorkspace",
- onDone: [
- {
- actions: ["assignWorkspace"],
- target: "gettingTemplateParameters",
- },
- ],
- onError: {
- actions: ["assignGetWorkspaceError"],
- target: "error",
- },
- },
- },
- gettingTemplateParameters: {
- entry: "clearGetTemplateParametersError",
- invoke: {
- src: "getTemplateParameters",
- onDone: [
- {
- actions: ["assignTemplateParameters"],
- target: "gettingWorkspaceBuildParameters",
- },
- ],
- onError: {
- actions: ["assignGetTemplateParametersError"],
- target: "error",
- },
- },
- },
- gettingWorkspaceBuildParameters: {
- entry: "clearGetWorkspaceBuildParametersError",
- invoke: {
- src: "getWorkspaceBuildParameters",
- onDone: {
- actions: ["assignWorkspaceBuildParameters"],
- target: "fillingParams",
- },
- onError: {
- actions: ["assignGetWorkspaceBuildParametersError"],
- target: "error",
- },
- },
- },
- fillingParams: {
- on: {
- UPDATE_WORKSPACE: {
- actions: ["assignCreateWorkspaceBuildRequest"],
- target: "updatingWorkspace",
- },
- },
- },
- updatingWorkspace: {
- entry: "clearUpdateWorkspaceError",
- invoke: {
- src: "updateWorkspace",
- onDone: {
- actions: ["onUpdateWorkspace"],
- target: "updated",
- },
- onError: {
- actions: ["assignUpdateWorkspaceError"],
- target: "fillingParams",
- },
- },
- },
- updated: {
- entry: "onUpdateWorkspace",
- type: "final",
- },
- error: {},
- },
- },
- {
- services: {
- getWorkspace: (context) => {
- 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) => {