mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-28 06:46:24 +03:00
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:
parent
6599bc136e
commit
cd6775da32
@ -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 { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
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 { SelectHeaderStep } from './SelectHeaderStep/SelectHeaderStep';
|
||||||
import { SelectSheetStep } from './SelectSheetStep/SelectSheetStep';
|
import { SelectSheetStep } from './SelectSheetStep/SelectSheetStep';
|
||||||
import { UploadStep } from './UploadStep/UploadStep';
|
import { UploadStep } from './UploadStep/UploadStep';
|
||||||
@ -52,6 +52,7 @@ export type StepState =
|
|||||||
| {
|
| {
|
||||||
type: StepType.validateData;
|
type: StepType.validateData;
|
||||||
data: any[];
|
data: any[];
|
||||||
|
importedColumns: Columns<string>;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: StepType.loading;
|
type: StepType.loading;
|
||||||
@ -216,6 +217,7 @@ export const UploadFlow = ({ nextStep, prevStep }: UploadFlowProps) => {
|
|||||||
setState({
|
setState({
|
||||||
type: StepType.validateData,
|
type: StepType.validateData,
|
||||||
data,
|
data,
|
||||||
|
importedColumns: columns,
|
||||||
});
|
});
|
||||||
setPreviousState(state);
|
setPreviousState(state);
|
||||||
nextStep();
|
nextStep();
|
||||||
@ -233,6 +235,7 @@ export const UploadFlow = ({ nextStep, prevStep }: UploadFlowProps) => {
|
|||||||
return (
|
return (
|
||||||
<ValidationStep
|
<ValidationStep
|
||||||
initialData={state.data}
|
initialData={state.data}
|
||||||
|
importedColumns={state.importedColumns}
|
||||||
file={uploadedFile}
|
file={uploadedFile}
|
||||||
onSubmitStart={() =>
|
onSubmitStart={() =>
|
||||||
setState({
|
setState({
|
||||||
|
@ -8,6 +8,10 @@ import { Heading } from '@/spreadsheet-import/components/Heading';
|
|||||||
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
|
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
|
||||||
import { Table } from '@/spreadsheet-import/components/Table';
|
import { Table } from '@/spreadsheet-import/components/Table';
|
||||||
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
|
import {
|
||||||
|
Columns,
|
||||||
|
ColumnType,
|
||||||
|
} from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
|
||||||
import { Data } from '@/spreadsheet-import/types';
|
import { Data } from '@/spreadsheet-import/types';
|
||||||
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
|
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
|
||||||
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
|
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
|
||||||
@ -62,6 +66,7 @@ const StyledNoRowsContainer = styled.div`
|
|||||||
|
|
||||||
type ValidationStepProps<T extends string> = {
|
type ValidationStepProps<T extends string> = {
|
||||||
initialData: Data<T>[];
|
initialData: Data<T>[];
|
||||||
|
importedColumns: Columns<string>;
|
||||||
file: File;
|
file: File;
|
||||||
onSubmitStart?: () => void;
|
onSubmitStart?: () => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
@ -69,6 +74,7 @@ type ValidationStepProps<T extends string> = {
|
|||||||
|
|
||||||
export const ValidationStep = <T extends string>({
|
export const ValidationStep = <T extends string>({
|
||||||
initialData,
|
initialData,
|
||||||
|
importedColumns,
|
||||||
file,
|
file,
|
||||||
onSubmitStart,
|
onSubmitStart,
|
||||||
onBack,
|
onBack,
|
||||||
@ -88,6 +94,7 @@ export const ValidationStep = <T extends string>({
|
|||||||
ReadonlySet<number | string>
|
ReadonlySet<number | string>
|
||||||
>(new Set());
|
>(new Set());
|
||||||
const [filterByErrors, setFilterByErrors] = useState(false);
|
const [filterByErrors, setFilterByErrors] = useState(false);
|
||||||
|
const [showUnmatchedColumns, setShowUnmatchedColumns] = useState(false);
|
||||||
|
|
||||||
const updateData = useCallback(
|
const updateData = useCallback(
|
||||||
(rows: typeof data) => {
|
(rows: typeof data) => {
|
||||||
@ -127,7 +134,24 @@ export const ValidationStep = <T extends string>({
|
|||||||
[data, updateData],
|
[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(() => {
|
const tableData = useMemo(() => {
|
||||||
if (filterByErrors) {
|
if (filterByErrors) {
|
||||||
@ -212,6 +236,15 @@ export const ValidationStep = <T extends string>({
|
|||||||
Show only rows with errors
|
Show only rows with errors
|
||||||
</StyledErrorToggleDescription>
|
</StyledErrorToggleDescription>
|
||||||
</StyledErrorToggle>
|
</StyledErrorToggle>
|
||||||
|
<StyledErrorToggle>
|
||||||
|
<Toggle
|
||||||
|
value={showUnmatchedColumns}
|
||||||
|
onChange={() => setShowUnmatchedColumns(!showUnmatchedColumns)}
|
||||||
|
/>
|
||||||
|
<StyledErrorToggleDescription>
|
||||||
|
Show unmatched columns
|
||||||
|
</StyledErrorToggleDescription>
|
||||||
|
</StyledErrorToggle>
|
||||||
<Button
|
<Button
|
||||||
Icon={IconTrash}
|
Icon={IconTrash}
|
||||||
title="Remove"
|
title="Remove"
|
||||||
|
@ -5,6 +5,7 @@ import { Providers } from '@/spreadsheet-import/components/Providers';
|
|||||||
import { ValidationStep } from '@/spreadsheet-import/steps/components/ValidationStep/ValidationStep';
|
import { ValidationStep } from '@/spreadsheet-import/steps/components/ValidationStep/ValidationStep';
|
||||||
import {
|
import {
|
||||||
editableTableInitialData,
|
editableTableInitialData,
|
||||||
|
importedColums,
|
||||||
mockRsiValues,
|
mockRsiValues,
|
||||||
} from '@/spreadsheet-import/tests/mockRsiValues';
|
} from '@/spreadsheet-import/tests/mockRsiValues';
|
||||||
import { DialogManagerScope } from '@/ui/feedback/dialog-manager/scopes/DialogManagerScope';
|
import { DialogManagerScope } from '@/ui/feedback/dialog-manager/scopes/DialogManagerScope';
|
||||||
@ -28,6 +29,7 @@ export const Default = () => (
|
|||||||
<ValidationStep
|
<ValidationStep
|
||||||
initialData={editableTableInitialData}
|
initialData={editableTableInitialData}
|
||||||
file={file}
|
file={file}
|
||||||
|
importedColumns={importedColums}
|
||||||
onBack={() => Promise.resolve()}
|
onBack={() => Promise.resolve()}
|
||||||
/>
|
/>
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { defaultSpreadsheetImportProps } from '@/spreadsheet-import/provider/components/SpreadsheetImport';
|
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 { Fields, SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||||
import { sleep } from '~/utils/sleep';
|
import { sleep } from '~/utils/sleep';
|
||||||
|
|
||||||
@ -88,6 +89,33 @@ const fields = [
|
|||||||
},
|
},
|
||||||
] as Fields<string>;
|
] 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>(
|
const mockComponentBehaviourForTypes = <T extends string>(
|
||||||
props: SpreadsheetOptions<T>,
|
props: SpreadsheetOptions<T>,
|
||||||
) => props;
|
) => props;
|
||||||
|
@ -157,43 +157,44 @@ export const graphqlMocks = {
|
|||||||
graphql.query('FindDuplicateCompany', () => {
|
graphql.query('FindDuplicateCompany', () => {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
data: {
|
data: {
|
||||||
companyDuplicates: {
|
companyDuplicates: [
|
||||||
edges: [
|
{
|
||||||
{
|
edges: [
|
||||||
node: {
|
{
|
||||||
...duplicateCompanyMock,
|
node: {
|
||||||
favorites: {
|
...duplicateCompanyMock,
|
||||||
edges: [],
|
favorites: {
|
||||||
__typename: 'FavoriteConnection',
|
edges: [],
|
||||||
},
|
__typename: 'FavoriteConnection',
|
||||||
attachments: {
|
},
|
||||||
edges: [],
|
attachments: {
|
||||||
__typename: 'AttachmentConnection',
|
edges: [],
|
||||||
},
|
__typename: 'AttachmentConnection',
|
||||||
people: {
|
},
|
||||||
edges: [],
|
people: {
|
||||||
__typename: 'PersonConnection',
|
edges: [],
|
||||||
},
|
__typename: 'PersonConnection',
|
||||||
opportunities: {
|
},
|
||||||
edges: [],
|
opportunities: {
|
||||||
__typename: 'OpportunityConnection',
|
edges: [],
|
||||||
},
|
__typename: 'OpportunityConnection',
|
||||||
activityTargets: {
|
},
|
||||||
edges: [],
|
activityTargets: {
|
||||||
__typename: 'ActivityTargetConnection',
|
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,
|
],
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
Loading…
Reference in New Issue
Block a user