fix: enable save button when changing currency default value#16864
fix: enable save button when changing currency default value#16864FelixMalfait merged 8 commits intomainfrom
Conversation
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
| resolver: zodResolver(settingsFieldFormSchema()), | ||
| values: { | ||
| icon: fieldMetadataItem?.icon ?? 'Icon', | ||
| type: fieldMetadataItem?.type as SettingsFieldType, | ||
| label: fieldMetadataItem?.label ?? '', | ||
| description: fieldMetadataItem?.description, | ||
| isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true, | ||
| settings: fieldMetadataItem?.settings, | ||
| defaultValues: { | ||
| icon: 'Icon', | ||
| type: FieldMetadataType.TEXT as SettingsFieldType, | ||
| label: '', | ||
| description: null, | ||
| isLabelSyncedWithName: true, | ||
| settings: undefined, | ||
| defaultValue: undefined, | ||
| }, | ||
| }); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
The name field is intentionally excluded and it has been the case before the changes. It's managed by a child component (SettingsDataModelFieldIconLabelForm) which has its own Controller with defaultValue={fieldMetadataItem?.name}.
|
🚀 Preview Environment Ready! Your preview environment is available at: http://bore.pub:45686 This environment will automatically shut down when the PR is closed or after 5 hours. |
|
You have run out of free Bugbot PR reviews for this billing cycle. This will reset on February 15. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
|
The first commit seemed like a better solution to me, we're trying to get rid of all useEffect. |
|
Just read the description I had missed it sorry, you mention useWebhookForm/useImapSmtpCaldavConnectionForm but they don't need an effect do they? |
|
You're right — those hooks don't need a useEffect because they call reset() directly in their query's onCompleted callback when async data arrives. In this case, However, I see your broader point: if we're trying to eliminate useEffect across the codebase, this comparison doesn't really justify adding a new one here. Will do some reworking here to figure out how to use the first pattern better. |
…alization and remove useEffect
| description: fieldMetadataItem?.description, | ||
| isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true, | ||
| settings: fieldMetadataItem?.settings, | ||
| settings, | ||
| defaultValue, | ||
| }, | ||
| }); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
We use the values prop to avoid useEffect, consistent with the codebase patterns. React Hook Form performs deep comparison, so isDirty is not reset when values are unchanged.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/twenty-front/src/modules/object-metadata/utils/getFieldMetadataItemInitialValues.ts">
<violation number="1" location="packages/twenty-front/src/modules/object-metadata/utils/getFieldMetadataItemInitialValues.ts:25">
P2: Missing validation for `currencyCode` when `defaultValue` exists. The existing `useCurrencySettingsFormInitialValues.ts` hook validates that `currencyCode` is a non-empty string (after stripping quotes) and falls back to USD if invalid. This function should apply the same validation to handle cases where `defaultValue` exists but `currencyCode` is missing or empty.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const defaultValue = fieldMetadataItem.defaultValue | ||
| ? { | ||
| amountMicros: fieldMetadataItem.defaultValue.amountMicros ?? null, | ||
| currencyCode: fieldMetadataItem.defaultValue.currencyCode, |
There was a problem hiding this comment.
P2: Missing validation for currencyCode when defaultValue exists. The existing useCurrencySettingsFormInitialValues.ts hook validates that currencyCode is a non-empty string (after stripping quotes) and falls back to USD if invalid. This function should apply the same validation to handle cases where defaultValue exists but currencyCode is missing or empty.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/object-metadata/utils/getFieldMetadataItemInitialValues.ts, line 25:
<comment>Missing validation for `currencyCode` when `defaultValue` exists. The existing `useCurrencySettingsFormInitialValues.ts` hook validates that `currencyCode` is a non-empty string (after stripping quotes) and falls back to USD if invalid. This function should apply the same validation to handle cases where `defaultValue` exists but `currencyCode` is missing or empty.</comment>
<file context>
@@ -0,0 +1,33 @@
+ const defaultValue = fieldMetadataItem.defaultValue
+ ? {
+ amountMicros: fieldMetadataItem.defaultValue.amountMicros ?? null,
+ currencyCode: fieldMetadataItem.defaultValue.currencyCode,
+ }
+ : {
</file context>
✅ Addressed in be42bfc
…InitialValues to use helper
| currencyCode: isNonEmptyString( | ||
| stripSimpleQuotesFromString( | ||
| fieldMetadataItem.defaultValue.currencyCode, | ||
| ), | ||
| ) | ||
| ? fieldMetadataItem.defaultValue.currencyCode | ||
| : applySimpleQuotesToString(CurrencyCode.USD), |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| label: fieldMetadataItem?.label ?? '', | ||
| description: fieldMetadataItem?.description, | ||
| isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true, | ||
| settings: fieldMetadataItem?.settings, | ||
| settings, | ||
| defaultValue, | ||
| }, | ||
| }); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
…taItemInitialValues
| label: fieldMetadataItem?.label ?? '', | ||
| description: fieldMetadataItem?.description, | ||
| isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true, | ||
| settings: fieldMetadataItem?.settings, | ||
| settings, | ||
| defaultValue, | ||
| }, | ||
| }); | ||
|
|
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
The values prop is appropriate here because:
- It syncs the form with external state (fieldMetadataItem)
- React Hook Form performs deep comparison, so isDirty should work correctly
- It avoids useEffect, following the codebase pattern
|
Introduced a helper, removed useEffect and made sure the default currency has the format needed by the form schema (i.e. single quoted like 'USD'). Also made the code DRY so that the default value initialization/formatting happens at a single place for both creating a new currency field and updating an existing one. |
|
Hey @mabdullahabaid! After you've done the QA of your Pull Request, you can mark it as done here. Thank you! |
1 similar comment
|
Hey @mabdullahabaid! After you've done the QA of your Pull Request, you can mark it as done here. Thank you! |
|
Thanks @mabdullahabaid for your contribution! |
…Save 按鈕 dirty state (twentyhq#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
valuesmode, which did not properly track dirty state for thedefaultValueandsettingsfields.Solution
Switched to the
defaultValues+reset()pattern:defaultValues(using placeholder values)reset()whenfieldMetadataItemloadsThis correctly tracks dirty state by comparing against the most recent
resetvalues.Note
A similar pattern is used in:
useWebhookFormuseImapSmtpCaldavConnectionFormThose hooks call
reset()via query callbacks. In this case,reset()is called inside auseEffectbecause 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.