Skip to content

Commit

Permalink
Feature : Adding percentage option to Input Number (#8481)
Browse files Browse the repository at this point in the history
fixing #7375

---------

Co-authored-by: guillim <[email protected]>
  • Loading branch information
guillim and guillim authored Nov 14, 2024
1 parent 090f612 commit 15b8b9b
Show file tree
Hide file tree
Showing 18 changed files with 167 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,5 @@ export const formatFieldMetadataItemAsFieldDefinition = ({
metadata: fieldDefintionMetadata,
type: field.type,
}),
settings: field.settings,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,6 @@ export const RecordBoardCard = ({
metadata: fieldDefinition.metadata,
type: fieldDefinition.type,
}),
settings: fieldDefinition.settings,
},
useUpdateRecord: useUpdateOneRecordHook,
hotkeyScope: InlineCellHotkeyScope.InlineCell,
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<NumberDisplay
value={fieldValue}
decimals={fieldDefinition.settings?.decimals}
/>
);
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 <NumberDisplay value={value} decimals={decimals} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,4 @@ export type FieldDefinition<T extends FieldMetadata> = {
infoTooltipContent?: string;
defaultValue?: any;
editButtonIcon?: IconComponent;
settings?: {
decimals?: number;
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ import { CurrencyCode } from './CurrencyCode';
export type FieldUuidMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldBooleanMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldTextMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldDateTimeMetadata = {
Expand All @@ -46,74 +49,90 @@ 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<string, never>;
};

export type FieldLinksMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldCurrencyMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
placeHolder: string;
isPositive?: boolean;
settings?: Record<string, never>;
};

export type FieldFullNameMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldEmailMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldEmailsMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldPhoneMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldRatingMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldAddressMetadata = {
objectMetadataNameSingular?: string;
placeHolder: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldRawJsonMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
placeHolder: string;
settings?: Record<string, never>;
};

export type FieldRichTextMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldPositionMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldRelationMetadata = {
Expand All @@ -125,40 +144,47 @@ export type FieldRelationMetadata = {
relationType?: RelationDefinitionType;
targetFieldMetadataName?: string;
useEditButton?: boolean;
settings?: Record<string, never>;
};

export type FieldSelectMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
options: { label: string; color: ThemeColor; value: string }[];
isNullable: boolean;
settings?: Record<string, never>;
};

export type FieldMultiSelectMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
options: { label: string; color: ThemeColor; value: string }[];
settings?: Record<string, never>;
};

export type FieldActorMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldArrayMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
values: { label: string; value: string }[];
settings?: Record<string, never>;
};

export type FieldPhonesMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldTsVectorMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
settings?: Record<string, never>;
};

export type FieldMetadata =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -12,7 +14,7 @@ import { isDefined } from '~/utils/isDefined';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';

type computeDraftValueFromFieldValueParams<FieldValue> = {
fieldDefinition: Pick<FieldDefinition<FieldMetadata>, 'type'>;
fieldDefinition: Pick<FieldDefinition<FieldMetadata>, 'type' | 'metadata'>;
fieldValue: FieldValue;
};

Expand Down Expand Up @@ -40,6 +42,18 @@ export const computeDraftValueFromFieldValue = <FieldValue>({
} as unknown as FieldInputDraftValue<FieldValue>;
}

if (
isFieldNumber(fieldDefinition) &&
isFieldNumberValue(fieldValue) &&
fieldDefinition.metadata.settings?.type === 'percentage'
) {
return (isUndefinedOrNull(fieldValue)
? ''
: (
fieldValue * 100
).toString()) as unknown as FieldInputDraftValue<FieldValue>;
}

if (isFieldRelation(fieldDefinition)) {
return computeEmptyDraftValue<FieldValue>({ fieldDefinition });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import { z } from 'zod';

export const numberFieldDefaultValueSchema = z.object({
decimals: z.number().nullable(),
type: z.enum(['percentage', 'number']).nullable(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,6 @@ describe('useRecordData', () => {
},
},
position: expect.any(Number),
settings: {
displayAsRelativeDate: true,
},
showLabel: undefined,
size: 100,
type: 'DATE_TIME',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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<
Expand All @@ -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 (
<SettingsDataModelFieldNumberDecimalsInput
value={count}
onChange={(value) => onChange({ decimals: value })}
disabled={disabled}
></SettingsDataModelFieldNumberDecimalsInput>
<>
<StyledFormCardTitle>Type</StyledFormCardTitle>
<Select
disabled={disabled}
dropdownId="selectNumberTypes"
options={[
{
label: 'Number',
value: 'number',
Icon: IconNumber9,
},
{
label: 'Percentage',
value: 'percentage',
Icon: IconPercentage,
},
]}
value={type}
onChange={(value) => onChange({ type: value, decimals: count })}
withSearchInput={false}
dropdownWidthAuto={true}
/>
<br />
<SettingsDataModelFieldNumberDecimalsInput
value={count}
onChange={(value) => onChange({ type: type, decimals: value })}
disabled={disabled}
/>
</>
);
}}
/>
Expand Down
Loading

0 comments on commit 15b8b9b

Please sign in to comment.