Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/components/CSVImporter/CSVImporter.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,18 @@ const Template: Story<typeof ImporterComponent> = (args: CSVImporterProps) => {

return (
<div>
{args.isModal && <button onClick={() => setIsOpen(true)}>Import</button>}
{args.isModal && (
<>
<button onClick={() => setIsOpen(true)}>Import</button>
<input
type="file"
onChange={(e) => {
args.file = e.target.files?.[0];
setIsOpen(true);
}}
/>
</>
)}
<ImporterComponent key={props.isModal?.toString()} {...props} />
</div>
);
Expand Down
54 changes: 26 additions & 28 deletions src/importer/components/UploaderWrapper/UploaderWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useState } from "react";
import { UploaderWrapperProps } from "./types";
import { useDropzone } from "react-dropzone";
import { Box, Text } from "@chakra-ui/react";
import { Button } from "@chakra-ui/button";
import { PiArrowCounterClockwise, PiFile } from "react-icons/pi";
import { Box, Text } from "@chakra-ui/react";
import useThemeStore from "../../stores/theme";
import { UploaderWrapperProps } from "./types";
import { PiArrowCounterClockwise, PiFile } from "react-icons/pi";

export default function UploaderWrapper({ onSuccess, setDataError, ...props }: UploaderWrapperProps) {
const [loading, setLoading] = useState(false);
Expand Down Expand Up @@ -34,24 +34,19 @@ export default function UploaderWrapper({ onSuccess, setDataError, ...props }: U
});

