fix: enable save button when changing currency default value (#16864)
Closes #16792 ## Problem When editing a currency field’s default value (for example, changing from **USD** to **UYU**), the **Save** button remained disabled. However, if the format setting was changed first (**short → full → short**), the Save button would then work correctly for currency changes. ## Root Cause The form was using **React Hook Form’s `values` mode**, which did not properly track dirty state for the `defaultValue` and `settings` fields. ## Solution Switched to the **`defaultValues` + `reset()`** pattern: - Initialize the form with `defaultValues` (using placeholder values) - Call `reset()` when `fieldMetadataItem` loads This correctly tracks dirty state by comparing against the most recent `reset` values. ## Note A similar pattern is used in: - `useWebhookForm` - `useImapSmtpCaldavConnectionForm` Those hooks call `reset()` via query callbacks. In this case, `reset()` is called inside a `useEffect` because the data comes from synchronous Recoil state rather than an async query. Found two solutions to the problem, created commits for both. I feel the useEffect pattern makes more sense here since it's cleaner in terms of code. --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
+12
-15
@@ -2,11 +2,10 @@ import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { type SettingsDataModelFieldCurrencyFormValues } from '@/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { CurrencyCode } from 'twenty-shared/constants';
|
||||
import { getFieldMetadataItemInitialValues } from '~/pages/settings/data-model/utils/getFieldMetadataItemInitialValues';
|
||||
import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/formatNumber';
|
||||
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
|
||||
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
|
||||
|
||||
type UseCurrencySettingsFormInitialValuesArgs = {
|
||||
existingFieldMetadataId: string;
|
||||
@@ -18,25 +17,23 @@ export const useCurrencySettingsFormInitialValues = ({
|
||||
existingFieldMetadataId,
|
||||
);
|
||||
|
||||
const initialAmountMicrosValue =
|
||||
(fieldMetadataItem?.defaultValue?.amountMicros as number | null) ?? null;
|
||||
const initialCurrencyCodeValue = isNonEmptyString(
|
||||
stripSimpleQuotesFromString(fieldMetadataItem?.defaultValue?.currencyCode),
|
||||
)
|
||||
? fieldMetadataItem?.defaultValue?.currencyCode
|
||||
: applySimpleQuotesToString(CurrencyCode.USD);
|
||||
const { settings, defaultValue } =
|
||||
getFieldMetadataItemInitialValues(fieldMetadataItem);
|
||||
|
||||
const initialFormValues: SettingsDataModelFieldCurrencyFormValues = {
|
||||
settings: {
|
||||
format: fieldMetadataItem?.settings?.format ?? 'short',
|
||||
decimals: fieldMetadataItem?.settings?.decimals ?? DEFAULT_DECIMAL_VALUE,
|
||||
settings: settings ?? {
|
||||
format: 'short',
|
||||
decimals: DEFAULT_DECIMAL_VALUE,
|
||||
},
|
||||
defaultValue: {
|
||||
amountMicros: initialAmountMicrosValue,
|
||||
currencyCode: initialCurrencyCodeValue,
|
||||
defaultValue: defaultValue ?? {
|
||||
amountMicros: null,
|
||||
currencyCode: applySimpleQuotesToString(CurrencyCode.USD),
|
||||
},
|
||||
};
|
||||
|
||||
const initialAmountMicrosValue = initialFormValues.defaultValue.amountMicros;
|
||||
const initialCurrencyCodeValue = initialFormValues.defaultValue.currencyCode;
|
||||
|
||||
const { resetField } =
|
||||
useFormContext<SettingsDataModelFieldCurrencyFormValues>();
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import { Section } from 'twenty-ui/layout';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getFieldMetadataItemInitialValues } from '~/pages/settings/data-model/utils/getFieldMetadataItemInitialValues';
|
||||
|
||||
//TODO: fix this type
|
||||
export type SettingsDataModelFieldEditFormValues = z.infer<
|
||||
@@ -107,6 +108,9 @@ export const SettingsObjectFieldEdit = () => {
|
||||
const getRelationMetadata = useGetRelationMetadata();
|
||||
const { updateOneFieldMetadataItem } = useUpdateOneFieldMetadataItem();
|
||||
|
||||
const { settings, defaultValue } =
|
||||
getFieldMetadataItemInitialValues(fieldMetadataItem);
|
||||
|
||||
const formConfig = useForm<SettingsDataModelFieldEditFormValues>({
|
||||
mode: 'onTouched',
|
||||
resolver: zodResolver(settingsFieldFormSchema()),
|
||||
@@ -116,7 +120,8 @@ export const SettingsObjectFieldEdit = () => {
|
||||
label: fieldMetadataItem?.label ?? '',
|
||||
description: fieldMetadataItem?.description,
|
||||
isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true,
|
||||
settings: fieldMetadataItem?.settings,
|
||||
settings,
|
||||
defaultValue,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { CurrencyCode } from 'twenty-shared/constants';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/formatNumber';
|
||||
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
|
||||
|
||||
export const getFieldMetadataItemInitialValues = (
|
||||
fieldMetadataItem: FieldMetadataItem | undefined,
|
||||
) => {
|
||||
if (fieldMetadataItem?.type !== FieldMetadataType.CURRENCY) {
|
||||
return {
|
||||
settings: fieldMetadataItem?.settings ?? undefined,
|
||||
defaultValue: fieldMetadataItem?.defaultValue ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const settings = fieldMetadataItem.settings ?? {
|
||||
format: 'short',
|
||||
decimals: DEFAULT_DECIMAL_VALUE,
|
||||
};
|
||||
|
||||
const currencyCode = isNonEmptyString(
|
||||
fieldMetadataItem.defaultValue?.currencyCode,
|
||||
)
|
||||
? fieldMetadataItem.defaultValue?.currencyCode
|
||||
: applySimpleQuotesToString(CurrencyCode.USD);
|
||||
|
||||
const defaultValue = {
|
||||
amountMicros: fieldMetadataItem.defaultValue?.amountMicros ?? null,
|
||||
currencyCode,
|
||||
};
|
||||
|
||||
return { settings, defaultValue };
|
||||
};
|
||||
Reference in New Issue
Block a user