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:
@ -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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -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: '',
|
||||
})),
|
||||
];
|
||||
};
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user