Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,26 @@

import { KEY_SEPARATOR } from '../constants';
import type { InputsType } from '~/routes/configuration';
import type { FormObjectValue, FormValue } from '../types';
import type { Restrictions, FormObjectValue, FormValue, ArrayRestrictions } from '../types';
import { isArrayRestrictions, isObjectRestrictions } from '../types/helpers';

/**
* Convert flat form values back to nested configuration object format.
* @param {InputsType} formValues - The flat form values with prefixed keys.
* @param {Restrictions} restrictions - The object with restrictions.
* Used to determine if the current value is an object or an array
* @param {string} prefix - The prefix to remove from keys (e.g., '/configuration').
* @returns {FormValue} The nested configuration object.
*/
export const convertFormValuesToConfigObject = (
formValues: InputsType,
restrictions: Restrictions | undefined,
prefix: string,
): FormValue => {
if (restrictions === undefined) {
throw new Error('Missing restrictions parameter');
}

const result: FormValue = {};

for (const [key, value] of Object.entries(formValues)) {
Expand All @@ -48,17 +56,27 @@ export const convertFormValuesToConfigObject = (
continue;
}

let current = result;
let currentValue = result; // pointer for currentValue place in the configuration
let currentRestrictions = restrictions; // pointer for currentRestrictions
for (let i = 0; i < keys.length - 1; i++) {
const currentKey = keys[i];
if (!(currentKey in current) || typeof current[currentKey] !== 'object') {
current[currentKey] = {};
currentRestrictions = isObjectRestrictions(currentRestrictions)
? currentRestrictions[currentKey]
: (currentRestrictions as ArrayRestrictions)[0][Number(currentKey)];

if (!(currentKey in currentValue)) {
// we need to create next level of nesting
if (isObjectRestrictions(currentRestrictions)) {
currentValue[currentKey] = {};
} else if (isArrayRestrictions(currentRestrictions)) {
currentValue[currentKey] = Array(currentRestrictions[0].length);
}
}
current = current[currentKey] as FormObjectValue;
currentValue = currentValue[currentKey] as FormObjectValue;
}

const finalKey = keys[keys.length - 1];
current[finalKey] = value.toString();
currentValue[finalKey] = value;
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export const getDefaultValuesFromConfigObject = (
}

if (isPrimitiveValue(val)) {
if (val === 'true') {
return { [prefix]: true };
}
if (val === 'false') {
return { [prefix]: false };
}
return { [prefix]: val };
}

Expand All @@ -41,5 +47,14 @@ export const getDefaultValuesFromConfigObject = (
result = { ...result, ...getDefaultValuesFromConfigObject(value, newPrefix) };
}

// this is an exception where the empty object / empty array is the leaf
// of the Configuration Form tree, because it is empty
// however we still need to render that in the UI, so this bit of info needs to be present
if (entries.length === 0) {
const emptyPrefix = `${prefix}${KEY_SEPARATOR}`;
// bypass typescript since this is an exception from the usual logic of the application
result = { [emptyPrefix]: val as unknown as string };
}

return result;
};
10 changes: 8 additions & 2 deletions Configuration/webapp/app/hooks/useConfigurationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { useForm, type KeepStateOptions, type SubmitHandler } from 'react-hook-f
import { getDefaultValuesFromConfigObject } from '~/components/form/utils/getDefaultValuesFromConfigObject';
import { convertFormValuesToConfigObject } from '~/components/form/utils/convertFormValuesToConfigObject';
import { useLocation } from 'react-router';
import type { FormValue } from '~/components/form/types';
import type { FormValue, Restrictions } from '~/components/form/types';
import { useConfigurationMutation } from '~/api/mutations/useConfigurationMutation';

const RESET_PROPS: KeepStateOptions = { keepDirty: false };
Expand All @@ -34,9 +34,11 @@ const RESET_PROPS: KeepStateOptions = { keepDirty: false };
export const useConfigurationForm = ({
configuration,
configurationName,
configurationRestrictions,
}: {
configuration: FormValue | undefined;
configurationName: string;
configurationRestrictions: Restrictions | undefined;
}) => {
const { pathname } = useLocation();
const mutation = useConfigurationMutation(configurationName);
Expand All @@ -51,7 +53,11 @@ export const useConfigurationForm = ({
});

const onSubmit: SubmitHandler<InputsType> = (data) => {
const configurationData = convertFormValuesToConfigObject(data, pathname);
const configurationData = convertFormValuesToConfigObject(
data,
configurationRestrictions,
pathname,
);
mutation.mutate(configurationData);
};

Expand Down
1 change: 1 addition & 0 deletions Configuration/webapp/app/routes/configuration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const ConfigurationPage = () => {
} = useConfigurationForm({
configuration,
configurationName,
configurationRestrictions,
});

const { showModal, handleProceed, handleSaveAndProceed, handleCancel } = useUnsavedChangesBlocker(
Expand Down
Loading