diff --git a/packages/twenty-front/src/modules/apollo/utils/loggerLink.ts b/packages/twenty-front/src/modules/apollo/utils/loggerLink.ts index 232746d43..ef90d059a 100644 --- a/packages/twenty-front/src/modules/apollo/utils/loggerLink.ts +++ b/packages/twenty-front/src/modules/apollo/utils/loggerLink.ts @@ -2,8 +2,8 @@ import { ApolloLink, gql, Operation } from '@apollo/client'; import { logDebug } from '~/utils/logDebug'; import { logError } from '~/utils/logError'; -import formatTitle from './formatTitle'; import { isDefined } from 'twenty-shared/utils'; +import formatTitle from './formatTitle'; const getGroup = (collapsed: boolean) => collapsed diff --git a/packages/twenty-front/src/modules/localization/utils/__tests__/formatDateISOStringToCustomUnicodeFormat.test.js b/packages/twenty-front/src/modules/localization/utils/__tests__/formatDateISOStringToCustomUnicodeFormat.test.js new file mode 100644 index 000000000..11c27143e --- /dev/null +++ b/packages/twenty-front/src/modules/localization/utils/__tests__/formatDateISOStringToCustomUnicodeFormat.test.js @@ -0,0 +1,50 @@ +import { formatDateISOStringToCustomUnicodeFormat } from '@/localization/utils/formatDateISOStringToCustomUnicodeFormat'; +import { formatInTimeZone } from 'date-fns-tz'; + +jest.mock('date-fns-tz'); + +describe('formatDateISOStringToCustomUnicodeFormat', () => { + const mockDate = '2023-08-15T10:30:00Z'; + const mockTimeZone = 'America/New_York'; + const mockTimeFormat = 'HH:mm'; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should use provided timezone', () => { + formatInTimeZone.mockReturnValue('06:30'); + + const result = formatDateISOStringToCustomUnicodeFormat( + mockDate, + mockTimeZone, + mockTimeFormat, + ); + + expect(formatInTimeZone).toHaveBeenCalledWith( + new Date(mockDate), + mockTimeZone, + mockTimeFormat, + ); + expect(result).toBe('06:30'); + }); + + it('should gracefully handle errors', () => { + formatInTimeZone.mockImplementation(() => { + throw new Error(); + }); + + const result = formatDateISOStringToCustomUnicodeFormat( + mockDate, + mockTimeZone, + 'f', + ); + + expect(formatInTimeZone).toHaveBeenCalledWith( + new Date(mockDate), + mockTimeZone, + 'f', + ); + expect(result).toBe('Invalid format string'); + }); +}); diff --git a/packages/twenty-front/src/modules/localization/utils/formatDateISOStringToCustomUnicodeFormat.ts b/packages/twenty-front/src/modules/localization/utils/formatDateISOStringToCustomUnicodeFormat.ts new file mode 100644 index 000000000..18ff0b81e --- /dev/null +++ b/packages/twenty-front/src/modules/localization/utils/formatDateISOStringToCustomUnicodeFormat.ts @@ -0,0 +1,13 @@ +import { formatInTimeZone } from 'date-fns-tz'; + +export const formatDateISOStringToCustomUnicodeFormat = ( + date: string, + timeZone: string, + dateFormat: string, +) => { + try { + return formatInTimeZone(new Date(date), timeZone, dateFormat); + } catch (e) { + return 'Invalid format string'; + } +}; diff --git a/packages/twenty-front/src/modules/localization/utils/formatDateISOStringToDate.ts b/packages/twenty-front/src/modules/localization/utils/formatDateISOStringToDate.ts index 9cbbf415e..9ae3efda1 100644 --- a/packages/twenty-front/src/modules/localization/utils/formatDateISOStringToDate.ts +++ b/packages/twenty-front/src/modules/localization/utils/formatDateISOStringToDate.ts @@ -6,5 +6,5 @@ export const formatDateISOStringToDate = ( timeZone: string, dateFormat: DateFormat, ) => { - return formatInTimeZone(new Date(date), timeZone, `${dateFormat}`); + return formatInTimeZone(new Date(date), timeZone, dateFormat); }; diff --git a/packages/twenty-front/src/modules/localization/utils/validateCustomDateFormat.ts b/packages/twenty-front/src/modules/localization/utils/validateCustomDateFormat.ts new file mode 100644 index 000000000..b34ff46d7 --- /dev/null +++ b/packages/twenty-front/src/modules/localization/utils/validateCustomDateFormat.ts @@ -0,0 +1,10 @@ +import { format } from 'date-fns'; + +export const validateCustomDateFormat = (formatString: string): boolean => { + try { + format(new Date(), formatString); + return true; + } catch { + return false; + } +}; diff --git a/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItem.ts index 1a57f47ca..83e464055 100644 --- a/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItem.ts +++ b/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItem.ts @@ -1,10 +1,12 @@ +import { FieldDateMetadataSettings } from '@/object-record/record-field/types/FieldMetadata'; + +import { ThemeColor } from 'twenty-ui/theme'; import { Field, Object as MetadataObject, RelationDefinition, RelationDefinitionType, } from '~/generated-metadata/graphql'; -import { ThemeColor } from 'twenty-ui/theme'; export type FieldMetadataItemOption = { color: ThemeColor; @@ -35,8 +37,6 @@ export type FieldMetadataItem = Omit< 'id' | 'nameSingular' | 'namePlural' >; } | null; - settings?: { - displayAsRelativeDate?: boolean; - }; + settings?: FieldDateMetadataSettings; isLabelSyncedWithName?: boolean | null; }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel.ts index 5a52f56a7..ccbb1c71a 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel.ts @@ -10,13 +10,13 @@ import { PERCENT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-tabl import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations'; import { t } from '@lingui/core/macro'; import isEmpty from 'lodash.isempty'; +import { FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION } from 'twenty-shared/constants'; +import { isDefined } from 'twenty-shared/utils'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { formatAmount } from '~/utils/format/formatAmount'; import { formatNumber } from '~/utils/format/number'; import { formatDateString } from '~/utils/string/formatDateString'; import { formatDateTimeString } from '~/utils/string/formatDateTimeString'; -import { FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION } from 'twenty-shared/constants'; -import { isDefined } from 'twenty-shared/utils'; export const computeAggregateValueAndLabel = ({ data, @@ -63,7 +63,7 @@ export const computeAggregateValueAndLabel = ({ let value; - const displayAsRelativeDate = field?.settings?.displayAsRelativeDate; + const dateFieldSettings = field?.settings; if ( COUNT_AGGREGATE_OPERATION_OPTIONS.includes( @@ -101,10 +101,10 @@ export const computeAggregateValueAndLabel = ({ value = aggregateValue as string; value = formatDateTimeString({ value, - displayAsRelativeDate, timeZone, dateFormat, timeFormat, + dateFieldSettings, }); break; } @@ -113,9 +113,9 @@ export const computeAggregateValueAndLabel = ({ value = aggregateValue as string; value = formatDateString({ value, - displayAsRelativeDate, timeZone, dateFormat, + dateFieldSettings, }); break; } diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/DateFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/DateFieldDisplay.tsx index 86039881b..7e41e0ff0 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/DateFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/DateFieldDisplay.tsx @@ -4,13 +4,9 @@ import { DateDisplay } from '@/ui/field/display/components/DateDisplay'; export const DateFieldDisplay = () => { const { fieldValue, fieldDefinition } = useDateFieldDisplay(); - const displayAsRelativeDate = - fieldDefinition.metadata?.settings?.displayAsRelativeDate; + const dateFieldSettings = fieldDefinition.metadata?.settings; return ( - + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/DateTimeFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/DateTimeFieldDisplay.tsx index 9d67dff92..7f3dc6a2d 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/DateTimeFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/DateTimeFieldDisplay.tsx @@ -4,13 +4,9 @@ import { DateTimeDisplay } from '@/ui/field/display/components/DateTimeDisplay'; export const DateTimeFieldDisplay = () => { const { fieldValue, fieldDefinition } = useDateTimeFieldDisplay(); - const displayAsRelativeDate = - fieldDefinition.metadata?.settings?.displayAsRelativeDate; + const dateFieldSettings = fieldDefinition.metadata?.settings; 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 654d56327..537e39141 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 @@ -28,18 +28,32 @@ export type FieldTextMetadata = BaseFieldMetadata & { }; }; +export enum FieldDateDisplayFormat { + RELATIVE = 'RELATIVE', + USER_SETTINGS = 'USER_SETTINGS', + CUSTOM = 'CUSTOM', +} + +export type FieldDateMetadataSettings = + | { + displayFormat?: FieldDateDisplayFormat.CUSTOM; + customUnicodeDateFormat: string; + } + | { + displayFormat?: Exclude< + FieldDateDisplayFormat, + FieldDateDisplayFormat.CUSTOM + >; + }; + export type FieldDateTimeMetadata = BaseFieldMetadata & { placeHolder: string; - settings?: { - displayAsRelativeDate?: boolean; - }; + settings?: FieldDateMetadataSettings; }; export type FieldDateMetadata = BaseFieldMetadata & { placeHolder: string; - settings?: { - displayAsRelativeDate?: boolean; - }; + settings?: FieldDateMetadataSettings; }; export type FieldNumberVariant = 'number' | 'percentage'; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isDateFIeldCustomDisplayFormat.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isDateFIeldCustomDisplayFormat.ts new file mode 100644 index 000000000..6c1051e25 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isDateFIeldCustomDisplayFormat.ts @@ -0,0 +1,6 @@ +import { FieldDateDisplayFormat } from '../FieldMetadata'; + +export const isDateFieldCustomDisplayFormat = ( + displayFormat: FieldDateDisplayFormat, +): displayFormat is FieldDateDisplayFormat => + displayFormat === FieldDateDisplayFormat.CUSTOM; diff --git a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts index 5310f118e..ecbe9735d 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts @@ -233,7 +233,7 @@ describe('useRecordData', () => { relationType: undefined, targetFieldMetadataName: '', settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, }, position: 10, diff --git a/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentSelect.tsx b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentSelect.tsx index 19d6ecf11..60fa4a572 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentSelect.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentSelect.tsx @@ -11,7 +11,7 @@ import { IconComponent } from 'twenty-ui/display'; type SettingsOptionCardContentSelectProps = { Icon?: IconComponent; title: React.ReactNode; - description?: string; + description?: string | React.ReactNode; disabled?: boolean; children?: React.ReactNode; }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm.tsx index dc0f0ee17..d8db554b1 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm.tsx @@ -1,20 +1,44 @@ import { Controller, useFormContext } from 'react-hook-form'; import { z } from 'zod'; +import { validateCustomDateFormat } from '@/localization/utils/validateCustomDateFormat'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; -import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle'; +import { FieldDateDisplayFormat } from '@/object-record/record-field/types/FieldMetadata'; +import { isDateFieldCustomDisplayFormat } from '@/object-record/record-field/types/guards/isDateFIeldCustomDisplayFormat'; +import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; +import { ADVANCED_SETTINGS_ANIMATION_DURATION } from '@/settings/constants/AdvancedSettingsAnimationDurations'; import { useDateSettingsFormInitialValues } from '@/settings/data-model/fields/forms/date/hooks/useDateSettingsFormInitialValues'; +import { getDisplayFormatLabel } from '@/settings/data-model/fields/forms/date/utils/getDisplayFormatLabel'; +import { getDisplayFormatSelectDescription } from '@/settings/data-model/fields/forms/date/utils/getDisplayFormatSelectDescription'; +import { Select } from '@/ui/input/components/Select'; +import { TextInput } from '@/ui/input/components/TextInput'; +import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { IconSlash } from 'twenty-ui/display'; +import { AnimatedExpandableContainer } from 'twenty-ui/layout'; + +const fieldDateSettings = z.discriminatedUnion('displayFormat', [ + z.object({ + displayFormat: z.enum([ + FieldDateDisplayFormat.RELATIVE, + FieldDateDisplayFormat.USER_SETTINGS, + ]), + }), + z.object({ + displayFormat: z.literal(FieldDateDisplayFormat.CUSTOM), + customUnicodeDateFormat: z.string().refine(validateCustomDateFormat), + }), +]); export const settingsDataModelFieldDateFormSchema = z.object({ - settings: z - .object({ - displayAsRelativeDate: z.boolean().optional(), - }) - .optional(), + settings: fieldDateSettings.optional(), }); +const StyledTextInput = styled(TextInput)` + padding: ${({ theme }) => theme.spacing(4)}; + padding-top: 0; +`; + export type SettingsDataModelFieldDateFormValues = z.infer< typeof settingsDataModelFieldDateFormSchema >; @@ -30,27 +54,78 @@ export const SettingsDataModelFieldDateForm = ({ }: SettingsDataModelFieldDateFormProps) => { const { t } = useLingui(); - const { control } = useFormContext(); + const { control, watch } = + useFormContext(); - const { initialDisplayAsRelativeDateValue } = + const { initialDisplayFormat, initialCustomUnicodeDateFormat } = useDateSettingsFormInitialValues({ fieldMetadataItem, }); + const displayFormatFromForm = watch('settings.displayFormat'); + + const activeDisplayFormat = displayFormatFromForm + ? displayFormatFromForm + : initialDisplayFormat; + + const showCustomFormatTextInput = + isDateFieldCustomDisplayFormat(activeDisplayFormat); + + const displayFormatSelectDescription = + getDisplayFormatSelectDescription(activeDisplayFormat); + return ( - ( - + ( + + + disabled={disabled} + selectSizeVariant="small" + dropdownWidth={120} + dropdownId="selectFieldDateDisplayFormat" + value={value} + onChange={onChange} + options={Object.keys(FieldDateDisplayFormat).map((key) => { + return { + label: getDisplayFormatLabel(key as FieldDateDisplayFormat), + value: key as FieldDateDisplayFormat, + }; + })} + /> + + )} + /> + + ( + onChange(value)} + disabled={false} + fullWidth + /> + )} /> - )} - /> + + ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateSettingsFormCard.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateSettingsFormCard.tsx index 418d9d93c..e6edc8a5b 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateSettingsFormCard.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateSettingsFormCard.tsx @@ -31,7 +31,7 @@ export const SettingsDataModelFieldDateSettingsFormCard = ({ fieldMetadataItem, objectMetadataItem, }: SettingsDataModelFieldDateSettingsFormCardProps) => { - const { initialDisplayAsRelativeDateValue } = + const { initialDisplayFormat, initialCustomUnicodeDateFormat } = useDateSettingsFormInitialValues({ fieldMetadataItem, }); @@ -46,9 +46,13 @@ export const SettingsDataModelFieldDateSettingsFormCard = ({ fieldMetadataItem={{ ...fieldMetadataItem, settings: { - displayAsRelativeDate: watchFormValue( - 'settings.displayAsRelativeDate', - initialDisplayAsRelativeDateValue, + displayFormat: watchFormValue( + 'settings.displayFormat', + initialDisplayFormat, + ), + customUnicodeDateFormat: watchFormValue( + 'settings.customUnicodeDateFormat', + initialCustomUnicodeDateFormat, ), }, }} diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/hooks/useDateSettingsFormInitialValues.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/hooks/useDateSettingsFormInitialValues.ts index 4726db3ec..d6ce4447a 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/hooks/useDateSettingsFormInitialValues.ts +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/hooks/useDateSettingsFormInitialValues.ts @@ -1,6 +1,7 @@ import { useFormContext } from 'react-hook-form'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { FieldDateDisplayFormat } from '@/object-record/record-field/types/FieldMetadata'; import { SettingsDataModelFieldDateFormValues } from '@/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm'; export const useDateSettingsFormInitialValues = ({ @@ -8,18 +9,24 @@ export const useDateSettingsFormInitialValues = ({ }: { fieldMetadataItem?: Pick; }) => { - const initialDisplayAsRelativeDateValue = - fieldMetadataItem?.settings?.displayAsRelativeDate; + const initialDisplayFormat = fieldMetadataItem?.settings + ?.displayFormat as FieldDateDisplayFormat; + const initialCustomUnicodeDateFormat = fieldMetadataItem?.settings + ?.customUnicodeDateFormat as string; const { resetField } = useFormContext(); const resetDefaultValueField = () => - resetField('settings.displayAsRelativeDate', { - defaultValue: initialDisplayAsRelativeDateValue, + resetField('settings', { + defaultValue: { + displayFormat: initialDisplayFormat, + customUnicodeDateFormat: initialCustomUnicodeDateFormat, + }, }); return { - initialDisplayAsRelativeDateValue, + initialDisplayFormat, + initialCustomUnicodeDateFormat, resetDefaultValueField, }; }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/utils/getDisplayFormatLabel.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/utils/getDisplayFormatLabel.ts new file mode 100644 index 000000000..97c8c21c9 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/utils/getDisplayFormatLabel.ts @@ -0,0 +1,17 @@ +import { FieldDateDisplayFormat } from '@/object-record/record-field/types/FieldMetadata'; +import { t } from '@lingui/core/macro'; + +export const getDisplayFormatLabel = ( + displayFormat: FieldDateDisplayFormat, +) => { + switch (displayFormat) { + case FieldDateDisplayFormat.CUSTOM: + return t`Custom`; + case FieldDateDisplayFormat.RELATIVE: + return t`Relative`; + case FieldDateDisplayFormat.USER_SETTINGS: + return t`Default`; + default: + return ''; + } +}; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/utils/getDisplayFormatSelectDescription.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/utils/getDisplayFormatSelectDescription.tsx new file mode 100644 index 000000000..f39564831 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/utils/getDisplayFormatSelectDescription.tsx @@ -0,0 +1,24 @@ +import { FieldDateDisplayFormat } from '@/object-record/record-field/types/FieldMetadata'; +import { Trans } from '@lingui/react/macro'; + +export const getDisplayFormatSelectDescription = ( + selectedDisplayFormat: FieldDateDisplayFormat, +) => { + if (selectedDisplayFormat === FieldDateDisplayFormat.CUSTOM) { + return ( + + Enter in{' '} + + Unicode + {' '} + format + + ); + } + return Choose the format used to display date value; +}; diff --git a/packages/twenty-front/src/modules/ui/field/display/components/DateDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/DateDisplay.tsx index 55f4998f5..092e12727 100644 --- a/packages/twenty-front/src/modules/ui/field/display/components/DateDisplay.tsx +++ b/packages/twenty-front/src/modules/ui/field/display/components/DateDisplay.tsx @@ -1,3 +1,4 @@ +import { FieldDateMetadataSettings } from '@/object-record/record-field/types/FieldMetadata'; import { UserContext } from '@/users/contexts/UserContext'; import { useContext } from 'react'; import { formatDateString } from '~/utils/string/formatDateString'; @@ -5,20 +6,17 @@ import { EllipsisDisplay } from './EllipsisDisplay'; type DateDisplayProps = { value: string | null | undefined; - displayAsRelativeDate?: boolean; + dateFieldSettings?: FieldDateMetadataSettings; }; -export const DateDisplay = ({ - value, - displayAsRelativeDate, -}: DateDisplayProps) => { +export const DateDisplay = ({ value, dateFieldSettings }: DateDisplayProps) => { const { dateFormat, timeZone } = useContext(UserContext); const formattedDate = formatDateString({ value, timeZone, dateFormat, - displayAsRelativeDate, + dateFieldSettings, }); return {formattedDate}; diff --git a/packages/twenty-front/src/modules/ui/field/display/components/DateTimeDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/DateTimeDisplay.tsx index 097f9180e..eadb5185c 100644 --- a/packages/twenty-front/src/modules/ui/field/display/components/DateTimeDisplay.tsx +++ b/packages/twenty-front/src/modules/ui/field/display/components/DateTimeDisplay.tsx @@ -1,3 +1,4 @@ +import { FieldDateMetadataSettings } from '@/object-record/record-field/types/FieldMetadata'; import { UserContext } from '@/users/contexts/UserContext'; import { useContext } from 'react'; import { formatDateTimeString } from '~/utils/string/formatDateTimeString'; @@ -5,21 +6,21 @@ import { EllipsisDisplay } from './EllipsisDisplay'; type DateTimeDisplayProps = { value: string | null | undefined; - displayAsRelativeDate?: boolean; + dateFieldSettings?: FieldDateMetadataSettings; }; export const DateTimeDisplay = ({ value, - displayAsRelativeDate, + dateFieldSettings, }: DateTimeDisplayProps) => { const { dateFormat, timeFormat, timeZone } = useContext(UserContext); const formattedDate = formatDateTimeString({ value, - displayAsRelativeDate, timeZone, dateFormat, timeFormat, + dateFieldSettings, }); return {formattedDate}; diff --git a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts index fe2659209..f9536a03d 100644 --- a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts +++ b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts @@ -339,7 +339,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -362,7 +362,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -385,7 +385,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -676,7 +676,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -699,7 +699,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -722,7 +722,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -906,7 +906,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -929,7 +929,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -952,7 +952,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -1550,7 +1550,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -1930,7 +1930,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -1953,7 +1953,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -2440,7 +2440,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -2463,7 +2463,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -2486,7 +2486,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -2848,7 +2848,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -2871,7 +2871,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -2894,7 +2894,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -3337,7 +3337,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -3360,7 +3360,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -3383,7 +3383,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -4186,7 +4186,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -4209,7 +4209,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -4232,7 +4232,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -4693,7 +4693,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -4716,7 +4716,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -4739,7 +4739,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -5281,7 +5281,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -5304,7 +5304,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -5327,7 +5327,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -5906,7 +5906,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -5929,7 +5929,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -5952,7 +5952,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6154,7 +6154,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6177,7 +6177,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6200,7 +6200,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6552,7 +6552,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6575,7 +6575,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6598,7 +6598,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6845,7 +6845,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6868,7 +6868,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -6891,7 +6891,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -7784,7 +7784,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -7807,7 +7807,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -7830,7 +7830,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8039,7 +8039,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8062,7 +8062,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8085,7 +8085,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8408,7 +8408,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8431,7 +8431,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8454,7 +8454,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8680,7 +8680,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8703,7 +8703,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -8726,7 +8726,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -9053,7 +9053,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -9076,7 +9076,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -9099,7 +9099,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -9238,7 +9238,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -9261,7 +9261,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -9284,7 +9284,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -10694,7 +10694,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -10717,7 +10717,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -10740,7 +10740,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -10945,7 +10945,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -10968,7 +10968,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -10991,7 +10991,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -11164,7 +11164,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -11187,7 +11187,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -11210,7 +11210,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -11951,7 +11951,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -11974,7 +11974,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -11997,7 +11997,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -12602,7 +12602,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -12625,7 +12625,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -12648,7 +12648,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -13269,7 +13269,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -13292,7 +13292,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -13315,7 +13315,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -13725,7 +13725,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -13748,7 +13748,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -13771,7 +13771,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -14646,7 +14646,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -14669,7 +14669,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -14692,7 +14692,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -15519,7 +15519,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -15542,7 +15542,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -15565,7 +15565,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -16790,7 +16790,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -16813,7 +16813,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -16836,7 +16836,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -17899,7 +17899,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -17922,7 +17922,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -17945,7 +17945,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -18196,7 +18196,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -18219,7 +18219,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -18242,7 +18242,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -18605,7 +18605,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -18628,7 +18628,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -18651,7 +18651,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -19184,7 +19184,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -19207,7 +19207,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -19230,7 +19230,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -19737,7 +19737,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -19760,7 +19760,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -19783,7 +19783,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -20188,7 +20188,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -20211,7 +20211,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -20234,7 +20234,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -21460,7 +21460,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -21483,7 +21483,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: "now", options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, @@ -21506,7 +21506,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = defaultValue: null, options: null, settings: { - displayAsRelativeDate: true + displayFormat: 'RELATIVE' }, isLabelSyncedWithName: false, relationDefinition: null, diff --git a/packages/twenty-front/src/testing/mock-data/workflow-run.ts b/packages/twenty-front/src/testing/mock-data/workflow-run.ts index 7282f63bd..9fae3eceb 100644 --- a/packages/twenty-front/src/testing/mock-data/workflow-run.ts +++ b/packages/twenty-front/src/testing/mock-data/workflow-run.ts @@ -319,7 +319,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -347,7 +347,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -375,7 +375,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -1216,7 +1216,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -1311,7 +1311,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -1365,7 +1365,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -2074,7 +2074,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -2132,7 +2132,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -2223,7 +2223,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -3539,7 +3539,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -3566,7 +3566,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -3593,7 +3593,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -5111,7 +5111,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -5137,7 +5137,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -5163,7 +5163,7 @@ export const oneFailedWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -6275,7 +6275,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -6303,7 +6303,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -6331,7 +6331,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -7172,7 +7172,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -7267,7 +7267,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -7321,7 +7321,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -8030,7 +8030,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -8088,7 +8088,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -8179,7 +8179,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -9541,7 +9541,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -9568,7 +9568,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -9595,7 +9595,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -11161,7 +11161,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -11187,7 +11187,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', @@ -11213,7 +11213,7 @@ export const oneSucceededWorkflowRunQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', diff --git a/packages/twenty-front/src/testing/mock-data/workflow.ts b/packages/twenty-front/src/testing/mock-data/workflow.ts index 8659e74aa..152e94074 100644 --- a/packages/twenty-front/src/testing/mock-data/workflow.ts +++ b/packages/twenty-front/src/testing/mock-data/workflow.ts @@ -993,7 +993,7 @@ export const workflowQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-01-30T09:40:10.256Z', updatedAt: '2025-01-30T09:40:10.256Z', @@ -1022,7 +1022,7 @@ export const workflowQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-01-30T09:40:10.256Z', updatedAt: '2025-01-30T09:40:10.256Z', @@ -1052,7 +1052,7 @@ export const workflowQueryResult = { isSystem: false, isUnique: false, settings: { - displayAsRelativeDate: true, + displayFormat: 'RELATIVE', }, createdAt: '2025-01-30T09:40:10.256Z', updatedAt: '2025-01-30T09:40:10.256Z', diff --git a/packages/twenty-front/src/utils/string/__tests__/formatDateString.test.ts b/packages/twenty-front/src/utils/string/__tests__/formatDateString.test.ts index aa129bea5..a88d7f61f 100644 --- a/packages/twenty-front/src/utils/string/__tests__/formatDateString.test.ts +++ b/packages/twenty-front/src/utils/string/__tests__/formatDateString.test.ts @@ -1,4 +1,5 @@ import { DateFormat } from '@/localization/constants/DateFormat'; +import { FieldDateDisplayFormat } from '@/object-record/record-field/types/FieldMetadata'; import { DateTime } from 'luxon'; import { formatDateString } from '~/utils/string/formatDateString'; @@ -26,7 +27,7 @@ describe('formatDateString', () => { expect(result).toBe(''); }); - it('should format date as relative when displayAsRelativeDate is true', () => { + it('should format date as relative when displayFormat is set to RELATIVE', () => { const mockDate = DateTime.now().minus({ months: 2 }).toISO(); const mockRelativeDate = 'about 2 months ago'; @@ -39,13 +40,15 @@ describe('formatDateString', () => { const result = formatDateString({ ...defaultParams, value: mockDate, - displayAsRelativeDate: true, + dateFieldSettings: { + displayFormat: FieldDateDisplayFormat.RELATIVE, + }, }); expect(result).toBe(mockRelativeDate); }); - it('should format date as datetime when displayAsRelativeDate is false', () => { + it('should format date as datetime when displayFormat is set to USER_SETTINGS', () => { const mockDate = '2023-01-01T12:00:00Z'; const mockFormattedDate = '1 Jan, 2023'; @@ -58,13 +61,40 @@ describe('formatDateString', () => { const result = formatDateString({ ...defaultParams, value: mockDate, - displayAsRelativeDate: false, + dateFieldSettings: { + displayFormat: FieldDateDisplayFormat.USER_SETTINGS, + }, }); expect(result).toBe(mockFormattedDate); }); - it('should format date as datetime by default when displayAsRelativeDate is not provided', () => { + it('should format date with custom format when displayFormat is set to CUSTOM', () => { + const mockDate = '2023-01-01T12:00:00Z'; + const mockFormattedDate = '2023'; + + jest.mock( + '@/localization/utils/formatDateISOStringToCustomUnicodeFormat', + () => ({ + formatDateISOStringToCustomUnicodeFormat: jest + .fn() + .mockReturnValue(mockFormattedDate), + }), + ); + + const result = formatDateString({ + ...defaultParams, + value: mockDate, + dateFieldSettings: { + displayFormat: FieldDateDisplayFormat.CUSTOM, + customUnicodeDateFormat: 'yyyy', + }, + }); + + expect(result).toBe(mockFormattedDate); + }); + + it('should format date as datetime by default when displayFormat is not provided', () => { const mockDate = '2023-01-01T12:00:00Z'; const mockFormattedDate = '1 Jan, 2023'; diff --git a/packages/twenty-front/src/utils/string/__tests__/formatDateTimeString.test.ts b/packages/twenty-front/src/utils/string/__tests__/formatDateTimeString.test.ts index 602c4add4..69ba8c54c 100644 --- a/packages/twenty-front/src/utils/string/__tests__/formatDateTimeString.test.ts +++ b/packages/twenty-front/src/utils/string/__tests__/formatDateTimeString.test.ts @@ -1,5 +1,6 @@ import { DateFormat } from '@/localization/constants/DateFormat'; import { TimeFormat } from '@/localization/constants/TimeFormat'; +import { FieldDateDisplayFormat } from '@/object-record/record-field/types/FieldMetadata'; import { DateTime } from 'luxon'; import { formatDateTimeString } from '~/utils/string/formatDateTimeString'; @@ -28,7 +29,7 @@ describe('formatDateTimeString', () => { expect(result).toBe(''); }); - it('should format date as relative when displayAsRelativeDate is true', () => { + it('should format date as relative when displayFormat is RELATIVE', () => { const mockDate = DateTime.now().minus({ months: 2 }).toISO(); const mockRelativeDate = 'about 2 months ago'; @@ -41,13 +42,15 @@ describe('formatDateTimeString', () => { const result = formatDateTimeString({ ...defaultParams, value: mockDate, - displayAsRelativeDate: true, + dateFieldSettings: { + displayFormat: FieldDateDisplayFormat.RELATIVE, + }, }); expect(result).toBe(mockRelativeDate); }); - it('should format date as datetime when displayAsRelativeDate is false', () => { + it('should format date as datetime when displayFormat is USER_SETTINGS', () => { const mockDate = '2023-01-01T12:00:00Z'; const mockFormattedDate = '1 Jan, 2023 12:00'; @@ -60,13 +63,40 @@ describe('formatDateTimeString', () => { const result = formatDateTimeString({ ...defaultParams, value: mockDate, - displayAsRelativeDate: false, + dateFieldSettings: { + displayFormat: FieldDateDisplayFormat.USER_SETTINGS, + }, }); expect(result).toBe(mockFormattedDate); }); - it('should format date as datetime by default when displayAsRelativeDate is not provided', () => { + it('should format date as datetime when displayFormat is set to CUSTOM', () => { + const mockDate = '2023-01-01T12:00:00Z'; + const mockFormattedDate = '2023'; + + jest.mock( + '@/localization/utils/formatDateISOStringToCustomUnicodeFormat', + () => ({ + formatDateISOStringToCustomUnicodeFormat: jest + .fn() + .mockReturnValue(mockFormattedDate), + }), + ); + + const result = formatDateTimeString({ + ...defaultParams, + value: mockDate, + dateFieldSettings: { + displayFormat: FieldDateDisplayFormat.CUSTOM, + customUnicodeDateFormat: 'yyyy', + }, + }); + + expect(result).toBe(mockFormattedDate); + }); + + it('should format date as datetime by default when displayFormat is not provided', () => { const mockDate = '2023-01-01T12:00:00Z'; const mockFormattedDate = '1 Jan, 2023 12:00'; diff --git a/packages/twenty-front/src/utils/string/formatDateString.ts b/packages/twenty-front/src/utils/string/formatDateString.ts index 87417854b..0ff388e62 100644 --- a/packages/twenty-front/src/utils/string/formatDateString.ts +++ b/packages/twenty-front/src/utils/string/formatDateString.ts @@ -1,23 +1,40 @@ import { DateFormat } from '@/localization/constants/DateFormat'; +import { formatDateISOStringToCustomUnicodeFormat } from '@/localization/utils/formatDateISOStringToCustomUnicodeFormat'; import { formatDateISOStringToDate } from '@/localization/utils/formatDateISOStringToDate'; import { formatDateISOStringToRelativeDate } from '@/localization/utils/formatDateISOStringToRelativeDate'; +import { + FieldDateDisplayFormat, + FieldDateMetadataSettings, +} from '@/object-record/record-field/types/FieldMetadata'; +import { isDefined } from 'twenty-shared/utils'; export const formatDateString = ({ value, timeZone, dateFormat, - displayAsRelativeDate, + dateFieldSettings, }: { timeZone: string; dateFormat: DateFormat; value?: string | null; - displayAsRelativeDate?: boolean; -}) => { - const formattedDate = value - ? displayAsRelativeDate - ? formatDateISOStringToRelativeDate(value) - : formatDateISOStringToDate(value, timeZone, dateFormat) - : ''; + dateFieldSettings?: FieldDateMetadataSettings; +}): string => { + if (!isDefined(value)) { + return ''; + } - return formattedDate; + switch (dateFieldSettings?.displayFormat) { + case FieldDateDisplayFormat.RELATIVE: + return formatDateISOStringToRelativeDate(value); + case FieldDateDisplayFormat.USER_SETTINGS: + return formatDateISOStringToDate(value, timeZone, dateFormat); + case FieldDateDisplayFormat.CUSTOM: + return formatDateISOStringToCustomUnicodeFormat( + value, + timeZone, + dateFieldSettings.customUnicodeDateFormat, + ); + default: + return formatDateISOStringToDate(value, timeZone, dateFormat); + } }; diff --git a/packages/twenty-front/src/utils/string/formatDateTimeString.ts b/packages/twenty-front/src/utils/string/formatDateTimeString.ts index 6b244fd5c..c95793a12 100644 --- a/packages/twenty-front/src/utils/string/formatDateTimeString.ts +++ b/packages/twenty-front/src/utils/string/formatDateTimeString.ts @@ -1,26 +1,52 @@ import { DateFormat } from '@/localization/constants/DateFormat'; import { TimeFormat } from '@/localization/constants/TimeFormat'; +import { formatDateISOStringToCustomUnicodeFormat } from '@/localization/utils/formatDateISOStringToCustomUnicodeFormat'; import { formatDateISOStringToDateTime } from '@/localization/utils/formatDateISOStringToDateTime'; import { formatDateISOStringToRelativeDate } from '@/localization/utils/formatDateISOStringToRelativeDate'; +import { + FieldDateDisplayFormat, + FieldDateMetadataSettings, +} from '@/object-record/record-field/types/FieldMetadata'; export const formatDateTimeString = ({ value, timeZone, dateFormat, timeFormat, - displayAsRelativeDate, + dateFieldSettings, }: { timeZone: string; dateFormat: DateFormat; timeFormat: TimeFormat; value?: string | null; - displayAsRelativeDate?: boolean; + dateFieldSettings?: FieldDateMetadataSettings; }) => { - const formattedDate = value - ? displayAsRelativeDate - ? formatDateISOStringToRelativeDate(value) - : formatDateISOStringToDateTime(value, timeZone, dateFormat, timeFormat) - : ''; + if (!value) { + return ''; + } - return formattedDate; + switch (dateFieldSettings?.displayFormat) { + case FieldDateDisplayFormat.RELATIVE: + return formatDateISOStringToRelativeDate(value); + case FieldDateDisplayFormat.USER_SETTINGS: + return formatDateISOStringToDateTime( + value, + timeZone, + dateFormat, + timeFormat, + ); + case FieldDateDisplayFormat.CUSTOM: + return formatDateISOStringToCustomUnicodeFormat( + value, + timeZone, + dateFieldSettings.customUnicodeDateFormat, + ); + default: + return formatDateISOStringToDateTime( + value, + timeZone, + dateFormat, + timeFormat, + ); + } }; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/0-52/0-52-upgrade-settings-field.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/0-52/0-52-upgrade-settings-field.ts new file mode 100644 index 000000000..00f8b4d3a --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/0-52/0-52-upgrade-settings-field.ts @@ -0,0 +1,90 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import chalk from 'chalk'; +import { Command } from 'nest-commander'; +import { FieldMetadataType } from 'twenty-shared/types'; +import { In, Repository } from 'typeorm'; +import { isDefined } from 'twenty-shared/utils'; + +import { DateDisplayFormat } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface'; + +import { + ActiveOrSuspendedWorkspacesMigrationCommandRunner, + RunOnWorkspaceArgs, +} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; + +type DeprecatedFieldMetadataDateSettings = { + displayAsRelativeDate?: boolean; +}; + +@Command({ + name: 'upgrade:0-52:upgrade-date-and-date-time-field-settings', + description: 'Upgrade settings column on all date and date time fields', +}) +export class UpgradeDateAndDateTimeFieldsSettingsJsonCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + @InjectRepository(ObjectMetadataEntity, 'metadata') + private readonly objectMetadataRepository: Repository, + @InjectRepository(FieldMetadataEntity, 'metadata') + private readonly fieldMetadataRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, + private readonly workspaceDataSourceService: WorkspaceDataSourceService, + ) { + super(workspaceRepository, twentyORMGlobalManager); + } + + override async runOnWorkspace({ + index, + total, + workspaceId, + }: RunOnWorkspaceArgs): Promise { + this.logger.log( + `Running command for workspace ${workspaceId} ${index + 1}/${total}`, + ); + + const fieldMetadataCollection = (await this.fieldMetadataRepository.find({ + where: { + workspaceId, + type: In([FieldMetadataType.DATE, FieldMetadataType.DATE_TIME]), + }, + })) as FieldMetadataEntity[]; + + const updatedFieldMetadataCollection = fieldMetadataCollection.map( + (field) => this.updateDateAndDateTimeFieldMetadata(field), + ); + + if (updatedFieldMetadataCollection.length > 0) { + await this.fieldMetadataRepository.save(updatedFieldMetadataCollection); + } + + this.logger.log( + chalk.green(`Command completed for workspace ${workspaceId}.`), + ); + } + + private updateDateAndDateTimeFieldMetadata( + field: FieldMetadataEntity, + ): FieldMetadataEntity { + const settings = field.settings as DeprecatedFieldMetadataDateSettings; + + if (!isDefined(settings?.displayAsRelativeDate)) { + return field; + } + + return { + ...field, + settings: { + displayFormat: settings.displayAsRelativeDate + ? DateDisplayFormat.RELATIVE + : DateDisplayFormat.USER_SETTINGS, + }, + }; + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/0-52/0-52-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/0-52/0-52-upgrade-version-command.module.ts index 459f1b509..5bcbecd33 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/0-52/0-52-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/0-52/0-52-upgrade-version-command.module.ts @@ -2,11 +2,28 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { BackfillWorkflowNextStepIdsCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-backfill-workflow-next-step-ids.command'; +import { UpgradeDateAndDateTimeFieldsSettingsJsonCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-upgrade-settings-field'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; @Module({ - imports: [TypeOrmModule.forFeature([Workspace], 'core')], - providers: [BackfillWorkflowNextStepIdsCommand], - exports: [BackfillWorkflowNextStepIdsCommand], + imports: [ + TypeOrmModule.forFeature([Workspace], 'core'), + TypeOrmModule.forFeature( + [ObjectMetadataEntity, FieldMetadataEntity], + 'metadata', + ), + WorkspaceDataSourceModule, + ], + providers: [ + BackfillWorkflowNextStepIdsCommand, + UpgradeDateAndDateTimeFieldsSettingsJsonCommand, + ], + exports: [ + BackfillWorkflowNextStepIdsCommand, + UpgradeDateAndDateTimeFieldsSettingsJsonCommand, + ], }) export class V0_52_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts index 1bc1c07c8..37c20eba7 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts @@ -17,6 +17,7 @@ import { UpdateDefaultViewRecordOpeningOnWorkflowObjectsCommand } from 'src/data import { InitializePermissionsCommand } from 'src/database/commands/upgrade-version-command/0-44/0-44-initialize-permissions.command'; import { UpdateViewAggregateOperationsCommand } from 'src/database/commands/upgrade-version-command/0-44/0-44-update-view-aggregate-operations.command'; import { UpgradeCreatedByEnumCommand } from 'src/database/commands/upgrade-version-command/0-51/0-51-update-workflow-trigger-type-enum.command'; +import { UpgradeDateAndDateTimeFieldsSettingsJsonCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-upgrade-settings-field'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; @@ -54,6 +55,9 @@ export class UpgradeCommand extends UpgradeCommandRunner { // 0.51 Commands protected readonly upgradeCreatedByEnumCommand: UpgradeCreatedByEnumCommand, + + // 0.52 Commands + protected readonly upgradeDateAndDateTimeFieldsSettingsJsonCommand: UpgradeDateAndDateTimeFieldsSettingsJsonCommand, ) { super( workspaceRepository, @@ -92,6 +96,13 @@ export class UpgradeCommand extends UpgradeCommandRunner { afterSyncMetadata: [], }; + const _commands_052: VersionCommands = { + beforeSyncMetadata: [ + this.upgradeDateAndDateTimeFieldsSettingsJsonCommand, + ], + afterSyncMetadata: [], + }; + this.commands = commands_051; } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface.ts index 385f1d086..ce6ba7cbd 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface.ts @@ -13,6 +13,12 @@ export type FieldMetadataDefaultSettings = { isForeignKey?: boolean; }; +export enum DateDisplayFormat { + RELATIVE = 'RELATIVE', + USER_SETTINGS = 'USER_SETTINGS', + CUSTOM = 'CUSTOM', +} + export type FieldNumberVariant = 'number' | 'percentage'; export type FieldMetadataNumberSettings = { @@ -26,11 +32,11 @@ export type FieldMetadataTextSettings = { }; export type FieldMetadataDateSettings = { - displayAsRelativeDate?: boolean; + displayFormat?: DateDisplayFormat; }; export type FieldMetadataDateTimeSettings = { - displayAsRelativeDate?: boolean; + displayFormat?: DateDisplayFormat; }; export type FieldMetadataRelationSettings = { diff --git a/packages/twenty-server/src/engine/twenty-orm/base.workspace-entity.ts b/packages/twenty-server/src/engine/twenty-orm/base.workspace-entity.ts index 44093f671..540dd8e36 100644 --- a/packages/twenty-server/src/engine/twenty-orm/base.workspace-entity.ts +++ b/packages/twenty-server/src/engine/twenty-orm/base.workspace-entity.ts @@ -1,6 +1,8 @@ import { msg } from '@lingui/core/macro'; import { FieldMetadataType } from 'twenty-shared/types'; +import { DateDisplayFormat } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface'; + import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator'; import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator'; import { WorkspaceIsPrimaryField } from 'src/engine/twenty-orm/decorators/workspace-is-primary-field.decorator'; @@ -28,7 +30,7 @@ export abstract class BaseWorkspaceEntity { icon: 'IconCalendar', defaultValue: 'now', settings: { - displayAsRelativeDate: true, + displayFormat: DateDisplayFormat.RELATIVE, }, }) createdAt: string; @@ -41,7 +43,7 @@ export abstract class BaseWorkspaceEntity { icon: 'IconCalendarClock', defaultValue: 'now', settings: { - displayAsRelativeDate: true, + displayFormat: DateDisplayFormat.RELATIVE, }, }) updatedAt: string; @@ -53,7 +55,7 @@ export abstract class BaseWorkspaceEntity { description: msg`Date when the record was deleted`, icon: 'IconCalendarMinus', settings: { - displayAsRelativeDate: true, + displayFormat: DateDisplayFormat.RELATIVE, }, }) @WorkspaceIsNullable() diff --git a/tools/eslint-rules/rules/no-hardcoded-colors.ts b/tools/eslint-rules/rules/no-hardcoded-colors.ts index 00e5c4b67..3505d4492 100644 --- a/tools/eslint-rules/rules/no-hardcoded-colors.ts +++ b/tools/eslint-rules/rules/no-hardcoded-colors.ts @@ -23,7 +23,7 @@ export const rule = ESLintUtils.RuleCreator(() => __filename)({ const testHardcodedColor = ( literal: TSESTree.Literal | TSESTree.TemplateLiteral, ) => { - const colorRegex = /(?:rgba?\()|(?:#[0-9a-fA-F]{2,6})/i; + const colorRegex = /(?:rgba?\()|(?:#[0-9a-fA-F]{3,6})\b/i; if ( literal.type === TSESTree.AST_NODE_TYPES.Literal &&