upload JSON file

This commit is contained in:
Delucse 2020-09-19 23:21:43 +02:00
parent 28ced177bd
commit 7579be52c9
7 changed files with 212 additions and 35 deletions

View File

@ -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));
};

View File

@ -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';

View File

@ -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,19 +96,29 @@ class BlocklyExample extends Component {
/>
}
/>
{this.state.checked ? !this.props.value ?
{this.state.checked ? !this.props.value || this.props.error.steps[this.props.index].xml ?
<FormHelperText style={{lineHeight: 'initial', marginBottom: '10px'}} className={this.props.classes.errorColor}>Reiche deine Blöcke ein, indem du auf den rot gefärbten Button klickst.</FormHelperText>
: <FormHelperText style={{lineHeight: 'initial', marginBottom: '10px'}}>Die letzte Einreichung erfolgte um {this.state.input} Uhr.</FormHelperText>
: 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(
<div>
<Grid container className={!this.props.value ? this.props.classes.errorBorder : null}>
<Grid item xs={12}>
<BlocklyWindow initialXml={this.props.value}/>
<BlocklyWindow initialXml={initialXml}/>
</Grid>
</Grid>
<Button
className={!this.props.value ? this.props.classes.errorButton : null }
className={!this.props.value || this.props.error.steps[this.props.index].xml ? this.props.classes.errorButton : null }
style={{marginTop: '5px', height: '40px'}}
variant='contained'
color='primary'
@ -115,6 +127,7 @@ class BlocklyExample extends Component {
{this.props.task ? 'Musterlösung einreichen' : 'Beispiel einreichen'}
</Button>
</div>
)}
: null}
</div>
);

View File

@ -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 {
<h1>Tutorial-Builder</h1>
<div ref={this.inputRef}>
<input
style={{display: 'none'}}
accept="application/json"
onChange={(e) => {this.uploadJsonFile(e.target.files[0])}}
id="open-json"
type="file"
/>
<label htmlFor="open-json">
<Button component="span" style={{marginRight: '10px', marginBottom: '10px'}} variant='contained' color='primary'>Datei laden</Button>
</label>
</div>
<Divider variant='fullWidth' style={{margin: '10px 0 30px 0'}}/>
<Id error={this.props.error} value={this.props.id}/>
<Title value={this.props.title} property={'title'} label={'Titel'} error={this.props.error}/>
@ -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));

View File

@ -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>

View File

@ -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" />}

View File

@ -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;
}