Don't display unmatched columns in csv import (#6037)

Adding a toggle so that, by default, we don't see dozens of fields but
only the field we matched, during the data validation step in csv export

<img width="785" alt="Screenshot 2024-06-26 at 13 25 36"
src="https://github.com/twentyhq/twenty/assets/6399865/ae558eb5-7461-4bc8-a836-ecff8b6d0dff">
This commit is contained in:
Félix Malfait 2024-06-26 15:37:37 +02:00 committed by GitHub
parent 6599bc136e
commit cd6775da32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 102 additions and 35 deletions

View File

@ -12,7 +12,7 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Modal } from '@/ui/layout/modal/components/Modal';
import { MatchColumnsStep } from './MatchColumnsStep/MatchColumnsStep';
import { Columns, MatchColumnsStep } from './MatchColumnsStep/MatchColumnsStep';
import { SelectHeaderStep } from './SelectHeaderStep/SelectHeaderStep';
import { SelectSheetStep } from './SelectSheetStep/SelectSheetStep';
import { UploadStep } from './UploadStep/UploadStep';
@ -52,6 +52,7 @@ export type StepState =
| {
type: StepType.validateData;
data: any[];
importedColumns: Columns<string>;
}
| {
type: StepType.loading;
@ -216,6 +217,7 @@ export const UploadFlow = ({ nextStep, prevStep }: UploadFlowProps) => {
setState({
type: StepType.validateData,
data,
importedColumns: columns,
});
setPreviousState(state);
nextStep();
@ -233,6 +235,7 @@ export const UploadFlow = ({ nextStep, prevStep }: UploadFlowProps) => {
return (
<ValidationStep
initialData={state.data}
importedColumns={state.importedColumns}
file={uploadedFile}
onSubmitStart={() =>
setState({

View File

@ -8,6 +8,10 @@ import { Heading } from '@/spreadsheet-import/components/Heading';
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
import { Table } from '@/spreadsheet-import/components/Table';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import {
Columns,
ColumnType,
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
import { Data } from '@/spreadsheet-import/types';
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
@ -62,6 +66,7 @@ const StyledNoRowsContainer = styled.div`
type ValidationStepProps<T extends string> = {
initialData: Data<T>[];
importedColumns: Columns<string>;
file: File;
onSubmitStart?: () => void;
onBack: () => void;
@ -69,6 +74,7 @@ type ValidationStepProps<T extends string> = {
export const ValidationStep = <T extends string>({
initialData,
importedColumns,
file,
onSubmitStart,
onBack,
@ -88,6 +94,7 @@ export const ValidationStep = <T extends string>({
ReadonlySet<number | string>
>(new Set());
const [filterByErrors, setFilterByErrors] = useState(false);
const [showUnmatchedColumns, setShowUnmatchedColumns] = useState(false);
const updateData = useCallback(
(rows: typeof data) => {
@ -127,7 +134,24 @@ export const ValidationStep = <T extends string>({
[data, updateData],
);
const columns = useMemo(() => generateColumns(fields), [fields]);
const columns = useMemo(
() =>
generateColumns(fields)
.map((column) => {
const hasBeenImported =
importedColumns.filter(
(importColumn) =>
(importColumn.type === ColumnType.matched &&
importColumn.value === column.key) ||
column.key === 'select-row',
).length > 0;
if (!hasBeenImported && !showUnmatchedColumns) return null;
return column;
})
.filter(Boolean),
[fields, importedColumns, showUnmatchedColumns],
);
const tableData = useMemo(() => {
if (filterByErrors) {
@ -212,6 +236,15 @@ export const ValidationStep = <T extends string>({
Show only rows with errors
</StyledErrorToggleDescription>
</StyledErrorToggle>
<StyledErrorToggle>
<Toggle
value={showUnmatchedColumns}
onChange={() => setShowUnmatchedColumns(!showUnmatchedColumns)}
/>
<StyledErrorToggleDescription>
Show unmatched columns
</StyledErrorToggleDescription>
</StyledErrorToggle>
<Button
Icon={IconTrash}
title="Remove"

View File

@ -5,6 +5,7 @@ import { Providers } from '@/spreadsheet-import/components/Providers';
import { ValidationStep } from '@/spreadsheet-import/steps/components/ValidationStep/ValidationStep';
import {
editableTableInitialData,
importedColums,
mockRsiValues,
} from '@/spreadsheet-import/tests/mockRsiValues';
import { DialogManagerScope } from '@/ui/feedback/dialog-manager/scopes/DialogManagerScope';
@ -28,6 +29,7 @@ export const Default = () => (
<ValidationStep
initialData={editableTableInitialData}
file={file}
importedColumns={importedColums}
onBack={() => Promise.resolve()}
/>
</ModalWrapper>

View File

@ -1,4 +1,5 @@
import { defaultSpreadsheetImportProps } from '@/spreadsheet-import/provider/components/SpreadsheetImport';
import { Columns } from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
import { Fields, SpreadsheetOptions } from '@/spreadsheet-import/types';
import { sleep } from '~/utils/sleep';
@ -88,6 +89,33 @@ const fields = [
},
] as Fields<string>;
export const importedColums: Columns<string> = [
{
header: 'Name',
index: 0,
type: 2,
value: 'name',
},
{
header: 'Surname',
index: 1,
type: 2,
value: 'surname',
},
{
header: 'Age',
index: 2,
type: 2,
value: 'age',
},
{
header: 'Team',
index: 3,
type: 2,
value: 'team',
},
];
const mockComponentBehaviourForTypes = <T extends string>(
props: SpreadsheetOptions<T>,
) => props;

View File

@ -157,43 +157,44 @@ export const graphqlMocks = {
graphql.query('FindDuplicateCompany', () => {
return HttpResponse.json({
data: {
companyDuplicates: {
edges: [
{
node: {
...duplicateCompanyMock,
favorites: {
edges: [],
__typename: 'FavoriteConnection',
},
attachments: {
edges: [],
__typename: 'AttachmentConnection',
},
people: {
edges: [],
__typename: 'PersonConnection',
},
opportunities: {
edges: [],
__typename: 'OpportunityConnection',
},
activityTargets: {
edges: [],
__typename: 'ActivityTargetConnection',
companyDuplicates: [
{
edges: [
{
node: {
...duplicateCompanyMock,
favorites: {
edges: [],
__typename: 'FavoriteConnection',
},
attachments: {
edges: [],
__typename: 'AttachmentConnection',
},
people: {
edges: [],
__typename: 'PersonConnection',
},
opportunities: {
edges: [],
__typename: 'OpportunityConnection',
},
activityTargets: {
edges: [],
__typename: 'ActivityTargetConnection',
},
},
cursor: null,
},
cursor: null,
],
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: null,
endCursor: null,
},
],
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: null,
endCursor: null,
},
totalCount: 1,
},
],
},
});
}),