Skip to content
Snippets Groups Projects
Commit a1a8f3ab authored by Wilson Chua's avatar Wilson Chua :rice_ball:
Browse files

Convert file upload to utilise react-dropzone, with a dedicated details section for each file

parent db2c4061
No related branches found
No related tags found
No related merge requests found
...@@ -20,13 +20,7 @@ git checkout scientia-integration ...@@ -20,13 +20,7 @@ git checkout scientia-integration
# Setup and run the development server on port 5000 # Setup and run the development server on port 5000
python3 -m venv venv python3 -m venv venv
source venv/bin/activate ./scripts/start_scientia_dev.sh
pip install --upgrade pip && pip install -r requirements.txt
export FLASK_ENV=development
export FLASK_APP=materials
flask create_all
flask populate -r materials/mocks/resources.json
flask run
``` ```
......
...@@ -4,7 +4,7 @@ import Button from "react-bootstrap/Button"; ...@@ -4,7 +4,7 @@ import Button from "react-bootstrap/Button";
import Col from "react-bootstrap/esm/Col"; import Col from "react-bootstrap/esm/Col";
import Row from "react-bootstrap/esm/Row"; import Row from "react-bootstrap/esm/Row";
import UploadModal from "./UploadModal" import UploadModal from "./UploadModal/UploadModal"
import { Resource, Folder } from "../index"; import { Resource, Folder } from "../index";
import CategoryList from "components/molecules/CategoryList"; import CategoryList from "components/molecules/CategoryList";
import CategoryHeader from "components/molecules/CategoryHeader"; import CategoryHeader from "components/molecules/CategoryHeader";
...@@ -47,8 +47,8 @@ const StaffView: React.FC<StaffViewProps> = ({ ...@@ -47,8 +47,8 @@ const StaffView: React.FC<StaffViewProps> = ({
<UploadModal <UploadModal
show={modal === "link"} show={modal === "link"}
onHide={closeModal} onHide={closeModal}
categories={folders.map(folder => folder.title)} categories={folders.map(folder => folder.title).sort()}
tags={tags} tags={tags.sort()}
/> />
{folders.map(({ title, id }) => { {folders.map(({ title, id }) => {
......
import React from "react";
import Modal from "react-bootstrap/Modal";
import Form from "react-bootstrap/Form";
import ButtonGroup from "react-bootstrap/ButtonGroup";
import Button from "react-bootstrap/Button";
import CreatableSelect from 'react-select/creatable';
import Tabs from 'react-bootstrap/Tabs'
import Tab from 'react-bootstrap/Tab'
interface Props {
show: boolean;
onHide: any;
categories: string[];
tags: string[];
}
const UploadModal: React.FC<Props> = ({
show,
onHide,
categories,
tags,
}) => {
return (
<Modal
style={{ zIndex: "10000" }}
show={show}
onHide={onHide}
centered
>
<Modal.Header closeButton>
<Modal.Title>Upload Resource</Modal.Title>
</Modal.Header>
<Form>
<Modal.Body>
<Form.Group>
<Form.Label>Title</Form.Label>
<Form.Control type="text" placeholder="Type a title for this resource here." />
</Form.Group>
<Tabs defaultActiveKey="file">
<Tab eventKey="file" title="File">
<Form.Group>
<Form.File label="Upload file for resource" />
</Form.Group>
</Tab>
<Tab eventKey="link" title="Link">
<Form.Group>
<Form.Label>URL</Form.Label>
<Form.Control type="text" placeholder="Paste link here." />
</Form.Group>
</Tab>
</Tabs>
<Form.Group>
<Form.Label>Category</Form.Label>
<CreatableSelect
isClearable
options={categories.map(category => ({
value: category,
label: category,
}))}
/>
</Form.Group>
<Form.Group>
<Form.Label>Tags</Form.Label>
<CreatableSelect
isClearable
isMulti
options={tags.map(tag => ({
value: tag,
label: tag,
}))}
/>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<ButtonGroup className="btn-block">
<Button
onClick={onHide}
variant="secondary"
>
Cancel
</Button>
<Button
variant="info"
>
Upload
</Button>
</ButtonGroup>
</Modal.Footer>
</Form>
</Modal>
);
};
export default UploadModal;
\ No newline at end of file
import React, { useState, useCallback } from "react";
import classNames from "classnames";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Modal from "react-bootstrap/Modal";
import Form from "react-bootstrap/Form";
import Accordion from "react-bootstrap/Accordion";
import Button from "react-bootstrap/Button";
import ButtonGroup from "react-bootstrap/ButtonGroup";
import Card from "react-bootstrap/Card";
import Tab from "react-bootstrap/Tab";
import Tabs from "react-bootstrap/Tabs";
import DatePicker from "react-datepicker";
import { useDropzone } from "react-dropzone";
import CreatableSelect from "react-select/creatable";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faCaretDown,
faFolder,
faFolderOpen,
} from "@fortawesome/free-solid-svg-icons";
import "react-datepicker/dist/react-datepicker.css";
import styles from "./style.module.scss";
// import { request } from "../../../../../utils/api"
// import { api } from "../../../../../constants/routes"
interface UploadModalProps {
show: boolean;
onHide: any;
categories: string[];
tags: string[];
}
const UploadModal: React.FC<UploadModalProps> = ({
show,
onHide,
categories,
tags,
}) => {
const maxSize = 1048576;
const prettyBytes = require('pretty-bytes');
const onDrop = useCallback(acceptedFiles => {
console.log(acceptedFiles);
}, []);
const { isDragActive, getRootProps, getInputProps, isDragAccept, isDragReject, acceptedFiles } = useDropzone({
onDrop,
minSize: 0,
maxSize,
multiple: true,
});
const handleSubmit = (event: any) => {
event.preventDefault();
// fetch('/api/form-submit-url', {
// method: 'POST',
// body: data,
// });
}
return (
<Modal
style={{ zIndex: "10000" }}
size="lg"
show={show}
onHide={onHide}
centered
>
<Modal.Header closeButton>
<Modal.Title>Upload Resource</Modal.Title>
</Modal.Header>
<Form onSubmit={handleSubmit}>
<Modal.Body>
<Tabs defaultActiveKey="file">
<Tab eventKey="file" title="File">
<div {...getRootProps({
className: classNames(
styles.dropzone,
isDragAccept ? styles.accept : isDragReject ? styles.reject : ""
)
})}>
<input {...getInputProps()}/>
<FontAwesomeIcon icon={isDragActive ? faFolderOpen : faFolder}/>
<p>Click here or drag and drop files to upload.</p>
</div>
<Accordion>
{acceptedFiles.length > 0 && acceptedFiles.map((acceptedFile, index) => (
<Card key={index}>
<Accordion.Toggle as={Card.Header} eventKey={`${index}`}>
<Row>
<Col>
{acceptedFile.name}
<span className={styles.filesizeDisplay}>
{prettyBytes(acceptedFile.size)}
</span>
</Col>
<Col md="auto">
<FontAwesomeIcon icon={faCaretDown}/>
</Col>
</Row>
</Accordion.Toggle>
<Accordion.Collapse eventKey={`${index}`}>
<Card.Body>
<ResourceDetailForm
id={`${index}`}
categories={categories}
tags={tags}
defaultTitle={acceptedFile.name}
/>
</Card.Body>
</Accordion.Collapse>
</Card>
))}
</Accordion>
</Tab>
<Tab eventKey="link" title="Link">
<Form.Group>
<Form.Label>URL</Form.Label>
<Form.Control type="text" placeholder="Paste link here." />
</Form.Group>
<ResourceDetailForm
id="link"
categories={categories}
tags={tags}
/>
</Tab>
</Tabs>
</Modal.Body>
<Modal.Footer>
<ButtonGroup className="btn-block">
<Button
onClick={onHide}
variant="secondary"
>
Cancel
</Button>
<Button
type="submit"
variant="info"
>
Upload
</Button>
</ButtonGroup>
</Modal.Footer>
</Form>
</Modal>
);
};
interface ResourceDetailFormProps {
id: string;
categories: string[];
tags: string[];
defaultTitle?: string;
}
const ResourceDetailForm: React.FC<ResourceDetailFormProps> = ({
id,
categories,
tags,
defaultTitle,
}) => {
const [showPicker, setShowPicker] = useState(false);
const [startDate, setStartDate] = useState(new Date());
// Form responses
// const [title, setTitle] = useState<string>(defaultTitle || "");
// const [category, setCategory] = useState("");
// const [tagsInput, setTags] = useState<string[]>([]);
return (<>
<Form.Group>
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
placeholder="Type a title for this resource here."
defaultValue={defaultTitle}
/>
</Form.Group>
<Form.Group>
<Form.Label>Category</Form.Label>
<CreatableSelect
isClearable
options={categories.map(category => ({
value: category,
label: category,
}))}
/>
</Form.Group>
<Form.Group>
<Form.Label>Tags</Form.Label>
<CreatableSelect
isClearable
isMulti
options={tags.map(tag => ({
value: tag,
label: tag,
}))}
/>
</Form.Group>
<Form.Group>
<Form.Switch
id={`${id}-visibilityPickerSwitch`}
label="Visible immediately"
onClick={() => setShowPicker(!showPicker)}
defaultChecked
/>
{showPicker &&
<DatePicker
selected={startDate}
onChange={(date:Date) => setStartDate(date)}
locale="en-GB"
showTimeInput
timeFormat="p"
dateFormat="Pp"
/>}
</Form.Group>
</>);
}
export default UploadModal;
\ No newline at end of file
@import "assets/scss/custom";
.dropzone {
text-align: center;
padding: 20px;
border: 3px dashed $gray-200;
background-color: $gray-100;
color: $gray-500;
margin-top: 20px;
margin-bottom: 20px;
}
.accept {
border-color: $green !important;
}
.reject {
border-color: $red !important;
}
.filesizeDisplay {
color: $gray-600;
padding-left: 1rem;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment