Import company and person from csv file (#1236)

* feat: wip implement back-end call csv import

* fix: rebase IconBrandTwitter missing

* feat: person and company csv import

* fix: test & clean

* fix: clean & test
This commit is contained in:
Jérémy M
2023-08-16 23:18:16 +02:00
committed by GitHub
parent 5890354d21
commit 8863bb0035
74 changed files with 950 additions and 312 deletions

View File

@ -0,0 +1,60 @@
import { useCallback, useState } from 'react';
import styled from '@emotion/styled';
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
import { Heading } from '@/spreadsheet-import/components/Heading';
import type { RawData } from '@/spreadsheet-import/types';
import { Modal } from '@/ui/modal/components/Modal';
import { SelectHeaderTable } from './components/SelectHeaderTable';
const StyledHeading = styled(Heading)`
margin-bottom: ${({ theme }) => theme.spacing(8)};
`;
const TableContainer = styled.div`
display: flex;
flex-grow: 1;
height: 0px;
`;
type SelectHeaderProps = {
data: RawData[];
onContinue: (headerValues: RawData, data: RawData[]) => Promise<void>;
};
export const SelectHeaderStep = ({ data, onContinue }: SelectHeaderProps) => {
const [selectedRows, setSelectedRows] = useState<ReadonlySet<number>>(
new Set([0]),
);
const [isLoading, setIsLoading] = useState(false);
const handleContinue = useCallback(async () => {
const [selectedRowIndex] = Array.from(new Set(selectedRows));
// We consider data above header to be redundant
const trimmedData = data.slice(selectedRowIndex + 1);
setIsLoading(true);
await onContinue(data[selectedRowIndex], trimmedData);
setIsLoading(false);
}, [onContinue, data, selectedRows]);
return (
<>
<Modal.Content>
<StyledHeading title="Select header row" />
<TableContainer>
<SelectHeaderTable
data={data}
selectedRows={selectedRows}
setSelectedRows={setSelectedRows}
/>
</TableContainer>
</Modal.Content>
<ContinueButton
onContinue={handleContinue}
title="Next"
isLoading={isLoading}
/>
</>
);
};

View File

@ -0,0 +1,51 @@
import { Column, FormatterProps, useRowSelection } from 'react-data-grid';
import type { RawData } from '@/spreadsheet-import/types';
import { Radio } from '@/ui/input/radio/components/Radio';
const SELECT_COLUMN_KEY = 'select-row';
function SelectFormatter(props: FormatterProps<unknown>) {
const [isRowSelected, onRowSelectionChange] = useRowSelection();
return (
<Radio
aria-label="Select"
checked={isRowSelected}
onChange={(event) => {
onRowSelectionChange({
row: props.row,
checked: Boolean(event.target.checked),
isShiftClick: (event.nativeEvent as MouseEvent).shiftKey,
});
}}
/>
);
}
export const SelectColumn: Column<any, any> = {
key: SELECT_COLUMN_KEY,
name: '',
width: 35,
minWidth: 35,
maxWidth: 35,
resizable: false,
sortable: false,
frozen: true,
cellClass: 'rdg-radio',
formatter: SelectFormatter,
};
export const generateSelectionColumns = (data: RawData[]) => {
const longestRowLength = data.reduce(
(acc, curr) => (acc > curr.length ? acc : curr.length),
0,
);
return [
SelectColumn,
...Array.from(Array(longestRowLength), (_, index) => ({
key: index.toString(),
name: '',
})),
];
};

View File

@ -0,0 +1,42 @@
import { useMemo } from 'react';
import { Table } from '@/spreadsheet-import/components/Table';
import type { RawData } from '@/spreadsheet-import/types';
import { generateSelectionColumns } from './SelectColumn';
interface Props {
data: RawData[];
selectedRows: ReadonlySet<number>;
setSelectedRows: (rows: ReadonlySet<number>) => void;
}
export const SelectHeaderTable = ({
data,
selectedRows,
setSelectedRows,
}: Props) => {
const columns = useMemo(() => generateSelectionColumns(data), [data]);
return (
<Table
rowKeyGetter={(row) => data.indexOf(row)}
rows={data}
columns={columns}
selectedRows={selectedRows}
onSelectedRowsChange={(newRows) => {
// allow selecting only one row
newRows.forEach((value) => {
if (!selectedRows.has(value as number)) {
setSelectedRows(new Set([value as number]));
return;
}
});
}}
onRowClick={(row) => {
setSelectedRows(new Set([data.indexOf(row)]));
}}
headerRowHeight={0}
/>
);
};