update tutorial structure

This commit is contained in:
Mario
2020-10-08 15:29:28 +02:00
parent aaeaf7a5aa
commit 89d0cc380b
19 changed files with 420 additions and 411 deletions
+45 -38
View File
@@ -33,7 +33,7 @@ const styles = (theme) => ({
class Builder extends Component {
constructor(props){
constructor(props) {
super(props);
this.state = {
open: false,
@@ -47,26 +47,33 @@ class Builder extends Component {
this.inputRef = React.createRef();
}
componentWillUnmount(){
componentWillUnmount() {
this.reset();
}
submit = () => {
console.log(this.props.id)
if (this.props.id === null) {
var randomID = Date.now();
} else {
randomID = this.props.id;
}
var isError = this.props.checkError();
if(isError){
this.setState({ snackbar: true, key: Date.now(), message: `Die Angaben für das Tutorial sind nicht vollständig.`, type: 'error'});
if (isError) {
this.setState({ snackbar: true, key: Date.now(), message: `Die Angaben für das Tutorial sind nicht vollständig.`, type: 'error' });
window.scrollTo(0, 0);
}
else{
else {
// export steps without attribute 'url'
var steps = this.props.steps.map(step => {
if(step.url){
if (step.url) {
delete step.url;
}
return step;
});
var tutorial = {
id: this.props.id,
id: randomID,
title: this.props.title,
steps: steps
}
@@ -77,15 +84,15 @@ class Builder extends Component {
reset = () => {
this.props.resetTutorial();
this.setState({ snackbar: true, key: Date.now(), message: `Das Tutorial wurde erfolgreich zurückgesetzt.`, type: 'success'});
this.setState({ snackbar: true, key: Date.now(), message: `Das Tutorial wurde erfolgreich zurückgesetzt.`, type: 'success' });
window.scrollTo(0, 0);
}
uploadJsonFile = (jsonFile) => {
this.props.progress(true);
if(jsonFile.type !== 'application/json'){
if (jsonFile.type !== 'application/json') {
this.props.progress(false);
this.setState({ open: true, string: false, title: 'Unzulässiger Dateityp', content: 'Die übergebene Datei entspricht nicht dem geforderten Format. Es sind nur JSON-Dateien zulässig.'});
this.setState({ open: true, string: false, title: 'Unzulässiger Dateityp', content: 'Die übergebene Datei entspricht nicht dem geforderten Format. Es sind nur JSON-Dateien zulässig.' });
}
else {
var reader = new FileReader();
@@ -97,27 +104,27 @@ class Builder extends Component {
}
uploadJsonString = () => {
this.setState({ open: true, string: true, title: 'JSON-String einfügen', content: ''});
this.setState({ open: true, string: true, title: 'JSON-String einfügen', content: '' });
}
readJson = (jsonString, isFile) => {
try {
var result = JSON.parse(jsonString);
if(!this.checkSteps(result.steps)){
if (!this.checkSteps(result.steps)) {
result.steps = [{}];
}
this.props.readJSON(result);
this.setState({ snackbar: true, key: Date.now(), message: `${isFile ? 'Die übergebene JSON-Datei' : 'Der übergebene JSON-String'} wurde erfolgreich übernommen.`, type: 'success'});
} catch(err){
this.setState({ snackbar: true, key: Date.now(), message: `${isFile ? 'Die übergebene JSON-Datei' : 'Der übergebene JSON-String'} wurde erfolgreich übernommen.`, type: 'success' });
} catch (err) {
console.log(err);
this.props.progress(false);
this.props.jsonString('');
this.setState({ open: true, string: false, title: 'Ungültiges JSON-Format', content: `${isFile ? 'Die übergebene Datei' : 'Der übergebene String'} enthält nicht valides JSON. Bitte überprüfe ${isFile ? 'die JSON-Datei' : 'den JSON-String'} und versuche es erneut.`});
this.setState({ open: true, string: false, title: 'Ungültiges JSON-Format', content: `${isFile ? 'Die übergebene Datei' : 'Der übergebene String'} enthält nicht valides JSON. Bitte überprüfe ${isFile ? 'die JSON-Datei' : 'den JSON-String'} und versuche es erneut.` });
}
}
checkSteps = (steps) => {
if(!(steps && steps.length > 0)){
if (!(steps && steps.length > 0)) {
return false;
}
return true;
@@ -131,41 +138,41 @@ class Builder extends Component {
render() {
return (
<div>
<Breadcrumbs content={[{link: '/tutorial', title: 'Tutorial'}, {link: '/tutorial/builder', title: 'Builder'}]}/>
<Breadcrumbs content={[{ link: '/tutorial', title: 'Tutorial' }, { link: '/tutorial/builder', title: 'Builder' }]} />
<h1>Tutorial-Builder</h1>
{/*upload JSON*/}
<div ref={this.inputRef}>
<input
style={{display: 'none'}}
style={{ display: 'none' }}
accept="application/json"
onChange={(e) => {this.uploadJsonFile(e.target.files[0])}}
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>
<Button component="span" style={{ marginRight: '10px', marginBottom: '10px' }} variant='contained' color='primary'>Datei laden</Button>
</label>
<Button style={{marginRight: '10px', marginBottom: '10px'}} variant='contained' color='primary' onClick={() => this.uploadJsonString()}>String laden</Button>
<Button style={{ marginRight: '10px', marginBottom: '10px' }} variant='contained' color='primary' onClick={() => this.uploadJsonString()}>String laden</Button>
</div>
<Divider variant='fullWidth' style={{margin: '10px 0 15px 0'}}/>
<Divider variant='fullWidth' style={{ margin: '10px 0 15px 0' }} />
{/*Tutorial-Builder-Form*/}
{this.props.error.type ?
<FormHelperText style={{lineHeight: 'initial'}} className={this.props.classes.errorColor}>{`Ein Tutorial muss mindestens jeweils eine Instruktion und eine Aufgabe enthalten.`}</FormHelperText>
: null}
<Id error={this.props.error.id} value={this.props.id}/>
<Textfield value={this.props.title} property={'title'} label={'Titel'} error={this.props.error.title}/>
<FormHelperText style={{ lineHeight: 'initial' }} className={this.props.classes.errorColor}>{`Ein Tutorial muss mindestens jeweils eine Instruktion und eine Aufgabe enthalten.`}</FormHelperText>
: null}
{/* <Id error={this.props.error.id} value={this.props.id} /> */}
<Textfield value={this.props.title} property={'title'} label={'Titel'} error={this.props.error.title} />
{this.props.steps.map((step, i) =>
<Step step={step} index={i} key={i}/>
<Step step={step} index={i} key={i} />
)}
{/*submit or reset*/}
<Divider variant='fullWidth' style={{margin: '30px 0 10px 0'}}/>
<Button style={{marginRight: '10px', marginTop: '10px'}} variant='contained' color='primary' onClick={() => this.submit()}>Tutorial-Vorlage erstellen</Button>
<Button style={{marginTop: '10px'}} variant='contained' onClick={() => this.reset()}>Zurücksetzen</Button>
<Divider variant='fullWidth' style={{ margin: '30px 0 10px 0' }} />
<Button style={{ marginRight: '10px', marginTop: '10px' }} variant='contained' color='primary' onClick={() => this.submit()}>Tutorial-Vorlage erstellen</Button>
<Button style={{ marginTop: '10px' }} variant='contained' onClick={() => this.reset()}>Zurücksetzen</Button>
<Backdrop className={this.props.classes.backdrop} open={this.props.isProgress}>
<CircularProgress color="inherit" />
@@ -182,16 +189,16 @@ class Builder extends Component {
button={'Schließen'}
actions={
this.state.string ?
<div>
<Button disabled={this.props.error.json || this.props.json === ''} variant='contained' onClick={() => {this.toggle(); this.props.progress(true); this.readJson(this.props.json, false);}} color="primary">Bestätigen</Button>
<Button onClick={() => {this.toggle(); this.props.jsonString('');}} color="primary">Abbrechen</Button>
</div>
: null
<div>
<Button disabled={this.props.error.json || this.props.json === ''} variant='contained' onClick={() => { this.toggle(); this.props.progress(true); this.readJson(this.props.json, false); }} color="primary">Bestätigen</Button>
<Button onClick={() => { this.toggle(); this.props.jsonString(''); }} color="primary">Abbrechen</Button>
</div>
: null
}
>
{this.state.string ?
<Textfield value={this.props.json} property={'json'} label={'JSON'} multiline error={this.props.error.json}/>
: null}
<Textfield value={this.props.json} property={'json'} label={'JSON'} multiline error={this.props.error.json} />
: null}
</Dialog>
<Snackbar
@@ -230,4 +237,4 @@ const mapStateToProps = state => ({
isProgress: state.builder.progress
});
export default connect(mapStateToProps, { checkError, readJSON, jsonString, progress, resetTutorial })(withStyles(styles, {withTheme: true})(Builder));
export default connect(mapStateToProps, { checkError, readJSON, jsonString, progress, resetTutorial })(withStyles(styles, { withTheme: true })(Builder));
+56 -54
View File
@@ -28,81 +28,83 @@ const styles = theme => ({
class Id extends Component {
handleChange = (e) => {
var value = parseInt(e.target.value);
if(Number.isInteger(value) && value > 0){
var value =
parseInt(e.target.value);
if (Number.isInteger(value) && value > 0) {
this.props.tutorialId(value);
if(this.props.error){
if (this.props.error) {
this.props.deleteError(undefined, 'id');
}
}
else {
this.props.tutorialId(value.toString());
this.props.setError(undefined,'id');
this.props.setError(undefined, 'id');
}
};
handleCounter = (step) => {
if(this.props.value+step < 1){
this.props.setError(undefined,'id');
if (this.props.value + step < 1) {
this.props.setError(undefined, 'id');
}
else if(this.props.error){
else if (this.props.error) {
this.props.deleteError(undefined, 'id');
}
if(!this.props.value || !Number.isInteger(this.props.value)){
this.props.tutorialId(0+step);
if (!this.props.value || !Number.isInteger(this.props.value)) {
this.props.tutorialId(0 + step);
}
else {
this.props.tutorialId(this.props.value+step);
this.props.tutorialId(this.props.value + step);
}
}
render() {
return (
<div style={{display: 'inline-flex', marginTop: '15px'}}>
<FormControl variant="outlined" style={{marginBottom: '10px', width: '250px'}}>
<InputLabel
htmlFor="id"
classes={{shrink: this.props.error ? this.props.classes.errorColorShrink : null}}
>
ID
<div style={{ display: 'inline-flex', marginTop: '15px' }}>
<FormControl variant="outlined" style={{ marginBottom: '10px', width: '250px' }}>
<InputLabel
htmlFor="id"
classes={{ shrink: this.props.error ? this.props.classes.errorColorShrink : null }}
>
ID
</InputLabel>
<OutlinedInput
style={{borderRadius: '25px', padding: '0 0 0 10px', width: '200px'}}
classes={{notchedOutline: this.props.error ? this.props.classes.errorBorder : null}}
error={this.props.error}
value={this.props.value}
name='id'
label='ID'
id='id'
onChange={this.handleChange}
inputProps={{
style: {marginRight: '10px'}
}}
endAdornment={
<div style={{display: 'flex'}}>
<Button
disabled={this.props.value === 1 || !Number.isInteger(this.props.value)}
onClick={() => this.handleCounter(-1)}
variant='contained'
color='primary'
style={{borderRadius: '25px 0 0 25px', height: '56px', boxShadow: '0 0 transparent'}}
>
<FontAwesomeIcon icon={faMinus} />
</Button>
<Button
onClick={() => this.handleCounter(1)}
variant='contained'
color='primary'
style={{borderRadius: '0 25px 25px 0', height: '56px', boxShadow: '0 0 transparent'}}
>
<FontAwesomeIcon icon={faPlus} />
</Button>
</div>
}
/>
{this.props.error ? <FormHelperText className={this.props.classes.errorColor}>Gib eine positive ganzzahlige Zahl ein.</FormHelperText> : null}
</FormControl>
<FormHelperText style={{marginLeft: '-40px', marginTop: '5px', lineHeight: 'initial', marginBottom: '10px', width: '200px'}}>Beachte, dass die ID eindeutig sein muss. Sie muss sich also zu den anderen Tutorials unterscheiden.</FormHelperText>
<OutlinedInput
style={{ borderRadius: '25px', padding: '0 0 0 10px', width: '200px' }}
classes={{ notchedOutline: this.props.error ? this.props.classes.errorBorder : null }}
error={this.props.error}
value={this.props.value}
name='id'
label='ID'
id='id'
onChange={this.handleChange}
inputProps={{
style: { marginRight: '10px' }
}}
endAdornment={
<div style={{ display: 'flex' }}>
<Button
disabled={this.props.value === 1 || !Number.isInteger(this.props.value)}
onClick={() => this.handleCounter(-1)}
variant='contained'
color='primary'
style={{ borderRadius: '25px 0 0 25px', height: '56px', boxShadow: '0 0 transparent' }}
>
<FontAwesomeIcon icon={faMinus} />
</Button>
<Button
onClick={() => this.handleCounter(1)}
variant='contained'
color='primary'
style={{ borderRadius: '0 25px 25px 0', height: '56px', boxShadow: '0 0 transparent' }}
>
<FontAwesomeIcon icon={faPlus} />
</Button>
</div>
}
/>
{this.props.error ? <FormHelperText className={this.props.classes.errorColor}>Gib eine positive ganzzahlige Zahl ein.</FormHelperText> : null}
</FormControl>
<FormHelperText style={{ marginLeft: '-40px', marginTop: '5px', lineHeight: 'initial', marginBottom: '10px', width: '200px' }}>Beachte, dass die ID eindeutig sein muss. Sie muss sich also zu den anderen Tutorials unterscheiden.</FormHelperText>
</div>
);
};
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { changeContent } from '../../../actions/tutorialBuilderActions';
import tutorials from '../../../data/tutorials.json';
import tutorials from '../../../data/tutorials';
import FormGroup from '@material-ui/core/FormGroup';
import Checkbox from '@material-ui/core/Checkbox';
@@ -17,7 +17,7 @@ class Requirements extends Component {
onChange = (e) => {
var requirements = this.props.value;
var value = parseInt(e.target.value)
if(e.target.checked){
if (e.target.checked) {
requirements.push(value);
}
else {
@@ -28,9 +28,9 @@ class Requirements extends Component {
render() {
return (
<FormControl style={{marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)'}}>
<FormLabel style={{color: 'black'}}>Voraussetzungen</FormLabel>
<FormHelperText style={{marginTop: '5px'}}>Beachte, dass die Reihenfolge des Anhakens maßgebend ist.</FormHelperText>
<FormControl style={{ marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)' }}>
<FormLabel style={{ color: 'black' }}>Voraussetzungen</FormLabel>
<FormHelperText style={{ marginTop: '5px' }}>Beachte, dass die Reihenfolge des Anhakens maßgebend ist.</FormHelperText>
<FormGroup>
{tutorials.map((tutorial, i) =>
<FormControlLabel