feat: wip import csv [part 1] (#1033)
* feat: wip import csv * feat: start implementing twenty UI * feat: new radio button component * feat: use new radio button component and fix scroll issue * fix: max height modal * feat: wip try to customize react-data-grid to match design * feat: wip match columns * feat: wip match column selection * feat: match column * feat: clean heading component & try to fix scroll in last step * feat: validation step * fix: small cleaning and remove unused component * feat: clean folder architecture * feat: remove translations * feat: remove chackra theme * feat: remove unused libraries * feat: use option button to open spreadsheet & fix stories * Fix lint and fix imports --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
130
front/src/modules/spreadsheet-import/utils/dataMutations.ts
Normal file
130
front/src/modules/spreadsheet-import/utils/dataMutations.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import type {
|
||||
Errors,
|
||||
Meta,
|
||||
} from '@/spreadsheet-import/components/steps/ValidationStep/types';
|
||||
import type {
|
||||
Data,
|
||||
Fields,
|
||||
Info,
|
||||
RowHook,
|
||||
TableHook,
|
||||
} from '@/spreadsheet-import/types';
|
||||
|
||||
export const addErrorsAndRunHooks = <T extends string>(
|
||||
data: (Data<T> & Partial<Meta>)[],
|
||||
fields: Fields<T>,
|
||||
rowHook?: RowHook<T>,
|
||||
tableHook?: TableHook<T>,
|
||||
): (Data<T> & Meta)[] => {
|
||||
const errors: Errors = {};
|
||||
|
||||
const addHookError = (rowIndex: number, fieldKey: T, error: Info) => {
|
||||
errors[rowIndex] = {
|
||||
...errors[rowIndex],
|
||||
[fieldKey]: error,
|
||||
};
|
||||
};
|
||||
|
||||
if (tableHook) {
|
||||
data = tableHook(data, addHookError);
|
||||
}
|
||||
|
||||
if (rowHook) {
|
||||
data = data.map((value, index) =>
|
||||
rowHook(value, (...props) => addHookError(index, ...props), data),
|
||||
);
|
||||
}
|
||||
|
||||
fields.forEach((field) => {
|
||||
field.validations?.forEach((validation) => {
|
||||
switch (validation.rule) {
|
||||
case 'unique': {
|
||||
const values = data.map((entry) => entry[field.key as T]);
|
||||
|
||||
const taken = new Set(); // Set of items used at least once
|
||||
const duplicates = new Set(); // Set of items used multiple times
|
||||
|
||||
values.forEach((value) => {
|
||||
if (validation.allowEmpty && !value) {
|
||||
// If allowEmpty is set, we will not validate falsy fields such as undefined or empty string.
|
||||
return;
|
||||
}
|
||||
|
||||
if (taken.has(value)) {
|
||||
duplicates.add(value);
|
||||
} else {
|
||||
taken.add(value);
|
||||
}
|
||||
});
|
||||
|
||||
values.forEach((value, index) => {
|
||||
if (duplicates.has(value)) {
|
||||
errors[index] = {
|
||||
...errors[index],
|
||||
[field.key]: {
|
||||
level: validation.level || 'error',
|
||||
message: validation.errorMessage || 'Field must be unique',
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'required': {
|
||||
data.forEach((entry, index) => {
|
||||
if (
|
||||
entry[field.key as T] === null ||
|
||||
entry[field.key as T] === undefined ||
|
||||
entry[field.key as T] === ''
|
||||
) {
|
||||
errors[index] = {
|
||||
...errors[index],
|
||||
[field.key]: {
|
||||
level: validation.level || 'error',
|
||||
message: validation.errorMessage || 'Field is required',
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'regex': {
|
||||
const regex = new RegExp(validation.value, validation.flags);
|
||||
data.forEach((entry, index) => {
|
||||
const value = entry[field.key]?.toString() ?? '';
|
||||
if (!value.match(regex)) {
|
||||
errors[index] = {
|
||||
...errors[index],
|
||||
[field.key]: {
|
||||
level: validation.level || 'error',
|
||||
message:
|
||||
validation.errorMessage ||
|
||||
`Field did not match the regex /${validation.value}/${validation.flags} `,
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return data.map((value, index) => {
|
||||
// This is required only for table. Mutates to prevent needless rerenders
|
||||
if (!('__index' in value)) {
|
||||
value.__index = v4();
|
||||
}
|
||||
const newValue = value as Data<T> & Meta;
|
||||
|
||||
if (errors[index]) {
|
||||
return { ...newValue, __errors: errors[index] };
|
||||
}
|
||||
if (!errors[index] && value?.__errors) {
|
||||
return { ...newValue, __errors: null };
|
||||
}
|
||||
return newValue;
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user