CSV importing and exporting fixes (#8824)
Fixes issue https://github.com/twentyhq/twenty/issues/5793 (and duplicate https://github.com/twentyhq/twenty/issues/8822) - Fix importing multi-select and array fields. - Fix exporting and importing RAW_JSON fields. --------- Co-authored-by: ad-elias <elias@autodiligence.com>
This commit is contained in:
@ -11,28 +11,36 @@ export const useExportProcessRecordsForCSV = (objectNameSingular: string) => {
|
||||
});
|
||||
|
||||
const processRecordsForCSVExport = (records: ObjectRecord[]) => {
|
||||
return records.map((record) => {
|
||||
const currencyFields = objectMetadataItem.fields.filter(
|
||||
(field) => field.type === FieldMetadataType.Currency,
|
||||
);
|
||||
return records.map((record) =>
|
||||
objectMetadataItem.fields.reduce(
|
||||
(processedRecord, field) => {
|
||||
if (!isDefined(record[field.name])) {
|
||||
return processedRecord;
|
||||
}
|
||||
|
||||
const processedRecord = {
|
||||
...record,
|
||||
};
|
||||
|
||||
for (const currencyField of currencyFields) {
|
||||
if (isDefined(record[currencyField.name])) {
|
||||
processedRecord[currencyField.name] = {
|
||||
amountMicros: convertCurrencyMicrosToCurrencyAmount(
|
||||
record[currencyField.name].amountMicros,
|
||||
),
|
||||
currencyCode: record[currencyField.name].currencyCode,
|
||||
} satisfies FieldCurrencyValue;
|
||||
}
|
||||
}
|
||||
|
||||
return processedRecord;
|
||||
});
|
||||
switch (field.type) {
|
||||
case FieldMetadataType.Currency:
|
||||
return {
|
||||
...processedRecord,
|
||||
[field.name]: {
|
||||
amountMicros: convertCurrencyMicrosToCurrencyAmount(
|
||||
record[field.name].amountMicros,
|
||||
),
|
||||
currencyCode: record[field.name].currencyCode,
|
||||
} satisfies FieldCurrencyValue,
|
||||
};
|
||||
case FieldMetadataType.RawJson:
|
||||
return {
|
||||
...processedRecord,
|
||||
[field.name]: JSON.stringify(record[field.name]),
|
||||
};
|
||||
default:
|
||||
return processedRecord;
|
||||
}
|
||||
},
|
||||
{ ...record },
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
return { processRecordsForCSVExport };
|
||||
|
||||
@ -290,6 +290,8 @@ const companyMocks = [
|
||||
name: 'Example Company',
|
||||
id: companyId,
|
||||
visaSponsorship: false,
|
||||
deletedAt: undefined,
|
||||
workPolicy: [],
|
||||
},
|
||||
],
|
||||
upsert: true,
|
||||
|
||||
@ -143,6 +143,25 @@ export const useBuildAvailableFieldsForImport = () => {
|
||||
fieldMetadataItem.label + ' (ID)',
|
||||
),
|
||||
});
|
||||
} else if (fieldMetadataItem.type === FieldMetadataType.MultiSelect) {
|
||||
availableFieldsForImport.push({
|
||||
icon: getIcon(fieldMetadataItem.icon),
|
||||
label: fieldMetadataItem.label,
|
||||
key: fieldMetadataItem.name,
|
||||
fieldType: {
|
||||
type: 'multiSelect',
|
||||
options:
|
||||
fieldMetadataItem.options?.map((option) => ({
|
||||
label: option.label,
|
||||
value: option.value,
|
||||
color: option.color,
|
||||
})) || [],
|
||||
},
|
||||
fieldValidationDefinitions: getSpreadSheetFieldValidationDefinitions(
|
||||
fieldMetadataItem.type,
|
||||
fieldMetadataItem.label + ' (ID)',
|
||||
),
|
||||
});
|
||||
} else if (fieldMetadataItem.type === FieldMetadataType.Boolean) {
|
||||
availableFieldsForImport.push({
|
||||
icon: getIcon(fieldMetadataItem.icon),
|
||||
|
||||
@ -9,6 +9,7 @@ import { COMPOSITE_FIELD_IMPORT_LABELS } from '@/object-record/spreadsheet-impor
|
||||
import { ImportedStructuredRow } from '@/spreadsheet-import/types';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { z } from 'zod';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { castToString } from '~/utils/castToString';
|
||||
import { convertCurrencyAmountToCurrencyMicros } from '~/utils/convertCurrencyToCurrencyMicros';
|
||||
@ -203,6 +204,35 @@ export const buildRecordFromImportedStructuredRow = (
|
||||
source: 'IMPORT',
|
||||
};
|
||||
break;
|
||||
case FieldMetadataType.Array:
|
||||
case FieldMetadataType.MultiSelect: {
|
||||
const stringArrayJSONSchema = z
|
||||
.preprocess((value) => {
|
||||
try {
|
||||
if (typeof value !== 'string') {
|
||||
return [];
|
||||
}
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}, z.array(z.string()))
|
||||
.catch([]);
|
||||
|
||||
recordToBuild[field.name] =
|
||||
stringArrayJSONSchema.parse(importedFieldValue);
|
||||
break;
|
||||
}
|
||||
case FieldMetadataType.RawJson: {
|
||||
if (typeof importedFieldValue === 'string') {
|
||||
try {
|
||||
recordToBuild[field.name] = JSON.parse(importedFieldValue);
|
||||
} catch {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
recordToBuild[field.name] = importedFieldValue;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user