Skip to content

Commit

Permalink
refactor: reset field default value on type change in Settings (twent…
Browse files Browse the repository at this point in the history
…yhq#5534)

Related issue: twentyhq#5412

See twentyhq#5436 (comment)
for context.
  • Loading branch information
thaisguigon authored May 24, 2024
1 parent 18fafbd commit 7f7ea59
Show file tree
Hide file tree
Showing 23 changed files with 326 additions and 131 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from 'zod';

import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
import { getOptionValueFromLabel } from '@/settings/data-model/fields/forms/utils/getOptionValueFromLabel';
import { getOptionValueFromLabel } from '@/settings/data-model/fields/forms/select/utils/getOptionValueFromLabel';
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
import { computeOptionValueFromLabelOrThrow } from '~/pages/settings/data-model/utils/compute-option-value-from-label.utils';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useEffect } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import styled from '@emotion/styled';
import { IconCheck, IconX } from 'twenty-ui';
import { z } from 'zod';

import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/fields/forms/boolean/hooks/useBooleanSettingsFormInitialValues';
import { Select } from '@/ui/input/components/Select';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { isDefined } from '~/utils/isDefined';
Expand All @@ -13,13 +13,13 @@ export const settingsDataModelFieldBooleanFormSchema = z.object({
defaultValue: z.boolean(),
});

type SettingsDataModelFieldBooleanFormValues = z.infer<
export type SettingsDataModelFieldBooleanFormValues = z.infer<
typeof settingsDataModelFieldBooleanFormSchema
>;

type SettingsDataModelFieldBooleanFormProps = {
className?: string;
fieldMetadataItem?: Pick<FieldMetadataItem, 'defaultValue'>;
fieldMetadataItem: Pick<FieldMetadataItem, 'defaultValue'>;
};

