diff --git a/site/src/components/RichParameterInput/RichParameterInput.tsx b/site/src/components/RichParameterInput/RichParameterInput.tsx index b9ad1fba4e2f0..751281e42813d 100644 --- a/site/src/components/RichParameterInput/RichParameterInput.tsx +++ b/site/src/components/RichParameterInput/RichParameterInput.tsx @@ -3,14 +3,16 @@ import Radio from "@mui/material/Radio"; import RadioGroup from "@mui/material/RadioGroup"; import TextField, { TextFieldProps } from "@mui/material/TextField"; import Tooltip from "@mui/material/Tooltip"; +import Button from "@mui/material/Button"; +import FormHelperText from "@mui/material/FormHelperText"; import { type Interpolation, type Theme } from "@emotion/react"; -import { type FC } from "react"; +import { type FC, type ReactNode, useState } from "react"; import { TemplateVersionParameter } from "api/typesGenerated"; import { MemoizedMarkdown } from "components/Markdown/Markdown"; import { Stack } from "components/Stack/Stack"; import { MultiTextField } from "./MultiTextField"; import { ExternalImage } from "components/ExternalImage/ExternalImage"; -import { AutofillSource } from "utils/richParameters"; +import { AutofillBuildParameter, AutofillSource } from "utils/richParameters"; import { Pill } from "components/Pill/Pill"; import ErrorOutline from "@mui/icons-material/ErrorOutline"; @@ -103,6 +105,15 @@ const styles = { width: 16, }, }, + suggestion: (theme) => ({ + color: theme.roles.info.fill.solid, + marginLeft: "-4px", + padding: "4px 6px", + lineHeight: "inherit", + fontSize: "inherit", + height: "unset", + minWidth: "unset", + }), } satisfies Record>; export interface ParameterLabelProps { @@ -169,17 +180,26 @@ export type RichParameterInputProps = Omit< "size" | "onChange" > & { parameter: TemplateVersionParameter; - autofillSource?: AutofillSource; + parameterAutofill?: AutofillBuildParameter; onChange: (value: string) => void; size?: Size; }; +const autofillDescription: Partial> = { + url: " from the URL.", +}; + export const RichParameterInput: FC = ({ - parameter, size = "medium", - autofillSource, + parameter, + parameterAutofill, + onChange, ...fieldProps }) => { + const autofillSource = parameterAutofill?.source; + const autofillValue = parameterAutofill?.value; + const [hideSuggestion, setHideSuggestion] = useState(false); + return ( = ({ >
- - {autofillSource && autofillSource !== "active_build" && ( + + {!parameter.ephemeral && + autofillSource === "user_history" && + autofillValue && + !hideSuggestion && ( + + {" "} + was recently used for this parameter. + + )} + {autofillSource && autofillDescription[autofillSource] && (
- 🪄 Autofilled:{" "} - { - { - ["url"]: "value supplied by URL.", - ["user_history"]: "recently used value.", - }[autofillSource] - } + 🪄 Autofilled {autofillDescription[autofillSource]}
)}
diff --git a/site/src/modules/templates/TemplateParameters/TemplateParameters.tsx b/site/src/modules/templates/TemplateParameters/TemplateParameters.tsx deleted file mode 100644 index 8f20720856ca2..0000000000000 --- a/site/src/modules/templates/TemplateParameters/TemplateParameters.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { TemplateVersionParameter } from "api/typesGenerated"; -import { FormFields, FormSection } from "components/Form/Form"; -import { - RichParameterInput, - RichParameterInputProps, -} from "components/RichParameterInput/RichParameterInput"; -import { ComponentProps, FC } from "react"; -import { AutofillSource } from "utils/richParameters"; - -export type TemplateParametersSectionProps = { - templateParameters: TemplateVersionParameter[]; - autofillSources?: Record; - getInputProps: ( - parameter: TemplateVersionParameter, - index: number, - ) => Omit; -} & Pick, "classes">; - -export const TemplateParametersSection: FC = ({ - templateParameters, - getInputProps, - autofillSources, - ...formSectionProps -}) => { - const hasMutableParameters = - templateParameters.filter((p) => p.mutable).length > 0; - - return ( - <> - {hasMutableParameters && ( - - - {templateParameters.map( - (parameter, index) => - parameter.mutable && ( - - ), - )} - - - )} - - ); -}; diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 9c9d64be0c8db..d83edb55a3e88 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -101,9 +101,14 @@ export const Parameters: Story = { autofillParameters: [ { name: "first_parameter", - value: "It works!", + value: "Cool suggestion", source: "user_history", }, + { + name: "third_parameter", + value: "aaaa", + source: "url", + }, ], }, }; diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index a3ef3163775c8..9bff96124662a 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -20,8 +20,7 @@ import { } from "components/Form/Form"; import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"; import { - AutofillBuildParameter, - AutofillSource, + type AutofillBuildParameter, getInitialRichParameterValues, useValidationSchemaForRichParameters, } from "utils/richParameters"; @@ -39,11 +38,11 @@ import { import { Pill } from "components/Pill/Pill"; import { RichParameterInput } from "components/RichParameterInput/RichParameterInput"; import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName"; -import { +import type { CreateWorkspaceMode, ExternalAuthPollingState, } from "./CreateWorkspacePage"; -import { CreateWSPermissions } from "./permissions"; +import type { CreateWSPermissions } from "./permissions"; export const Language = { duplicationWarning: @@ -137,15 +136,13 @@ export const CreateWorkspacePageView: FC = ({ error, ); - const autofillSources = useMemo(() => { - return autofillParameters.reduce( - (acc, param) => { - acc[param.name] = param.source; - return acc; - }, - {} as Record, - ); - }, [autofillParameters]); + const autofillByName = useMemo( + () => + Object.fromEntries( + autofillParameters.map((param) => [param.name, param]), + ), + [autofillParameters], + ); const hasAllRequiredExternalAuth = externalAuth.every( (auth) => auth.optional || auth.authenticated, @@ -301,9 +298,9 @@ export const CreateWorkspacePageView: FC = ({ value, }); }} - autofillSource={autofillSources[parameter.name]} key={parameter.name} parameter={parameter} + parameterAutofill={autofillByName[parameter.name]} disabled={isDisabled} /> ); @@ -330,6 +327,7 @@ const styles = { lineHeight: "inherit", fontSize: "inherit", height: "unset", + minWidth: "unset", }), hasDescription: { paddingBottom: 16, diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx index 296bd6500b42f..d98f67f33c3a2 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx @@ -1,5 +1,8 @@ -import { getWorkspaceParameters, postWorkspaceBuild } from "api/api"; +import Button from "@mui/material/Button"; +import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined"; import { Helmet } from "react-helmet-async"; +import { getWorkspaceParameters, postWorkspaceBuild } from "api/api"; +import { EmptyState } from "components/EmptyState/EmptyState"; import { pageTitle } from "utils/page"; import { WorkspacePermissions, @@ -11,7 +14,7 @@ import { templateByName } from "api/queries/templates"; import { useMutation, useQuery } from "react-query"; import { Loader } from "components/Loader/Loader"; import { - WorkspaceParametersFormValues, + type WorkspaceParametersFormValues, WorkspaceParametersForm, } from "./WorkspaceParametersForm"; import { useNavigate } from "react-router-dom"; @@ -19,12 +22,8 @@ import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"; import { type FC } from "react"; import { isApiValidationError } from "api/errors"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; -import { EmptyState } from "components/EmptyState/EmptyState"; -import Button from "@mui/material/Button"; -import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined"; +import type { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; import { docs } from "utils/docs"; -import { AutofillBuildParameter } from "utils/richParameters"; const WorkspaceParametersPage: FC = () => { const workspace = useWorkspaceSettings(); @@ -127,12 +126,10 @@ export const WorkspaceParametersPageView: FC< ({ - ...p, - source: "active_build", - }), - )} + autofillParams={data.buildParameters.map((p) => ({ + ...p, + source: "active_build", + }))} templateVersionRichParameters={data.templateVersionRichParameters} error={submitError} isSubmitting={isSubmitting} diff --git a/site/src/utils/richParameters.test.ts b/site/src/utils/richParameters.test.ts index 97d12747bf053..a83f7942a8d30 100644 --- a/site/src/utils/richParameters.test.ts +++ b/site/src/utils/richParameters.test.ts @@ -1,4 +1,4 @@ -import { TemplateVersionParameter } from "api/typesGenerated"; +import type { TemplateVersionParameter } from "api/typesGenerated"; import { getInitialRichParameterValues } from "./richParameters"; test("getInitialRichParameterValues return default value when default build parameter is not valid", () => { @@ -46,7 +46,7 @@ test("getInitialRichParameterValues return default value when default build para const cpuParameter = templateParameters[0]; const [cpuParameterInitialValue] = getInitialRichParameterValues( templateParameters, - [{ name: cpuParameter.name, value: "100", source: "user_history" }], + [{ name: cpuParameter.name, value: "100", source: "url" }], ); expect(cpuParameterInitialValue.value).toBe(cpuParameter.default_value); diff --git a/site/src/utils/richParameters.ts b/site/src/utils/richParameters.ts index a2baa4459ae68..12182c1c89a8f 100644 --- a/site/src/utils/richParameters.ts +++ b/site/src/utils/richParameters.ts @@ -33,7 +33,9 @@ export const getInitialRichParameterValues = ( return { name: parameter.name, value: - autofillParam && isValidValue(parameter, autofillParam) + autofillParam && + isValidValue(parameter, autofillParam) && + autofillParam.source !== "user_history" ? autofillParam.value : parameter.default_value, };