From b03e3befac554cbbe6169fbf141d362118aa41bd Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Mon, 19 May 2025 20:38:18 +0000 Subject: [PATCH 1/2] fix: get presets working correctly with dynamic params --- .../DynamicParameter/DynamicParameter.tsx | 11 ++- .../CreateWorkspacePageExperimental.tsx | 2 +- .../CreateWorkspacePageViewExperimental.tsx | 73 +++++++++++++++---- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx b/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx index cbc7852bd14e5..d17c92503314c 100644 --- a/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx +++ b/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx @@ -222,6 +222,15 @@ const DebouncedParameterField: FC = ({ const onChangeEvent = useEffectEvent(onChange); // prevDebouncedValueRef is to prevent calling the onChangeEvent on the initial render const prevDebouncedValueRef = useRef(); + const prevValueRef = useRef(value); + + // This is necessary in the case of fields being set by preset parameters + useEffect(() => { + if (value !== undefined && value !== prevValueRef.current) { + setLocalValue(value); + prevValueRef.current = value; + } + }, [value]); useEffect(() => { if (prevDebouncedValueRef.current !== undefined) { @@ -458,7 +467,7 @@ const ParameterField: FC = ({ { onChange(value.toString()); }} diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx index 8268ded111b59..fbb35c61ee047 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageExperimental.tsx @@ -101,7 +101,7 @@ const CreateWorkspacePageExperimental: FC = () => { } }, []); - // On sends all initial parameter values to the websocket + // On page load, sends all initial parameter values to the websocket // (including defaults and autofilled from the url) // This ensures the backend has the complete initial state of the form, // which is vital for correctly rendering dynamic UI elements where parameter visibility diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx index 434cd23fb9a92..dff5b679a7e3c 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx @@ -213,39 +213,76 @@ export const CreateWorkspacePageViewExperimental: FC< setPresetParameterNames(selectedPreset.Parameters.map((p) => p.Name)); - for (const presetParameter of selectedPreset.Parameters) { + const currentValues = form.values.rich_parameter_values ?? []; + + const updates = selectedPreset.Parameters.map((presetParameter) => { const parameterIndex = parameters.findIndex( (p) => p.name === presetParameter.Name, ); - if (parameterIndex === -1) continue; + if (parameterIndex === -1) return null; const parameterField = `rich_parameter_values.${parameterIndex}`; + const parameter = parameters[parameterIndex]; + const currentValue = currentValues.find( + (p) => p.name === presetParameter.Name, + )?.value; + + if (currentValue !== presetParameter.Value) { + return { + field: parameterField, + fieldValue: { + name: presetParameter.Name, + value: presetParameter.Value, + }, + parameter, + presetValue: presetParameter.Value, + }; + } + return null; + }).filter( + (update): update is NonNullable => update !== null, + ); + + if (updates.length > 0) { + for (const update of updates) { + form.setFieldValue(update.field, update.fieldValue); + form.setFieldTouched(update.parameter.name, true); + } - form.setFieldValue(parameterField, { - name: presetParameter.Name, - value: presetParameter.Value, - }); + sendDynamicParamsRequest( + updates.map((update) => ({ + parameter: update.parameter, + value: update.presetValue, + })), + ); } }, [ presetOptions, selectedPresetIndex, presets, form.setFieldValue, + form.setFieldTouched, parameters, + form.values.rich_parameter_values, ]); // send the last user modified parameter and all touched parameters to the websocket const sendDynamicParamsRequest = ( - parameter: PreviewParameter, - value: string, + parameters: Array<{ parameter: PreviewParameter; value: string }>, ) => { const formInputs: Record = {}; - formInputs[parameter.name] = value; - const parameters = form.values.rich_parameter_values ?? []; + const formParameters = form.values.rich_parameter_values ?? []; + + for (const { parameter, value } of parameters) { + formInputs[parameter.name] = value; + } for (const [fieldName, isTouched] of Object.entries(form.touched)) { - if (isTouched && fieldName !== parameter.name) { - const param = parameters.find((p) => p.name === fieldName); + if ( + isTouched && + !parameters.some((p) => p.parameter.name === fieldName) + ) { + const param = formParameters.find((p) => p.name === fieldName); if (param?.value) { formInputs[fieldName] = param.value; } @@ -260,12 +297,20 @@ export const CreateWorkspacePageViewExperimental: FC< parameterField: string, value: string, ) => { + const currentFormValue = form.values.rich_parameter_values?.find( + (p) => p.name === parameter.name, + )?.value; + await form.setFieldValue(parameterField, { name: parameter.name, value, }); - form.setFieldTouched(parameter.name, true); - sendDynamicParamsRequest(parameter, value); + + // Only send the request if the value has changed from the form value + if (currentFormValue !== value) { + form.setFieldTouched(parameter.name, true); + sendDynamicParamsRequest([{ parameter, value }]); + } }; useSyncFormParameters({ From 7498908c2f2121eabfc32aa78053d5e61e426c8e Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Mon, 19 May 2025 21:05:09 +0000 Subject: [PATCH 2/2] fix: cleanup --- .../DynamicParameter/DynamicParameter.tsx | 2 +- .../CreateWorkspacePageViewExperimental.tsx | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx b/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx index d17c92503314c..94fa3bc383074 100644 --- a/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx +++ b/site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx @@ -467,7 +467,7 @@ const ParameterField: FC = ({ { onChange(value.toString()); }} diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx index dff5b679a7e3c..630faf8e806d2 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx @@ -215,11 +215,18 @@ export const CreateWorkspacePageViewExperimental: FC< const currentValues = form.values.rich_parameter_values ?? []; - const updates = selectedPreset.Parameters.map((presetParameter) => { + const updates: Array<{ + field: string; + fieldValue: TypesGen.WorkspaceBuildParameter; + parameter: PreviewParameter; + presetValue: string; + }> = []; + + for (const presetParameter of selectedPreset.Parameters) { const parameterIndex = parameters.findIndex( (p) => p.name === presetParameter.Name, ); - if (parameterIndex === -1) return null; + if (parameterIndex === -1) continue; const parameterField = `rich_parameter_values.${parameterIndex}`; const parameter = parameters[parameterIndex]; @@ -228,7 +235,7 @@ export const CreateWorkspacePageViewExperimental: FC< )?.value; if (currentValue !== presetParameter.Value) { - return { + updates.push({ field: parameterField, fieldValue: { name: presetParameter.Name, @@ -236,12 +243,9 @@ export const CreateWorkspacePageViewExperimental: FC< }, parameter, presetValue: presetParameter.Value, - }; + }); } - return null; - }).filter( - (update): update is NonNullable => update !== null, - ); + } if (updates.length > 0) { for (const update of updates) {