add back drag & drop to upload

This commit is contained in:
proddy
2024-08-15 12:14:14 +02:00
parent 4e640a0abe
commit 817b2d1ad7
17 changed files with 3235 additions and 1873 deletions

View File

@@ -0,0 +1,104 @@
import { type ChangeEvent, useRef, useState } from 'react';
import { AiOutlineCloudUpload } from 'react-icons/ai';
import { MdClear } from 'react-icons/md';
import UploadIcon from '@mui/icons-material/Upload';
import { Button } from '@mui/material';
import { useI18nContext } from 'i18n/i18n-react';
import './drag-drop.css';
const DragNdrop = ({ onFileSelected, width, height }) => {
const [file, setFile] = useState<File>();
const inputRef = useRef<HTMLInputElement | null>(null);
const { LL } = useI18nContext();
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) {
return;
}
setFile(e.target.files[0]);
};
const handleDrop = (event) => {
event.preventDefault();
const droppedFiles = event.dataTransfer.files;
if (droppedFiles.length > 0) {
setFile(droppedFiles[0]);
}
};
const handleRemoveFile = (event) => {
event.stopPropagation();
setFile(undefined);
};
const handleUploadClick = (event) => {
event.stopPropagation();
onFileSelected(file);
};
const handleBrowseClick = () => {
inputRef.current?.click();
};
return (
<section className="drag-drop" style={{ width: width, height: height }}>
<div
className={`document-uploader ${file ? 'upload-box active' : 'upload-box'}`}
onDrop={handleDrop}
onDragOver={(event) => event.preventDefault()}
onClick={handleBrowseClick}
>
<>
<div className="upload-info">
<AiOutlineCloudUpload />
<div>
<p>Drag and drop a file here or click to select one</p>
</div>
</div>
<input
type="file"
hidden
id="browse"
onChange={handleFileChange}
ref={inputRef}
accept=".json,.txt,.csv,.bin"
multiple={false}
/>
</>
{file && (
<>
<div className="file-list">
<div className="file-item">
<div className="file-info">
<p>{file.name}</p>
</div>
<div className="file-actions">
<MdClear
style={{ width: 18, verticalAlign: 'middle' }}
onClick={(e) => handleRemoveFile(e)}
/>
</div>
</div>
</div>
<Button
startIcon={<UploadIcon />}
variant="outlined"
color="secondary"
onClick={handleUploadClick}
>
{LL.UPLOAD()}
</Button>
</>
)}
</div>
</section>
);
};
export default DragNdrop;

View File

@@ -1,19 +1,26 @@
import { ChangeEvent, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
import UploadIcon from '@mui/icons-material/Upload';
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
import { Box, Button, LinearProgress, Typography } from '@mui/material';
import * as SystemApi from 'api/system';
import { useRequest } from 'alova/client';
import RestartMonitor from 'app/status/RestartMonitor';
import MessageBox from 'components/MessageBox';
import { useI18nContext } from 'i18n/i18n-react';
import DragNdrop from './DragNdrop';
const SingleUpload = () => {
const [md5, setMd5] = useState<string>();
const [restarting, setRestarting] = useState<boolean>();
const [restarting, setRestarting] = useState<boolean>(false);
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
const [file, setFile] = useState<File>();
const { LL } = useI18nContext();
const {
loading: isUploading,
@@ -28,55 +35,40 @@ const SingleUpload = () => {
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
setFile(undefined);
} else {
setRestarting(true);
setRestartNeeded(true);
// setRestarting(true);
}
});
const [file, setFile] = useState<File>();
const inputRef = useRef<HTMLInputElement | null>(null);
const { send: restartCommand } = useRequest(SystemApi.restart(), {
immediate: false
});
const { LL } = useI18nContext();
const handleUploadClick = () => {
inputRef.current?.click();
};
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) {
return;
}
setFile(e.target.files[0]);
await sendUpload(e.target.files[0]).catch((error: Error) => {
if (error.message === 'The user aborted a request') {
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
} else if (error.message === 'Network Error') {
toast.warning('Invalid file extension or incompatible bin file');
} else {
toast.error(error.message);
}
const restart = async () => {
await restartCommand().catch((error: Error) => {
toast.error(error.message);
});
setRestarting(true);
};
useEffect(async () => {
if (file) {
console.log('going to upload file ', file.name);
await sendUpload(file).catch((error: Error) => {
if (error.message === 'The user aborted a request') {
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
} else if (error.message === 'Network Error') {
toast.warning('Invalid file extension or incompatible bin file');
} else {
toast.error(error.message);
}
});
}
}, [file]);
return (
<>
<input
type="file"
ref={inputRef}
style={{ display: 'none' }}
onChange={handleFileChange}
/>
<Button
sx={{ ml: 2 }}
startIcon={<UploadIcon />}
variant="outlined"
color="secondary"
onClick={handleUploadClick}
>
{file ? LL.UPLOADING() + ` ${file.name}` : LL.UPLOAD()}
</Button>
<DragNdrop onFileSelected={setFile} width="340px" height="140px" />
{isUploading && (
<>
@@ -111,6 +103,19 @@ const SingleUpload = () => {
</Box>
)}
{restartNeeded && (
<MessageBox mt={4} level="warning" message={LL.RESTART_TEXT(0)}>
<Button
startIcon={<PowerSettingsNewIcon />}
variant="contained"
color="error"
onClick={restart}
>
{LL.RESTART()}
</Button>
</MessageBox>
)}
{restarting && <RestartMonitor />}
</>
);

View File

@@ -0,0 +1,83 @@
.drag-drop {
/* background: #fff; */
border: 1px solid var(--border-color);
border-radius: 8px;
}
.document-uploader {
border: 2px dashed #4282fe;
/* background-color: #f4fbff; */
padding: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
border-radius: 8px;
cursor: pointer;
&.active {
border-color: #6dc24b;
}
.upload-info {
display: flex;
align-items: center;
margin-bottom: 1rem;
svg {
font-size: 36px;
margin-right: 1rem;
}
div {
p {
margin: 0;
font-size: 14px;
}
}
}
.file-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
border: 1px solid var(--border-color);
border-radius: 8px;
.file-info {
display: flex;
flex-direction: column;
gap: 0.25rem;
flex: 1;
p {
margin: 0;
font-size: 14px;
color: #6dc24b;
}
}
.file-actions {
cursor: pointer;
&:hover {
svg {
color: #d44;
}
}
}
}
input[type='file'] {
display: none;
}
}