Skip to content

fix: enable save button when changing currency default value#16864

Merged
FelixMalfait merged 8 commits intomainfrom
issue-16792
Jan 6, 2026
Merged

fix: enable save button when changing currency default value#16864
FelixMalfait merged 8 commits intomainfrom
issue-16792

Conversation

@mabdullahabaid
Copy link
Copy Markdown
Member

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.

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Comment on lines 112 to 122
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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Dec 30, 2025

🚀 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.

@cursor
Copy link
Copy Markdown

cursor bot commented Jan 6, 2026

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.

@FelixMalfait
Copy link
Copy Markdown
Member

The first commit seemed like a better solution to me, we're trying to get rid of all useEffect.
But in the first commit you seem to do a lot of casting/transformation so what I would do in that case is try to go back to the root (why isn't it properly formatted/typed?) and fix that. If it cannot be fixed cleanly then justify why exactly

@FelixMalfait
Copy link
Copy Markdown
Member

Just read the description I had missed it sorry, you mention useWebhookForm/useImapSmtpCaldavConnectionForm but they don't need an effect do they?

@mabdullahabaid
Copy link
Copy Markdown
Member Author

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, fieldMetadataItem comes from synchronous Recoil state (via useFieldMetadataItem), so there's no query callback to hook into. The useEffect watches for when fieldMetadataItem becomes defined and then calls reset().

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.

Comment on lines 117 to 127
description: fieldMetadataItem?.description,
isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true,
settings: fieldMetadataItem?.settings,
settings,
defaultValue,
},
});

This comment was marked as outdated.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +27 to +33
currencyCode: isNonEmptyString(
stripSimpleQuotesFromString(
fieldMetadataItem.defaultValue.currencyCode,
),
)
? fieldMetadataItem.defaultValue.currencyCode
: applySimpleQuotesToString(CurrencyCode.USD),

This comment was marked as outdated.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment on lines 120 to 126
label: fieldMetadataItem?.label ?? '',
description: fieldMetadataItem?.description,
isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true,
settings: fieldMetadataItem?.settings,
settings,
defaultValue,
},
});

This comment was marked as outdated.

Comment on lines 120 to 127
label: fieldMetadataItem?.label ?? '',
description: fieldMetadataItem?.description,
isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true,
settings: fieldMetadataItem?.settings,
settings,
defaultValue,
},
});

This comment was marked as outdated.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@mabdullahabaid
Copy link
Copy Markdown
Member Author

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.

@FelixMalfait FelixMalfait added this pull request to the merge queue Jan 6, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 6, 2026
@FelixMalfait FelixMalfait added this pull request to the merge queue Jan 6, 2026
Merged via the queue into main with commit a46619e Jan 6, 2026
68 checks passed
@FelixMalfait FelixMalfait deleted the issue-16792 branch January 6, 2026 18:25
@twenty-eng-sync
Copy link
Copy Markdown

Hey @mabdullahabaid! After you've done the QA of your Pull Request, you can mark it as done here. Thank you!

1 similar comment
@twenty-eng-sync
Copy link
Copy Markdown

Hey @mabdullahabaid! After you've done the QA of your Pull Request, you can mark it as done here. Thank you!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 6, 2026

Thanks @mabdullahabaid for your contribution!
This marks your 90th PR on the repo. You're top 1% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

NotYen pushed a commit to NotYen/twenty-ym that referenced this pull request Jan 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Change of default currency can't be saved

2 participants