diff --git a/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition.ts b/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition.ts
index f372cd2eb3ac..8ba9ebe23315 100644
--- a/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition.ts
+++ b/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition.ts
@@ -54,6 +54,5 @@ export const formatFieldMetadataItemAsFieldDefinition = ({
metadata: fieldDefintionMetadata,
type: field.type,
}),
- settings: field.settings,
};
};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
index 66dbd0695553..92d897a614ae 100644
--- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
@@ -361,7 +361,6 @@ export const RecordBoardCard = ({
metadata: fieldDefinition.metadata,
type: fieldDefinition.type,
}),
- settings: fieldDefinition.settings,
},
useUpdateRecord: useUpdateOneRecordHook,
hotkeyScope: InlineCellHotkeyScope.InlineCell,
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 cb30dbed3776..da254a440736 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,12 +1,16 @@
import { useNumberFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useNumberFieldDisplay';
import { NumberDisplay } from '@/ui/field/display/components/NumberDisplay';
+import { formatNumber } from '~/utils/format/number';
export const NumberFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useNumberFieldDisplay();
- return (
-
- );
+ const decimals = fieldDefinition.metadata.settings?.decimals;
+ const type = fieldDefinition.metadata.settings?.type;
+ const value =
+ type === 'percentage' && fieldValue
+ ? `${formatNumber(Number(fieldValue) * 100, decimals)}%`
+ : fieldValue
+ ? formatNumber(Number(fieldValue), decimals)
+ : null;
+ return ;
};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useNumberField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useNumberField.ts
index 097bcb8beef5..4b27ad3ae9c9 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useNumberField.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useNumberField.ts
@@ -11,6 +11,7 @@ import {
castAsNumberOrNull,
} from '~/utils/cast-as-number-or-null';
+import { isNull } from '@sniptt/guards';
import { FieldContext } from '../../contexts/FieldContext';
import { usePersistField } from '../../hooks/usePersistField';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
@@ -33,12 +34,23 @@ export const useNumberField = () => {
const persistField = usePersistField();
const persistNumberField = (newValue: string) => {
+ if (fieldDefinition?.metadata?.settings?.type === 'percentage') {
+ newValue = newValue.replaceAll('%', '');
+ if (!canBeCastAsNumberOrNull(newValue)) {
+ return;
+ }
+ const castedValue = castAsNumberOrNull(newValue);
+ if (!isNull(castedValue)) {
+ persistField(castedValue / 100);
+ return;
+ }
+ persistField(null);
+ return;
+ }
if (!canBeCastAsNumberOrNull(newValue)) {
return;
}
-
const castedValue = castAsNumberOrNull(newValue);
-
persistField(castedValue);
};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldDefinition.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldDefinition.ts
index 1d8107c17953..16f05f2418d2 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldDefinition.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldDefinition.ts
@@ -16,7 +16,4 @@ export type FieldDefinition = {
infoTooltipContent?: string;
defaultValue?: any;
editButtonIcon?: IconComponent;
- settings?: {
- decimals?: number;
- };
};
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 434d168e7e11..53e037dbdaf6 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
@@ -10,17 +10,20 @@ import { CurrencyCode } from './CurrencyCode';
export type FieldUuidMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldBooleanMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldTextMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
+ settings?: Record;
};
export type FieldDateTimeMetadata = {
@@ -46,17 +49,23 @@ export type FieldNumberMetadata = {
fieldName: string;
placeHolder: string;
isPositive?: boolean;
+ settings?: {
+ decimals?: number;
+ type?: 'percentage' | 'number';
+ };
};
export type FieldLinkMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
+ settings?: Record;
};
export type FieldLinksMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldCurrencyMetadata = {
@@ -64,56 +73,66 @@ export type FieldCurrencyMetadata = {
fieldName: string;
placeHolder: string;
isPositive?: boolean;
+ settings?: Record;
};
export type FieldFullNameMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
+ settings?: Record;
};
export type FieldEmailMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
+ settings?: Record;
};
export type FieldEmailsMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldPhoneMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
+ settings?: Record;
};
export type FieldRatingMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldAddressMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
+ settings?: Record;
};
export type FieldRawJsonMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
placeHolder: string;
+ settings?: Record;
};
export type FieldRichTextMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldPositionMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldRelationMetadata = {
@@ -125,6 +144,7 @@ export type FieldRelationMetadata = {
relationType?: RelationDefinitionType;
targetFieldMetadataName?: string;
useEditButton?: boolean;
+ settings?: Record;
};
export type FieldSelectMetadata = {
@@ -132,33 +152,39 @@ export type FieldSelectMetadata = {
fieldName: string;
options: { label: string; color: ThemeColor; value: string }[];
isNullable: boolean;
+ settings?: Record;
};
export type FieldMultiSelectMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
options: { label: string; color: ThemeColor; value: string }[];
+ settings?: Record;
};
export type FieldActorMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldArrayMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
values: { label: string; value: string }[];
+ settings?: Record;
};
export type FieldPhonesMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldTsVectorMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
+ settings?: Record;
};
export type FieldMetadata =
diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/computeDraftValueFromFieldValue.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/computeDraftValueFromFieldValue.ts
index 298ef3c9a4a9..47a404873b14 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/utils/computeDraftValueFromFieldValue.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/utils/computeDraftValueFromFieldValue.ts
@@ -3,6 +3,8 @@ import { FieldInputDraftValue } from '@/object-record/record-field/types/FieldIn
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency';
import { isFieldCurrencyValue } from '@/object-record/record-field/types/guards/isFieldCurrencyValue';
+import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
+import { isFieldNumberValue } from '@/object-record/record-field/types/guards/isFieldNumberValue';
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
import { isFieldRawJsonValue } from '@/object-record/record-field/types/guards/isFieldRawJsonValue';
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
@@ -12,7 +14,7 @@ import { isDefined } from '~/utils/isDefined';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
type computeDraftValueFromFieldValueParams = {
- fieldDefinition: Pick, 'type'>;
+ fieldDefinition: Pick, 'type' | 'metadata'>;
fieldValue: FieldValue;
};
@@ -40,6 +42,18 @@ export const computeDraftValueFromFieldValue = ({
} as unknown as FieldInputDraftValue;
}
+ if (
+ isFieldNumber(fieldDefinition) &&
+ isFieldNumberValue(fieldValue) &&
+ fieldDefinition.metadata.settings?.type === 'percentage'
+ ) {
+ return (isUndefinedOrNull(fieldValue)
+ ? ''
+ : (
+ fieldValue * 100
+ ).toString()) as unknown as FieldInputDraftValue;
+ }
+
if (isFieldRelation(fieldDefinition)) {
return computeEmptyDraftValue({ fieldDefinition });
}
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 48981e4a5a34..a08ff263b37c 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
@@ -2,4 +2,5 @@ import { z } from 'zod';
export const numberFieldDefaultValueSchema = z.object({
decimals: z.number().nullable(),
+ type: z.enum(['percentage', 'number']).nullable(),
});
diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/__tests__/useRecordData.test.tsx b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/__tests__/useRecordData.test.tsx
index 4190d08b1a67..8ea445ef16b3 100644
--- a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/__tests__/useRecordData.test.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/__tests__/useRecordData.test.tsx
@@ -304,9 +304,6 @@ describe('useRecordData', () => {
},
},
position: expect.any(Number),
- settings: {
- displayAsRelativeDate: true,
- },
showLabel: undefined,
size: 100,
type: 'DATE_TIME',
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 ec5686ff3eff..139be3993f3c 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
@@ -4,7 +4,9 @@ import { z } from 'zod';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { numberFieldDefaultValueSchema } from '@/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema';
import { SettingsDataModelFieldNumberDecimalsInput } from '@/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberDecimalInput';
-import { CardContent } from 'twenty-ui';
+import { Select } from '@/ui/input/components/Select';
+import styled from '@emotion/styled';
+import { CardContent, IconNumber9, IconPercentage } from 'twenty-ui';
import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/number';
export const settingsDataModelFieldNumberFormSchema = z.object({
@@ -15,6 +17,13 @@ export type SettingsDataModelFieldNumberFormValues = z.infer<
typeof settingsDataModelFieldNumberFormSchema
>;
+const StyledFormCardTitle = styled.div`
+ color: ${({ theme }) => theme.font.color.light};
+ font-size: ${({ theme }) => theme.font.size.xs};
+ font-weight: ${({ theme }) => theme.font.weight.semiBold};
+ margin-bottom: ${({ theme }) => theme.spacing(1)};
+`;
+
type SettingsDataModelFieldNumberFormProps = {
disabled?: boolean;
fieldMetadataItem: Pick<
@@ -36,17 +45,43 @@ export const SettingsDataModelFieldNumberForm = ({
defaultValue={{
decimals:
fieldMetadataItem?.settings?.decimals ?? DEFAULT_DECIMAL_VALUE,
+ type: fieldMetadataItem?.settings?.type || 'number',
}}
control={control}
render={({ field: { onChange, value } }) => {
const count = value?.decimals ?? 0;
+ const type = value?.type ?? 'number';
return (
- onChange({ decimals: value })}
- disabled={disabled}
- >
+ <>
+ Type
+