Import - Upsert on composite fields (#12615)

To test : 
- Import a record with Id column (for upsert-ing) + some subfields in
each composite fields. Check that only matched subfields are updated
(Main issue)
- Import a record with a multi-select field - Check it works + Match
multi-select field on a non multi-select column, check it does not work.
(Specific bug fixed in second commit is : undefined value in multi
select column (corresponding to no item selected) caused error in
multi-select parsing).

closes https://github.com/twentyhq/core-team-issues/issues/990
This commit is contained in:
Etienne
2025-06-17 11:07:51 +02:00
committed by GitHub
parent 093073d5e2
commit 713d3defef
5 changed files with 144 additions and 217 deletions

View File

@ -30,7 +30,6 @@ export const MatchColumnSelectFieldSelectDropdownContent = ({
onSelectSuggestedOption,
onCancelSelect,
onDoNotImportSelect,
options,
suggestedOptions,
}: {
selectedValue: SelectOption | undefined;
@ -40,9 +39,6 @@ export const MatchColumnSelectFieldSelectDropdownContent = ({
onSelectSuggestedOption: (selectedSuggestedOption: SelectOption) => void;
onCancelSelect: () => void;
onDoNotImportSelect: () => void;
options: readonly ReadonlyDeep<
SelectOption & { fieldMetadataTypeLabel?: string }
>[];
suggestedOptions: readonly ReadonlyDeep<
SelectOption & { fieldMetadataTypeLabel?: string }
>[];
@ -121,7 +117,6 @@ export const MatchColumnSelectFieldSelectDropdownContent = ({
key={option.value}
selected={selectedValue?.value === option.value}
onClick={() => handleSuggestedOptionClick(option)}
disabled={option.disabled}
LeftIcon={option.Icon}
text={option.label}
contextualText={option.fieldMetadataTypeLabel}
@ -140,10 +135,6 @@ export const MatchColumnSelectFieldSelectDropdownContent = ({
key={field.id}
selected={selectedValue?.value === field.name}
onClick={() => handleFieldClick(field)}
disabled={
options.find((option) => option.value === field.name)
?.disabled && selectedValue?.value !== field.name
}
LeftIcon={getIcon(field.icon)}
text={field.label}
contextualText={getFieldMetadataTypeLabel(field.type)}

View File

@ -150,7 +150,6 @@ export const MatchColumnToFieldSelect = ({
onSelectSuggestedOption={handleSelectSuggestedOption}
onCancelSelect={handleCancelSelectClick}
onDoNotImportSelect={handleDoNotImportSelect}
options={options}
suggestedOptions={suggestedOptions}
/>
)

View File

@ -10,8 +10,6 @@ import { setColumn } from '@/spreadsheet-import/utils/setColumn';
import { setIgnoreColumn } from '@/spreadsheet-import/utils/setIgnoreColumn';
import { setSubColumn } from '@/spreadsheet-import/utils/setSubColumn';
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Modal } from '@/ui/layout/modal/components/Modal';
@ -77,7 +75,6 @@ export const MatchColumnsStep = <T extends string>({
onError,
}: MatchColumnsStepProps) => {
const { enqueueDialog } = useDialogManager();
const { enqueueSnackBar } = useSnackBar();
const dataExample = data.slice(0, 2);
const { fields } = useSpreadsheetImportInternal<T>();
const [isLoading, setIsLoading] = useState(false);
@ -131,10 +128,6 @@ export const MatchColumnsStep = <T extends string>({
if (columnIndex === index) {
return setColumn(column, field, data);
} else if (index === existingFieldIndex) {
enqueueSnackBar('Another column unselected', {
detailedMessage: 'Columns cannot duplicate',
variant: SnackBarVariant.Error,
});
return setColumn(column);
} else {
return column;
@ -143,15 +136,7 @@ export const MatchColumnsStep = <T extends string>({
);
}
},
[
columns,
onRevertIgnore,
onIgnore,
fields,
setColumns,
data,
enqueueSnackBar,
],
[columns, onRevertIgnore, onIgnore, fields, setColumns, data],
);
const handleContinue = useCallback(

View File

@ -5,6 +5,7 @@ import { SpreadsheetColumn } from '@/spreadsheet-import/types/SpreadsheetColumn'
import { SpreadsheetColumnType } from '@/spreadsheet-import/types/SpreadsheetColumnType';
import { SpreadsheetMatchedOptions } from '@/spreadsheet-import/types/SpreadsheetMatchedOptions';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { z } from 'zod';
import { uniqueEntries } from './uniqueEntries';
@ -53,6 +54,7 @@ export const setColumn = <T extends string>(
data
?.flatMap((row) => {
const value = row[oldColumn.index];
if (!isDefined(value)) return [];
const options = JSON.parse(z.string().parse(value));
return z.array(z.string()).parse(options);
})