update tutorial structure
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user