From a44ba2065d5cbddef76fdd6a00b3582ca2404d6b Mon Sep 17 00:00:00 2001 From: Naifer <161821705+omarNaifer12@users.noreply.github.com> Date: Mon, 16 Jun 2025 10:48:18 +0100 Subject: [PATCH] feat: add short number formatting option to number field (#12613) resolve #11927 Add a new 'Short Number' option that disables decimals and resets the value to 0 when selected. https://github.com/user-attachments/assets/d3524115-e3ec-4a07-9dbf-e19d03cf65dd https://github.com/user-attachments/assets/2f2b46d1-06d9-4a92-8f37-0291d46accab --------- Co-authored-by: prastoin --- .../display/components/NumberFieldDisplay.tsx | 5 ++- .../record-field/types/FieldMetadata.ts | 7 +++- .../numberFieldDefaultValueSchema.ts | 3 +- .../SettingsDataModelFieldNumberForm.tsx | 32 ++++++++++++------- .../constants/NumberDataModelSelectOptions.ts | 25 +++++++++++++-- .../field-metadata-validation.service.ts | 3 +- .../display/icon/components/TablerIcons.ts | 9 +++--- packages/twenty-ui/src/display/index.ts | 9 +++--- 8 files changed, 67 insertions(+), 26 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/NumberFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/NumberFieldDisplay.tsx index b5ebb3cbd..5f6a7341c 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/NumberFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/NumberFieldDisplay.tsx @@ -1,6 +1,7 @@ import { useNumberFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useNumberFieldDisplay'; import { NumberDisplay } from '@/ui/field/display/components/NumberDisplay'; import { isDefined } from 'twenty-shared/utils'; +import { formatAmount } from '~/utils/format/formatAmount'; import { formatNumber } from '~/utils/format/number'; export const NumberFieldDisplay = () => { @@ -13,7 +14,9 @@ export const NumberFieldDisplay = () => { const value = type === 'percentage' ? `${formatNumber(Number(fieldValue) * 100, decimals)}%` - : formatNumber(Number(fieldValue), decimals); + : type === 'shortNumber' + ? formatAmount(Number(fieldValue)) + : formatNumber(Number(fieldValue), decimals); return ; }; 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 b45b61303..40689f517 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 @@ -56,7 +56,12 @@ export type FieldDateMetadata = BaseFieldMetadata & { settings?: FieldDateMetadataSettings; }; -export type FieldNumberVariant = 'number' | 'percentage'; +export const FIELD_NUMBER_VARIANT = [ + 'number', + 'percentage', + 'shortNumber', +] as const; +export type FieldNumberVariant = (typeof FIELD_NUMBER_VARIANT)[number]; export type FieldNumberMetadata = BaseFieldMetadata & { placeHolder: string; diff --git a/packages/twenty-front/src/modules/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema.ts b/packages/twenty-front/src/modules/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema.ts index a08ff263b..f9453b48e 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema.ts @@ -1,6 +1,7 @@ +import { FIELD_NUMBER_VARIANT } from '@/object-record/record-field/types/FieldMetadata'; import { z } from 'zod'; export const numberFieldDefaultValueSchema = z.object({ decimals: z.number().nullable(), - type: z.enum(['percentage', 'number']).nullable(), + type: z.enum(FIELD_NUMBER_VARIANT).nullable(), }); diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx index d7faed474..b0e113e5c 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx @@ -6,11 +6,11 @@ import { numberFieldDefaultValueSchema } from '@/object-record/record-field/vali import { Separator } from '@/settings/components/Separator'; import { SettingsOptionCardContentCounter } from '@/settings/components/SettingsOptions/SettingsOptionCardContentCounter'; import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; +import { NUMBER_DATA_MODEL_SELECT_OPTIONS } from '@/settings/data-model/fields/forms/number/constants/NumberDataModelSelectOptions'; import { Select } from '@/ui/input/components/Select'; import { useLingui } from '@lingui/react/macro'; import { IconDecimal, IconEye } from 'twenty-ui/display'; import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/number'; -import { NUMBER_DATA_MODEL_SELECT_OPTIONS } from '@/settings/data-model/fields/forms/number/constants/NumberDataModelSelectOptions'; export const settingsDataModelFieldNumberFormSchema = z.object({ settings: numberFieldDefaultValueSchema, @@ -60,7 +60,13 @@ export const SettingsDataModelFieldNumberForm = ({ dropdownId="number-type" dropdownWidth={120} value={type} - onChange={(value) => onChange({ type: value, decimals: count })} + onChange={(value) => + onChange({ + type: value, + decimals: + value === 'shortNumber' ? DEFAULT_DECIMAL_VALUE : count, + }) + } disabled={disabled} needIconCheck={false} options={NUMBER_DATA_MODEL_SELECT_OPTIONS.map((option) => ({ @@ -70,16 +76,18 @@ export const SettingsDataModelFieldNumberForm = ({ /> - 1 ? 's' : ''}`} - value={count} - onChange={(value) => onChange({ type: type, decimals: value })} - disabled={disabled} - minValue={0} - maxValue={100} // needs to be changed - /> + {type !== 'shortNumber' && ( + 1 ? 's' : ''}`} + value={count} + onChange={(value) => onChange({ type: type, decimals: value })} + disabled={disabled} + minValue={0} + maxValue={100} // needs to be changed + /> + )} ); }} diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/constants/NumberDataModelSelectOptions.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/constants/NumberDataModelSelectOptions.ts index 3763e3b7c..2022367a0 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/constants/NumberDataModelSelectOptions.ts +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/constants/NumberDataModelSelectOptions.ts @@ -1,15 +1,36 @@ +import { FieldNumberVariant } from '@/object-record/record-field/types/FieldMetadata'; +import { MessageDescriptor } from '@lingui/core'; import { msg } from '@lingui/core/macro'; -import { IconNumber9, IconPercentage } from 'twenty-ui/display'; +import { ForwardRefExoticComponent, RefAttributes } from 'react'; +import { + IconComponent, + IconComponentProps, + IconLetterK, + IconNumber9, + IconPercentage, +} from 'twenty-ui/display'; +type NumberDataModelSelectOptions = { + Icon: ForwardRefExoticComponent< + IconComponentProps & RefAttributes + >; + label: MessageDescriptor; + value: FieldNumberVariant; +}; export const NUMBER_DATA_MODEL_SELECT_OPTIONS = [ { Icon: IconNumber9, label: msg`Number`, value: 'number', }, + { + Icon: IconLetterK, + label: msg`Short`, + value: 'shortNumber', + }, { Icon: IconPercentage, label: msg`Percentage`, value: 'percentage', }, -]; +] as const satisfies Array; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata-validation.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata-validation.service.ts index 997f42c2b..a6ce1c3ea 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata-validation.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata-validation.service.ts @@ -25,6 +25,7 @@ import { enum ValueType { PERCENTAGE = 'percentage', NUMBER = 'number', + SHORT_NUMBER = 'shortNumber', } class NumberSettingsValidation { @@ -35,7 +36,7 @@ class NumberSettingsValidation { @IsOptional() @IsEnum(ValueType) - type?: 'percentage' | 'number'; + type?: 'percentage' | 'number' | 'shortNumber'; } class TextSettingsValidation { diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts index 00c4e65f5..03fd5e31c 100644 --- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts +++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts @@ -4,18 +4,18 @@ export { IconAlertCircle, IconAlertTriangle, IconApi, - IconApps, IconAppWindow, + IconApps, IconArchive, IconArchiveOff, IconArrowBackUp, IconArrowDown, IconArrowLeft, IconArrowRight, - IconArrowsDiagonal, - IconArrowsVertical, IconArrowUp, IconArrowUpRight, + IconArrowsDiagonal, + IconArrowsVertical, IconAt, IconBaselineDensitySmall, IconBell, @@ -47,8 +47,8 @@ export { IconChevronDown, IconChevronLeft, IconChevronRight, - IconChevronsRight, IconChevronUp, + IconChevronsRight, IconCircleDot, IconCircleOff, IconCirclePlus, @@ -186,6 +186,7 @@ export { IconLayoutSidebarRight, IconLayoutSidebarRightCollapse, IconLayoutSidebarRightExpand, + IconLetterK, IconLibraryPlus, IconLifebuoy, IconLink, diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts index 6318cf854..12b95a0b8 100644 --- a/packages/twenty-ui/src/display/index.ts +++ b/packages/twenty-ui/src/display/index.ts @@ -65,18 +65,18 @@ export { IconAlertCircle, IconAlertTriangle, IconApi, - IconApps, IconAppWindow, + IconApps, IconArchive, IconArchiveOff, IconArrowBackUp, IconArrowDown, IconArrowLeft, IconArrowRight, - IconArrowsDiagonal, - IconArrowsVertical, IconArrowUp, IconArrowUpRight, + IconArrowsDiagonal, + IconArrowsVertical, IconAt, IconBaselineDensitySmall, IconBell, @@ -108,8 +108,8 @@ export { IconChevronDown, IconChevronLeft, IconChevronRight, - IconChevronsRight, IconChevronUp, + IconChevronsRight, IconCircleDot, IconCircleOff, IconCirclePlus, @@ -247,6 +247,7 @@ export { IconLayoutSidebarRight, IconLayoutSidebarRightCollapse, IconLayoutSidebarRightExpand, + IconLetterK, IconLibraryPlus, IconLifebuoy, IconLink,