return (
<Box
padding="15px"
border="1px solid var(--color-border)"
borderRadius="var(--border-radius-2)"
>
<Box
{...getRootProps()}
width="100%"
<Box padding="15px" border="1px solid var(--color-border)" borderRadius="var(--border-radius-2)">
<Box
{...getRootProps()}
width="100%"
height="100%"
display="flex"
justifyContent="center"
alignItems="center"
flexDirection="column"
display="flex"
justifyContent="center"
alignItems="center"
flexDirection="column"
flex={1}
border="2px dashed var(--color-border)"
borderRadius="var(--border-radius-2)"
>
<input {...getInputProps()}/>
borderRadius="var(--border-radius-2)">
<input {...getInputProps()} />
{isDragActive ? (
<Text>Drop your file here</Text>
) : loading ? (
Expand All @@ -60,17 +55,20 @@ export default function UploaderWrapper({ onSuccess, setDataError, ...props }: U
<>
<Text>Drop your file here</Text>
<Text>or</Text>
<Button
leftIcon={<PiFile />}
onClick={open}
mt="6px"
colorScheme={"secondary"}
<Button
leftIcon={<PiFile />}
onClick={open}
mt="6px"
colorScheme={"secondary"}
variant={theme === "light" ? "outline" : "solid"}
_hover={theme === "light" ? {
background: "var(--color-border)",
color: "var(--color-text)"
} : undefined}
>
_hover={
theme === "light"
? {
background: "var(--color-border)",
color: "var(--color-text)",
}
: undefined
}>
Browse files
</Button>
</>
Expand Down
121 changes: 64 additions & 57 deletions src/importer/features/main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default function Main(props: CSVImporterProps) {
customStyles,
showDownloadTemplateButton,
skipHeaderRowSelection,
file,
} = props;
const skipHeader = skipHeaderRowSelection ?? false;

Expand Down Expand Up @@ -80,6 +81,10 @@ export default function Main(props: CSVImporterProps) {
}
}, [data]);

useEffect(() => {
if (file) processFile(file);
}, [file]);

// Actions
const reload = () => {
setData(emptyData);
Expand Down Expand Up @@ -107,6 +112,64 @@ export default function Main(props: CSVImporterProps) {
);
}

const processFile = async (file: File) => {
setDataError(null);
const fileType = file.name.slice(file.name.lastIndexOf(".") + 1);
if (!["csv", "xls", "xlsx"].includes(fileType)) {
setDataError("Only CSV, XLS, and XLSX files can be uploaded");
return;
}
const reader = new FileReader();
const isNotBlankRow = (row: string[]) => row.some((cell) => cell.toString().trim() !== "");
reader.onload = async (e) => {
const bstr = e?.target?.result;
if (!bstr) {
return;
}
switch (fileType) {
case "csv":
Papa.parse(bstr.toString(), {
complete: function (results) {
const csvData = results.data as Array<Array<string>>;
const rows: FileRow[] = csvData.filter(isNotBlankRow).map((row: string[], index: number) => ({ index, values: row }));
setData({
fileName: file.name,
rows: rows,
sheetList: [],
errors: results.errors.map((error) => error.message),
});
goNext();
},
});
break;
case "xlsx":
case "xls":
const workbook = XLSX.read(bstr as string, { type: "binary" });
const sheetList = workbook.SheetNames;
const data = XLSX.utils.sheet_to_json(workbook.Sheets[sheetList[0]], { header: 1 }) as Array<Array<string>>;
const rows: FileRow[] = data.filter(isNotBlankRow).map((row: string[], index: number) => ({ index, values: row }));
setData({
fileName: file.name,
rows: rows,
sheetList: sheetList,
errors: [], // TODO: Handle any parsing errors
});
goNext();
break;
}
};

switch (fileType) {
case "csv":
reader.readAsText(file, "utf-8");
break;
case "xlsx":
case "xls":
reader.readAsBinaryString(file);
break;
}
};

const renderContent = () => {
switch (currentStep) {
case StepEnum.Upload:
Expand All @@ -116,63 +179,7 @@ export default function Main(props: CSVImporterProps) {
skipHeaderRowSelection={skipHeader || false}
showDownloadTemplateButton={showDownloadTemplateButton}
setDataError={setDataError}
onSuccess={async (file: File) => {
setDataError(null);
const fileType = file.name.slice(file.name.lastIndexOf(".") + 1);
if (!["csv", "xls", "xlsx"].includes(fileType)) {
setDataError("Only CSV, XLS, and XLSX files can be uploaded");
return;
}
const reader = new FileReader();
const isNotBlankRow = (row: string[]) => row.some((cell) => cell.toString().trim() !== "");
reader.onload = async (e) => {
const bstr = e?.target?.result;
if (!bstr) {
return;
}
switch (fileType) {
case "csv":
Papa.parse(bstr.toString(), {
complete: function (results) {
const csvData = results.data as Array<Array<string>>;
const rows: FileRow[] = csvData.filter(isNotBlankRow).map((row: string[], index: number) => ({ index, values: row }));
setData({
fileName: file.name,
rows: rows,
sheetList: [],
errors: results.errors.map((error) => error.message),
});
goNext();
},
});
break;
case "xlsx":
case "xls":
const workbook = XLSX.read(bstr as string, { type: "binary" });
const sheetList = workbook.SheetNames;
const data = XLSX.utils.sheet_to_json(workbook.Sheets[sheetList[0]], { header: 1 }) as Array<Array<string>>;
const rows: FileRow[] = data.filter(isNotBlankRow).map((row: string[], index: number) => ({ index, values: row }));
setData({
fileName: file.name,
rows: rows,
sheetList: sheetList,
errors: [], // TODO: Handle any parsing errors
});
goNext();
break;
}
};

switch (fileType) {
case "csv":
reader.readAsText(file, "utf-8");
break;
case "xlsx":
case "xls":
reader.readAsBinaryString(file);
break;
}
}}
onSuccess={processFile}
/>
);
case StepEnum.RowSelection:
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export type CSVImporterProps = (HTMLAttributes<HTMLDialogElement> & HTMLAttribut
customStyles?: Record<string, string> | string;
showDownloadTemplateButton?: boolean;
skipHeaderRowSelection?: boolean;
file?: File;
} & ModalParams;