ui for files manipulation
This commit is contained in:
parent
318f6f9d8f
commit
f24c394388
100
src/pages/files/add.jsx
Normal file
100
src/pages/files/add.jsx
Normal 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;
|
130
src/pages/files/file-selector.jsx
Normal file
130
src/pages/files/file-selector.jsx
Normal 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;
|
37
src/pages/files/file-view.jsx
Normal file
37
src/pages/files/file-view.jsx
Normal 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
67
src/pages/files/index.jsx
Normal 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;
|
Loading…
Reference in New Issue
Block a user