fix: guard against invalid date and undefined links field value (#16039)
closes https://github.com/twentyhq/twenty/issues/15854 - Add `isValid()` check in `formatDateISOStringToDateTime` before calling `formatInTimeZone` - Add `isDefined()` guard in `getFieldLinkDefinedLinks` (mirrors `phonesUtils` pattern) before: https://github.com/user-attachments/assets/1eb89fa4-70b6-4794-8860-0a42522598b5 https://github.com/user-attachments/assets/781c7d37-c435-4832-98d4-8e6925b51e10 after: https://github.com/user-attachments/assets/b1c22d25-4e8e-45c3-af98-be1684a5962e https://github.com/user-attachments/assets/ce084a9d-03b8-4f88-8522-a32cb1b7cf2f --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
co-authored by
Charles Bochet
Lucas Bordeau
parent
b56dcd8c22
commit
e5e5ae8e1d
+10
-8
@@ -1,5 +1,6 @@
|
||||
import { type DateFormat } from '@/localization/constants/DateFormat';
|
||||
import { type TimeFormat } from '@/localization/constants/TimeFormat';
|
||||
import { isValid } from 'date-fns';
|
||||
import { formatInTimeZone } from 'date-fns-tz';
|
||||
|
||||
export const formatDateISOStringToDateTime = ({
|
||||
@@ -15,14 +16,15 @@ export const formatDateISOStringToDateTime = ({
|
||||
timeFormat: TimeFormat;
|
||||
localeCatalog: Locale;
|
||||
}) => {
|
||||
const parsedDate = new Date(date);
|
||||
|
||||
if (!isValid(parsedDate)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// TODO: replace this with shiftPointInTimeToFromTimezoneDifference to remove date-fns-tz, which formatInTimeZone is doig under the hood :
|
||||
// https://github.com/marnusw/date-fns-tz/blob/4f3383b26a5907a73b14512a2701f3dfd8cf1579/src/toZonedTime/index.ts#L36C9-L36C27
|
||||
return formatInTimeZone(
|
||||
new Date(date),
|
||||
timeZone,
|
||||
`${dateFormat} ${timeFormat}`,
|
||||
{
|
||||
locale: localeCatalog,
|
||||
},
|
||||
);
|
||||
return formatInTimeZone(parsedDate, timeZone, `${dateFormat} ${timeFormat}`, {
|
||||
locale: localeCatalog,
|
||||
});
|
||||
};
|
||||
|
||||
+17
@@ -1,6 +1,23 @@
|
||||
import { getFieldLinkDefinedLinks } from '@/object-record/record-field/ui/meta-types/input/utils/getFieldLinkDefinedLinks';
|
||||
import { type FieldLinksValue } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
|
||||
describe('getFieldLinkDefinedLinks', () => {
|
||||
describe('Field value', () => {
|
||||
it('should return an empty array if fieldValue is undefined', () => {
|
||||
const result = getFieldLinkDefinedLinks(
|
||||
undefined as unknown as FieldLinksValue,
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if fieldValue is null', () => {
|
||||
const result = getFieldLinkDefinedLinks(
|
||||
null as unknown as FieldLinksValue,
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Primary link', () => {
|
||||
it('should not return primary link when primaryLinkUrl is null', () => {
|
||||
expect(
|
||||
|
||||
+4
@@ -3,6 +3,10 @@ import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isDefined, isValidUrl } from 'twenty-shared/utils';
|
||||
|
||||
export const getFieldLinkDefinedLinks = (fieldValue: FieldLinksValue) => {
|
||||
if (!isDefined(fieldValue)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
isNonEmptyString(fieldValue.primaryLinkUrl)
|
||||
? {
|
||||
|
||||
+3
-1
@@ -69,7 +69,9 @@ export const SettingsDataModelFieldPreview = ({
|
||||
labelIdentifierFieldMetadataItem?.name === fieldMetadataItem.name;
|
||||
|
||||
const fieldName = fieldMetadataItem.name;
|
||||
const recordId = `${objectNameSingular}-${fieldName}-preview`;
|
||||
const fieldType = fieldMetadataItem.type;
|
||||
|
||||
const recordId = `${objectNameSingular}-${fieldName}-${fieldType}-preview`;
|
||||
|
||||
const fieldPreviewValue = useFieldPreviewValue({
|
||||
fieldMetadataItem,
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ export const SettingsDataModelRelationFieldPreview = ({
|
||||
|
||||
const fieldName = v4();
|
||||
|
||||
const recordId = `${relationTargetObjectNameSingular}-${fieldName}-preview`;
|
||||
const recordId = `${relationTargetObjectNameSingular}-${fieldName}-RELATION-preview`;
|
||||
|
||||
const isRelation = fieldMetadataItem.type === FieldMetadataType.RELATION;
|
||||
const metadata = {
|
||||
|
||||
Reference in New Issue
Block a user