diff --git a/package.json b/package.json index b57e05d..2799fb2 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react-markdown": "^8.0.0", "react-markdown-editor-lite": "^1.3.2", "react-mde": "^11.5.0", + "react-rating-stars-component": "^2.2.0", "react-redux": "^7.2.4", "react-router-dom": "^5.2.0", "react-scripts": "^5.0.0", diff --git a/src/App.css b/src/App.css index 9903a22..8457170 100644 --- a/src/App.css +++ b/src/App.css @@ -43,6 +43,20 @@ blockquote p { display: inline; } +.tutorial table, +th, +td { + border: 1px solid #ddd; +} + +.tutorial th { + padding-top: 12px; + padding-bottom: 12px; + text-align: left; + background-color: #4eaf47; + color: white; +} + .overlay { display: flex; flex-direction: column; diff --git a/src/App.js b/src/App.js index f4aad55..29289e5 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,6 @@ import React, { Component } from "react"; -import { BrowserRouter as Router } from "react-router-dom"; +import { Router } from "react-router-dom"; import { createBrowserHistory } from "history"; import { Provider } from "react-redux"; diff --git a/src/actions/tutorialActions.js b/src/actions/tutorialActions.js index 372697a..226bba6 100644 --- a/src/actions/tutorialActions.js +++ b/src/actions/tutorialActions.js @@ -84,6 +84,77 @@ export const getTutorials = () => (dispatch, getState) => { }); }; +export const getAllTutorials = () => (dispatch, getState) => { + axios + .get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/getAllTutorials`) + .then((res) => { + var tutorials = res.data.tutorials; + existingTutorials(tutorials, getState().tutorial.status).then( + (status) => { + dispatch({ + type: TUTORIAL_SUCCESS, + payload: status, + }); + dispatch(updateStatus(status)); + dispatch({ + type: GET_TUTORIALS, + payload: tutorials, + }); + dispatch({ type: TUTORIAL_PROGRESS }); + dispatch(returnSuccess(res.data.message, res.status)); + } + ); + }) + .catch((err) => { + if (err.response) { + dispatch( + returnErrors( + err.response.data.message, + err.response.status, + "GET_TUTORIALS_FAIL" + ) + ); + } + dispatch({ type: TUTORIAL_PROGRESS }); + }); +}; + +export const getUserTutorials = () => (dispatch, getState) => { + axios + .get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/getUserTutorials`) + .then((res) => { + var tutorials = res.data.tutorials; + existingTutorials(tutorials, getState().tutorial.status).then( + (status) => { + dispatch({ + type: TUTORIAL_SUCCESS, + payload: status, + }); + dispatch(updateStatus(status)); + dispatch({ + type: GET_TUTORIALS, + payload: tutorials, + }); + dispatch({ type: TUTORIAL_PROGRESS }); + dispatch(returnSuccess(res.data.message, res.status)); + } + ); + }) + .catch((err) => { + console.log(err); + if (err.response) { + dispatch( + returnErrors( + err.response.data.message, + err.response.status, + "GET_TUTORIALS_FAIL" + ) + ); + } + dispatch({ type: TUTORIAL_PROGRESS }); + }); +}; + export const updateStatus = (status) => (dispatch, getState) => { if (getState().auth.isAuthenticated) { // update user account in database - sync with redux store diff --git a/src/actions/tutorialBuilderActions.js b/src/actions/tutorialBuilderActions.js index 72d7f3e..cb4e798 100644 --- a/src/actions/tutorialBuilderActions.js +++ b/src/actions/tutorialBuilderActions.js @@ -4,6 +4,9 @@ import { BUILDER_CHANGE, BUILDER_ERROR, BUILDER_TITLE, + BUILDER_PUBLIC, + BUILDER_DIFFICULTY, + BUILDER_REVIEW, BUILDER_ID, BUILDER_ADD_STEP, BUILDER_DELETE_STEP, @@ -35,6 +38,30 @@ export const tutorialTitle = (title) => (dispatch) => { dispatch(changeTutorialBuilder()); }; +export const tutorialPublic = (pub) => (dispatch) => { + dispatch({ + type: BUILDER_PUBLIC, + payload: pub, + }); + dispatch(changeTutorialBuilder()); +}; + +export const tutorialDifficulty = (difficulty) => (dispatch) => { + dispatch({ + type: BUILDER_DIFFICULTY, + payload: difficulty, + }); + dispatch(changeTutorialBuilder()); +}; + +export const tutorialReview = (review) => (dispatch) => { + dispatch({ + type: BUILDER_REVIEW, + payload: review, + }); + dispatch(changeTutorialBuilder()); +}; + export const tutorialSteps = (steps) => (dispatch) => { dispatch({ type: BUILDER_ADD_STEP, @@ -320,6 +347,7 @@ export const readJSON = (json) => (dispatch, getState) => { return object; }); dispatch(tutorialTitle(json.title)); + dispatch(tutorialDifficulty(json.difficulty)); dispatch(tutorialSteps(steps)); dispatch(setSubmitError()); dispatch(progress(false)); diff --git a/src/actions/types.js b/src/actions/types.js index 6a10987..84e2580 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -21,6 +21,7 @@ export const NAME = "NAME"; export const TUTORIAL_PROGRESS = "TUTORIAL_PROGRESS"; export const GET_TUTORIAL = "GET_TUTORIAL"; export const GET_TUTORIALS = "GET_TUTORIALS"; +export const GET_USERTUTORIALS = "GET_USERTUTORIALS"; export const GET_STATUS = "GET_STATUS"; export const TUTORIAL_SUCCESS = "TUTORIAL_SUCCESS"; export const TUTORIAL_ERROR = "TUTORIAL_ERROR"; @@ -32,6 +33,9 @@ export const JSON_STRING = "JSON_STRING"; export const BUILDER_CHANGE = "BUILDER_CHANGE"; export const BUILDER_TITLE = "BUILDER_TITLE"; +export const BUILDER_DIFFICULTY = "BUILDER_DIFFICULTY"; +export const BUILDER_PUBLIC = "BUILDER_PUBLIC"; +export const BUILDER_REVIEW = "BUILDER_REVIEW"; export const BUILDER_ID = "BUILDER_ID"; export const BUILDER_ADD_STEP = "BUILDER_ADD_STEP"; export const BUILDER_DELETE_STEP = "BUILDER_DELETE_STEP"; diff --git a/src/components/Alert.js b/src/components/Alert.js index ce10c87..01bac66 100644 --- a/src/components/Alert.js +++ b/src/components/Alert.js @@ -1,34 +1,29 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; -import { withStyles } from '@material-ui/core/styles'; -import { fade } from '@material-ui/core/styles/colorManipulator'; +import { withStyles } from "@material-ui/core/styles"; +import { alpha } from "@material-ui/core/styles"; -import Typography from '@material-ui/core/Typography'; +import Typography from "@material-ui/core/Typography"; const styles = (theme) => ({ alert: { - marginBottom: '20px', + marginBottom: "20px", border: `1px solid ${theme.palette.primary.main}`, - padding: '10px 20px', - borderRadius: '4px', - background: fade(theme.palette.primary.main, 0.3), - color: 'rgb(70,70,70)' - } + padding: "10px 20px", + borderRadius: "4px", + background: alpha(theme.palette.primary.main, 0.3), + color: "rgb(70,70,70)", + }, }); - export class Alert extends Component { - - render(){ - return( + render() { + return (
- - {this.props.children} - + {this.props.children}
); } } - export default withStyles(styles, { withTheme: true })(Alert); diff --git a/src/components/Blockly/BlocklyWindow.js b/src/components/Blockly/BlocklyWindow.js index 599005b..4ff8341 100644 --- a/src/components/Blockly/BlocklyWindow.js +++ b/src/components/Blockly/BlocklyWindow.js @@ -128,7 +128,7 @@ BlocklyWindow.propTypes = { onChangeWorkspace: PropTypes.func.isRequired, clearStats: PropTypes.func.isRequired, renderer: PropTypes.string.isRequired, - sounds: PropTypes.string.isRequired, + sounds: PropTypes.bool.isRequired, language: PropTypes.string.isRequired, }; diff --git a/src/components/Blockly/msg/de/ui.js b/src/components/Blockly/msg/de/ui.js index 3d0ce92..bd2e96b 100644 --- a/src/components/Blockly/msg/de/ui.js +++ b/src/components/Blockly/msg/de/ui.js @@ -228,6 +228,12 @@ export const UI = { builder_requirements_head: "Voraussetzungen", builder_requirements_order: "Beachte, dass die Reihenfolge des Anhakens maßgebend ist.", + builder_difficulty: "Schwierigkeitsgrad", + builder_public_head: "Tutorial veröffentlichen", + builder_public_label: "Tutorial für alle Nutzer:innen veröffentlichen", + builder_review_head: "Tutorial veröffentlichen", + builder_review_text: + "Du kannst dein Tutorial direkt über den Link mit anderen Personen teilen. Wenn du dein Tutorial für alle Nutzer:innen in der Überischt veröffenltichen wollen kannst du es hier aktivieren. Ein Administrator wird dein Tutorial ansehen und anschließend freischalten.", /** * Login diff --git a/src/components/Blockly/msg/en/ui.js b/src/components/Blockly/msg/en/ui.js index 3c25df5..fc49196 100644 --- a/src/components/Blockly/msg/en/ui.js +++ b/src/components/Blockly/msg/en/ui.js @@ -222,6 +222,12 @@ export const UI = { builder_requirements_head: "Requirements.", builder_requirements_order: "Note that the order of ticking is authoritative.", + builder_difficulty: "Difficulty level", + builder_public_head: "Publish tutorial", + builder_public_label: "Publish tutorial for all users", + builder_review_head: "Publish tutorial", + builder_review_text: + "You can share your tutorial with other people directly from the link. If you want to publish your tutorial for all users in the overview you can activate it here. An administrator will view your tutorial and then activate it.", /** * Login diff --git a/src/components/CodeEditor/Compile.js b/src/components/CodeEditor/Compile.js index 2a357af..b3d477e 100644 --- a/src/components/CodeEditor/Compile.js +++ b/src/components/CodeEditor/Compile.js @@ -190,7 +190,7 @@ class Compile extends Component { className={`compileBlocks ${this.props.classes.iconButton}`} onClick={() => this.compile()} > - + ) : ( diff --git a/src/components/CodeEditor/Sidebar.js b/src/components/CodeEditor/Sidebar.js index a30bd74..072b504 100644 --- a/src/components/CodeEditor/Sidebar.js +++ b/src/components/CodeEditor/Sidebar.js @@ -1,20 +1,21 @@ import React from "react"; import Blockly from "blockly"; +import { useSelector } from "react-redux"; import Accordion from "@material-ui/core/Accordion"; import AccordionSummary from "@material-ui/core/AccordionSummary"; import AccordionDetails from "@material-ui/core/AccordionDetails"; import Typography from "@material-ui/core/Typography"; import { LibraryVersions } from "../../data/versions.js"; -//import { useMonaco } from "@monaco-editor/react"; -//import { Button } from "@material-ui/core"; +import { useMonaco } from "@monaco-editor/react"; +import { Button } from "@material-ui/core"; import Dialog from "../Dialog"; import SerialMonitor from "./SerialMonitor.js"; -//import axios from "axios"; +import axios from "axios"; const Sidebar = () => { const [alert, setAlert] = React.useState(false); //const [examples, setExamples] = React.useState([]); - + const user = useSelector((state) => state.auth.user); // useEffect(() => { // axios // .get("https://coelho.opensensemap.org/items/blocklysamples") @@ -22,16 +23,23 @@ const Sidebar = () => { // setExamples(res.data.data); // }); // }, []); - - //const monaco = useMonaco(); - // const loadCode = (code) => { - // monaco.editor.getModels()[0].setValue(code); - // }; + const monaco = useMonaco(); + const loadCode = (code) => { + monaco.editor.getModels()[0].setValue(code); + }; const toggleDialog = () => { setAlert(false); }; + const getOsemScript = (id) => { + axios + .get(`https://api.opensensemap.org/boxes/${id}/script/`) + .then((res) => { + loadCode(res.data); + }); + }; + return (
{"serial" in navigator ? ( @@ -50,7 +58,6 @@ const Sidebar = () => { ) : null} - {/* { style={{ padding: "1rem", margin: "1rem" }} variant="contained" color="primary" + key={i} onClick={() => loadCode(object.code)} > {object.name} @@ -76,6 +84,34 @@ const Sidebar = () => { */} + {user ? ( + + + Deine openSenseMap Codes + + + + {user.boxes.map((box, i) => { + return ( + + ); + })} + + + + ) : null} ({ diff --git a/src/components/TooltipViewer.js b/src/components/TooltipViewer.js index 47790e1..7d69a57 100644 --- a/src/components/TooltipViewer.js +++ b/src/components/TooltipViewer.js @@ -1,47 +1,55 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; -import withWidth from '@material-ui/core/withWidth'; +import withWidth from "@material-ui/core/withWidth"; -import { Card } from '@material-ui/core'; -import * as Blockly from 'blockly' -import CardContent from '@material-ui/core/CardContent'; +import { Card } from "@material-ui/core"; +import * as Blockly from "blockly"; +import CardContent from "@material-ui/core/CardContent"; -import Typography from '@material-ui/core/Typography'; -import ReactMarkdown from 'react-markdown'; +import Typography from "@material-ui/core/Typography"; +import ReactMarkdown from "react-markdown"; class TooltipViewer extends Component { - - render() { - return ( - + {Blockly.Msg.tooltip_viewer} - - {this.props.tooltip} - {this.props.helpurl !== '' ? {`${Blockly.Msg.tooltip_moreInformation} [${Blockly.Msg.labels_here}](${this.props.helpurl})`} : null} + + {this.props.tooltip} + - + {this.props.helpurl !== "" ? ( + {`${Blockly.Msg.tooltip_moreInformation} [${Blockly.Msg.labels_here}](${this.props.helpurl})`} + ) : null} ); - }; + } } TooltipViewer.propTypes = { tooltip: PropTypes.string.isRequired, - helpurl: PropTypes.string.isRequired + helpurl: PropTypes.string.isRequired, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ tooltip: state.workspace.code.tooltip, - helpurl: state.workspace.code.helpurl + helpurl: state.workspace.code.helpurl, }); export default connect(mapStateToProps, null)(withWidth()(TooltipViewer)); diff --git a/src/components/Tutorial/Assessment.js b/src/components/Tutorial/Assessment.js index 73d3f63..4272185 100644 --- a/src/components/Tutorial/Assessment.js +++ b/src/components/Tutorial/Assessment.js @@ -20,6 +20,8 @@ import ReactMarkdown from "react-markdown"; import { faCode } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { withStyles } from "@material-ui/core/styles"; +import remarkGfm from "remark-gfm"; +import remarkGemoji from "remark-gemoji"; const styles = (theme) => ({ codeOn: { @@ -101,7 +103,9 @@ class Assessment extends Component { }} > - {currentTask.text} + + {currentTask.text} + { if (step._id) { newTutorial.append(`steps[${i}][_id]`, step._id); @@ -284,6 +304,7 @@ class Builder extends Component { newTutorial.append(`steps[${i}][xml]`, step.xml); } }); + console.log(newTutorial); return newTutorial; } }; @@ -362,6 +383,12 @@ class Builder extends Component { (tutorial) => tutorial.creator === this.props.user.email ); } + + // } else { + // filteredTutorials = this.props.userTutorials.filter( + // (tutorial) => tutorial.creator === this.props.user.email + // ); + return (
{filteredTutorials.map((tutorial) => ( - {tutorial.title} + + {tutorial.title}{" "} + {tutorial.review && tutorial.public === false ? ( +
+ + +
+ ) : tutorial.public === false ? ( + + ) : null} +
+ /* ) : tutorial.public === false ? ( + + {tutorial.title} + + ) : ( + {tutorial.title} + )} */ ))} @@ -476,6 +520,45 @@ class Builder extends Component { label={"Titel"} error={this.props.error.title} /> +
+ + + + + + + {this.props.user.blocklyRole === "admin" ? ( + + ) : null} +
{this.props.steps.map((step, i) => ( @@ -608,6 +691,8 @@ class Builder extends Component { } Builder.propTypes = { + getAllTutorials: PropTypes.func.isRequired, + getUserTutorials: PropTypes.func.isRequired, getTutorials: PropTypes.func.isRequired, resetTutorial: PropTypes.func.isRequired, clearMessages: PropTypes.func.isRequired, @@ -620,6 +705,9 @@ Builder.propTypes = { resetTutorialBuilder: PropTypes.func.isRequired, tutorialProgress: PropTypes.func.isRequired, title: PropTypes.string.isRequired, + difficulty: PropTypes.number.isRequired, + public: PropTypes.bool.isRequired, + review: PropTypes.bool.isRequired, id: PropTypes.string.isRequired, steps: PropTypes.array.isRequired, change: PropTypes.number.isRequired, @@ -634,12 +722,16 @@ Builder.propTypes = { const mapStateToProps = (state) => ({ title: state.builder.title, + difficulty: state.builder.difficulty, + review: state.builder.review, + public: state.builder.public, id: state.builder.id, steps: state.builder.steps, change: state.builder.change, error: state.builder.error, json: state.builder.json, isProgress: state.builder.progress, + userTutorials: state.tutorial.userTutorials, tutorials: state.tutorial.tutorials, message: state.message, user: state.auth.user, @@ -654,6 +746,8 @@ export default connect(mapStateToProps, { tutorialId, resetTutorialBuilder, getTutorials, + getUserTutorials, + getAllTutorials, resetTutorial, tutorialProgress, clearMessages, diff --git a/src/components/Tutorial/Builder/Difficulty.js b/src/components/Tutorial/Builder/Difficulty.js new file mode 100644 index 0000000..18111b9 --- /dev/null +++ b/src/components/Tutorial/Builder/Difficulty.js @@ -0,0 +1,101 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { + tutorialDifficulty, + jsonString, + changeContent, + setError, + deleteError, +} from "../../../actions/tutorialBuilderActions"; + +import { withStyles } from "@material-ui/core/styles"; +import ReactStars from "react-rating-stars-component"; +import * as Blockly from "blockly"; +import FormGroup from "@material-ui/core/FormGroup"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import FormControl from "@material-ui/core/FormControl"; +import FormLabel from "@material-ui/core/FormLabel"; + +const styles = (theme) => ({ + multiline: { + padding: "18.5px 14px 18.5px 24px", + }, + errorColor: { + color: `${theme.palette.error.dark} !important`, + }, + errorColorShrink: { + color: `rgba(0, 0, 0, 0.54) !important`, + }, + errorBorder: { + borderColor: `${theme.palette.error.dark} !important`, + }, +}); + +class Difficulty extends Component { + ratingChanged = (newRating) => { + console.log(newRating); + this.handleChange(newRating); + }; + + handleChange = (e) => { + var value = e; + console.log(value); + if (this.props.property === "difficulty") { + this.props.tutorialDifficulty(value); + } else if (this.props.property === "json") { + this.props.jsonString(value); + } else { + this.props.changeContent( + value, + this.props.index, + this.props.property, + this.props.property2 + ); + } + }; + + render() { + return ( + + + {Blockly.Msg.builder_difficulty} + + + } + halfIcon={} + fullIcon={} + activeColor="#ffd700" + /> + } + label="Schwierigkeitsgrad" + labelPlacement="start" + /> + + + ); + } +} + +Difficulty.propTypes = { + tutorialDifficulty: PropTypes.func.isRequired, + jsonString: PropTypes.func.isRequired, + changeContent: PropTypes.func.isRequired, +}; + +export default connect(null, { + tutorialDifficulty, + jsonString, + changeContent, + setError, + deleteError, +})(withStyles(styles, { withTheme: true })(Difficulty)); diff --git a/src/components/Tutorial/Builder/Public.js b/src/components/Tutorial/Builder/Public.js new file mode 100644 index 0000000..6ad001b --- /dev/null +++ b/src/components/Tutorial/Builder/Public.js @@ -0,0 +1,93 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { + tutorialPublic, + jsonString, + changeContent, + setError, + deleteError, +} from "../../../actions/tutorialBuilderActions"; + +import { withStyles } from "@material-ui/core/styles"; + +import * as Blockly from "blockly"; +import Checkbox from "@material-ui/core/Checkbox"; +import FormGroup from "@material-ui/core/FormGroup"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import FormControl from "@material-ui/core/FormControl"; +import FormLabel from "@material-ui/core/FormLabel"; + +const styles = (theme) => ({ + multiline: { + padding: "18.5px 14px 18.5px 24px", + }, + errorColor: { + color: `${theme.palette.error.dark} !important`, + }, + errorColorShrink: { + color: `rgba(0, 0, 0, 0.54) !important`, + }, + errorBorder: { + borderColor: `${theme.palette.error.dark} !important`, + }, +}); + +class Public extends Component { + handleChange = (e) => { + var value = e.target.checked; + console.log(value); + if (this.props.property === "public") { + this.props.tutorialPublic(value); + } else if (this.props.property === "json") { + this.props.jsonString(value); + } else { + this.props.changeContent( + value, + this.props.index, + this.props.property, + this.props.property2 + ); + } + }; + + render() { + return ( + + + {Blockly.Msg.builder_public_head} + + + + } + label={Blockly.Msg.builder_public_label} + labelPlacement="start" + /> + + + ); + } +} + +Public.propTypes = { + tutorialPublic: PropTypes.func.isRequired, + jsonString: PropTypes.func.isRequired, + changeContent: PropTypes.func.isRequired, +}; + +export default connect(null, { + tutorialPublic, + jsonString, + changeContent, + setError, + deleteError, +})(withStyles(styles, { withTheme: true })(Public)); diff --git a/src/components/Tutorial/Builder/Review.js b/src/components/Tutorial/Builder/Review.js new file mode 100644 index 0000000..add30e0 --- /dev/null +++ b/src/components/Tutorial/Builder/Review.js @@ -0,0 +1,93 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { + tutorialReview, + jsonString, + changeContent, + setError, + deleteError, +} from "../../../actions/tutorialBuilderActions"; + +import { withStyles } from "@material-ui/core/styles"; + +import * as Blockly from "blockly"; +import Checkbox from "@material-ui/core/Checkbox"; +import FormGroup from "@material-ui/core/FormGroup"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import FormControl from "@material-ui/core/FormControl"; +import FormLabel from "@material-ui/core/FormLabel"; + +const styles = (theme) => ({ + multiline: { + padding: "18.5px 14px 18.5px 24px", + }, + errorColor: { + color: `${theme.palette.error.dark} !important`, + }, + errorColorShrink: { + color: `rgba(0, 0, 0, 0.54) !important`, + }, + errorBorder: { + borderColor: `${theme.palette.error.dark} !important`, + }, +}); + +class Review extends Component { + handleChange = (e) => { + var value = e.target.checked; + if (this.props.property === "review") { + this.props.tutorialReview(value); + } else if (this.props.property === "json") { + this.props.jsonString(value); + } else { + this.props.changeContent( + value, + this.props.index, + this.props.property, + this.props.property2 + ); + } + }; + + render() { + return ( + + + {Blockly.Msg.builder_review_head} + + {Blockly.Msg.builder_review_text} + + + } + label="Ich möchte mein Tutorial öffentlich machen" + labelPlacement="start" + /> + + + ); + } +} + +Review.propTypes = { + tutorialReview: PropTypes.func.isRequired, + jsonString: PropTypes.func.isRequired, + changeContent: PropTypes.func.isRequired, +}; + +export default connect(null, { + tutorialReview, + jsonString, + changeContent, + setError, + deleteError, +})(withStyles(styles, { withTheme: true })(Review)); diff --git a/src/components/Tutorial/Requirement.js b/src/components/Tutorial/Requirement.js index 87784cc..42e3165 100644 --- a/src/components/Tutorial/Requirement.js +++ b/src/components/Tutorial/Requirement.js @@ -1,124 +1,233 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; -import clsx from 'clsx'; -import { withRouter, Link } from 'react-router-dom'; - -import { fade } from '@material-ui/core/styles/colorManipulator'; -import { withStyles } from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import List from '@material-ui/core/List'; -import Tooltip from '@material-ui/core/Tooltip'; +import clsx from "clsx"; +import { withRouter, Link } from "react-router-dom"; +import { alpha } from "@material-ui/core/styles"; +import { withStyles } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import List from "@material-ui/core/List"; +import Tooltip from "@material-ui/core/Tooltip"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons"; -import * as Blockly from 'blockly' +import * as Blockly from "blockly"; -const styles = theme => ({ +const styles = (theme) => ({ outerDiv: { - width: '50px', - height: '50px', - position: 'absolute', - color: fade(theme.palette.secondary.main, 0.6) + width: "50px", + height: "50px", + position: "absolute", + color: alpha(theme.palette.secondary.main, 0.6), }, outerDivError: { - stroke: fade(theme.palette.error.dark, 0.6), - color: fade(theme.palette.error.dark, 0.6) + stroke: alpha(theme.palette.error.dark, 0.6), + color: alpha(theme.palette.error.dark, 0.6), }, outerDivSuccess: { - stroke: fade(theme.palette.primary.main, 0.6), - color: fade(theme.palette.primary.main, 0.6) + stroke: alpha(theme.palette.primary.main, 0.6), + color: alpha(theme.palette.primary.main, 0.6), }, outerDivOther: { - stroke: fade(theme.palette.secondary.main, 0.6) + stroke: alpha(theme.palette.secondary.main, 0.6), }, innerDiv: { - width: 'inherit', - height: 'inherit', - display: 'table-cell', - verticalAlign: 'middle', - textAlign: 'center' + width: "inherit", + height: "inherit", + display: "table-cell", + verticalAlign: "middle", + textAlign: "center", }, link: { color: theme.palette.text.primary, - position: 'relative', - height: '50px', - display: 'flex', - margin: '5px 0 5px 10px', - textDecoration: 'none' + position: "relative", + height: "50px", + display: "flex", + margin: "5px 0 5px 10px", + textDecoration: "none", }, hoverLink: { - '&:hover': { - background: fade(theme.palette.secondary.main, 0.5), - borderRadius: '0 25px 25px 0 ' - } - } + "&:hover": { + background: alpha(theme.palette.secondary.main, 0.5), + borderRadius: "0 25px 25px 0 ", + }, + }, }); - class Requirement extends Component { - render() { var requirements = this.props.requirements; - var tutorialIds = requirements.map(requirement => requirement._id); + var tutorialIds = requirements.map((requirement) => requirement._id); return ( -
+
{Blockly.Msg.tutorials_requirements} {tutorialIds.map((tutorialId, i) => { - var title = requirements[i].title - var status = this.props.status.filter(status => status._id === tutorialId)[0]; + var title = requirements[i].title; + var status = this.props.status.filter( + (status) => status._id === tutorialId + )[0]; var tasks = status.tasks; - var error = status.tasks.filter(task => task.type === 'error').length > 0; - var success = status.tasks.filter(task => task.type === 'success').length / tasks.length - var tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other'; + var error = + status.tasks.filter((task) => task.type === "error").length > 0; + var success = + status.tasks.filter((task) => task.type === "success").length / + tasks.length; + var tutorialStatus = + success === 1 ? "Success" : error ? "Error" : "Other"; return ( - - + +
-
- - {error || success === 1 ? - - : } - {success < 1 && !error ? - - - : null} +
+ + {error || success === 1 ? ( + + ) : ( + + )} + {success < 1 && !error ? ( + + ) : null}
-
+
- {error || success === 1 ? - - : 0 ? this.props.classes.outerDivSuccess : {}}>{Math.round(success * 100)}% - } + {error || success === 1 ? ( + + ) : ( + 0 + ? this.props.classes.outerDivSuccess + : {} + } + > + {Math.round(success * 100)}% + + )}
-
- {title} +
+ + {title} +
- ) - } - )} + ); + })}
); - }; + } } Requirement.propTypes = { status: PropTypes.array.isRequired, - change: PropTypes.number.isRequired + change: PropTypes.number.isRequired, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ change: state.tutorial.change, status: state.tutorial.status, }); -export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(withRouter(Requirement))); +export default connect( + mapStateToProps, + null +)(withStyles(styles, { withTheme: true })(withRouter(Requirement))); diff --git a/src/components/Tutorial/SolutionCheck.js b/src/components/Tutorial/SolutionCheck.js index 9c3e54d..0f1ddeb 100644 --- a/src/components/Tutorial/SolutionCheck.js +++ b/src/components/Tutorial/SolutionCheck.js @@ -63,7 +63,7 @@ class SolutionCheck extends Component { style={{ width: "40px", height: "40px", marginRight: "5px" }} onClick={() => this.check()} > - + diff --git a/src/components/Tutorial/StepperHorizontal.js b/src/components/Tutorial/StepperHorizontal.js index 6a64f39..ce4083f 100644 --- a/src/components/Tutorial/StepperHorizontal.js +++ b/src/components/Tutorial/StepperHorizontal.js @@ -7,8 +7,8 @@ import { withRouter } from "react-router-dom"; import clsx from "clsx"; // import tutorials from '../../data/tutorials'; +import { alpha } from "@material-ui/core/styles"; -import { fade } from "@material-ui/core/styles/colorManipulator"; import { withStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import Tooltip from "@material-ui/core/Tooltip"; @@ -28,13 +28,13 @@ const styles = (theme) => ({ justifyContent: "space-between", }, stepperSuccess: { - backgroundColor: fade(theme.palette.primary.main, 0.6), + backgroundColor: alpha(theme.palette.primary.main, 0.6), }, stepperError: { - backgroundColor: fade(theme.palette.error.dark, 0.6), + backgroundColor: alpha(theme.palette.error.dark, 0.6), }, stepperOther: { - backgroundColor: fade(theme.palette.secondary.main, 0.6), + backgroundColor: alpha(theme.palette.secondary.main, 0.6), }, color: { backgroundColor: "transparent ", diff --git a/src/components/Tutorial/StepperVertical.js b/src/components/Tutorial/StepperVertical.js index 5735c3f..b1fa614 100644 --- a/src/components/Tutorial/StepperVertical.js +++ b/src/components/Tutorial/StepperVertical.js @@ -1,36 +1,35 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { tutorialStep } from '../../actions/tutorialActions'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { tutorialStep } from "../../actions/tutorialActions"; -import { withRouter } from 'react-router-dom'; +import { withRouter } from "react-router-dom"; -import clsx from 'clsx'; - -import { fade } from '@material-ui/core/styles/colorManipulator'; -import { withStyles } from '@material-ui/core/styles'; -import Stepper from '@material-ui/core/Stepper'; -import Step from '@material-ui/core/Step'; -import StepLabel from '@material-ui/core/StepLabel'; -import Tooltip from '@material-ui/core/Tooltip'; +import clsx from "clsx"; +import { alpha } from "@material-ui/core/styles"; +import { withStyles } from "@material-ui/core/styles"; +import Stepper from "@material-ui/core/Stepper"; +import Step from "@material-ui/core/Step"; +import StepLabel from "@material-ui/core/StepLabel"; +import Tooltip from "@material-ui/core/Tooltip"; const styles = (theme) => ({ verticalStepper: { padding: 0, - width: '30px', + width: "30px", }, stepIcon: { borderStyle: `solid`, // borderWidth: '2px', - borderRadius: '50%', + borderRadius: "50%", borderColor: theme.palette.secondary.main, - width: '12px', - height: '12px', - margin: '0 auto', + width: "12px", + height: "12px", + margin: "0 auto", }, stepIconLarge: { - width: '24px', - height: '24px' + width: "24px", + height: "24px", }, stepIconLargeSuccess: { borderColor: theme.palette.primary.main, @@ -39,28 +38,27 @@ const styles = (theme) => ({ borderColor: theme.palette.error.dark, }, stepIconActiveOther: { - backgroundColor: theme.palette.secondary.main + backgroundColor: theme.palette.secondary.main, }, stepIconActiveSuccess: { - backgroundColor: fade(theme.palette.primary.main, 0.6) + backgroundColor: alpha(theme.palette.primary.main, 0.6), }, stepIconActiveError: { - backgroundColor: fade(theme.palette.error.dark, 0.6) + backgroundColor: alpha(theme.palette.error.dark, 0.6), }, connector: { - height: '10px', + height: "10px", borderLeft: `2px solid black`, - margin: 'auto' - } + margin: "auto", + }, }); class StepperVertical extends Component { - - componentDidMount(){ + componentDidMount() { this.props.tutorialStep(0); } - componentDidUpdate(props){ + componentDidUpdate(props) { if (props.tutorial._id !== this.props.match.params.tutorialId) { this.props.tutorialStep(0); } @@ -69,60 +67,103 @@ class StepperVertical extends Component { render() { var steps = this.props.steps; var activeStep = this.props.activeStep; - var tutorialStatus = this.props.status.filter(status => status._id === this.props.tutorial._id)[0]; + var tutorialStatus = this.props.status.filter( + (status) => status._id === this.props.tutorial._id + )[0]; return ( -
+
} - classes={{root: this.props.classes.verticalStepper}} + classes={{ root: this.props.classes.verticalStepper }} > {steps.map((step, i) => { - var tasksIndex = tutorialStatus.tasks.findIndex(task => task._id === step._id); - var taskType = tasksIndex > -1 ? tutorialStatus.tasks[tasksIndex].type : null; - var taskStatus = taskType === 'success' ? 'Success' : taskType === 'error' ? 'Error' : 'Other'; + var tasksIndex = tutorialStatus.tasks.findIndex( + (task) => task._id === step._id + ); + var taskType = + tasksIndex > -1 ? tutorialStatus.tasks[tasksIndex].type : null; + var taskStatus = + taskType === "success" + ? "Success" + : taskType === "error" + ? "Error" + : "Other"; return ( - -
{ this.props.tutorialStep(i)}}> + +
{ + this.props.tutorialStep(i); + } + } + > - + >
- )})} + ); + })}
); - }; + } } - StepperVertical.propTypes = { status: PropTypes.array.isRequired, change: PropTypes.number.isRequired, activeStep: PropTypes.number.isRequired, tutorialStep: PropTypes.func.isRequired, - tutorial: PropTypes.object.isRequired + tutorial: PropTypes.object.isRequired, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ change: state.tutorial.change, status: state.tutorial.status, activeStep: state.tutorial.activeStep, - tutorial: state.tutorial.tutorials[0] + tutorial: state.tutorial.tutorials[0], }); -export default connect(mapStateToProps, { tutorialStep })(withRouter(withStyles(styles, {withTheme: true})(StepperVertical))); +export default connect(mapStateToProps, { tutorialStep })( + withRouter(withStyles(styles, { withTheme: true })(StepperVertical)) +); diff --git a/src/components/Tutorial/TutorialHome.js b/src/components/Tutorial/TutorialHome.js index ee91c30..2a73f3b 100644 --- a/src/components/Tutorial/TutorialHome.js +++ b/src/components/Tutorial/TutorialHome.js @@ -3,9 +3,13 @@ import PropTypes from "prop-types"; import { connect } from "react-redux"; import { getTutorials, + getAllTutorials, + getUserTutorials, resetTutorial, tutorialProgress, } from "../../actions/tutorialActions"; +import { progress } from "../../actions/tutorialBuilderActions"; + import { clearMessages } from "../../actions/messageActions"; import clsx from "clsx"; @@ -13,16 +17,26 @@ import clsx from "clsx"; import Breadcrumbs from "../Breadcrumbs"; import { Link } from "react-router-dom"; - -import { fade } from "@material-ui/core/styles/colorManipulator"; +import { alpha } from "@material-ui/core/styles"; import { withStyles } from "@material-ui/core/styles"; import Grid from "@material-ui/core/Grid"; import Paper from "@material-ui/core/Paper"; import Typography from "@material-ui/core/Typography"; -import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons"; +import { + faCheck, + faTimes, + faShareAlt, + faEye, + faUserCheck, +} from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as Blockly from "blockly"; +import ReactStars from "react-rating-stars-component"; +import Tooltip from "@material-ui/core/Tooltip"; +import IconButton from "@material-ui/core/IconButton"; +import Snackbar from "../Snackbar"; +import Divider from "@material-ui/core/Divider"; const styles = (theme) => ({ outerDiv: { @@ -31,18 +45,18 @@ const styles = (theme) => ({ bottom: "-30px", width: "160px", height: "160px", - color: fade(theme.palette.secondary.main, 0.6), + color: alpha(theme.palette.secondary.main, 0.6), }, outerDivError: { - stroke: fade(theme.palette.error.dark, 0.6), - color: fade(theme.palette.error.dark, 0.6), + stroke: alpha(theme.palette.error.dark, 0.6), + color: alpha(theme.palette.error.dark, 0.6), }, outerDivSuccess: { - stroke: fade(theme.palette.primary.main, 0.6), - color: fade(theme.palette.primary.main, 0.6), + stroke: alpha(theme.palette.primary.main, 0.6), + color: alpha(theme.palette.primary.main, 0.6), }, outerDivOther: { - stroke: fade(theme.palette.secondary.main, 0.6), + stroke: alpha(theme.palette.secondary.main, 0.6), }, innerDiv: { width: "inherit", @@ -51,26 +65,89 @@ const styles = (theme) => ({ verticalAlign: "middle", textAlign: "center", }, + button: { + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + width: "40px", + height: "40px", + "&:hover": { + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + }, + }, + link: { + color: theme.palette.primary.main, + textDecoration: "none", + "&:hover": { + color: theme.palette.primary.main, + textDecoration: "underline", + }, + }, }); class TutorialHome extends Component { + constructor(props) { + super(props); + this.state = { + userTutorials: [], + tutorials: [], + snackbar: false, + }; + } + componentDidMount() { this.props.tutorialProgress(); // retrieve tutorials only if a potential user is loaded - authentication // is finished (success or failed) - if (!this.props.progress) { - this.props.getTutorials(); + // if (!this.props.progress) { + // if (this.props.user) { + // if (this.props.user.blocklyRole === "admin") { + // this.props.getAllTutorials(); + // } + // if (this.props.user.blocklyRole === "creator") { + // this.props.getUserTutorials(); + // this.props.getTutorials(); + // console.log("get user tutorials"); + // console.log(this.props.userTutorials); + // } + // } else { + // this.props.getTutorials(); + // } + // } + if (!this.props.authProgress) { + if (this.props.user) { + if (this.props.user.role === "admin") { + this.props.getAllTutorials(); + } else { + this.props.getUserTutorials(); + //this.props.getTutorials(); + } + } else { + this.props.getTutorials(); + } } } componentDidUpdate(props, state) { - if (props.progress !== this.props.progress && !this.props.progress) { - // authentication is completed - this.props.getTutorials(); - } + if ( + props.authProgress !== this.props.authProgress && + !this.props.authProgress + ) + if (this.props.user) { + if (this.props.user.role === "admin") { + // authentication is completed + this.props.getAllTutorials(); + } else { + this.props.getUserTutorials(); + } + } else { + this.props.getTutorials(); + } + if (this.props.message.id === "GET_TUTORIALS_FAIL") { alert(this.props.message.msg); } + console.log(this.props.user); } componentWillUnmount() { @@ -81,13 +158,25 @@ class TutorialHome extends Component { } render() { + var userTutorials = []; + const publicTutorials = this.props.tutorials.filter( + (tutorial) => tutorial.public === true + ); + if (this.props.user && this.props.user.blocklyRole === "admin") { + userTutorials = this.props.tutorials; + } + if (this.props.user && this.props.user.blocklyRole === "creator") { + userTutorials = this.props.tutorials.filter( + (tutorial) => tutorial.creator === this.props.user.email + ); + } return this.props.isLoading ? null : (
-

