feat: ability to switch currency format (#12542)
Fixes #11927 I have added 'format' in the zod schema of currency, and for using it, I am separately passing 'format' to 'currencyDisplay.' The feature is working correctly. --------- Co-authored-by: prastoin <paul@twenty.com> Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
This commit is contained in:
@ -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({
|
||||
|
||||
@ -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 <CurrencyDisplay currencyValue={fieldValue} />;
|
||||
return (
|
||||
<CurrencyDisplay
|
||||
currencyValue={fieldValue}
|
||||
fieldDefinition={fieldDefinition}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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<FieldCurrencyValue | undefined>(
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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),
|
||||
});
|
||||
@ -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({
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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, 'defaultValue'>;
|
||||
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<SettingsDataModelFieldCurrencyFormValues>();
|
||||
|
||||
const { initialAmountMicrosValue, initialCurrencyCodeValue } =
|
||||
useCurrencySettingsFormInitialValues({ fieldMetadataItem });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Controller
|
||||
@ -69,6 +79,32 @@ export const SettingsDataModelFieldCurrencyForm = ({
|
||||
</SettingsOptionCardContentSelect>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="settings.format"
|
||||
control={control}
|
||||
defaultValue={initialSettingsValue.format}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<SettingsOptionCardContentSelect
|
||||
Icon={IconCheckbox}
|
||||
title={t`Format`}
|
||||
description={t`Choose between Short and Full`}
|
||||
>
|
||||
<Select<FieldCurrencyFormat>
|
||||
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}
|
||||
/>
|
||||
</SettingsOptionCardContentSelect>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
|
||||
|
||||
@ -31,9 +31,10 @@ export const SettingsDataModelFieldCurrencySettingsFormCard = ({
|
||||
fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
}: SettingsDataModelFieldCurrencySettingsFormCardProps) => {
|
||||
const { initialDefaultValue } = useCurrencySettingsFormInitialValues({
|
||||
fieldMetadataItem,
|
||||
});
|
||||
const { initialDefaultValue, initialSettingsValue } =
|
||||
useCurrencySettingsFormInitialValues({
|
||||
fieldMetadataItem,
|
||||
});
|
||||
|
||||
const { watch: watchFormValue } =
|
||||
useFormContext<SettingsDataModelFieldCurrencyFormValues>();
|
||||
@ -45,6 +46,7 @@ export const SettingsDataModelFieldCurrencySettingsFormCard = ({
|
||||
fieldMetadataItem={{
|
||||
...fieldMetadataItem,
|
||||
defaultValue: watchFormValue('defaultValue', initialDefaultValue),
|
||||
settings: watchFormValue('settings', initialSettingsValue),
|
||||
}}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
|
||||
@ -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<FieldMetadataItem, 'defaultValue' | 'settings'>;
|
||||
};
|
||||
export const useCurrencySettingsFormInitialValues = ({
|
||||
fieldMetadataItem,
|
||||
}: {
|
||||
fieldMetadataItem?: Pick<FieldMetadataItem, 'defaultValue'>;
|
||||
}) => {
|
||||
}: 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<SettingsDataModelFieldCurrencyFormValues>();
|
||||
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<FieldCurrencyMetadata>;
|
||||
};
|
||||
|
||||
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 <EllipsisDisplay>{0}</EllipsisDisplay>;
|
||||
}
|
||||
@ -39,7 +50,11 @@ export const CurrencyDisplay = ({ currencyValue }: CurrencyDisplayProps) => {
|
||||
/>{' '}
|
||||
</>
|
||||
)}
|
||||
{amountToDisplay !== null ? formatAmount(amountToDisplay) : ''}
|
||||
{amountToDisplay !== null
|
||||
? !isDefined(format) || format === 'short'
|
||||
? formatAmount(amountToDisplay)
|
||||
: formatNumber(amountToDisplay)
|
||||
: null}
|
||||
</EllipsisDisplay>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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';
|
||||
|
||||
Reference in New Issue
Block a user