5425 - Introducing support for all Composite Fields Import (#5470)
Adding support for all Composite Fields while using the "import" functionality. This includes: - Currency - Address Edit : - Refactored a lot of types in the spreadsheet import module - Renamed a lot of functions, hooks and types that were not self-explanatory enough --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import {
|
||||
Data,
|
||||
Field,
|
||||
ImportedStructuredRow,
|
||||
Info,
|
||||
RowHook,
|
||||
TableHook,
|
||||
@ -8,11 +8,11 @@ import {
|
||||
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
|
||||
|
||||
describe('addErrorsAndRunHooks', () => {
|
||||
type FullData = Data<'name' | 'age' | 'country'>;
|
||||
type FullData = ImportedStructuredRow<'name' | 'age' | 'country'>;
|
||||
const requiredField: Field<'name'> = {
|
||||
key: 'name',
|
||||
label: 'Name',
|
||||
validations: [{ rule: 'required' }],
|
||||
fieldValidationDefinitions: [{ rule: 'required' }],
|
||||
icon: null,
|
||||
fieldType: { type: 'input' },
|
||||
};
|
||||
@ -20,7 +20,7 @@ describe('addErrorsAndRunHooks', () => {
|
||||
const regexField: Field<'age'> = {
|
||||
key: 'age',
|
||||
label: 'Age',
|
||||
validations: [
|
||||
fieldValidationDefinitions: [
|
||||
{ rule: 'regex', value: '\\d+', errorMessage: 'Regex error' },
|
||||
],
|
||||
icon: null,
|
||||
@ -30,7 +30,7 @@ describe('addErrorsAndRunHooks', () => {
|
||||
const uniqueField: Field<'country'> = {
|
||||
key: 'country',
|
||||
label: 'Country',
|
||||
validations: [{ rule: 'unique' }],
|
||||
fieldValidationDefinitions: [{ rule: 'unique' }],
|
||||
icon: null,
|
||||
fieldType: { type: 'input' },
|
||||
};
|
||||
@ -38,7 +38,7 @@ describe('addErrorsAndRunHooks', () => {
|
||||
const functionValidationFieldTrue: Field<'email'> = {
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
validations: [
|
||||
fieldValidationDefinitions: [
|
||||
{
|
||||
rule: 'function',
|
||||
isValid: () => true,
|
||||
@ -52,7 +52,7 @@ describe('addErrorsAndRunHooks', () => {
|
||||
const functionValidationFieldFalse: Field<'email'> = {
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
validations: [
|
||||
fieldValidationDefinitions: [
|
||||
{
|
||||
rule: 'function',
|
||||
isValid: () => false,
|
||||
@ -63,8 +63,11 @@ describe('addErrorsAndRunHooks', () => {
|
||||
fieldType: { type: 'input' },
|
||||
};
|
||||
|
||||
const validData: Data<'name' | 'age'> = { name: 'John', age: '30' };
|
||||
const dataWithoutNameAndInvalidAge: Data<'name' | 'age'> = {
|
||||
const validData: ImportedStructuredRow<'name' | 'age'> = {
|
||||
name: 'John',
|
||||
age: '30',
|
||||
};
|
||||
const dataWithoutNameAndInvalidAge: ImportedStructuredRow<'name' | 'age'> = {
|
||||
name: '',
|
||||
age: 'Invalid',
|
||||
};
|
||||
@ -74,7 +77,7 @@ describe('addErrorsAndRunHooks', () => {
|
||||
country: 'Brazil',
|
||||
};
|
||||
|
||||
const data: Data<'name' | 'age'>[] = [
|
||||
const data: ImportedStructuredRow<'name' | 'age'>[] = [
|
||||
validData,
|
||||
dataWithoutNameAndInvalidAge,
|
||||
];
|
||||
@ -180,7 +183,12 @@ describe('addErrorsAndRunHooks', () => {
|
||||
it('should not add errors for unique field with empty values if allowEmpty is true', () => {
|
||||
const result = addErrorsAndRunHooks(
|
||||
[{ country: '' }, { country: '' }],
|
||||
[{ ...uniqueField, validations: [{ rule: 'unique', allowEmpty: true }] }],
|
||||
[
|
||||
{
|
||||
...uniqueField,
|
||||
fieldValidationDefinitions: [{ rule: 'unique', allowEmpty: true }],
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
expect(result[0].__errors).toBeUndefined();
|
||||
|
||||
@ -2,7 +2,7 @@ import {
|
||||
Column,
|
||||
ColumnType,
|
||||
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||
import { Field, Validation } from '@/spreadsheet-import/types';
|
||||
import { Field, FieldValidationDefinition } from '@/spreadsheet-import/types';
|
||||
import { findUnmatchedRequiredFields } from '@/spreadsheet-import/utils/findUnmatchedRequiredFields';
|
||||
|
||||
const nameField: Field<'Name'> = {
|
||||
@ -22,9 +22,15 @@ const ageField: Field<'Age'> = {
|
||||
type: 'input',
|
||||
},
|
||||
};
|
||||
const validations: Validation[] = [{ rule: 'required' }];
|
||||
const nameFieldWithValidations: Field<'Name'> = { ...nameField, validations };
|
||||
const ageFieldWithValidations: Field<'Age'> = { ...ageField, validations };
|
||||
const validations: FieldValidationDefinition[] = [{ rule: 'required' }];
|
||||
const nameFieldWithValidations: Field<'Name'> = {
|
||||
...nameField,
|
||||
fieldValidationDefinitions: validations,
|
||||
};
|
||||
const ageFieldWithValidations: Field<'Age'> = {
|
||||
...ageField,
|
||||
fieldValidationDefinitions: validations,
|
||||
};
|
||||
|
||||
type ColumnValues = 'Name' | 'Age';
|
||||
|
||||
|
||||
@ -3,11 +3,11 @@ import { v4 } from 'uuid';
|
||||
|
||||
import {
|
||||
Errors,
|
||||
Meta,
|
||||
ImportedStructuredRowMetadata,
|
||||
} from '@/spreadsheet-import/steps/components/ValidationStep/types';
|
||||
import {
|
||||
Data,
|
||||
Fields,
|
||||
ImportedStructuredRow,
|
||||
Info,
|
||||
RowHook,
|
||||
TableHook,
|
||||
@ -16,11 +16,11 @@ import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const addErrorsAndRunHooks = <T extends string>(
|
||||
data: (Data<T> & Partial<Meta>)[],
|
||||
data: (ImportedStructuredRow<T> & Partial<ImportedStructuredRowMetadata>)[],
|
||||
fields: Fields<T>,
|
||||
rowHook?: RowHook<T>,
|
||||
tableHook?: TableHook<T>,
|
||||
): (Data<T> & Meta)[] => {
|
||||
): (ImportedStructuredRow<T> & ImportedStructuredRowMetadata)[] => {
|
||||
const errors: Errors = {};
|
||||
|
||||
const addHookError = (rowIndex: number, fieldKey: T, error: Info) => {
|
||||
@ -41,8 +41,8 @@ export const addErrorsAndRunHooks = <T extends string>(
|
||||
}
|
||||
|
||||
fields.forEach((field) => {
|
||||
field.validations?.forEach((validation) => {
|
||||
switch (validation.rule) {
|
||||
field.fieldValidationDefinitions?.forEach((fieldValidationDefinition) => {
|
||||
switch (fieldValidationDefinition.rule) {
|
||||
case 'unique': {
|
||||
const values = data.map((entry) => entry[field.key as T]);
|
||||
|
||||
@ -51,7 +51,7 @@ export const addErrorsAndRunHooks = <T extends string>(
|
||||
|
||||
values.forEach((value) => {
|
||||
if (
|
||||
validation.allowEmpty === true &&
|
||||
fieldValidationDefinition.allowEmpty === true &&
|
||||
(isUndefinedOrNull(value) || value === '' || !value)
|
||||
) {
|
||||
// If allowEmpty is set, we will not validate falsy fields such as undefined or empty string.
|
||||
@ -70,8 +70,10 @@ export const addErrorsAndRunHooks = <T extends string>(
|
||||
errors[index] = {
|
||||
...errors[index],
|
||||
[field.key]: {
|
||||
level: validation.level || 'error',
|
||||
message: validation.errorMessage || 'Field must be unique',
|
||||
level: fieldValidationDefinition.level || 'error',
|
||||
message:
|
||||
fieldValidationDefinition.errorMessage ||
|
||||
'Field must be unique',
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -88,8 +90,10 @@ export const addErrorsAndRunHooks = <T extends string>(
|
||||
errors[index] = {
|
||||
...errors[index],
|
||||
[field.key]: {
|
||||
level: validation.level || 'error',
|
||||
message: validation.errorMessage || 'Field is required',
|
||||
level: fieldValidationDefinition.level || 'error',
|
||||
message:
|
||||
fieldValidationDefinition.errorMessage ||
|
||||
'Field is required',
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -97,7 +101,10 @@ export const addErrorsAndRunHooks = <T extends string>(
|
||||
break;
|
||||
}
|
||||
case 'regex': {
|
||||
const regex = new RegExp(validation.value, validation.flags);
|
||||
const regex = new RegExp(
|
||||
fieldValidationDefinition.value,
|
||||
fieldValidationDefinition.flags,
|
||||
);
|
||||
data.forEach((entry, index) => {
|
||||
const value = entry[field.key]?.toString();
|
||||
|
||||
@ -105,10 +112,10 @@ export const addErrorsAndRunHooks = <T extends string>(
|
||||
errors[index] = {
|
||||
...errors[index],
|
||||
[field.key]: {
|
||||
level: validation.level || 'error',
|
||||
level: fieldValidationDefinition.level || 'error',
|
||||
message:
|
||||
validation.errorMessage ||
|
||||
`Field did not match the regex /${validation.value}/${validation.flags} `,
|
||||
fieldValidationDefinition.errorMessage ||
|
||||
`Field did not match the regex /${fieldValidationDefinition.value}/${fieldValidationDefinition.flags} `,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -119,12 +126,17 @@ export const addErrorsAndRunHooks = <T extends string>(
|
||||
data.forEach((entry, index) => {
|
||||
const value = entry[field.key]?.toString();
|
||||
|
||||
if (isNonEmptyString(value) && !validation.isValid(value)) {
|
||||
if (
|
||||
isNonEmptyString(value) &&
|
||||
!fieldValidationDefinition.isValid(value)
|
||||
) {
|
||||
errors[index] = {
|
||||
...errors[index],
|
||||
[field.key]: {
|
||||
level: validation.level || 'error',
|
||||
message: validation.errorMessage || 'Field is invalid',
|
||||
level: fieldValidationDefinition.level || 'error',
|
||||
message:
|
||||
fieldValidationDefinition.errorMessage ||
|
||||
'Field is invalid',
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -140,7 +152,8 @@ export const addErrorsAndRunHooks = <T extends string>(
|
||||
if (!('__index' in value)) {
|
||||
value.__index = v4();
|
||||
}
|
||||
const newValue = value as Data<T> & Meta;
|
||||
const newValue = value as ImportedStructuredRow<T> &
|
||||
ImportedStructuredRowMetadata;
|
||||
|
||||
if (isDefined(errors[index])) {
|
||||
return { ...newValue, __errors: errors[index] };
|
||||
|
||||
@ -8,7 +8,9 @@ export const findUnmatchedRequiredFields = <T extends string>(
|
||||
fields
|
||||
.filter(
|
||||
(field) =>
|
||||
field.validations?.some((validation) => validation.rule === 'required'),
|
||||
field.fieldValidationDefinitions?.some(
|
||||
(validation) => validation.rule === 'required',
|
||||
),
|
||||
)
|
||||
.filter(
|
||||
(field) =>
|
||||
|
||||
@ -2,13 +2,17 @@ import {
|
||||
Columns,
|
||||
ColumnType,
|
||||
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||
import { Data, Fields, RawData } from '@/spreadsheet-import/types';
|
||||
import {
|
||||
Fields,
|
||||
ImportedRow,
|
||||
ImportedStructuredRow,
|
||||
} from '@/spreadsheet-import/types';
|
||||
|
||||
import { normalizeCheckboxValue } from './normalizeCheckboxValue';
|
||||
|
||||
export const normalizeTableData = <T extends string>(
|
||||
columns: Columns<T>,
|
||||
data: RawData[],
|
||||
data: ImportedRow[],
|
||||
fields: Fields<T>,
|
||||
) =>
|
||||
data.map((row) =>
|
||||
@ -63,5 +67,5 @@ export const normalizeTableData = <T extends string>(
|
||||
default:
|
||||
return acc;
|
||||
}
|
||||
}, {} as Data<T>),
|
||||
}, {} as ImportedStructuredRow<T>),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user