badge selection
This commit is contained in:
		
							parent
							
								
									4f002d8694
								
							
						
					
					
						commit
						42981a4f11
					
				
							
								
								
									
										1
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								.env
									
									
									
									
									
								
							| @ -3,6 +3,7 @@ REACT_APP_BOARD=sensebox-mcu | |||||||
| REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de | REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de | ||||||
| 
 | 
 | ||||||
| REACT_APP_MYBADGES=https://mybadges.org | REACT_APP_MYBADGES=https://mybadges.org | ||||||
|  | REACT_APP_MYBADGES_API=https://mybadges.org/api/v1 | ||||||
| 
 | 
 | ||||||
| # in days | # in days | ||||||
| REACT_APP_SHARE_LINK_EXPIRES=30 | REACT_APP_SHARE_LINK_EXPIRES=30 | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ | |||||||
|   }, |   }, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "start": "react-scripts start", |     "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", |     "build": "react-scripts build", | ||||||
|     "test": "react-scripts test", |     "test": "react-scripts test", | ||||||
|     "eject": "react-scripts eject" |     "eject": "react-scripts eject" | ||||||
|  | |||||||
| @ -25,7 +25,6 @@ export const loadUser = () => (dispatch) => { | |||||||
|       if(err.response){ |       if(err.response){ | ||||||
|         dispatch(returnErrors(err.response.data.message, err.response.status)); |         dispatch(returnErrors(err.response.data.message, err.response.status)); | ||||||
|       } |       } | ||||||
|       console.log('auth failed'); |  | ||||||
|       var status = []; |       var status = []; | ||||||
|       if (window.localStorage.getItem('status')) { |       if (window.localStorage.getItem('status')) { | ||||||
|         status = JSON.parse(window.localStorage.getItem('status')); |         status = JSON.parse(window.localStorage.getItem('status')); | ||||||
|  | |||||||
| @ -191,6 +191,9 @@ export const setSubmitError = () => (dispatch, getState) => { | |||||||
|   if (builder.title === '') { |   if (builder.title === '') { | ||||||
|     dispatch(setError(undefined, 'title')); |     dispatch(setError(undefined, 'title')); | ||||||
|   } |   } | ||||||
|  |   if (builder.title === null) { | ||||||
|  |     dispatch(setError(undefined, 'badge')); | ||||||
|  |   } | ||||||
|   var type = builder.steps.map((step, i) => { |   var type = builder.steps.map((step, i) => { | ||||||
|     // media and xml are directly checked for errors in their components and
 |     // media and xml are directly checked for errors in their components and
 | ||||||
|     // therefore do not have to be checked again
 |     // therefore do not have to be checked again
 | ||||||
| @ -230,7 +233,7 @@ export const setSubmitError = () => (dispatch, getState) => { | |||||||
| export const checkError = () => (dispatch, getState) => { | export const checkError = () => (dispatch, getState) => { | ||||||
|   dispatch(setSubmitError()); |   dispatch(setSubmitError()); | ||||||
|   var error = getState().builder.error; |   var error = getState().builder.error; | ||||||
|   if (error.id || error.title || error.type) { |   if (error.id || error.title || error.badge ||error.type) { | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   for (var i = 0; i < error.steps.length; i++) { |   for (var i = 0; i < error.steps.length; i++) { | ||||||
| @ -251,7 +254,7 @@ export const progress = (inProgress) => (dispatch) => { | |||||||
| export const resetTutorial = () => (dispatch, getState) => { | export const resetTutorial = () => (dispatch, getState) => { | ||||||
|   dispatch(jsonString('')); |   dispatch(jsonString('')); | ||||||
|   dispatch(tutorialTitle('')); |   dispatch(tutorialTitle('')); | ||||||
|   dispatch(tutorialBadge('')); |   dispatch(tutorialBadge(undefined)); | ||||||
|   var steps = [ |   var steps = [ | ||||||
|     { |     { | ||||||
|       type: 'instruction', |       type: 'instruction', | ||||||
|  | |||||||
							
								
								
									
										189
									
								
								src/components/Tutorial/Builder/Badge.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/components/Tutorial/Builder/Badge.js
									
									
									
									
									
										Normal file
									
								
							| @ -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 ( | ||||||
|  |       <div style={{marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)'}}> | ||||||
|  |         <FormControlLabel | ||||||
|  |           labelPlacement="end" | ||||||
|  |           label={"Badge"} | ||||||
|  |           control={ | ||||||
|  |             <Switch | ||||||
|  |               checked={this.state.checked} | ||||||
|  |               onChange={(e) => this.onChangeSwitch(e.target.checked)} | ||||||
|  |               color="primary" | ||||||
|  |             /> | ||||||
|  |           } | ||||||
|  |         /> | ||||||
|  |         {this.state.checked ? | ||||||
|  |           <div style={{marginTop: '10px'}}> | ||||||
|  |             <FormControl variant="outlined" fullWidth> | ||||||
|  |               <InputLabel | ||||||
|  |                 htmlFor={'badge'} | ||||||
|  |                 classes={{shrink: this.props.error ? this.props.classes.errorColorShrink : null}} | ||||||
|  |               > | ||||||
|  |                 {'Badge'} | ||||||
|  |               </InputLabel> | ||||||
|  |               <OutlinedInput | ||||||
|  |                 style={{borderRadius: '25px'}} | ||||||
|  |                 classes={{notchedOutline: this.props.error ? this.props.classes.errorBorder : null}} | ||||||
|  |                 error={this.props.error} | ||||||
|  |                 value={this.state.badgeName} | ||||||
|  |                 label={'Badge'} | ||||||
|  |                 id={'badge'} | ||||||
|  |                 onChange={(e) => this.onChange(e)} | ||||||
|  |                 onInput={(e) => this.onChangeBadge(e)} | ||||||
|  |                 fullWidth={true} | ||||||
|  |                 endAdornment={ | ||||||
|  |                       <IconButton | ||||||
|  |                         onClick={this.deleteBadge} | ||||||
|  |                         edge="end" | ||||||
|  |                       > | ||||||
|  |                         <FontAwesomeIcon size='xs' icon={faTimes} /> | ||||||
|  |                       </IconButton> | ||||||
|  |                     } | ||||||
|  |               /> | ||||||
|  |               {this.props.error && this.state.filteredBadges.length === 0 ? | ||||||
|  |               <FormHelperText className={this.props.classes.errorColor}>Wähle ein Badge aus.</FormHelperText> | ||||||
|  |               : null} | ||||||
|  |             </FormControl> | ||||||
|  |             <List style={{paddingTop: 0}}> | ||||||
|  |             {this.state.filteredBadges.map((badge, i) => ( | ||||||
|  |               badge === 'Keine Übereinstimmung gefunden.' ? | ||||||
|  |                 <ListItem button key={i} onClick={this.deleteBadge} style={{border: '1px solid rgba(0, 0, 0, 0.23)', borderRadius: '25px'}}> | ||||||
|  |                   <ListItemText>{badge}</ListItemText> | ||||||
|  |                 </ListItem> | ||||||
|  |               : | ||||||
|  |               <ListItem button key={i} onClick={() => {this.setBadge(badge)}} style={{border: '1px solid rgba(0, 0, 0, 0.23)', borderRadius: '25px'}}> | ||||||
|  |                 <ListItemText>{`${badge.name}`}</ListItemText> | ||||||
|  |               </ListItem> | ||||||
|  |             ))} | ||||||
|  |             </List> | ||||||
|  |           </div> | ||||||
|  |         : null} | ||||||
|  |       </div> | ||||||
|  |     ); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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)); | ||||||
| @ -12,6 +12,7 @@ import { saveAs } from 'file-saver'; | |||||||
| import { detectWhitespacesAndReturnReadableResult } from '../../../helpers/whitespace'; | import { detectWhitespacesAndReturnReadableResult } from '../../../helpers/whitespace'; | ||||||
| 
 | 
 | ||||||
| import Breadcrumbs from '../../Breadcrumbs'; | import Breadcrumbs from '../../Breadcrumbs'; | ||||||
|  | import Badge from './Badge'; | ||||||
| import Textfield from './Textfield'; | import Textfield from './Textfield'; | ||||||
| import Step from './Step'; | import Step from './Step'; | ||||||
| import Dialog from '../../Dialog'; | import Dialog from '../../Dialog'; | ||||||
| @ -190,7 +191,9 @@ class Builder extends Component { | |||||||
|       var steps = this.props.steps; |       var steps = this.props.steps; | ||||||
|       var newTutorial = new FormData(); |       var newTutorial = new FormData(); | ||||||
|       newTutorial.append('title', this.props.title); |       newTutorial.append('title', this.props.title); | ||||||
|  |       if(this.props.badge){ | ||||||
|         newTutorial.append('badge', this.props.badge); |         newTutorial.append('badge', this.props.badge); | ||||||
|  |       } | ||||||
|       steps.forEach((step, i) => { |       steps.forEach((step, i) => { | ||||||
|         if(step._id){ |         if(step._id){ | ||||||
|           newTutorial.append(`steps[${i}][_id]`, step._id); |           newTutorial.append(`steps[${i}][_id]`, step._id); | ||||||
| @ -348,7 +351,7 @@ class Builder extends Component { | |||||||
|             : null} |             : null} | ||||||
|           {/* <Id error={this.props.error.id} value={this.props.id} /> */} |           {/* <Id error={this.props.error.id} value={this.props.id} /> */} | ||||||
|           <Textfield value={this.props.title} property={'title'} label={'Titel'} error={this.props.error.title} /> |           <Textfield value={this.props.title} property={'title'} label={'Titel'} error={this.props.error.title} /> | ||||||
|           <Textfield value={this.props.badge} property={'badge'} label={'Badge'} /> |           <Badge error={this.props.error.badge}/> | ||||||
| 
 | 
 | ||||||
|           {this.props.steps.map((step, i) => |           {this.props.steps.map((step, i) => | ||||||
|             <Step step={step} index={i} key={i} /> |             <Step step={step} index={i} key={i} /> | ||||||
| @ -435,7 +438,6 @@ Builder.propTypes = { | |||||||
|   change: PropTypes.number.isRequired, |   change: PropTypes.number.isRequired, | ||||||
|   error: PropTypes.object.isRequired, |   error: PropTypes.object.isRequired, | ||||||
|   json: PropTypes.string.isRequired, |   json: PropTypes.string.isRequired, | ||||||
|   badge: PropTypes.string.isRequired, |  | ||||||
|   isProgress: PropTypes.bool.isRequired, |   isProgress: PropTypes.bool.isRequired, | ||||||
|   tutorials: PropTypes.array.isRequired, |   tutorials: PropTypes.array.isRequired, | ||||||
|   message: PropTypes.object.isRequired, |   message: PropTypes.object.isRequired, | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ const initialState = { | |||||||
|   progress: false, |   progress: false, | ||||||
|   json: '', |   json: '', | ||||||
|   title: '', |   title: '', | ||||||
|   badge: '', |  | ||||||
|   id: '', |   id: '', | ||||||
|   steps: [ |   steps: [ | ||||||
|     { |     { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user