diff --git a/src/renderer/components/Data/NewDatasetModal.tsx b/src/renderer/components/Data/NewDatasetModal.tsx index 9c0112efce22091e3971b42f37cfd01593f0b079..b234b64c8341ee92b720ee7d357ed595b9c77a21 100644 --- a/src/renderer/components/Data/NewDatasetModal.tsx +++ b/src/renderer/components/Data/NewDatasetModal.tsx @@ -14,6 +14,8 @@ import { CircularProgress, } from '@mui/joy'; import { PlusCircleIcon } from 'lucide-react'; +import Dropzone from 'react-dropzone'; +import { IoCloudUploadOutline } from 'react-icons/io5'; import * as chatAPI from '../../lib/transformerlab-api-sdk'; @@ -25,13 +27,13 @@ export default function DatasetDetailsModal({ open, setOpen }) { const [trainFileUploaded, setTrainFileUploaded] = useState(false); const [evalFileUploaded, setEvalFileUploaded] = useState(false); const [uploading, setUploading] = useState(false); - const [formData, setFormData] = useState(new FormData()); + const [dropzoneActive, setDropzoneActive] = React.useState(false); // Reset newDatasetName when the modal is open/closed // useEffect(() => { // setNewDatasetName(''); // }, [open]); - const { data, isLoading } = useSWR( + const { data, isLoading, mutate } = useSWR( chatAPI.Endpoints.Dataset.LocalList(), fetcher ); @@ -42,16 +44,10 @@ export default function DatasetDetailsModal({ open, setOpen }) { setNewDatasetName(''); setEvalFileUploaded(false); setTrainFileUploaded(false); - setFormData(new FormData()); + mutate(); }; - useEffect(() => { - //This needs to be in a useEffect because we need to wait for state variables to update - if (trainFileUploaded && evalFileUploaded) { - uploadFiles(); - } - }, [trainFileUploaded, evalFileUploaded]); - const uploadFiles = async () => { + const uploadFiles = async (formData) => { setUploading(true); //This is for the loading spinner //Create the dataset before uploading const response = await fetch( @@ -95,8 +91,10 @@ export default function DatasetDetailsModal({ open, setOpen }) { > <ModalDialog> <ModalClose /> - <Typography level="h5">{newDatasetName || 'New'} Dataset</Typography> - <Divider sx={{ my: 2 }} /> + <Typography level="title-lg"> + {newDatasetName || 'New'} Dataset + </Typography> + <Divider sx={{ my: 1 }} /> <Sheet sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> {newDatasetName === '' && ( <form @@ -139,60 +137,81 @@ export default function DatasetDetailsModal({ open, setOpen }) { <Modal open={showUploadDialog} onClose={handleClose}> <ModalDialog> <ModalClose /> - <Typography level="h5">Upload Dataset</Typography> + <Typography level="title-lg">Upload Dataset</Typography> <Divider sx={{ my: 2 }} /> <Box //Making the modal a set size sx={{ display: 'flex', flexDirection: 'column', - gap: 3, + gap: 2, overflowY: 'hidden', width: '25vw', justifyContent: 'center', }} > - {!trainFileUploaded && - !evalFileUploaded && - `Add dataset files here. You must name one file '${newDatasetName}_train.jsonl' - and the second one '${newDatasetName}_eval.jsonl'. Files should be in JSONL - format, with one JSON object per line.`} - {trainFileUploaded && - !evalFileUploaded && - `Please upload the eval file, named '${newDatasetName}_eval.jsonl'`} - {!trainFileUploaded && - evalFileUploaded && - `Please upload the train file, named '${newDatasetName}_train.jsonl'`} + <Dropzone + onDrop={async (acceptedFiles) => { + setDropzoneActive(false); + + const formData = new FormData(); + for (const file of acceptedFiles) { + formData.append('files', file); + } + await uploadFiles(formData); + }} + onDragEnter={() => { + setDropzoneActive(true); + }} + onDragLeave={() => { + setDropzoneActive(false); + }} + noClick + > + {({ getRootProps, getInputProps }) => ( + <div id="dropzone_baby" {...getRootProps()}> + <Sheet + color="primary" + variant="soft" + sx={{ + display: 'flex', + flexDirection: 'column', + marginBottom: '0rem', + overflow: 'hidden', + minHeight: '130px', + border: dropzoneActive + ? '2px solid var(--joy-palette-warning-400)' + : '2px dashed var(--joy-palette-neutral-300)', + borderRadius: '8px', + flex: 1, + justifyContent: 'center', + alignItems: 'center', + color: 'var(--joy-palette-neutral-400)', + }} + > + <IoCloudUploadOutline size="36px" /> Drag files here + <Typography level="body-xs" color="neutral" mt={3}> + Allowed filetypes: .jsonl, .json + </Typography> + </Sheet> + </div> + )} + </Dropzone> <Button startDecorator={<PlusCircleIcon />} onClick={() => { var input = document.createElement('input'); input.type = 'file'; input.multiple = true; //Allow multiple files - input.accept = '.jsonl'; //Only allow JSONL files + + // input.accept = '.jsonl'; //Only allow JSONL files input.onchange = async (e) => { let files = Array.from(input.files); console.log(files); + const formData = new FormData(); for (const file of files) { - //Only appending the files with the correct names - //Preventing the user from uploading the same file twice - if ( - !trainFileUploaded && - file.name === `${newDatasetName}_train.jsonl` - ) { - formData.append('files', file); //Have to append to files attribute since there are multiple files - setTrainFileUploaded(true); - } else if ( - !evalFileUploaded && - file.name === `${newDatasetName}_eval.jsonl` - ) { - formData.append('files', file); - setEvalFileUploaded(true); - } else { - alert( - `Error: Please upload files with the name '${newDatasetName}_train.jsonl' and '${newDatasetName}_eval.jsonl'` - ); - } + formData.append('files', file); } + await uploadFiles(formData); }; input.click(); }} @@ -200,10 +219,6 @@ export default function DatasetDetailsModal({ open, setOpen }) { > {uploading ? <CircularProgress /> : 'Add files'} </Button> - <Typography level="caption">{`Learn more about JSONL here: https://jsonlines.org/ Don't worry we will support more file types soon :)`}</Typography> - <Typography level="body-xs" color="neutral"> - Allowed filetypes: .jsonl - </Typography> </Box> </ModalDialog> </Modal>