ui for files manipulation

This commit is contained in:
grimhilt 2023-07-30 19:15:01 +02:00
parent 318f6f9d8f
commit f24c394388
4 changed files with 334 additions and 0 deletions

100
src/pages/files/add.jsx Normal file
View File

@ -0,0 +1,100 @@
import { Group, Modal, Text, rem, Button, List } from '@mantine/core';
import { IconUpload, IconPhoto, IconX } from '@tabler/icons-react';
import { Dropzone, IMAGE_MIME_TYPE, MIME_TYPES } from '@mantine/dropzone';
import { useState } from 'react';
import API from '../../services/api';
import setNotification from '../errors/error-notification';
const ModalAddFile = ({ opened, handler, addFiles }) => {
const [files, setFiles] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const validate = (object) => {
setIsLoading(false);
setFiles([]);
addFiles(object);
handler();
};
const addFilesToList = (files) => {
files.forEach((file) => {
setFiles((prev) => [...prev, file]);
});
};
const handleSubmit = () => {
setIsLoading(true);
const formData = new FormData();
files.forEach((file) => formData.append('file', file));
API.upload(formData)
.then((res) => {
if (res.status === 200) {
validate(files);
}
})
.catch((err) => {
setNotification(true, err.message);
setIsLoading(false);
});
};
return (
<Modal.Root opened={opened} onClose={() => validate([])}>
<Modal.Overlay />
<Modal.Content>
<Modal.Header>
<Modal.Title>
<Text fw={700} fz="lg">
Add File
</Text>
</Modal.Title>
<Modal.CloseButton />
</Modal.Header>
<Modal.Body>
<Dropzone
onDrop={(files) => addFilesToList(files)}
onReject={(files) => setNotification(true, `Rejected files: ${files.map((file) => file.name)}`)}
maxSize={1024 ** 3} // 1 GB
accept={[IMAGE_MIME_TYPE, MIME_TYPES.mp4]}
>
<Group position="center" spacing="xl" style={{ minHeight: rem(220), pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload size="3.2rem" stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size="3.2rem" stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size="3.2rem" stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" color="dimmed" inline mt={7}>
Attach as many files as you like, each file should not exceed 1 GB
</Text>
</div>
</Group>
</Dropzone>
<List>
{files.map((file, index) => (
<List.Item key={index}>{file.name}</List.Item>
))}
</List>
<Group position="right" mt="md">
<Button variant="light" color="red" onClick={() => validate([])}>
Cancel
</Button>
<Button variant="light" color="green" loading={isLoading} onClick={handleSubmit}>
Upload
</Button>
</Group>
</Modal.Body>
</Modal.Content>
</Modal.Root>
);
};
export default ModalAddFile;

View File

@ -0,0 +1,130 @@
import { Modal, Button, Text, Group, Grid, TextInput, ScrollArea } from '@mantine/core';
import { IconSearch } from '@tabler/icons-react';
import { useEffect, useState } from 'react';
import ModalAddFile from './add';
import SelectorItem from '../../components/select-item';
import API from '../../services/api';
import setNotification from '../errors/error-notification';
const ModalFileSelector = ({ opened, handleClose, handleSubmit, ...props }) => {
const [files, setFiles] = useState([]);
const [showAdd, setShowAdd] = useState(false);
const [search, setSearch] = useState('');
let resfiless = [];
const toggleShowAdd = () => setShowAdd(!showAdd);
const clickHandler = (files) => {
if (props.multi) {
// eslint-disable-next-line eqeqeq
const indexfiles = resfiless.findIndex((item) => item._id == files._id);
if (indexfiles === -1) {
resfiless.push(files);
} else {
resfiless.splice(indexfiles, 1);
}
} else {
handleSubmitLocal(files);
}
};
const handleSubmitLocal = (files) => {
handleSubmit(props.multi ? resfiless : files);
handleClose();
};
useEffect(() => {
API.getFiles()
.then((res) => {
if (res.status === 200) {
setFiles(res.data);
}
})
.catch((err) => {
setNotification(true, err.response.data.error);
});
return () => {};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (search.length >= 2) {
API.searchfiles(search)
.then((res) => {
if (res.status === 200) {
setFiles(res.data);
}
})
.catch((err) => {
setNotification(true, err.response.data.error);
});
} else if (search.length === 0) {
API.getFiles()
.then((res) => {
if (res.status === 200) {
setFiles(res.data);
}
})
.catch((err) => {
setNotification(true, err.response.data.error);
});
}
}, [search]);
return (
<Modal.Root opened={opened} onClose={handleClose} size="lg">
<Modal.Overlay />
<Modal.Content>
<Modal.Header>
<Modal.Title>
<Text fw={700} fz="lg">
Select file{props.multi ? 's' : ''}
</Text>
</Modal.Title>
<Modal.CloseButton />
</Modal.Header>
<Modal.Body>
<TextInput
placeholder="Type to Search"
mb="sm"
radius="md"
onChange={(e) => setSearch(e.target.value)}
icon={<IconSearch size="1rem" stroke={1.5} />}
/>
<ScrollArea h={430} offsetScrollbars>
<Grid columns={3}>
{files.map((file) => (
<SelectorItem file={file} clickHandler={clickHandler} key={file.id} />
))}
</Grid>
</ScrollArea>
<Group position="right" mt="md">
{props.multi && (
<>
<Button variant="light" color="gray" onClick={() => setShowAdd(true)}>
Add file(s)
</Button>
<Button variant="light" color="red" onClick={handleClose}>
Cancel
</Button>
<Button type="submit" variant="light" color="green" onClick={() => handleSubmitLocal()}>
Validate
</Button>
</>
)}
</Group>
{showAdd && (
<ModalAddFile
opened={showAdd}
handler={toggleShowAdd}
addFiles={(files) => setFiles((prev) => [...prev, files])}
/>
)}
</Modal.Body>
</Modal.Content>
</Modal.Root>
);
};
export default ModalFileSelector;

View File

@ -0,0 +1,37 @@
import { Card, Divider, Text, Title, Image, Badge, Button, Group } from '@mantine/core';
import API from '../../services/api';
const FileView = ({ file, onSelect, onDelete, ...props }) => {
const deleteHandler = async () => {
try {
await API.deleteFile(file.id);
onDelete(file.id);
} catch (error) {
console.log(error);
}
};
return (
<Card shadow="sm" padding="md" withBorder>
<Card.Section>
<Image src={'/api/file/' + file?.id ?? ''} alt={file?.name ?? ''} withPlaceholder fit="contain" />
</Card.Section>
<Text>{file?.name ?? 'File Name'}</Text>
<Group position="center" grow>
{!props.noSelect ? (
<Button color="green" mt="sm" variant="light" onClick={() => onSelect(file?.id)}>
Select
</Button>
) : (
<></>
)}
<Button color="red" mt="sm" variant="light">
Delete
</Button>
</Group>
</Card>
);
};
export default FileView;

67
src/pages/files/index.jsx Normal file
View File

@ -0,0 +1,67 @@
import { Button, Paper, Grid, Text, Title, Group, List, Image, ScrollArea, Center } from '@mantine/core';
import { useEffect, useState } from 'react';
import API from '../../services/api';
import setNotification from '../errors/error-notification';
import ModalAddFile from './add';
import FileView from '../files/file-view';
const Files = () => {
const [showAddFile, setShowAddFile] = useState(false);
const [files, setFiles] = useState([]);
const toggleShowAddFile = () => setShowAddFile(!showAddFile);
const handleAddFiles = (files) => {
console.log(files);
};
useEffect(() => {
API.getFiles()
.then((res) => {
if (res.status === 200) {
setFiles(res.data);
}
})
.catch((err) => {
setNotification(true, err.response.data.error);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
<Group position="apart" mt="md">
<Title m="md" order={2}>
Files
</Title>
<Button variant="light" mt="sm" onClick={toggleShowAddFile}>
Add file
</Button>
</Group>
<Paper p="xs" radius="sm" shadow="sm" withBorder my="md">
<ScrollArea.Autosize mah={700} offsetScrollbars>
<Title order={3}>Files</Title>
<Grid columns={12}>
{files.map((file) => (
<Grid.Col xl={2} lg={3} md={3} sm={4} xs={4} key={file.id}>
<FileView key={file.id} file={file} noSelect />
</Grid.Col>
))}
</Grid>
</ScrollArea.Autosize>
<Center>
<Button onClick={() => {}} mt="md">
Load More
</Button>
</Center>
</Paper>
<ModalAddFile
opened={showAddFile}
handler={toggleShowAddFile}
addFiles={(files) => handleAddFiles(files)}
/>
</>
);
};
export default Files;