From 7579be52c92ee359cb618bba33297883ead0b163 Mon Sep 17 00:00:00 2001 From: Delucse <46593742+Delucse@users.noreply.github.com> Date: Sat, 19 Sep 2020 23:21:43 +0200 Subject: [PATCH] upload JSON file --- src/actions/tutorialBuilderActions.js | 63 ++++++++- src/actions/types.js | 1 + .../Tutorial/Builder/BlocklyExample.js | 47 ++++--- src/components/Tutorial/Builder/Builder.js | 120 +++++++++++++++++- src/components/Tutorial/Builder/Step.js | 6 +- src/components/Tutorial/Builder/StepType.js | 2 +- src/reducers/tutorialBuilderReducer.js | 8 +- 7 files changed, 212 insertions(+), 35 deletions(-) diff --git a/src/actions/tutorialBuilderActions.js b/src/actions/tutorialBuilderActions.js index 4cfee80..03f7662 100644 --- a/src/actions/tutorialBuilderActions.js +++ b/src/actions/tutorialBuilderActions.js @@ -1,4 +1,4 @@ -import { BUILDER_CHANGE, BUILDER_ERROR, BUILDER_TITLE, BUILDER_ID, BUILDER_ADD_STEP, BUILDER_DELETE_STEP, BUILDER_CHANGE_STEP, BUILDER_CHANGE_ORDER, BUILDER_DELETE_PROPERTY } from './types'; +import { PROGRESS, BUILDER_CHANGE, BUILDER_ERROR, BUILDER_TITLE, BUILDER_ID, BUILDER_ADD_STEP, BUILDER_DELETE_STEP, BUILDER_CHANGE_STEP, BUILDER_CHANGE_ORDER, BUILDER_DELETE_PROPERTY } from './types'; export const changeTutorialBuilder = () => (dispatch) => { dispatch({ @@ -14,6 +14,14 @@ export const tutorialTitle = (title) => (dispatch) => { dispatch(changeTutorialBuilder()); }; +export const tutorialSteps = (steps) => (dispatch) => { + dispatch({ + type: BUILDER_ADD_STEP, + payload: steps + }); + dispatch(changeTutorialBuilder()); +}; + export const tutorialId = (id) => (dispatch) => { dispatch({ type: BUILDER_ID, @@ -116,7 +124,6 @@ export const changeErrorStepIndex = (fromIndex, toIndex) => (dispatch, getState) export const setError = (index, property) => (dispatch, getState) => { var error = getState().builder.error; - console.log(index); if(index !== undefined){ error.steps[index][property] = true; } @@ -147,21 +154,21 @@ export const deleteError = (index, property) => (dispatch, getState) => { export const setSubmitError = () => (dispatch, getState) => { var builder = getState().builder; - if(builder.id === ''){ + if(builder.id === undefined || builder.id === ''){ dispatch(setError(undefined, 'id')); } - if(builder.title === ''){ + if(builder.id === undefined || builder.title === ''){ dispatch(setError(undefined, 'title')); } for(var i = 0; i < builder.steps.length; i++){ builder.steps[i].id = i+1; - if(i === 0 && builder.steps[i].hardware.length < 1){ + if(i === 0 && (builder.steps[i].hardware === undefined || builder.steps[i].hardware.length < 1)){ dispatch(setError(i, 'hardware')); } - if(builder.steps[i].headline === ''){ + if(builder.steps[i].headline === undefined || builder.steps[i].headline === ''){ dispatch(setError(i, 'headline')); } - if(builder.steps[i].text === ''){ + if(builder.steps[i].text === undefined || builder.steps[i].text === ''){ dispatch(setError(i, 'text')); } } @@ -180,3 +187,45 @@ export const checkError = () => (dispatch, getState) => { } return false; } + +export const progress = (inProgress) => (dispatch) => { + dispatch({ + type: PROGRESS, + payload: inProgress + }) +}; + +export const resetTutorial = () => (dispatch, getState) => { + dispatch(tutorialTitle('')); + dispatch(tutorialId('')); + var steps = [ + { + id: 1, + type: 'instruction', + headline: '', + text: '', + hardware: [], + requirements: [] + } + ]; + dispatch(tutorialSteps(steps)); + dispatch({ + type: BUILDER_ERROR, + payload: { + steps: [{}] + } + }); +}; + +export const readJSON = (json) => (dispatch, getState) => { + dispatch(resetTutorial()); + dispatch({ + type: BUILDER_ERROR, + payload: {steps: [{},{}]} + }); + dispatch(tutorialTitle(json.title)); + dispatch(tutorialId(json.id)); + dispatch(tutorialSteps(json.steps)); + dispatch(setSubmitError()); + dispatch(progress(false)); +}; diff --git a/src/actions/types.js b/src/actions/types.js index c5fb2a5..e31e72f 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -25,3 +25,4 @@ export const BUILDER_CHANGE_STEP = 'BUILDER_CHANGE_STEP'; export const BUILDER_CHANGE_ORDER = 'BUILDER_CHANGE_ORDER'; export const BUILDER_DELETE_PROPERTY = 'BUILDER_DELETE_PROPERTY'; export const BUILDER_ERROR = 'BUILDER_ERROR'; +export const PROGRESS = 'PROGRESS'; diff --git a/src/components/Tutorial/Builder/BlocklyExample.js b/src/components/Tutorial/Builder/BlocklyExample.js index c8e8994..9c64bf4 100644 --- a/src/components/Tutorial/Builder/BlocklyExample.js +++ b/src/components/Tutorial/Builder/BlocklyExample.js @@ -5,7 +5,9 @@ import { changeContent, deleteProperty, setError, deleteError } from '../../../a import moment from 'moment'; import localization from 'moment/locale/de'; +import * as Blockly from 'blockly/core'; +import { parseXml } from '../../../helpers/compareXml'; import BlocklyWindow from '../../Blockly/BlocklyWindow'; import { withStyles } from '@material-ui/core/styles'; @@ -94,27 +96,38 @@ class BlocklyExample extends Component { /> } /> - {this.state.checked ? !this.props.value ? + {this.state.checked ? !this.props.value || this.props.error.steps[this.props.index].xml ? Reiche deine Blöcke ein, indem du auf den rot gefärbten Button klickst. : Die letzte Einreichung erfolgte um {this.state.input} Uhr. : null} - {this.state.checked ? -
- - - + {this.state.checked ? () => { + var initialXml = this.props.value; + // check if value is valid xml; + try{ + Blockly.Xml.textToDom(initialXml); + } + catch(err){ + initialXml = null; + this.props.setError(this.props.index, 'xml'); + } + return( +
+ + + + - - -
+ +
+ )} : null} ); diff --git a/src/components/Tutorial/Builder/Builder.js b/src/components/Tutorial/Builder/Builder.js index 2789fd3..9b3aa8e 100644 --- a/src/components/Tutorial/Builder/Builder.js +++ b/src/components/Tutorial/Builder/Builder.js @@ -1,10 +1,11 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { checkError } from '../../../actions/tutorialBuilderActions'; +import { checkError, readJSON, progress, resetTutorial } from '../../../actions/tutorialBuilderActions'; import { saveAs } from 'file-saver'; +import data from '../../../data/hardware.json'; import { detectWhitespacesAndReturnReadableResult } from '../../../helpers/whitespace'; import Breadcrumbs from '../../Breadcrumbs'; @@ -12,14 +13,30 @@ import Id from './Id'; import Title from './Textfield'; import Step from './Step'; +import { withStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; +import Backdrop from '@material-ui/core/Backdrop'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import Divider from '@material-ui/core/Divider'; + +const styles = (theme) => ({ + backdrop: { + zIndex: theme.zIndex.drawer + 1, + color: '#fff', + } +}); class Builder extends Component { + constructor(props){ + super(props); + this.inputRef = React.createRef(); + } + submit = () => { var isError = this.props.checkError(); if(isError){ - alert('Error'); + window.scrollTo(0, 0); } else{ var tutorial = { @@ -32,6 +49,74 @@ class Builder extends Component { } } + reset = () => { + this.props.resetTutorial(); + window.scrollTo(0, 0); + } + + uploadJsonFile = (jsonFile) => { + this.props.progress(true); + if(jsonFile.type !== 'application/json'){ + alert('falscher Dateityp'); + this.props.progress(false); + this.setState({ open: true, file: false, title: 'Unzulässiger Dateityp', content: 'Die übergebene Datei entsprach nicht dem geforderten Format. Es sind nur JSON-Dateien zulässig.' }); + } + else { + var reader = new FileReader(); + reader.readAsText(jsonFile); + reader.onloadend = () => { + try { + var result = JSON.parse(reader.result); + if(this.checkSteps(result.steps)){ + alert('Hier'); + this.props.readJSON(result); + } + else{ + this.props.progress(false); + alert('die JSON-Datei hat nicht die richtige Form'); + } + } catch(err){ + this.props.progress(false); + alert('ungültige JSON-Datei'); + this.setState({ open: true, file: false, title: 'Ungültige XML', content: 'Die XML-Datei konnte nicht in Blöcke zerlegt werden. Bitte überprüfe den XML-Code und versuche es erneut.' }); + } + }; + } + } + + checkSteps = (steps) => { + if(!(steps && steps.length > 0)){ + alert(1); + return false; + } + steps.map((step, i) => { + if(i === 0){ + if(!(step.requirements && + step.requirements.length > 0 && + step.requirements.filter(requirement => typeof(requirement) === 'number').length === step.requirements.length)){ + alert(3); + return false; + } + var hardwareIds = data.map(hardware => hardware.id); + if(!(step.hardware && + step.hardware.length > 0 && + step.hardware.filter(hardware => typeof(hardware) === 'string' && hardwareIds.includes(hardware)).length === step.hardware.length)){ + alert(4); + return false; + } + } + if(!(step.headline && typeof(step.headline)==='string')){ + alert(5); + return false; + } + if(!(step.text && typeof(step.text)==='string')){ + alert(6); + return false; + } + }); + return true; + } + render() { return ( @@ -40,6 +125,20 @@ class Builder extends Component {

Tutorial-Builder

+
+ {this.uploadJsonFile(e.target.files[0])}} + id="open-json" + type="file" + /> + +
+ + @@ -49,7 +148,11 @@ class Builder extends Component { )} - <Button variant='contained' color='primary' onClick={() => this.submit()}>Tutorial-Vorlage erstellen</Button> + <Button style={{marginRight: '10px'}} variant='contained' color='primary' onClick={() => this.submit()}>Tutorial-Vorlage erstellen</Button> + <Button variant='contained' onClick={() => this.reset()}>Zurücksetzen</Button> + <Backdrop className={this.props.classes.backdrop} open={this.props.isProgress}> + <CircularProgress color="inherit" /> + </Backdrop> </div> @@ -63,10 +166,14 @@ class Builder extends Component { Builder.propTypes = { checkError: PropTypes.func.isRequired, + readJSON: PropTypes.func.isRequired, + progress: PropTypes.func.isRequired, + resetTutorial: PropTypes.func.isRequired, title: PropTypes.string.isRequired, steps: PropTypes.array.isRequired, change: PropTypes.number.isRequired, - error: PropTypes.object.isRequired + error: PropTypes.object.isRequired, + isProgress: PropTypes.bool.isRequired }; const mapStateToProps = state => ({ @@ -74,7 +181,8 @@ const mapStateToProps = state => ({ id: state.builder.id, steps: state.builder.steps, change: state.builder.change, - error: state.builder.error + error: state.builder.error, + isProgress: state.builder.progress }); -export default connect(mapStateToProps, { checkError })(Builder); +export default connect(mapStateToProps, { checkError, readJSON, progress, resetTutorial })(withStyles(styles, {withTheme: true})(Builder)); diff --git a/src/components/Tutorial/Builder/Step.js b/src/components/Tutorial/Builder/Step.js index 7124131..740a686 100644 --- a/src/components/Tutorial/Builder/Step.js +++ b/src/components/Tutorial/Builder/Step.js @@ -89,11 +89,11 @@ class Step extends Component { <Textfield value={this.props.step.text} property={'text'} label={this.props.step.type === 'task' ? 'Aufgabenstellung' : 'Instruktionen'} index={index} multiline error={this.props.error} errorText={`Gib Instruktionen für die ${this.props.step.type === 'task' ? 'Aufgabe' : 'Anleitung'} ein.`}/> {index === 0 ? <div> - <Requirements value={this.props.step.requirements} index={index}/> - <Hardware value={this.props.step.hardware} index={index} error={this.props.error}/> + <Requirements value={this.props.step.requirements ? this.props.step.requirements : []} index={index}/> + <Hardware value={this.props.step.hardware ? this.props.step.hardware : []} index={index} error={this.props.error}/> </div> : null} - <BlocklyExample value={this.props.step.xml} index={index} task={this.props.step.type === 'task'} /> + <BlocklyExample value={this.props.step.xml} index={index} task={this.props.step.type === 'task'} error={this.props.error}/> </div> </div> </div> diff --git a/src/components/Tutorial/Builder/StepType.js b/src/components/Tutorial/Builder/StepType.js index f482990..910b2df 100644 --- a/src/components/Tutorial/Builder/StepType.js +++ b/src/components/Tutorial/Builder/StepType.js @@ -11,7 +11,7 @@ class StepType extends Component { render() { return ( - <RadioGroup row value={this.props.value} onChange={(e) => {this.props.changeContent(this.props.index, 'type', e.target.value)}}> + <RadioGroup row value={this.props.value === 'task' ? 'task' : 'instruction'} onChange={(e) => {this.props.changeContent(this.props.index, 'type', e.target.value)}}> <FormControlLabel style={{color: 'black'}} value="instruction" control={<Radio color="primary" />} diff --git a/src/reducers/tutorialBuilderReducer.js b/src/reducers/tutorialBuilderReducer.js index 3103403..6296da2 100644 --- a/src/reducers/tutorialBuilderReducer.js +++ b/src/reducers/tutorialBuilderReducer.js @@ -1,7 +1,8 @@ -import { BUILDER_CHANGE, BUILDER_ERROR, BUILDER_TITLE, BUILDER_ID, BUILDER_ADD_STEP, BUILDER_DELETE_STEP, BUILDER_CHANGE_STEP,BUILDER_CHANGE_ORDER, BUILDER_DELETE_PROPERTY } from '../actions/types'; +import { PROGRESS, BUILDER_CHANGE, BUILDER_ERROR, BUILDER_TITLE, BUILDER_ID, BUILDER_ADD_STEP, BUILDER_DELETE_STEP, BUILDER_CHANGE_STEP,BUILDER_CHANGE_ORDER, BUILDER_DELETE_PROPERTY } from '../actions/types'; const initialState = { change: 0, + progress: false, title: '', id: '', steps: [ @@ -50,6 +51,11 @@ export default function(state = initialState, action){ ...state, error: action.payload } + case PROGRESS: + return { + ...state, + progress: action.payload + } default: return state; }