diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useCurrencyField.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useCurrencyField.ts index 5eadc2ca91..771b7ead68 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useCurrencyField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useCurrencyField.ts @@ -10,6 +10,7 @@ import { isFieldCurrency } from '@/object-record/record-field/ui/types/guards/is import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/formatNumber'; export const useCurrencyField = () => { const { recordId, fieldDefinition } = useContext(FieldContext); @@ -37,6 +38,9 @@ export const useCurrencyField = () => { const defaultValue = fieldDefinition.defaultValue; + const decimals = + fieldDefinition.metadata.settings?.decimals ?? DEFAULT_DECIMAL_VALUE; + return { fieldDefinition, fieldValue, @@ -44,5 +48,6 @@ export const useCurrencyField = () => { setDraftValue, setFieldValue, defaultValue, + decimals, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/CurrencyFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/CurrencyFieldInput.tsx index b071e71475..d3fee39eda 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/CurrencyFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/CurrencyFieldInput.tsx @@ -17,7 +17,8 @@ import { convertCurrencyAmountToCurrencyMicros } from '~/utils/convertCurrencyTo import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const CurrencyFieldInput = () => { - const { draftValue, setDraftValue, defaultValue } = useCurrencyField(); + const { draftValue, setDraftValue, defaultValue, decimals } = + useCurrencyField(); const { onClickOutside, onEnter, onEscape, onShiftTab, onTab } = useContext( FieldInputEventContext, @@ -137,6 +138,7 @@ export const CurrencyFieldInput = () => { instanceId={instanceId} value={draftValue?.amount?.toString() ?? ''} currencyCode={currencyCode} + decimals={decimals} autoFocus placeholder={t`Currency`} onClickOutside={handleClickOutside} diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/types/FieldMetadata.ts index 234779a272..57d1639443 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/types/FieldMetadata.ts @@ -91,6 +91,7 @@ export type FieldCurrencyMetadata = BaseFieldMetadata & { isPositive?: boolean; settings?: { format: FieldCurrencyFormat | null; + decimals?: number; }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/validation-schemas/currencyFieldSettingsSchema.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/validation-schemas/currencyFieldSettingsSchema.ts index 4663db477c..b36e75af56 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/validation-schemas/currencyFieldSettingsSchema.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/validation-schemas/currencyFieldSettingsSchema.ts @@ -3,4 +3,5 @@ import { z } from 'zod'; export const currencyFieldSettingsSchema = z.object({ format: z.enum(fieldMetadataCurrencyFormat), + decimals: z.number().int().min(0).max(5).optional(), }); diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm.tsx index ab0bfff816..a7cbae7d32 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm.tsx @@ -1,15 +1,26 @@ import { Controller, useFormContext } from 'react-hook-form'; import { z } from 'zod'; -import { type FieldCurrencyFormat } from '@/object-record/record-field/ui/types/FieldMetadata'; +import { + fieldMetadataCurrencyFormat, + type FieldCurrencyFormat, +} from '@/object-record/record-field/ui/types/FieldMetadata'; import { currencyFieldDefaultValueSchema } from '@/object-record/record-field/ui/validation-schemas/currencyFieldDefaultValueSchema'; import { currencyFieldSettingsSchema } from '@/object-record/record-field/ui/validation-schemas/currencyFieldSettingsSchema'; +import { Separator } from '@/settings/components/Separator'; +import { SettingsOptionCardContentCounter } from '@/settings/components/SettingsOptions/SettingsOptionCardContentCounter'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { CURRENCIES } from '@/settings/data-model/constants/Currencies'; import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues'; import { Select } from '@/ui/input/components/Select'; +import { plural } from '@lingui/core/macro'; import { useLingui } from '@lingui/react/macro'; -import { IconCheckbox, IconCurrencyDollar } from 'twenty-ui/display'; +import { + IconCheckbox, + IconCurrencyDollar, + IconDecimal, +} from 'twenty-ui/display'; +import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/formatNumber'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; export const settingsDataModelFieldCurrencyFormSchema = z.object({ @@ -75,31 +86,65 @@ export const SettingsDataModelFieldCurrencyForm = ({ )} /> + ( - - - dropdownWidth={140} - value={value} - onChange={onChange} - disabled={disabled} - dropdownId="object-field-format-select" - options={[ - { label: 'Short', value: 'short' }, - { label: 'Full', value: 'full' }, - ]} - selectSizeVariant="small" - withSearchInput={false} - /> - - )} + defaultValue={initialSettingsValue} + render={({ field: { onChange, value } }) => { + const format = value?.format ?? fieldMetadataCurrencyFormat[0]; + const decimals = value?.decimals ?? DEFAULT_DECIMAL_VALUE; + + return ( + <> + + + dropdownWidth={140} + value={format} + onChange={(newFormat) => + onChange({ + format: newFormat, + decimals: + newFormat === fieldMetadataCurrencyFormat[0] + ? DEFAULT_DECIMAL_VALUE + : decimals, + }) + } + disabled={disabled} + dropdownId="object-field-format-select" + options={[ + { label: 'Short', value: fieldMetadataCurrencyFormat[0] }, + { label: 'Full', value: fieldMetadataCurrencyFormat[1] }, + ]} + selectSizeVariant="small" + withSearchInput={false} + /> + + + {format === 'full' && ( + + onChange({ format, decimals: newDecimals }) + } + disabled={disabled} + minValue={0} + maxValue={5} + /> + )} + + ); + }} /> ); diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues.ts index b427ab1ce2..e0cd4d9376 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues.ts +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues.ts @@ -4,6 +4,7 @@ import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetada import { type SettingsDataModelFieldCurrencyFormValues } from '@/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm'; import { isNonEmptyString } from '@sniptt/guards'; import { CurrencyCode } from 'twenty-shared/constants'; +import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/formatNumber'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString'; @@ -28,6 +29,7 @@ export const useCurrencySettingsFormInitialValues = ({ const initialFormValues: SettingsDataModelFieldCurrencyFormValues = { settings: { format: fieldMetadataItem?.settings?.format ?? 'short', + decimals: fieldMetadataItem?.settings?.decimals ?? DEFAULT_DECIMAL_VALUE, }, defaultValue: { amountMicros: initialAmountMicrosValue, diff --git a/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx index 9c554fdfc1..c4b61fd10d 100644 --- a/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx +++ b/packages/twenty-front/src/modules/ui/field/display/components/CurrencyDisplay.tsx @@ -9,6 +9,7 @@ import { import { SETTINGS_FIELD_CURRENCY_CODES } from '@/settings/data-model/constants/SettingsFieldCurrencyCodes'; import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay'; import { isDefined } from 'twenty-shared/utils'; +import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/formatNumber'; import { formatToShortNumber } from '~/utils/format/formatToShortNumber'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; @@ -32,6 +33,9 @@ export const CurrencyDisplay = ({ : currencyValue?.amountMicros / 1000000; const format = fieldDefinition.metadata.settings?.format; + const decimals = fieldDefinition.metadata.settings?.decimals; + const decimalsToUse = decimals ?? DEFAULT_DECIMAL_VALUE; + const { formatNumber } = useNumberFormat(); return ( @@ -48,7 +52,7 @@ export const CurrencyDisplay = ({ {amountToDisplay !== null ? !isDefined(format) || format === 'short' ? formatToShortNumber(amountToDisplay) - : formatNumber(amountToDisplay) + : formatNumber(amountToDisplay, { decimals: decimalsToUse }) : null} ); diff --git a/packages/twenty-front/src/modules/ui/field/input/components/CurrencyInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/CurrencyInput.tsx index 53bb97d0c0..00cbb6c583 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/CurrencyInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/CurrencyInput.tsx @@ -41,6 +41,7 @@ export type CurrencyInputProps = { placeholder?: string; autoFocus?: boolean; value: string; + decimals?: number; currencyCode: string; onEnter: (newText: string) => void; onEscape: (newText: string) => void; @@ -64,6 +65,7 @@ export const CurrencyInput = ({ onClickOutside, onChange, onSelect, + decimals, }: CurrencyInputProps) => { const theme = useTheme(); @@ -114,6 +116,7 @@ export const CurrencyInput = ({ mask={Number} thousandsSeparator="," radix="." + scale={decimals} onAccept={(value: string) => handleChange(value)} inputRef={wrapperRef} autoComplete="off"