diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useExportProcessRecordsForCSV.ts b/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useExportProcessRecordsForCSV.ts index 2eb9eb433..0bce87764 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useExportProcessRecordsForCSV.ts +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useExportProcessRecordsForCSV.ts @@ -1,9 +1,9 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { isDefined } from 'twenty-shared/utils'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { convertCurrencyMicrosToCurrencyAmount } from '~/utils/convertCurrencyToCurrencyMicros'; -import { isDefined } from 'twenty-shared/utils'; export const useExportProcessRecordsForCSV = (objectNameSingular: string) => { const { objectMetadataItem } = useObjectMetadataItem({ diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/CurrencyFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/CurrencyFieldDisplay.tsx index bbf110d16..8ba76547e 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/CurrencyFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/CurrencyFieldDisplay.tsx @@ -2,7 +2,12 @@ import { useCurrencyFieldDisplay } from '@/object-record/record-field/meta-types import { CurrencyDisplay } from '@/ui/field/display/components/CurrencyDisplay'; export const CurrencyFieldDisplay = () => { - const { fieldValue } = useCurrencyFieldDisplay(); + const { fieldValue, fieldDefinition } = useCurrencyFieldDisplay(); - return ; + return ( + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useCurrencyFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useCurrencyFieldDisplay.ts index 7b78c9b11..7ba20a7d1 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useCurrencyFieldDisplay.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useCurrencyFieldDisplay.ts @@ -2,12 +2,21 @@ import { useContext } from 'react'; import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; +import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; +import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency'; +import { FieldMetadataType } from 'twenty-shared/types'; import { FieldContext } from '../../contexts/FieldContext'; import { FieldCurrencyValue } from '../../types/FieldMetadata'; export const useCurrencyFieldDisplay = () => { const { recordId, fieldDefinition } = useContext(FieldContext); + assertFieldMetadata( + FieldMetadataType.CURRENCY, + isFieldCurrency, + fieldDefinition, + ); + const fieldName = fieldDefinition.metadata.fieldName; const fieldValue = useRecordFieldValue( diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts index 40689f517..632cbe916 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts @@ -84,7 +84,9 @@ export type FieldLinksMetadata = BaseFieldMetadata & { export type FieldCurrencyMetadata = BaseFieldMetadata & { placeHolder: string; isPositive?: boolean; - settings?: null; + settings?: { + format: FieldCurrencyFormat | null; + }; }; export type FieldFullNameMetadata = BaseFieldMetadata & { @@ -211,6 +213,9 @@ export type FieldLinksValue = { primaryLinkUrl: string | null; secondaryLinks?: { label: string | null; url: string | null }[] | null; }; + +export const fieldMetadataCurrencyFormat = ['short', 'full'] as const; +export type FieldCurrencyFormat = (typeof fieldMetadataCurrencyFormat)[number]; export type FieldCurrencyValue = { currencyCode: CurrencyCode; amountMicros: number | null; diff --git a/packages/twenty-front/src/modules/object-record/record-field/validation-schemas/currencyFieldSettingsSchema.ts b/packages/twenty-front/src/modules/object-record/record-field/validation-schemas/currencyFieldSettingsSchema.ts new file mode 100644 index 000000000..473087f1b --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/validation-schemas/currencyFieldSettingsSchema.ts @@ -0,0 +1,6 @@ +import { fieldMetadataCurrencyFormat } from '@/object-record/record-field/types/FieldMetadata'; +import { z } from 'zod'; + +export const currencyFieldSettingsSchema = z.object({ + format: z.enum(fieldMetadataCurrencyFormat), +}); diff --git a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportProcessRecordsForCSV.ts b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportProcessRecordsForCSV.ts index 8cd6b2aab..28fd0745d 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportProcessRecordsForCSV.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportProcessRecordsForCSV.ts @@ -1,9 +1,9 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { isDefined } from 'twenty-shared/utils'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { convertCurrencyMicrosToCurrencyAmount } from '~/utils/convertCurrencyToCurrencyMicros'; -import { isDefined } from 'twenty-shared/utils'; export const useExportProcessRecordsForCSV = (objectNameSingular: string) => { const { objectMetadataItem } = useObjectMetadataItem({ diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx index bb3f874f2..1fde22271 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx @@ -13,22 +13,22 @@ import { SettingsPath } from '@/types/SettingsPath'; import { TextInput } from '@/ui/input/components/TextInput'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { t } from '@lingui/core/macro'; import { Section } from '@react-email/components'; import { useState } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; +import { H2Title, IconSearch } from 'twenty-ui/display'; +import { UndecoratedLink } from 'twenty-ui/navigation'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { SettingsDataModelFieldTypeFormValues } from '~/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect'; import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; -import { t } from '@lingui/core/macro'; -import { H2Title, IconSearch } from 'twenty-ui/display'; -import { UndecoratedLink } from 'twenty-ui/navigation'; type SettingsObjectNewFieldSelectorProps = { className?: string; excludedFieldTypes?: FieldType[]; fieldMetadataItem?: Pick< FieldMetadataItem, - 'defaultValue' | 'options' | 'type' + 'defaultValue' | 'options' | 'type' | 'settings' >; objectNamePlural: string; 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 cab05076a..6e53d59ca 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 @@ -2,17 +2,20 @@ import { Controller, useFormContext } from 'react-hook-form'; import { z } from 'zod'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { FieldCurrencyFormat } from '@/object-record/record-field/types/FieldMetadata'; import { currencyFieldDefaultValueSchema } from '@/object-record/record-field/validation-schemas/currencyFieldDefaultValueSchema'; +import { currencyFieldSettingsSchema } from '@/object-record/record-field/validation-schemas/currencyFieldSettingsSchema'; 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 { useLingui } from '@lingui/react/macro'; -import { IconCurrencyDollar } from 'twenty-ui/display'; +import { IconCheckbox, IconCurrencyDollar } from 'twenty-ui/display'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; export const settingsDataModelFieldCurrencyFormSchema = z.object({ defaultValue: currencyFieldDefaultValueSchema, + settings: currencyFieldSettingsSchema, }); export type SettingsDataModelFieldCurrencyFormValues = z.infer< @@ -21,7 +24,10 @@ export type SettingsDataModelFieldCurrencyFormValues = z.infer< type SettingsDataModelFieldCurrencyFormProps = { disabled?: boolean; - fieldMetadataItem: Pick; + fieldMetadataItem: Pick< + FieldMetadataItem, + 'icon' | 'label' | 'type' | 'defaultValue' | 'settings' + >; }; export const SettingsDataModelFieldCurrencyForm = ({ @@ -29,12 +35,16 @@ export const SettingsDataModelFieldCurrencyForm = ({ fieldMetadataItem, }: SettingsDataModelFieldCurrencyFormProps) => { const { t } = useLingui(); + const { + initialAmountMicrosValue, + initialCurrencyCodeValue, + initialSettingsValue, + } = useCurrencySettingsFormInitialValues({ + fieldMetadataItem, + }); const { control } = useFormContext(); - const { initialAmountMicrosValue, initialCurrencyCodeValue } = - useCurrencySettingsFormInitialValues({ fieldMetadataItem }); - return ( <> )} /> + ( + + + 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} + /> + + )} + /> ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencySettingsFormCard.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencySettingsFormCard.tsx index 3112b316c..708bdc16f 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencySettingsFormCard.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencySettingsFormCard.tsx @@ -1,5 +1,5 @@ -import { useFormContext } from 'react-hook-form'; import styled from '@emotion/styled'; +import { useFormContext } from 'react-hook-form'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard'; @@ -17,7 +17,7 @@ type SettingsDataModelFieldCurrencySettingsFormCardProps = { disabled?: boolean; fieldMetadataItem: Pick< FieldMetadataItem, - 'icon' | 'label' | 'type' | 'defaultValue' + 'icon' | 'label' | 'type' | 'defaultValue' | 'settings' >; } & Pick; @@ -31,9 +31,10 @@ export const SettingsDataModelFieldCurrencySettingsFormCard = ({ fieldMetadataItem, objectMetadataItem, }: SettingsDataModelFieldCurrencySettingsFormCardProps) => { - const { initialDefaultValue } = useCurrencySettingsFormInitialValues({ - fieldMetadataItem, - }); + const { initialDefaultValue, initialSettingsValue } = + useCurrencySettingsFormInitialValues({ + fieldMetadataItem, + }); const { watch: watchFormValue } = useFormContext(); @@ -45,6 +46,7 @@ export const SettingsDataModelFieldCurrencySettingsFormCard = ({ fieldMetadataItem={{ ...fieldMetadataItem, defaultValue: watchFormValue('defaultValue', initialDefaultValue), + settings: watchFormValue('settings', initialSettingsValue), }} objectMetadataItem={objectMetadataItem} /> 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 d9ca44541..cf5dc4ab2 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 @@ -5,31 +5,42 @@ import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode'; import { SettingsDataModelFieldCurrencyFormValues } from '@/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; +type UseCurrencySettingsFormInitialValuesArgs = { + fieldMetadataItem?: Pick; +}; export const useCurrencySettingsFormInitialValues = ({ fieldMetadataItem, -}: { - fieldMetadataItem?: Pick; -}) => { +}: UseCurrencySettingsFormInitialValuesArgs) => { const initialAmountMicrosValue = (fieldMetadataItem?.defaultValue?.amountMicros as number | null) ?? null; const initialCurrencyCodeValue = fieldMetadataItem?.defaultValue?.currencyCode ?? applySimpleQuotesToString(CurrencyCode.USD); - const initialDefaultValue = { - amountMicros: initialAmountMicrosValue, - currencyCode: initialCurrencyCodeValue, + const initialFormValues: SettingsDataModelFieldCurrencyFormValues = { + settings: { + format: fieldMetadataItem?.settings?.format ?? 'short', + }, + defaultValue: { + amountMicros: initialAmountMicrosValue, + currencyCode: initialCurrencyCodeValue, + }, }; const { resetField } = useFormContext(); - const resetDefaultValueField = () => - resetField('defaultValue', { defaultValue: initialDefaultValue }); + const resetDefaultValueField = () => { + resetField('defaultValue', { + defaultValue: initialFormValues.defaultValue, + }); + resetField('settings', { defaultValue: initialFormValues.settings }); + }; return { initialAmountMicrosValue, initialCurrencyCodeValue, - initialDefaultValue, + initialSettingsValue: initialFormValues.settings, + initialDefaultValue: initialFormValues.defaultValue, resetDefaultValueField, }; }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/preview/utils/getCurrencyFieldPreviewValue.ts b/packages/twenty-front/src/modules/settings/data-model/fields/preview/utils/getCurrencyFieldPreviewValue.ts index 1cc84e4eb..c8263c37d 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/preview/utils/getCurrencyFieldPreviewValue.ts +++ b/packages/twenty-front/src/modules/settings/data-model/fields/preview/utils/getCurrencyFieldPreviewValue.ts @@ -11,7 +11,7 @@ export const getCurrencyFieldPreviewValue = ({ }: { fieldMetadataItem: Pick< FieldMetadataItem, - 'defaultValue' | 'options' | 'type' + 'defaultValue' | 'options' | 'type' | 'settings' >; }): FieldCurrencyValue | null => { if (fieldMetadataItem.type !== FieldMetadataType.CURRENCY) return null; 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 c21898b2a..92f102a8a 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 @@ -1,17 +1,26 @@ import { useTheme } from '@emotion/react'; -import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata'; +import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; +import { + FieldCurrencyMetadata, + FieldCurrencyValue, +} from '@/object-record/record-field/types/FieldMetadata'; 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 { formatAmount } from '~/utils/format/formatAmount'; +import { formatNumber } from '~/utils/format/number'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; type CurrencyDisplayProps = { currencyValue: FieldCurrencyValue | null | undefined; + fieldDefinition: FieldDefinition; }; -export const CurrencyDisplay = ({ currencyValue }: CurrencyDisplayProps) => { +export const CurrencyDisplay = ({ + currencyValue, + fieldDefinition, +}: CurrencyDisplayProps) => { const theme = useTheme(); const shouldDisplayCurrency = isDefined(currencyValue?.currencyCode); @@ -24,6 +33,8 @@ export const CurrencyDisplay = ({ currencyValue }: CurrencyDisplayProps) => { ? null : currencyValue?.amountMicros / 1000000; + const format = fieldDefinition.metadata.settings?.format; + if (!shouldDisplayCurrency) { return {0}; } @@ -39,7 +50,11 @@ export const CurrencyDisplay = ({ currencyValue }: CurrencyDisplayProps) => { />{' '} )} - {amountToDisplay !== null ? formatAmount(amountToDisplay) : ''} + {amountToDisplay !== null + ? !isDefined(format) || format === 'short' + ? formatAmount(amountToDisplay) + : formatNumber(amountToDisplay) + : null} ); }; diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows.ts index 9e0cb3380..58cc95af3 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows.ts @@ -1,5 +1,5 @@ -import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const QUICK_LEAD_WORKFLOW_ID = '8b213cac-a68b-4ffe-817a-3ec994e9932d'; const QUICK_LEAD_WORKFLOW_VERSION_ID = 'ac67974f-c524-4288-9d88-af8515400b68';