{Blockly.Msg.tutorials_home_head}

+

Alle Tutorials

- {this.props.tutorials.map((tutorial, i) => { + {publicTutorials.map((tutorial, i) => { var status = this.props.status.filter( (status) => status._id === tutorial._id )[0]; @@ -99,6 +188,12 @@ class TutorialHome extends Component { tasks.length; var tutorialStatus = success === 1 ? "Success" : error ? "Error" : "Other"; + const firstExample = { + size: 30, + value: tutorial.difficulty, + edit: false, + isHalf: true, + }; return ( {tutorial.title} +
+ {this.props.user ? ( +
+

User Tutorials

+ + {userTutorials.map((tutorial, i) => { + var status = this.props.status.filter( + (status) => status._id === tutorial._id + )[0]; + var tasks = status.tasks; + var error = + status.tasks.filter((task) => task.type === "error").length > + 0; + var success = + status.tasks.filter((task) => task.type === "success") + .length / tasks.length; + var tutorialStatus = + success === 1 ? "Success" : error ? "Error" : "Other"; + const firstExample = { + size: 30, + value: tutorial.difficulty, + edit: false, + isHalf: true, + }; + return ( + + + + {tutorial.title} + + + +

+ Creator:{tutorial.creator}
+

+ + { + navigator.clipboard.writeText( + `${window.location.origin}/tutorial/${tutorial._id}` + ); + this.setState({ + snackbar: true, + key: Date.now(), + message: + Blockly.Msg.messages_copylink_success, + type: "success", + }); + }} + > + + + + + + + + + {tutorial.review ? ( + + + + + + ) : null} + +
+

+
+
+ ); + })} +
+
+ ) : null}
); } @@ -223,6 +432,8 @@ class TutorialHome extends Component { TutorialHome.propTypes = { getTutorials: PropTypes.func.isRequired, + getAllTutorials: PropTypes.func.isRequired, + getUserTutorials: PropTypes.func.isRequired, resetTutorial: PropTypes.func.isRequired, tutorialProgress: PropTypes.func.isRequired, clearMessages: PropTypes.func.isRequired, @@ -232,19 +443,27 @@ TutorialHome.propTypes = { isLoading: PropTypes.bool.isRequired, message: PropTypes.object.isRequired, progress: PropTypes.bool.isRequired, + user: PropTypes.object.isRequired, + authProgress: PropTypes.bool.isRequired, }; const mapStateToProps = (state) => ({ change: state.tutorial.change, status: state.tutorial.status, tutorials: state.tutorial.tutorials, + userTutorials: state.tutorial.userTutorials, isLoading: state.tutorial.progress, message: state.message, progress: state.auth.progress, + user: state.auth.user, + authProgress: state.auth.progress, }); export default connect(mapStateToProps, { getTutorials, + progress, + getUserTutorials, + getAllTutorials, resetTutorial, clearMessages, tutorialProgress, diff --git a/src/components/Workspace/Compile.js b/src/components/Workspace/Compile.js index 2a357af..b3d477e 100644 --- a/src/components/Workspace/Compile.js +++ b/src/components/Workspace/Compile.js @@ -190,7 +190,7 @@ class Compile extends Component { className={`compileBlocks ${this.props.classes.iconButton}`} onClick={() => this.compile()} > - + ) : ( diff --git a/src/components/Workspace/CopyCode.js b/src/components/Workspace/CopyCode.js index b701ded..02d255d 100644 --- a/src/components/Workspace/CopyCode.js +++ b/src/components/Workspace/CopyCode.js @@ -69,7 +69,7 @@ class CopyCode extends Component { className={`copyCode ${this.props.classes.iconButton}`} onClick={() => this.copyCode()} > - + ) : ( diff --git a/src/components/Workspace/ShareProject.js b/src/components/Workspace/ShareProject.js index 8b962cc..e86d817 100644 --- a/src/components/Workspace/ShareProject.js +++ b/src/components/Workspace/ShareProject.js @@ -1,72 +1,87 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { shareProject } from '../../actions/projectActions'; -import { clearMessages } from '../../actions/messageActions'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { shareProject } from "../../actions/projectActions"; +import { clearMessages } from "../../actions/messageActions"; -import moment from 'moment'; +import moment from "moment"; -import Dialog from '../Dialog'; -import Snackbar from '../Snackbar'; +import Dialog from "../Dialog"; +import Snackbar from "../Snackbar"; -import { Link } from 'react-router-dom'; +import { Link } from "react-router-dom"; -import { withStyles } from '@material-ui/core/styles'; -import IconButton from '@material-ui/core/IconButton'; -import Tooltip from '@material-ui/core/Tooltip'; -import Typography from '@material-ui/core/Typography'; +import { withStyles } from "@material-ui/core/styles"; +import IconButton from "@material-ui/core/IconButton"; +import Tooltip from "@material-ui/core/Tooltip"; +import Typography from "@material-ui/core/Typography"; import { faShareAlt, faCopy } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import * as Blockly from 'blockly/core'; +import * as Blockly from "blockly/core"; const styles = (theme) => ({ button: { backgroundColor: theme.palette.primary.main, color: theme.palette.primary.contrastText, - width: '40px', - height: '40px', - '&:hover': { + width: "40px", + height: "40px", + "&:hover": { backgroundColor: theme.palette.primary.main, color: theme.palette.primary.contrastText, - } + }, }, link: { color: theme.palette.primary.main, - textDecoration: 'none', - '&:hover': { + textDecoration: "none", + "&:hover": { color: theme.palette.primary.main, - textDecoration: 'underline' - } - } + textDecoration: "underline", + }, + }, }); - class WorkspaceFunc extends Component { - constructor(props) { super(props); this.inputRef = React.createRef(); this.state = { snackbar: false, - type: '', - key: '', - message: '', - title: '', - content: '', + type: "", + key: "", + message: "", + title: "", + content: "", open: false, - id: '', + id: "", }; } componentDidUpdate(props) { if (this.props.message !== props.message) { - if (this.props.message.id === 'SHARE_SUCCESS' && (!this.props.multiple || this.props.message.status === this.props.project._id)) { - this.setState({ share: true, open: true, title: Blockly.Msg.messages_SHARE_SUCCESS, id: this.props.message.status }); - } - else if (this.props.message.id === 'SHARE_FAIL' && (!this.props.multiple || this.props.message.status === this.props.project._id)) { - this.setState({ snackbar: true, key: Date.now(), message: Blockly.Msg.messages_SHARE_FAIL, type: 'error' }); + if ( + this.props.message.id === "SHARE_SUCCESS" && + (!this.props.multiple || + this.props.message.status === this.props.project._id) + ) { + this.setState({ + share: true, + open: true, + title: Blockly.Msg.messages_SHARE_SUCCESS, + id: this.props.message.status, + }); + } else if ( + this.props.message.id === "SHARE_FAIL" && + (!this.props.multiple || + this.props.message.status === this.props.project._id) + ) { + this.setState({ + snackbar: true, + key: Date.now(), + message: Blockly.Msg.messages_SHARE_FAIL, + type: "error", + }); window.scrollTo(0, 0); } } @@ -77,18 +92,25 @@ class WorkspaceFunc extends Component { } toggleDialog = () => { - this.setState({ open: !this.state, title: '', content: '' }); - } + this.setState({ open: !this.state, title: "", content: "" }); + }; shareBlocks = () => { - if (this.props.projectType === 'project' && this.props.project.shared) { + if (this.props.projectType === "project" && this.props.project.shared) { // project is already shared - this.setState({ open: true, title: Blockly.Msg.messages_SHARE_SUCCESS, id: this.props.project._id }); + this.setState({ + open: true, + title: Blockly.Msg.messages_SHARE_SUCCESS, + id: this.props.project._id, + }); + } else { + this.props.shareProject( + this.props.name || this.props.project.title, + this.props.projectType, + this.props.project ? this.props.project._id : undefined + ); } - else { - this.props.shareProject(this.props.name || this.props.project.title, this.props.projectType, this.props.project ? this.props.project._id : undefined); - } - } + }; render() { return ( @@ -116,43 +138,89 @@ class WorkspaceFunc extends Component { onClick={this.toggleDialog} button={Blockly.Msg.button_close} > -
- Über den folgenden Link kannst du dein Programm teilen: - this.toggleDialog()} className={this.props.classes.link}>{`${window.location.origin}/share/${this.state.id}`} - +
+ + Über den folgenden Link kannst du dein Programm teilen: + + this.toggleDialog()} + className={this.props.classes.link} + >{`${window.location.origin}/share/${this.state.id}`} + { - navigator.clipboard.writeText(`${window.location.origin}/share/${this.state.id}`); - this.setState({ snackbar: true, key: Date.now(), message: Blockly.Msg.messages_copylink_success, type: 'success' }); + navigator.clipboard.writeText( + `${window.location.origin}/share/${this.state.id}` + ); + this.setState({ + snackbar: true, + key: Date.now(), + message: Blockly.Msg.messages_copylink_success, + type: "success", + }); }} > - {this.props.project && this.props.project.shared && this.props.message.id !== 'SHARE_SUCCESS' ? - {`Das Projekt wurde bereits geteilt. Der Link ist noch mindestens ${moment(this.props.project.shared).diff(moment().utc(), 'days') === 0 ? - moment(this.props.project.shared).diff(moment().utc(), 'hours') === 0 ? - `${moment(this.props.project.shared).diff(moment().utc(), 'minutes')} Minuten` - : `${moment(this.props.project.shared).diff(moment().utc(), 'hours')} Stunden` - : `${moment(this.props.project.shared).diff(moment().utc(), 'days')} Tage`} gültig.`} - : {`Der Link ist nun ${process.env.REACT_APP_SHARE_LINK_EXPIRES} Tage gültig.`}} + {this.props.project && + this.props.project.shared && + this.props.message.id !== "SHARE_SUCCESS" ? ( + {`Das Projekt wurde bereits geteilt. Der Link ist noch mindestens ${ + moment(this.props.project.shared).diff( + moment().utc(), + "days" + ) === 0 + ? moment(this.props.project.shared).diff( + moment().utc(), + "hours" + ) === 0 + ? `${moment(this.props.project.shared).diff( + moment().utc(), + "minutes" + )} Minuten` + : `${moment(this.props.project.shared).diff( + moment().utc(), + "hours" + )} Stunden` + : `${moment(this.props.project.shared).diff( + moment().utc(), + "days" + )} Tage` + } gültig.`} + ) : ( + {`Der Link ist nun ${process.env.REACT_APP_SHARE_LINK_EXPIRES} Tage gültig.`} + )}
); - }; + } } WorkspaceFunc.propTypes = { shareProject: PropTypes.func.isRequired, clearMessages: PropTypes.func.isRequired, name: PropTypes.string.isRequired, - message: PropTypes.object.isRequired + message: PropTypes.object.isRequired, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ name: state.workspace.name, - message: state.message + message: state.message, }); -export default connect(mapStateToProps, { shareProject, clearMessages })(withStyles(styles, { withTheme: true })(WorkspaceFunc)); +export default connect(mapStateToProps, { shareProject, clearMessages })( + withStyles(styles, { withTheme: true })(WorkspaceFunc) +); diff --git a/src/data/hardware.json b/src/data/hardware.json index de7aefc..6149f46 100644 --- a/src/data/hardware.json +++ b/src/data/hardware.json @@ -3,7 +3,8 @@ "id": "senseboxmcu", "name": "senseBox MCU", "src": "senseboxmcu.png", - "url": "https://docs.sensebox.de/hardware/allgemein-sensebox-mcu/" + "url": "https://docs.sensebox.de/hardware/allgemein-sensebox-mcu/", + "description": "test" }, { "id": "breadboard", diff --git a/src/reducers/generalReducer.js b/src/reducers/generalReducer.js index 1d1acfe..7cde579 100644 --- a/src/reducers/generalReducer.js +++ b/src/reducers/generalReducer.js @@ -25,7 +25,7 @@ const initialSounds = () => { if (window.localStorage.getItem("sounds")) { return window.localStorage.getItem("sounds"); } else { - return "off"; + return false; } }; diff --git a/src/reducers/tutorialBuilderReducer.js b/src/reducers/tutorialBuilderReducer.js index 1eb80f4..cb3a9dc 100644 --- a/src/reducers/tutorialBuilderReducer.js +++ b/src/reducers/tutorialBuilderReducer.js @@ -10,6 +10,9 @@ import { BUILDER_CHANGE_STEP, BUILDER_CHANGE_ORDER, BUILDER_DELETE_PROPERTY, + BUILDER_DIFFICULTY, + BUILDER_PUBLIC, + BUILDER_REVIEW, } from "../actions/types"; const initialState = { @@ -17,6 +20,9 @@ const initialState = { progress: false, json: "", title: "", + difficulty: 0, + public: false, + review: false, id: "", steps: [ { @@ -45,6 +51,21 @@ export default function foo(state = initialState, action) { ...state, title: action.payload, }; + case BUILDER_PUBLIC: + return { + ...state, + public: action.payload, + }; + case BUILDER_DIFFICULTY: + return { + ...state, + difficulty: action.payload, + }; + case BUILDER_REVIEW: + return { + ...state, + review: action.payload, + }; case BUILDER_ID: return { ...state, diff --git a/src/reducers/tutorialReducer.js b/src/reducers/tutorialReducer.js index a44a31f..b60f7f7 100644 --- a/src/reducers/tutorialReducer.js +++ b/src/reducers/tutorialReducer.js @@ -1,24 +1,23 @@ -import { TUTORIAL_PROGRESS, GET_TUTORIAL, GET_TUTORIALS, GET_STATUS, TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE, TUTORIAL_XML, TUTORIAL_STEP } from '../actions/types'; - - -// -// const initialStatus = () => { -// if(store.getState().auth.user){ -// return store.getState().auth.user.status || [] -// } -// else if (window.localStorage.getItem('status')) { -// var status = JSON.parse(window.localStorage.getItem('status')); -// return status; -// } -// return []; -// }; +import { + TUTORIAL_PROGRESS, + GET_TUTORIAL, + GET_TUTORIALS, + GET_USERTUTORIALS, + GET_STATUS, + TUTORIAL_SUCCESS, + TUTORIAL_ERROR, + TUTORIAL_CHANGE, + TUTORIAL_XML, + TUTORIAL_STEP, +} from "../actions/types"; const initialState = { status: [], activeStep: 0, change: 0, tutorials: [], - progress: false + userTutorials: [], + progress: false, }; export default function foo(state = initialState, action) { @@ -26,18 +25,23 @@ export default function foo(state = initialState, action) { case TUTORIAL_PROGRESS: return { ...state, - progress: !state.progress - } + progress: !state.progress, + }; case GET_TUTORIALS: return { ...state, - tutorials: action.payload + tutorials: action.payload, + }; + case GET_USERTUTORIALS: + return { + ...state, + tutorials: action.payload, }; case GET_TUTORIAL: return { ...state, - tutorials: [action.payload] - } + tutorials: [action.payload], + }; case TUTORIAL_SUCCESS: case TUTORIAL_ERROR: case TUTORIAL_XML: @@ -46,23 +50,23 @@ export default function foo(state = initialState, action) { // and 'TUTORIAL_XML' the function 'updateStatus' is called return { ...state, - status: action.payload + status: action.payload, }; case GET_STATUS: return { ...state, - status: action.payload + status: action.payload, }; case TUTORIAL_CHANGE: return { ...state, - change: state.change += 1 - } + change: (state.change += 1), + }; case TUTORIAL_STEP: return { ...state, - activeStep: action.payload - } + activeStep: action.payload, + }; default: return state; } diff --git a/src/store.js b/src/store.js index bf6460e..3203857 100644 --- a/src/store.js +++ b/src/store.js @@ -1,6 +1,6 @@ -import { createStore, applyMiddleware, compose } from 'redux'; -import thunk from 'redux-thunk'; -import rootReducer from './reducers'; +import { createStore, applyMiddleware, compose } from "redux"; +import thunk from "redux-thunk"; +import rootReducer from "./reducers"; const initialState = {}; @@ -11,7 +11,7 @@ const store = createStore( initialState, compose( applyMiddleware(...middleware), - // window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() + window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ) );