const StyledContainer = styled(CardContent)`
Expand All @@ -38,24 +38,20 @@ export const SettingsDataModelFieldBooleanForm = ({
className,
fieldMetadataItem,
}: SettingsDataModelFieldBooleanFormProps) => {
const { control, resetField } =
useFormContext<SettingsDataModelFieldBooleanFormValues>();
const { control } = useFormContext<SettingsDataModelFieldBooleanFormValues>();

const isEditMode = isDefined(fieldMetadataItem?.defaultValue);
const initialValue = fieldMetadataItem?.defaultValue ?? true;

// Reset defaultValue on mount, so it doesn't conflict with other field types.
useEffect(() => {
resetField('defaultValue', { defaultValue: initialValue });
}, [initialValue, resetField]);
const { initialDefaultValue } = useBooleanSettingsFormInitialValues({
fieldMetadataItem,
});

return (
<StyledContainer>
<StyledLabel>Default Value</StyledLabel>
<Controller
name="defaultValue"
control={control}
defaultValue={initialValue}
defaultValue={initialDefaultValue}
render={({ field: { onChange, value } }) => (
<Select
className={className}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useFormContext } from 'react-hook-form';
import styled from '@emotion/styled';

import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
import {
SettingsDataModelFieldBooleanForm,
SettingsDataModelFieldBooleanFormValues,
} from '@/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanForm';
import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/fields/forms/boolean/hooks/useBooleanSettingsFormInitialValues';
import {
SettingsDataModelFieldPreviewCard,
SettingsDataModelFieldPreviewCardProps,
} from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewCard';

type SettingsDataModelFieldBooleanSettingsFormCardProps = {
fieldMetadataItem: Pick<
FieldMetadataItem,
'icon' | 'label' | 'type' | 'defaultValue'
>;
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;

const StyledFieldPreviewCard = styled(SettingsDataModelFieldPreviewCard)`
display: grid;
flex: 1 1 100%;
`;

export const SettingsDataModelFieldBooleanSettingsFormCard = ({
fieldMetadataItem,
objectMetadataItem,
}: SettingsDataModelFieldBooleanSettingsFormCardProps) => {
const { initialDefaultValue } = useBooleanSettingsFormInitialValues({
fieldMetadataItem,
});

const { watch: watchFormValue } =
useFormContext<SettingsDataModelFieldBooleanFormValues>();

return (
<SettingsDataModelPreviewFormCard
preview={
<StyledFieldPreviewCard
fieldMetadataItem={{
...fieldMetadataItem,
defaultValue: watchFormValue('defaultValue', initialDefaultValue),
}}
objectMetadataItem={objectMetadataItem}
/>
}
form={
<SettingsDataModelFieldBooleanForm
fieldMetadataItem={fieldMetadataItem}
/>
}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useFormContext } from 'react-hook-form';

import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { SettingsDataModelFieldBooleanFormValues } from '@/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanForm';

export const useBooleanSettingsFormInitialValues = ({
fieldMetadataItem,
}: {
fieldMetadataItem?: Pick<FieldMetadataItem, 'defaultValue'>;
}) => {
const initialDefaultValue =
(fieldMetadataItem?.defaultValue as SettingsDataModelFieldBooleanFormValues['defaultValue']) ??
true;

const { resetField } =
useFormContext<SettingsDataModelFieldBooleanFormValues>();

const resetDefaultValueField = () => {
resetField('defaultValue', { defaultValue: initialDefaultValue });
};

return {
initialDefaultValue,
resetDefaultValueField,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,17 @@ import { z } from 'zod';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
import { SETTINGS_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
import {
SettingsDataModelFieldBooleanForm,
settingsDataModelFieldBooleanFormSchema,
} from '@/settings/data-model/fields/forms/components/boolean/SettingsDataModelFieldBooleanForm';
import {
SettingsDataModelFieldCurrencyForm,
settingsDataModelFieldCurrencyFormSchema,
} from '@/settings/data-model/fields/forms/components/currency/SettingsDataModelFieldCurrencyForm';
import { settingsDataModelFieldRelationFormSchema } from '@/settings/data-model/fields/forms/components/relation/SettingsDataModelFieldRelationForm';
import { SettingsDataModelFieldRelationSettingsFormCard } from '@/settings/data-model/fields/forms/components/relation/SettingsDataModelFieldRelationSettingsFormCard';
import { settingsDataModelFieldBooleanFormSchema } from '@/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanForm';
import { SettingsDataModelFieldBooleanSettingsFormCard } from '@/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanSettingsFormCard';
import { settingsDataModelFieldCurrencyFormSchema } from '@/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm';
import { SettingsDataModelFieldCurrencySettingsFormCard } from '@/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencySettingsFormCard';
import { settingsDataModelFieldRelationFormSchema } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm';
import { SettingsDataModelFieldRelationSettingsFormCard } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationSettingsFormCard';
import {
settingsDataModelFieldMultiSelectFormSchema,
settingsDataModelFieldSelectFormSchema,
} from '@/settings/data-model/fields/forms/components/select/SettingsDataModelFieldSelectForm';
import { SettingsDataModelFieldSelectSettingsFormCard } from '@/settings/data-model/fields/forms/components/select/SettingsDataModelFieldSelectSettingsFormCard';
} from '@/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm';
import { SettingsDataModelFieldSelectSettingsFormCard } from '@/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectSettingsFormCard';
import {
SettingsDataModelFieldPreviewCard,
SettingsDataModelFieldPreviewCardProps,
Expand Down Expand Up @@ -108,6 +104,25 @@ export const SettingsDataModelFieldSettingsFormCard = ({
}: SettingsDataModelFieldSettingsFormCardProps) => {
if (!previewableTypes.includes(fieldMetadataItem.type)) return null;

if (fieldMetadataItem.type === FieldMetadataType.Boolean) {
return (
<SettingsDataModelFieldBooleanSettingsFormCard
fieldMetadataItem={fieldMetadataItem}
objectMetadataItem={objectMetadataItem}
/>
);
}

if (fieldMetadataItem.type === FieldMetadataType.Currency) {
return (
<SettingsDataModelFieldCurrencySettingsFormCard
disabled={disableCurrencyForm}
fieldMetadataItem={fieldMetadataItem}
objectMetadataItem={objectMetadataItem}
/>
);
}

if (fieldMetadataItem.type === FieldMetadataType.Relation) {
return (
<SettingsDataModelFieldRelationSettingsFormCard
Expand Down Expand Up @@ -137,18 +152,6 @@ export const SettingsDataModelFieldSettingsFormCard = ({
objectMetadataItem={objectMetadataItem}
/>
}
form={
fieldMetadataItem.type === FieldMetadataType.Boolean ? (
<SettingsDataModelFieldBooleanForm
fieldMetadataItem={fieldMetadataItem}
/>
) : fieldMetadataItem.type === FieldMetadataType.Currency ? (
<SettingsDataModelFieldCurrencyForm
disabled={disableCurrencyForm}
fieldMetadataItem={fieldMetadataItem}
/>
) : undefined
}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
SETTINGS_FIELD_TYPE_CONFIGS,
SettingsFieldTypeConfig,
} from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/fields/forms/boolean/hooks/useBooleanSettingsFormInitialValues';
import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues';
import { useSelectSettingsFormInitialValues } from '@/settings/data-model/fields/forms/select/hooks/useSelectSettingsFormInitialValues';
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
import { Select, SelectOption } from '@/ui/input/components/Select';
import { FieldMetadataType } from '~/generated-metadata/graphql';
Expand All @@ -28,7 +31,10 @@ type SettingsDataModelFieldTypeSelectProps = {
className?: string;
disabled?: boolean;
excludedFieldTypes?: SettingsSupportedFieldType[];
fieldMetadataItem?: FieldMetadataItem;
fieldMetadataItem?: Pick<
FieldMetadataItem,
'defaultValue' | 'options' | 'type'
>;
};

export const SettingsDataModelFieldTypeSelect = ({
Expand All @@ -51,6 +57,34 @@ export const SettingsDataModelFieldTypeSelect = ({
value: key as SettingsSupportedFieldType,
}));

const { resetDefaultValueField: resetBooleanDefaultValueField } =
useBooleanSettingsFormInitialValues({ fieldMetadataItem });

const { resetDefaultValueField: resetCurrencyDefaultValueField } =
useCurrencySettingsFormInitialValues({ fieldMetadataItem });

const { resetDefaultValueField: resetSelectDefaultValueField } =
useSelectSettingsFormInitialValues({ fieldMetadataItem });

// Reset defaultValue on type change with a valid value for the selected type
// so the form does not become invalid.
const resetDefaultValueField = (nextValue: SettingsSupportedFieldType) => {
switch (nextValue) {
case FieldMetadataType.Boolean:
resetBooleanDefaultValueField();
break;
case FieldMetadataType.Currency:
resetCurrencyDefaultValueField();
break;
case FieldMetadataType.Select:
case FieldMetadataType.MultiSelect:
resetSelectDefaultValueField();
break;
default:
break;
}
};

return (
<Controller
name="type"
Expand All @@ -67,7 +101,10 @@ export const SettingsDataModelFieldTypeSelect = ({
disabled={disabled}
dropdownId="object-field-type-select"
value={value}
onChange={onChange}
onChange={(nextValue) => {
onChange(nextValue);
resetDefaultValueField(nextValue);
}}
options={fieldTypeOptions}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { useEffect } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { z } from 'zod';

import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
import { currencyCodeSchema } from '@/object-record/record-field/validation-schemas/currencyCodeSchema';
import { SETTINGS_FIELD_CURRENCY_CODES } from '@/settings/data-model/constants/SettingsFieldCurrencyCodes';
import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues';
import { Select } from '@/ui/input/components/Select';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
Expand All @@ -14,7 +13,7 @@ import { simpleQuotesStringSchema } from '~/utils/validation-schemas/simpleQuote

export const settingsDataModelFieldCurrencyFormSchema = z.object({
defaultValue: z.object({
amountMicros: z.null(),
amountMicros: z.number().nullable(),
currencyCode: simpleQuotesStringSchema.refine(
(value) =>
currencyCodeSchema.safeParse(stripSimpleQuotesFromString(value))
Expand All @@ -24,13 +23,13 @@ export const settingsDataModelFieldCurrencyFormSchema = z.object({
}),
});

type SettingsDataModelFieldCurrencyFormValues = z.infer<
export type SettingsDataModelFieldCurrencyFormValues = z.infer<
typeof settingsDataModelFieldCurrencyFormSchema
>;

type SettingsDataModelFieldCurrencyFormProps = {
disabled?: boolean;
fieldMetadataItem?: Pick<FieldMetadataItem, 'defaultValue'>;
fieldMetadataItem: Pick<FieldMetadataItem, 'defaultValue'>;
};

const OPTIONS = Object.entries(SETTINGS_FIELD_CURRENCY_CODES).map(
Expand All @@ -45,25 +44,11 @@ export const SettingsDataModelFieldCurrencyForm = ({
disabled,
fieldMetadataItem,
}: SettingsDataModelFieldCurrencyFormProps) => {
const { control, resetField } =
const { control } =
useFormContext<SettingsDataModelFieldCurrencyFormValues>();

const initialAmountMicrosValue = null;
const initialCurrencyCode =
(fieldMetadataItem?.defaultValue?.currencyCode as CurrencyCode) ??
CurrencyCode.USD;
const initialCurrencyCodeValue =
applySimpleQuotesToString(initialCurrencyCode);

// Reset defaultValue on mount, so it doesn't conflict with other field types.
useEffect(() => {
resetField('defaultValue', {
defaultValue: {
amountMicros: initialAmountMicrosValue,
currencyCode: initialCurrencyCodeValue,
},
});
}, [initialCurrencyCodeValue, resetField]);
const { initialAmountMicrosValue, initialCurrencyCodeValue } =
useCurrencySettingsFormInitialValues({ fieldMetadataItem });

return (
<CardContent>
Expand Down
Loading

0 comments on commit 7f7ea59

Please sign in to comment.