diff --git a/.env b/.env index 3ba890f..81b9303 100644 --- a/.env +++ b/.env @@ -3,6 +3,7 @@ REACT_APP_BOARD=sensebox-mcu REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de REACT_APP_MYBADGES=https://mybadges.org +REACT_APP_MYBADGES_API=https://mybadges.org/api/v1 # in days REACT_APP_SHARE_LINK_EXPIRES=30 diff --git a/package.json b/package.json index 60a336a..4a234e7 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ }, "scripts": { "start": "react-scripts start", - "dev": "set \"REACT_APP_BLOCKLY_API=http://localhost:8080\" && npm start", + "dev": "set \"REACT_APP_BLOCKLY_API=http://localhost:8080\" && set \"REACT_APP_MYBADGES_API=http://localhost:3001/api/v1\"&& npm start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" diff --git a/src/actions/authActions.js b/src/actions/authActions.js index 8e97232..b5d6531 100644 --- a/src/actions/authActions.js +++ b/src/actions/authActions.js @@ -25,7 +25,6 @@ export const loadUser = () => (dispatch) => { if(err.response){ dispatch(returnErrors(err.response.data.message, err.response.status)); } - console.log('auth failed'); var status = []; if (window.localStorage.getItem('status')) { status = JSON.parse(window.localStorage.getItem('status')); diff --git a/src/actions/tutorialBuilderActions.js b/src/actions/tutorialBuilderActions.js index d7448f3..25f27b0 100644 --- a/src/actions/tutorialBuilderActions.js +++ b/src/actions/tutorialBuilderActions.js @@ -191,6 +191,9 @@ export const setSubmitError = () => (dispatch, getState) => { if (builder.title === '') { dispatch(setError(undefined, 'title')); } + if (builder.title === null) { + dispatch(setError(undefined, 'badge')); + } var type = builder.steps.map((step, i) => { // media and xml are directly checked for errors in their components and // therefore do not have to be checked again @@ -230,7 +233,7 @@ export const setSubmitError = () => (dispatch, getState) => { export const checkError = () => (dispatch, getState) => { dispatch(setSubmitError()); var error = getState().builder.error; - if (error.id || error.title || error.type) { + if (error.id || error.title || error.badge ||error.type) { return true; } for (var i = 0; i < error.steps.length; i++) { @@ -251,7 +254,7 @@ export const progress = (inProgress) => (dispatch) => { export const resetTutorial = () => (dispatch, getState) => { dispatch(jsonString('')); dispatch(tutorialTitle('')); - dispatch(tutorialBadge('')); + dispatch(tutorialBadge(undefined)); var steps = [ { type: 'instruction', diff --git a/src/components/Tutorial/Builder/Badge.js b/src/components/Tutorial/Builder/Badge.js new file mode 100644 index 0000000..a2c08c5 --- /dev/null +++ b/src/components/Tutorial/Builder/Badge.js @@ -0,0 +1,189 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { tutorialBadge, deleteProperty, setError, deleteError } from '../../../actions/tutorialBuilderActions'; + +import axios from 'axios'; + +import { withStyles } from '@material-ui/core/styles'; +import Switch from '@material-ui/core/Switch'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import IconButton from '@material-ui/core/IconButton'; +import OutlinedInput from '@material-ui/core/OutlinedInput'; +import InputLabel from '@material-ui/core/InputLabel'; +import FormControl from '@material-ui/core/FormControl'; + +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faTimes } from "@fortawesome/free-solid-svg-icons"; + +const styles = (theme) => ({ + errorColor: { + color: `${theme.palette.error.dark} !important` + }, + errorColorShrink: { + color: `rgba(0, 0, 0, 0.54) !important` + }, + errorBorder: { + borderColor: `${theme.palette.error.dark} !important` + } +}); + +class Badge extends Component { + + constructor(props){ + super(props); + this.state={ + checked: props.badge ? true : false, + badgeName: '', + filteredBadges: [], + badges: [] + }; + } + + componentDidMount(){ + this.getBadges(); + } + + componentDidUpdate(props){ + if(props.badge !== this.props.badge){ + this.setState({ checked: this.props.badge !== undefined ? true : false, badgeName: this.props.badge ? this.state.badges.filter(badge => badge._id === this.props.badge)[0].name : '' }); + } + } + + getBadges = () => { + axios.get(`${process.env.REACT_APP_MYBADGES_API}/badge`) + .then(res => { + this.setState({badges: res.data.badges, badgeName: this.props.badge ? res.data.badges.filter(badge => badge._id === this.props.badge)[0].name : '' }); + }) + .catch(err => { + console.log(err); + }); + }; + + deleteBadge = () => { + this.setState({ filteredBadges: [], badgeName: '' }); + this.props.tutorialBadge(null); + this.props.setError(this.props.index, 'badge'); + }; + + setBadge = (badge) => { + this.setState({ filteredBadges: [] }); + this.props.tutorialBadge(badge._id); + this.props.deleteError(this.props.index, 'badge'); + }; + + onChange = e => { + this.setState({ badgeName: e.target.value }); + }; + + onChangeBadge = e => { + if(e.target.value && this.props.badge === null){ + var filteredBadges = this.state.badges.filter(badge => new RegExp(e.target.value, 'i').test(badge.name)); + if(filteredBadges.length < 1){ + filteredBadges = ['Keine Übereinstimmung gefunden.']; + } + this.setState({filteredBadges: filteredBadges}); + } + else { + this.setState({filteredBadges: []}); + } + }; + + onChangeSwitch = (value) => { + var oldValue = this.state.checked; + this.setState({checked: value}); + if(oldValue !== value){ + if(value){ + this.props.setError(this.props.index, 'badge'); + this.props.tutorialBadge(null); + } else { + this.props.deleteError(this.props.index, 'badge'); + this.props.tutorialBadge(undefined); + } + } + } + + render() { + return ( +
+ this.onChangeSwitch(e.target.checked)} + color="primary" + /> + } + /> + {this.state.checked ? +
+ + + {'Badge'} + + this.onChange(e)} + onInput={(e) => this.onChangeBadge(e)} + fullWidth={true} + endAdornment={ + + + + } + /> + {this.props.error && this.state.filteredBadges.length === 0 ? + Wähle ein Badge aus. + : null} + + + {this.state.filteredBadges.map((badge, i) => ( + badge === 'Keine Übereinstimmung gefunden.' ? + + {badge} + + : + {this.setBadge(badge)}} style={{border: '1px solid rgba(0, 0, 0, 0.23)', borderRadius: '25px'}}> + {`${badge.name}`} + + ))} + +
+ : null} +
+ ); + }; +} + +Badge.propTypes = { + tutorialBadge: PropTypes.func.isRequired, + deleteProperty: PropTypes.func.isRequired, + setError: PropTypes.func.isRequired, + deleteError: PropTypes.func.isRequired, + badge: PropTypes.string.isRequired +}; + +const mapStateToProps = state => ({ + badge: state.builder.badge, + change: state.builder.change +}); + + +export default connect(mapStateToProps, { tutorialBadge, deleteProperty, setError, deleteError })(withStyles(styles, {withTheme: true})(Badge)); diff --git a/src/components/Tutorial/Builder/Builder.js b/src/components/Tutorial/Builder/Builder.js index ac773f6..bd4e8a0 100644 --- a/src/components/Tutorial/Builder/Builder.js +++ b/src/components/Tutorial/Builder/Builder.js @@ -12,6 +12,7 @@ import { saveAs } from 'file-saver'; import { detectWhitespacesAndReturnReadableResult } from '../../../helpers/whitespace'; import Breadcrumbs from '../../Breadcrumbs'; +import Badge from './Badge'; import Textfield from './Textfield'; import Step from './Step'; import Dialog from '../../Dialog'; @@ -190,7 +191,9 @@ class Builder extends Component { var steps = this.props.steps; var newTutorial = new FormData(); newTutorial.append('title', this.props.title); - newTutorial.append('badge', this.props.badge); + if(this.props.badge){ + newTutorial.append('badge', this.props.badge); + } steps.forEach((step, i) => { if(step._id){ newTutorial.append(`steps[${i}][_id]`, step._id); @@ -348,7 +351,7 @@ class Builder extends Component { : null} {/* */} - + {this.props.steps.map((step, i) => @@ -435,7 +438,6 @@ Builder.propTypes = { change: PropTypes.number.isRequired, error: PropTypes.object.isRequired, json: PropTypes.string.isRequired, - badge: PropTypes.string.isRequired, isProgress: PropTypes.bool.isRequired, tutorials: PropTypes.array.isRequired, message: PropTypes.object.isRequired, diff --git a/src/reducers/tutorialBuilderReducer.js b/src/reducers/tutorialBuilderReducer.js index cad4c2e..bfbe654 100644 --- a/src/reducers/tutorialBuilderReducer.js +++ b/src/reducers/tutorialBuilderReducer.js @@ -5,7 +5,6 @@ const initialState = { progress: false, json: '', title: '', - badge: '', id: '', steps: [ {