From 2f7310c7a69329b49b6b7d8b9b55e7295172433b Mon Sep 17 00:00:00 2001 From: AlixH Date: Sat, 22 Feb 2020 20:32:16 +0100 Subject: [PATCH] big refactor ! --- .../server/controller/PluginController.js | 49 ++- back-end/server/models/Plugin.js | 2 - .../src/Components/LoginForm/LoginForm.css | 13 +- .../src/Components/LoginForm/LoginForm.js | 9 +- front-end/src/Components/Navbar/NavBar.css | 9 +- .../PluginUploadForm/PluginUploadForm.css | 89 ++++- .../PluginUploadForm/PluginUploadForm.js | 332 ---------------- .../PluginUploadForm/PluginUploadForm.jsx | 365 ++++++++++++++++++ package.json | 1 + 9 files changed, 504 insertions(+), 365 deletions(-) delete mode 100644 front-end/src/Components/PluginUploadForm/PluginUploadForm.js create mode 100644 front-end/src/Components/PluginUploadForm/PluginUploadForm.jsx diff --git a/back-end/server/controller/PluginController.js b/back-end/server/controller/PluginController.js index 13d8a85..73fcb5b 100644 --- a/back-end/server/controller/PluginController.js +++ b/back-end/server/controller/PluginController.js @@ -9,7 +9,7 @@ module.exports = { console.log(err); next(err); } else { - res.json({ status: "success", message: "Plugin found!!!", data: { plugins: pluginInfo } }); + res.json({status: "success", message: "Plugin found!!!", data: {plugins: pluginInfo}}); } }); }, @@ -24,7 +24,7 @@ module.exports = { for (let plugin of plugins) { pluginsList.push(plugin); } - res.json({ status: "success", message: "Plugin list found!!!", data: { plugins: pluginsList } }); + res.json({status: "success", message: "Plugin list found!!!", data: {plugins: pluginsList}}); } }); @@ -47,7 +47,7 @@ module.exports = { if (err) next(err); else { - res.json({ status: "success", message: "Plugin deleted successfully!!!", data: null }); + res.json({status: "success", message: "Plugin deleted successfully!!!", data: null}); } }); }, @@ -55,6 +55,7 @@ module.exports = { create: function (req, res, next) { console.log("createPlugin"); + Plugin.create({ name: req.body.name, description: req.body.description, version: req.body.version, author: req.body.author, updated_on: req.body.updated_on, video_url: req.body.video_url, @@ -64,13 +65,13 @@ module.exports = { if (err) next(err); else - res.json({ status: "success", message: "Plugin added successfully!!!", data: null }); + res.json({status: "success", message: "Plugin added successfully!!!", data: null}); }); }, /** * Add rating to one plugin's rating list - */ + */ rate: (req, res, next) => { console.log(">>> rate plugin <<<"); @@ -81,18 +82,22 @@ module.exports = { * Reminder: ** Plugin.findOne{filter} returns one single plugin that matches the filter ** Plugin.find{filter} returns a list containing one single plugin that matches the filter - */ - Plugin.findOne({ _id: pluginId }, (err, plugin) => { + */ + Plugin.findOne({_id: pluginId}, (err, plugin) => { if (err) { next(err); } else { let newRatings = plugin.ratings; newRatings.push(note); - Plugin.updateOne({ _id: plugin._id }, { ratings: newRatings }, (error, p) => { + Plugin.updateOne({_id: plugin._id}, {ratings: newRatings}, (error, p) => { if (error) { next(error); } else { - res.json({ status: "Success", message: `Plugin ${plugin.name} has been rated ${note}`, data: null }); + res.json({ + status: "Success", + message: `Plugin ${plugin.name} has been rated ${note}`, + data: null + }); } }); } @@ -101,13 +106,13 @@ module.exports = { /** * Get plugin's score - */ + */ getScore: (req, res, next) => { console.log(">>> getScore <<<"); let pluginId = req.body.pluginId; - Plugin.findOne({ _id: pluginId }, (err, plugin) => { + Plugin.findOne({_id: pluginId}, (err, plugin) => { if (err) { next(err); } else { @@ -117,34 +122,38 @@ module.exports = { if (ratings.length > 0) { /** * Sum all the ratings into score - */ + */ score = ratings.reduce((a, b) => a + b, 0) / ratings.length; } - res.json({ status: "Success", data: { score: score } }); + res.json({status: "Success", data: {score: score}}); } }); }, /** * Get plugin's score - */ + */ comment: (req, res, next) => { console.log(">>> comment <<<"); let pluginId = req.body.pluginId; let commentText = req.body.commentText; - Plugin.findOne({ _id: pluginId }, (err, plugin) => { + Plugin.findOne({_id: pluginId}, (err, plugin) => { if (err) { next(err); } else { let comments = plugin.comments; comments.push(commentText); - Plugin.updateOne({ _id: plugin._id }, { comments: comments }, (error, p) => { + Plugin.updateOne({_id: plugin._id}, {comments: comments}, (error, p) => { if (error) { next(error); } else { - res.json({ status: "Success", message: `Comment ${commentText} has been added to plugin ${plugin.name}`, data: null }); + res.json({ + status: "Success", + message: `Comment ${commentText} has been added to plugin ${plugin.name}`, + data: null + }); } }); } @@ -153,13 +162,13 @@ module.exports = { /** * Get Zip File by pluginId - */ + */ getZipFile: (req, res, next) => { console.log(">>> commentgetZipFile <<<"); let pluginId = req.query.pluginId; - Plugin.findOne({ _id: pluginId }, (err, plugin) => { + Plugin.findOne({_id: pluginId}, (err, plugin) => { if (err) { next(err); } else { @@ -176,7 +185,7 @@ module.exports = { /** * Echapper les caractères spéciaux HTML -*/ + */ let escapeHtml = (unsafe) => { return unsafe .replace(/&/g, "&") diff --git a/back-end/server/models/Plugin.js b/back-end/server/models/Plugin.js index 2e9f38d..c2ae11d 100644 --- a/back-end/server/models/Plugin.js +++ b/back-end/server/models/Plugin.js @@ -21,7 +21,6 @@ const pluginSchema = mongoose.Schema({ author: { type: String, trim: true, - required: true, }, updated_on: { type: Date, @@ -39,7 +38,6 @@ const pluginSchema = mongoose.Schema({ zip_url: { type: String, trim: true, - required: true }, category: { type: String, diff --git a/front-end/src/Components/LoginForm/LoginForm.css b/front-end/src/Components/LoginForm/LoginForm.css index b3730b7..8a69b8f 100644 --- a/front-end/src/Components/LoginForm/LoginForm.css +++ b/front-end/src/Components/LoginForm/LoginForm.css @@ -15,6 +15,15 @@ label { font-weight: bold; } +#skip_login{ + color: royalblue; + text-decoration: underline; + font-size: 15px; + margin-top: 2%; +} #skip_login:hover{ + cursor: pointer; + } + .message { font-size: 25px; @@ -24,7 +33,7 @@ label { .input { width: 80%; - margin-bottom:5%; + margin-top:5%; padding: 2%; size: 20px; padding:0; @@ -45,6 +54,7 @@ label { justify-content: space-evenly; align-items: center; width: 80%; + margin-top: 5%; } @@ -58,6 +68,7 @@ label { background-color: #68585f ; } + .card { width: 30%; min-width: 400px; diff --git a/front-end/src/Components/LoginForm/LoginForm.js b/front-end/src/Components/LoginForm/LoginForm.js index 90e5206..63378a9 100644 --- a/front-end/src/Components/LoginForm/LoginForm.js +++ b/front-end/src/Components/LoginForm/LoginForm.js @@ -9,12 +9,14 @@ import TextField from '@material-ui/core/TextField'; import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Button from "@material-ui/core/Button"; +import {useHistory} from 'react-router-dom'; function LoginForm() { const [email, setEmail] = useState(""); const [password,setEPassword] = useState(""); const dispatch = useDispatch(); + const history = useHistory(); function register() { console.log("Unavailable feature : Register new user ! It should arrive soon !") @@ -102,6 +104,10 @@ function LoginForm() { }); } + function skipLogin() { + history.push('/home') + } + return (
@@ -111,6 +117,7 @@ function LoginForm() {
- +

skipLogin()} id={"skip_login"}>Continuer en tant que visiteur

diff --git a/front-end/src/Components/Navbar/NavBar.css b/front-end/src/Components/Navbar/NavBar.css index fdce49a..2d7bca1 100644 --- a/front-end/src/Components/Navbar/NavBar.css +++ b/front-end/src/Components/Navbar/NavBar.css @@ -26,13 +26,13 @@ #logout_button{ - background-color: #F50057; - color: whitesmoke; + background-color: whitesmoke ; } #upload_button{ - background-color: white; + background-color: #F50057; margin-right: 3%; + color: whitesmoke; } #buttons{ @@ -44,6 +44,7 @@ } #login_button{ - background-color: whitesmoke; + background-color: #F50057; + color: whitesmoke; } diff --git a/front-end/src/Components/PluginUploadForm/PluginUploadForm.css b/front-end/src/Components/PluginUploadForm/PluginUploadForm.css index 094903d..0a605b5 100644 --- a/front-end/src/Components/PluginUploadForm/PluginUploadForm.css +++ b/front-end/src/Components/PluginUploadForm/PluginUploadForm.css @@ -1,21 +1,100 @@ -.card{ +#card{ height: 100%; + padding: 2%; + width: 60%; } -#wrapper{ - height: 100%; +#plugin_upload_page{ + display: flex; + flex-direction: row; + justify-content: center; + padding-top: 4%; } -#plugin_upload_page{ +.tag{ + +} + +#zip_section{ + display: flex; + flex-direction: column; + width: 80%; +} + +#file_section{ display: flex; flex-direction: row; justify-content: center; - align-items: baseline; +} + +#check_icon{ + font-size: 100px; +} + +#validation{ + display: flex; + flex-direction: column; + align-items: center; } .textField { width: 100%; + padding: 1%; +} + +#bottom_buttons{ + display: flex; + flex-direction: row; + justify-content: space-evenly; + width: 30%; +} + + +#zip_button{ + margin-bottom: 5%; +} + +#upload_form{ + width: 70%; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; +} +#tags_input{ + width: 100%; +} + +#tags{ + width: 100%; } +#upload_form_content{ + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.upload_input{ + width:45%; + size: 12px; +} + +#desc{ + width: 100%; +} + .button { width: 100%; +} + +#select{ + width: 290px; +} + +#stepper{ + padding: 0; + margin-bottom: 5%; + width: 80%; } \ No newline at end of file diff --git a/front-end/src/Components/PluginUploadForm/PluginUploadForm.js b/front-end/src/Components/PluginUploadForm/PluginUploadForm.js deleted file mode 100644 index 4c564b6..0000000 --- a/front-end/src/Components/PluginUploadForm/PluginUploadForm.js +++ /dev/null @@ -1,332 +0,0 @@ -import React, {Component} from "react"; -import {connect, useDispatch} from 'react-redux'; -import './PluginUploadForm.css'; -import {SET_PLUGINUPLOAD_SUCCESS} from "../../store/actions/PluginUploadActions/PluginUploadSuccess"; -import {SET_PLUGINUPLOAD_PENDING} from "../../store/actions/PluginUploadActions/PluginUploadPending"; -import {SET_PLUGINUPLOAD_ERROR} from "../../store/actions/PluginUploadActions/PluginUploadError"; -import TextField from '@material-ui/core/TextField'; -import Checkbox from '@material-ui/core/Checkbox'; -import Button from '@material-ui/core/Button'; -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; -import Grid from "@material-ui/core/Grid"; -import CardHeader from '@material-ui/core/CardHeader'; -import NavBar from "../Navbar/Navbar"; - -class PluginUploadForm extends Component { - constructor(props) { - super(props); - this.state = {}; - this.state = { - name: "", - description: "", - video_url: "", - image_url: "", - version: "", - open_source: false, - category: "", - tags: "", - authorId: "", - zipFile: null, - }; - this.onSubmit = this.onSubmit.bind(this); - } - - render() { - - let { - name, - description, - video_url, - image_url, - version, - open_source, - category, - tags, - authorId - } = this.state; - - let { - pluginUploadPending, - pluginUploadSuccess, - pluginUploadError - } = this.props; - - return ( -
- -
- - - - -
- - - this.setState({name: e.target.value})} - /> - - - this.setState({description: e.target.value})} - /> - - - this.setState({video_url: e.target.value})} - /> - - - this.setState({image_url: e.target.value})} - /> - - - this.setState({version: e.target.value})} - /> - - - this.setState({category: e.target.value})} - /> - - - this.setState({tags: e.target.value})} - /> - - - this.setState({authorId: e.target.value})} - /> - - - - - -

Open Source?

- this.setState({open_source: !open_source})}/> -
- - - - -
-
-
-
-
-
- ); - } - - onSubmit(e) { - e.preventDefault(); - let { - name, - description, - video_url, - image_url, - version, - open_source, - category, - tags, - authorId, - zipFile, - } = this.state; - - console.log("___ payload to submit in request : _____"); - console.log(this.state); - - this.props.upload(name, description, video_url, image_url, version, open_source, category, tags, authorId, zipFile); - this.setState({ - name: "", - description: "", - video_url: "", - image_url: "", - version: "", - open_source: "", - category: "", - tags: "", - authorId: "", - zipFile: null, - }); - } -} - -const upload = (name, description, video_url, image_url, version, open_source, category, tags, authorId, zipFile) => { - return dispatch => { - dispatch(setPluginUploadPending(true)); - - callPluginUploadApi(name, description, video_url, image_url, version, open_source, category, tags, authorId, zipFile, (error) => { - if (error) { - dispatch(setPluginUploadPending(false)); - dispatch(setPluginUploadError(true)); - } else { - dispatch(setPluginUploadPending(false)); - dispatch(setPluginUploadSuccess()); - } - }); - } -}; - -/** - * This action indicates that the plugin upload request has been sent : pending status - * @param {*} pluginUploadPending - */ -const setPluginUploadPending = (pluginUploadPending) => { - return { - type: SET_PLUGINUPLOAD_PENDING, - pluginUploadPending - }; -} - -/** - * This action indicates that the plugin upload request has succeeded - * @param {*} pluginUploadSuccess - */ -const setPluginUploadSuccess = (pluginUploadSuccess) => { - return { - type: SET_PLUGINUPLOAD_SUCCESS, - pluginUploadSuccess - }; -} - -/** - * This action indicates that the plugin upload request has failed - * @param {*} pluginUploadError - */ -const setPluginUploadError = (pluginUploadError) => { - return { - type: SET_PLUGINUPLOAD_ERROR, - pluginUploadError - } -} - -/** - * This method is a callback after calling the upload method - * needs: name, description, version, author, updated_on, video_url, thumbnail_url, zip_url, category, open_source, tags - * @param {*} name - * @param {*} description - * @param {*} version - * @param {*} authorId - * @param {*} video_url - * @param {*} image_url - * @param {*} category - * @param {*} open_source - * @param {*} tags - * @param {*} callback - */ -const callPluginUploadApi = async (name, description, video_url, image_url, version, open_source, category, tags, authorId, zipFile, callback) => { - let Url = `http://localhost:4000/plugins/add`; - let response = await fetch(Url, { - method: 'post', - mode: 'cors', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - "name": name, - "author": authorId, - "description": description, - "video_url": video_url, - "thumbnail_url": image_url, - "version": version, - "opensource": open_source, - "category": category, - "tags": tags, - "authorId": authorId, - "zip_url": zipFile, - }) - }); - - await response.json().then((data) => { - if (data.status === "error") { - console.log("data.status = error"); - callback(true); - } else { - callback(false); - } - }); -} - -const mapStateToProps = (state) => { - return { - pluginUploadPending: state.pluginUploadPending, - pluginUploadSuccess: state.pluginUploadSuccess, - pluginUploadError: state.pluginUploadError - }; -}; - -const mapDispatchToProps = (dispatch) => { - return { - upload: (name, description, video_url, image_url, version, open_source, category, tags, authorId) => - upload(name, description, video_url, image_url, version, open_source, category, tags, authorId)(dispatch) - }; -}; - -export default connect(mapStateToProps, mapDispatchToProps)(PluginUploadForm); \ No newline at end of file diff --git a/front-end/src/Components/PluginUploadForm/PluginUploadForm.jsx b/front-end/src/Components/PluginUploadForm/PluginUploadForm.jsx new file mode 100644 index 0000000..01367f8 --- /dev/null +++ b/front-end/src/Components/PluginUploadForm/PluginUploadForm.jsx @@ -0,0 +1,365 @@ +import React, {useRef, useState} from "react"; +import './PluginUploadForm.css'; +import {SET_PLUGINUPLOAD_SUCCESS} from "../../store/actions/PluginUploadActions/PluginUploadSuccess"; +import {SET_PLUGINUPLOAD_PENDING} from "../../store/actions/PluginUploadActions/PluginUploadPending"; +import {SET_PLUGINUPLOAD_ERROR} from "../../store/actions/PluginUploadActions/PluginUploadError"; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from '@material-ui/core/CardHeader'; +import NavBar from "../Navbar/Navbar"; +import Stepper from "@material-ui/core/Stepper"; +import Step from "@material-ui/core/Step"; +import StepLabel from "@material-ui/core/StepLabel"; +import Select from "@material-ui/core/Select"; +import CheckCircleIcon from '@material-ui/icons/CheckCircle'; +import MenuItem from "@material-ui/core/MenuItem"; +import {InputLabel} from "@material-ui/core"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import Switch from "@material-ui/core/Switch"; +import Chip from "@material-ui/core/Chip"; +import {useDispatch} from "react-redux"; + +function PluginUploadForm() { + + const stepsOpenSource = ['Formulaire', 'Upload archive ZIP', 'Validation']; + const stepsClosedSource = ['Formulaire', 'Validation']; + const zip_input = useRef(); + const dispatch = useDispatch(); + const [openSource, setOpenSource] = useState(false); + const [description, setDescription] = useState(""); + const [steps, setSteps] = useState(stepsClosedSource); + const [name, setName] = useState(""); + const [imageUrl, setImageUrl] = useState(""); + const [zipFile, setZipFile] = useState(null); + const [tags, setTags] = useState([]); + const [videoUrl, setVideoUrl] = useState(""); + const [category, setCategory] = useState(""); + const [version, setVersion] = useState(""); + const [activeStep, setActiveStep] = useState(0); + let formValid = description !== "" && name !== "" && imageUrl !== "" && category !== "" && version !== ""; + + console.log("################ ", zipFile); + + function removeTag(index) { + let tmp = [...tags]; + console.log(index); + tmp.splice(index, 1); + setTags(tmp) + + } + + + function upload() { + dispatch(setPluginUploadPending(true)); + callPluginUploadApi(name, description, videoUrl, imageUrl, version, openSource, category, tags, zipFile, (error) => { + if (error) { + dispatch(setPluginUploadPending(false)); + dispatch(setPluginUploadError(true)); + } else { + dispatch(setPluginUploadPending(false)); + dispatch(setPluginUploadSuccess()); + } + }); + } + + /** + * This action indicates that the plugin upload request has been sent : pending status + * @param {*} pluginUploadPending + */ + const setPluginUploadPending = (pluginUploadPending) => { + return { + type: SET_PLUGINUPLOAD_PENDING, + pluginUploadPending + }; + } + + /** + * This action indicates that the plugin upload request has succeeded + * @param {*} pluginUploadSuccess + */ + const setPluginUploadSuccess = (pluginUploadSuccess) => { + return { + type: SET_PLUGINUPLOAD_SUCCESS, + pluginUploadSuccess + }; + } + + /** + * This action indicates that the plugin upload request has failed + * @param {*} pluginUploadError + */ + const setPluginUploadError = (pluginUploadError) => { + return { + type: SET_PLUGINUPLOAD_ERROR, + pluginUploadError + } + } + ; + /** + * This method is a callback after calling the upload method + * needs: name, description, version, author, updated_on, video_url, thumbnail_url, zip_url, category, open_source, tags + * @param {*} name + * @param {*} description + * @param {*} version + * @param {*} video_url + * @param {*} image_url + * @param {*} category + * @param {*} open_source + * @param {*} tags + * @param zipFile + * @param {*} callback + */ + const callPluginUploadApi = async (name, description, video_url, image_url, version, open_source, category, tags, zipFile, callback) => { + let Url = `http://localhost:4000/plugins/add`; + let response = await fetch(Url, { + method: 'post', + mode: 'cors', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + "name": name, + "author": "", + "description": description, + "video_url": video_url, + "thumbnail_url": image_url, + "version": version, + "opensource": open_source, + "category": category, + "tags": tags, + "authorId": "", + "zip_url": "zip_url", + }) + }); + + await response.json().then((data) => { + if (data.status === "error") { + console.log("data.status = error"); + callback(true); + } else { + callback(false); + } + }); + }; + + + function handleKeyPress(e) { + const input = e.target.value; + if (e.key === 'Enter' && input.trim() !== '') { + setTags([...tags, input]); + e.target.value = "" + } + } + + function nextStep() { + setActiveStep(activeStep + 1); + } + + function handleSwitch(e) { + const input = e.target.checked; + setOpenSource(input); + if (input === true) { + setSteps(stepsOpenSource) + } else if (input === false) { + setSteps(stepsClosedSource) + } + } + + function prevStep() { + setActiveStep(activeStep - 1) + } + + + function zipSelected(e) { + const file = e.target.files[0]; + const name = file.name; + if (name.split('.').length > 1 && name.split('.')[1] == 'zip') { + setZipFile(file) + } else { + alert("Vous devez sélectionner un fichier ZIP") + } + } + + function deleteFile() { + setZipFile(null); + zip_input.current.value = null; + console.log(zip_input.current) + } + + function submit() { + upload(); + nextStep(); + } + + return ( +
+ + + + + + + {steps.map(label => ( + + {label} + + ))} + + {activeStep === 0 && + (
+
+ setName(e.target.value)} + /> +
+
+ setVersion(e.target.value)} + /> +
+
+ setVideoUrl(e.target.value)} + name="video_url" + value={videoUrl} + label="URL Vidéo" + defaultValue="" + variant="outlined" + /> +
+
+ setImageUrl(e.target.value)} + id="outlined-required" + autoComplete={"off"} + name="image_url" + value={imageUrl} + label="URL image" + defaultValue="" + variant="outlined" + /> +
+
+ Catégorie* + +
+
+ handleSwitch(e)} checked={openSource} value={openSource}/> + } + label="Open-Source" + />
+
+ setDescription(e.currentTarget.value)} + /> +
+
+ {tags.map((tag, index) => { + return removeTag(index)} index={index} className={"tag"} + color={"primary"} variant={"outlined"} label={tag}/> + })} +
+
+ handleKeyPress(e)} + /> +
+
) + } + {activeStep !== 0 && activeStep !== steps.length - 1 && +
+
+ + {zipFile !== null &&

deleteFile()} style={{ + color: "blue", + textDecoration: "underline", + marginLeft: '2%' + }}>Supprimer

} +
+ {zipFile !== null && +

Fichier chargé! ({zipFile.name})

} +
+ } + {activeStep === steps.length - 1 && +
+

Félicitations : votre plugin a été ajouté au magasin!

+ +
+ } +
+ {activeStep !== 0 && activeStep !== steps.length - 1 && +
+ +
+ } + {activeStep === 0 && openSource && +
+ +
+ } + {((activeStep !== 0 && activeStep !== steps.length - 1) || (activeStep === 0 && !openSource)) && +
+ +
} +
+
+
+
+); +} + + +export default PluginUploadForm; \ No newline at end of file diff --git a/package.json b/package.json index f92a06e..d2d837b 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@material-ui/core": "^4.9.2", + "@sentisis/react-tags-input": "^1.0.6", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.4.0", "@testing-library/user-event": "^7.2.1",