diff --git a/package.json b/package.json
index 72a6539..44ff8d2 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
+ "axios": "^0.21.0",
"blockly": "^3.20200924.0",
"file-saver": "^2.0.2",
"mnemonic-id": "^3.2.7",
diff --git a/src/actions/messageActions.js b/src/actions/messageActions.js
new file mode 100644
index 0000000..517c973
--- /dev/null
+++ b/src/actions/messageActions.js
@@ -0,0 +1,32 @@
+import { GET_ERRORS, CLEAR_MESSAGES, GET_SUCCESS } from './types';
+
+// RETURN Errors
+export const returnErrors = (msg, status, id = null) => {
+ return {
+ type: GET_ERRORS,
+ payload: {
+ msg: msg,
+ status: status,
+ id: id
+ }
+ };
+};
+
+// RETURN Success
+export const returnSuccess = (msg, status, id = null) => {
+ return {
+ type: GET_SUCCESS,
+ payload: {
+ msg: msg,
+ status: status,
+ id: id
+ }
+ };
+};
+
+// CLEAR_MESSAGES
+export const clearMessages = () => {
+ return {
+ type: CLEAR_MESSAGES
+ };
+};
diff --git a/src/actions/tutorialActions.js b/src/actions/tutorialActions.js
index 4ab1980..10013e0 100644
--- a/src/actions/tutorialActions.js
+++ b/src/actions/tutorialActions.js
@@ -1,6 +1,34 @@
-import { TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE, TUTORIAL_XML, TUTORIAL_ID, TUTORIAL_STEP } from './types';
+import { TUTORIAL_PROGRESS, GET_TUTORIAL, TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE, TUTORIAL_XML, TUTORIAL_ID, TUTORIAL_STEP } from './types';
-import tutorials from '../data/tutorials';
+import axios from 'axios';
+import { returnErrors, returnSuccess } from './messageActions';
+
+// import tutorials from '../data/tutorials';
+
+export const getTutorial = (id) => (dispatch) => {
+ dispatch({type: TUTORIAL_PROGRESS});
+ axios.get(`https://api.blockly.sensebox.de/tutorial/${id}`)
+ .then(res => {
+ dispatch({type: TUTORIAL_PROGRESS});
+ dispatch({
+ type: GET_TUTORIAL,
+ payload: res.data
+ });
+ })
+ .catch(err => {
+ dispatch({type: TUTORIAL_PROGRESS});
+ if(err.response){
+ dispatch(returnErrors(err.response.data.message, err.response.status, 'GET_TUTORIAL_FAIL'));
+ }
+ });
+};
+
+export const resetTutorial = () => (dispatch) => {
+ dispatch({
+ type: GET_TUTORIAL,
+ payload: {}
+ });
+};
export const tutorialChange = () => (dispatch) => {
dispatch({
@@ -10,7 +38,7 @@ export const tutorialChange = () => (dispatch) => {
export const tutorialCheck = (status, step) => (dispatch, getState) => {
var tutorialsStatus = getState().tutorial.status;
- var id = getState().tutorial.currentId;
+ var id = getState().tutorial.tutorial.id;
var tutorialsStatusIndex = tutorialsStatus.findIndex(tutorialStatus => tutorialStatus.id === id);
var tasksIndex = tutorialsStatus[tutorialsStatusIndex].tasks.findIndex(task => task.id === step.id);
tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex] = {
@@ -25,11 +53,12 @@ export const tutorialCheck = (status, step) => (dispatch, getState) => {
};
export const storeTutorialXml = (code) => (dispatch, getState) => {
- var id = getState().tutorial.currentId;
+ var tutorial = getState().tutorial.tutorial;
+ var id = tutorial.id;
if (id !== null) {
var activeStep = getState().tutorial.activeStep;
- var steps = tutorials.filter(tutorial => tutorial.id === id)[0].steps;
- if (steps[activeStep].type === 'task') {
+ var steps = tutorial.steps;//tutorials.filter(tutorial => tutorial.id === id)[0].steps;
+ if (steps && steps[activeStep].type === 'task') {
var tutorialsStatus = getState().tutorial.status;
var tutorialsStatusIndex = tutorialsStatus.findIndex(tutorialStatus => tutorialStatus.id === id);
var tasksIndex = tutorialsStatus[tutorialsStatusIndex].tasks.findIndex(task => task.id === steps[activeStep].id);
@@ -46,12 +75,12 @@ export const storeTutorialXml = (code) => (dispatch, getState) => {
};
-export const tutorialId = (id) => (dispatch) => {
- dispatch({
- type: TUTORIAL_ID,
- payload: id
- });
-};
+// export const tutorialId = (id) => (dispatch) => {
+// dispatch({
+// type: TUTORIAL_ID,
+// payload: id
+// });
+// };
export const tutorialStep = (step) => (dispatch) => {
dispatch({
diff --git a/src/actions/types.js b/src/actions/types.js
index c65c9ec..15ca8c1 100644
--- a/src/actions/types.js
+++ b/src/actions/types.js
@@ -7,7 +7,8 @@ export const DELETE_BLOCK = 'DELETE_BLOCK';
export const CLEAR_STATS = 'CLEAR_STATS';
export const NAME = 'NAME';
-
+export const TUTORIAL_PROGRESS = 'TUTORIAL_PROGRESS';
+export const GET_TUTORIAL = 'GET_TUTORIAL';
export const TUTORIAL_SUCCESS = 'TUTORIAL_SUCCESS';
export const TUTORIAL_ERROR = 'TUTORIAL_ERROR';
export const TUTORIAL_CHANGE = 'TUTORIAL_CHANGE';
@@ -30,3 +31,8 @@ export const PROGRESS = 'PROGRESS';
export const VISIT = 'VISIT';
+
+// messages
+export const GET_ERRORS = 'GET_ERRORS';
+export const GET_SUCCESS = 'GET_SUCCESS';
+export const CLEAR_MESSAGES = 'CLEAR_MESSAGES';
diff --git a/src/components/Tutorial/Assessment.js b/src/components/Tutorial/Assessment.js
index 3cd546b..2729774 100644
--- a/src/components/Tutorial/Assessment.js
+++ b/src/components/Tutorial/Assessment.js
@@ -27,7 +27,7 @@ class Assessment extends Component {
}
render() {
- var tutorialId = this.props.currentTutorialId;
+ var tutorialId = this.props.tutorial.id //currentTutorialId;
var currentTask = this.props.step;
var status = this.props.status.filter(status => status.id === tutorialId)[0];
var taskIndex = status.tasks.findIndex(task => task.id === currentTask.id);
@@ -61,16 +61,18 @@ class Assessment extends Component {
}
Assessment.propTypes = {
- currentTutorialId: PropTypes.number,
+ // currentTutorialId: PropTypes.number,
status: PropTypes.array.isRequired,
change: PropTypes.number.isRequired,
- workspaceName: PropTypes.func.isRequired
+ workspaceName: PropTypes.func.isRequired,
+ tutorial: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
change: state.tutorial.change,
status: state.tutorial.status,
- currentTutorialId: state.tutorial.currentId
+ tutorial: state.tutorial.tutorial
+ // currentTutorialId: state.tutorial.currentId
});
export default connect(mapStateToProps, { workspaceName })(withWidth()(Assessment));
diff --git a/src/components/Tutorial/Instruction.js b/src/components/Tutorial/Instruction.js
index 4ffd491..fb82d8d 100644
--- a/src/components/Tutorial/Instruction.js
+++ b/src/components/Tutorial/Instruction.js
@@ -56,11 +56,11 @@ class Instruction extends Component {
}
Instruction.propTypes = {
- currentTutorialId: PropTypes.number,
+ // currentTutorialId: PropTypes.number,
};
const mapStateToProps = state => ({
- currentTutorialId: state.tutorial.currentId
+ // currentTutorialId: state.tutorial.currentId
});
export default connect(mapStateToProps, null)(Instruction);
diff --git a/src/components/Tutorial/SolutionCheck.js b/src/components/Tutorial/SolutionCheck.js
index 92c093f..08bec49 100644
--- a/src/components/Tutorial/SolutionCheck.js
+++ b/src/components/Tutorial/SolutionCheck.js
@@ -8,7 +8,7 @@ import { withRouter } from 'react-router-dom';
import Compile from '../Compile';
import Dialog from '../Dialog';
-import tutorials from '../../data/tutorials';
+// import tutorials from '../../data/tutorials';
import { checkXml } from '../../helpers/compareXml';
import { withStyles } from '@material-ui/core/styles';
@@ -47,7 +47,7 @@ class SolutionCheck extends Component {
}
check = () => {
- const tutorial = tutorials.filter(tutorial => tutorial.id === this.props.currentTutorialId)[0];
+ const tutorial = this.props.tutorial //tutorials.filter(tutorial => tutorial.id === this.props.currentTutorialId)[0];
const step = tutorial.steps[this.props.activeStep];
var msg = checkXml(step.xml, this.props.xml);
this.props.tutorialCheck(msg.type, step);
@@ -55,7 +55,7 @@ class SolutionCheck extends Component {
}
render() {
- const steps = tutorials.filter(tutorial => tutorial.id === this.props.currentTutorialId)[0].steps;
+ const steps = this.props.tutorial.steps //tutorials.filter(tutorial => tutorial.id === this.props.currentTutorialId)[0].steps;
return (
@@ -114,15 +114,17 @@ class SolutionCheck extends Component {
SolutionCheck.propTypes = {
tutorialCheck: PropTypes.func.isRequired,
tutorialStep: PropTypes.func.isRequired,
- currentTutorialId: PropTypes.number,
+ // currentTutorialId: PropTypes.number,
activeStep: PropTypes.number.isRequired,
- xml: PropTypes.string.isRequired
+ xml: PropTypes.string.isRequired,
+ tutorial: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
- currentTutorialId: state.tutorial.currentId,
+ // currentTutorialId: state.tutorial.currentId,
activeStep: state.tutorial.activeStep,
- xml: state.workspace.code.xml
+ xml: state.workspace.code.xml,
+ tutorial: state.tutorial.tutorial
});
export default connect(mapStateToProps, { tutorialCheck, tutorialStep })(withStyles(styles, { withTheme: true })(withRouter(SolutionCheck)));
diff --git a/src/components/Tutorial/StepperHorizontal.js b/src/components/Tutorial/StepperHorizontal.js
index 6853550..f100ffb 100644
--- a/src/components/Tutorial/StepperHorizontal.js
+++ b/src/components/Tutorial/StepperHorizontal.js
@@ -50,14 +50,14 @@ const styles = (theme) => ({
class StepperHorizontal extends Component {
render() {
- var tutorialId = this.props.currentTutorialId;
+ var tutorialId = this.props.tutorial.id //this.props.currentTutorialId;
var tutorialIndex = this.props.currentTutorialIndex;
var status = this.props.status.filter(status => status.id === tutorialId)[0];
var tasks = status.tasks;
var error = tasks.filter(task => task.type === 'error').length > 0;
var success = tasks.filter(task => task.type === 'success').length / tasks.length;
var tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other';
- var title = tutorials.filter(tutorial => tutorial.id === tutorialId)[0].title;
+ var title = this.props.tutorial.title;
return (
{error || success > 0 ?
@@ -96,15 +96,17 @@ class StepperHorizontal extends Component {
StepperHorizontal.propTypes = {
status: PropTypes.array.isRequired,
change: PropTypes.number.isRequired,
- currentTutorialId: PropTypes.number.isRequired,
- currentTutorialIndex: PropTypes.number.isRequired
+ // currentTutorialId: PropTypes.number.isRequired,
+ currentTutorialIndex: PropTypes.number.isRequired,
+ tutorial: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
change: state.tutorial.change,
status: state.tutorial.status,
- currentTutorialId: state.tutorial.currentId,
- currentTutorialIndex: state.tutorial.currentIndex
+ // currentTutorialId: state.tutorial.currentId,
+ currentTutorialIndex: state.tutorial.currentIndex,
+ tutorial: state.tutorial.tutorial
});
export default connect(mapStateToProps, null)(withRouter(withStyles(styles, { withTheme: true })(StepperHorizontal)));
diff --git a/src/components/Tutorial/StepperVertical.js b/src/components/Tutorial/StepperVertical.js
index dcaf36e..81e81a6 100644
--- a/src/components/Tutorial/StepperVertical.js
+++ b/src/components/Tutorial/StepperVertical.js
@@ -61,7 +61,7 @@ class StepperVertical extends Component {
}
componentDidUpdate(props){
- if(props.currentTutorialId !== Number(this.props.match.params.tutorialId)){
+ if (props.tutorial.id !== Number(this.props.match.params.tutorialId)) {
this.props.tutorialStep(0);
}
}
@@ -69,7 +69,7 @@ class StepperVertical extends Component {
render() {
var steps = this.props.steps;
var activeStep = this.props.activeStep;
- var tutorialStatus = this.props.status.filter(status => status.id === this.props.currentTutorialId)[0];
+ var tutorialStatus = this.props.status.filter(status => status.id === this.props.tutorial.id/*currentTutorialId*/)[0];
return (
({
change: state.tutorial.change,
status: state.tutorial.status,
- currentTutorialId: state.tutorial.currentId,
- activeStep: state.tutorial.activeStep
+ // currentTutorialId: state.tutorial.currentId,
+ activeStep: state.tutorial.activeStep,
+ tutorial: state.tutorial.tutorial
});
export default connect(mapStateToProps, { tutorialStep })(withRouter(withStyles(styles, {withTheme: true})(StepperVertical)));
diff --git a/src/components/Tutorial/Tutorial.js b/src/components/Tutorial/Tutorial.js
index 09c8e27..bb9f097 100644
--- a/src/components/Tutorial/Tutorial.js
+++ b/src/components/Tutorial/Tutorial.js
@@ -2,7 +2,8 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { workspaceName } from '../../actions/workspaceActions';
-import { tutorialId, tutorialStep } from '../../actions/tutorialActions';
+import { clearMessages } from '../../actions/messageActions';
+import { getTutorial, resetTutorial, tutorialStep } from '../../actions/tutorialActions';
import Breadcrumbs from '../Breadcrumbs';
import StepperHorizontal from './StepperHorizontal';
@@ -13,79 +14,99 @@ import NotFound from '../NotFound';
import { detectWhitespacesAndReturnReadableResult } from '../../helpers/whitespace';
-import tutorials from '../../data/tutorials';
+// import tutorials from '../../data/tutorials';
import Card from '@material-ui/core/Card';
import Button from '@material-ui/core/Button';
+import LinearProgress from '@material-ui/core/LinearProgress';
class Tutorial extends Component {
componentDidMount() {
- this.props.tutorialId(Number(this.props.match.params.tutorialId));
+ this.props.getTutorial(this.props.match.params.tutorialId);
+ // this.props.tutorialId(Number(this.props.match.params.tutorialId));
}
componentDidUpdate(props, state) {
- if (props.currentTutorialId !== Number(this.props.match.params.tutorialId)) {
- this.props.tutorialId(Number(this.props.match.params.tutorialId));
+ if (props.tutorial.id && !props.isLoading && Number(props.tutorial.id) !== Number(this.props.match.params.tutorialId)) {
+ this.props.getTutorial(this.props.match.params.tutorialId);
+ // this.props.tutorialId(Number(this.props.match.params.tutorialId));
+ }
+ if(this.props.message.id === 'GET_TUTORIAL_FAIL'){
+ alert(this.props.message.msg);
+ this.props.clearMessages();
}
}
componentWillUnmount() {
- this.props.tutorialId(null);
+ this.props.resetTutorial();
this.props.workspaceName(null);
+ if(this.props.message.msg){
+ this.props.clearMessages();
+ }
}
render() {
- var currentTutorialId = this.props.currentTutorialId;
- var tutorial = tutorials.filter(tutorial => tutorial.id === currentTutorialId)[0];
- var steps = tutorial ? tutorial.steps : null;
- var step = steps ? steps[this.props.activeStep] : null;
- var name = step ? `${detectWhitespacesAndReturnReadableResult(tutorial.title)}_${detectWhitespacesAndReturnReadableResult(step.headline)}` : null;
return (
- !Number.isInteger(currentTutorialId) || currentTutorialId < 1 || !tutorial ?
-
- :
-
-
+
+ {this.props.isLoading ?
:
+ Object.keys(this.props.tutorial).length === 0 ?
+ : (() => {
+ var tutorial = this.props.tutorial;
+ var steps = this.props.tutorial.steps;
+ var step = steps[this.props.activeStep];
+ var name = `${detectWhitespacesAndReturnReadableResult(tutorial.title)}_${detectWhitespacesAndReturnReadableResult(step.headline)}`;
+ return(
+
+
-
+
-
-
- {/* calc(Card-padding: 10px + Button-height: 35px + Button-marginTop: 15px)*/}
-
- {step ?
- step.type === 'instruction' ?
-
- : // if step.type === 'assessment'
- : null}
+
+
+ {/* calc(Card-padding: 10px + Button-height: 35px + Button-marginTop: 15px)*/}
+
+ {step ?
+ step.type === 'instruction' ?
+
+ : // if step.type === 'assessment'
+ : null}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ )})()
+ }
+
);
};
}
Tutorial.propTypes = {
- tutorialId: PropTypes.func.isRequired,
+ getTutorial: PropTypes.func.isRequired,
+ resetTutorial: PropTypes.func.isRequired,
+ clearMessages: PropTypes.func.isRequired,
tutorialStep: PropTypes.func.isRequired,
workspaceName: PropTypes.func.isRequired,
- currentTutorialId: PropTypes.number,
status: PropTypes.array.isRequired,
change: PropTypes.number.isRequired,
- activeStep: PropTypes.number.isRequired
+ activeStep: PropTypes.number.isRequired,
+ tutorial: PropTypes.object.isRequired,
+ isLoading: PropTypes.bool.isRequired,
+ message: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
change: state.tutorial.change,
status: state.tutorial.status,
- currentTutorialId: state.tutorial.currentId,
- activeStep: state.tutorial.activeStep
+ activeStep: state.tutorial.activeStep,
+ tutorial: state.tutorial.tutorial,
+ isLoading: state.tutorial.progress,
+ message: state.message
});
-export default connect(mapStateToProps, { tutorialId, tutorialStep, workspaceName })(Tutorial);
+export default connect(mapStateToProps, { getTutorial, resetTutorial, tutorialStep, clearMessages, workspaceName })(Tutorial);
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 077c60c..16eae53 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -3,10 +3,12 @@ import workspaceReducer from './workspaceReducer';
import tutorialReducer from './tutorialReducer';
import tutorialBuilderReducer from './tutorialBuilderReducer';
import generalReducer from './generalReducer';
+import messageReducer from './messageReducer';
export default combineReducers({
workspace: workspaceReducer,
tutorial: tutorialReducer,
builder: tutorialBuilderReducer,
- general: generalReducer
+ general: generalReducer,
+ message: messageReducer
});
diff --git a/src/reducers/messageReducer.js b/src/reducers/messageReducer.js
new file mode 100644
index 0000000..c8579d7
--- /dev/null
+++ b/src/reducers/messageReducer.js
@@ -0,0 +1,27 @@
+import { GET_ERRORS, CLEAR_MESSAGES, GET_SUCCESS } from '../actions/types';
+
+const initialState = {
+ msg: {},
+ status: null,
+ id: null
+};
+
+export default function(state = initialState, action){
+ switch(action.type){
+ case GET_ERRORS:
+ case GET_SUCCESS:
+ return {
+ msg: action.payload.msg,
+ status: action.payload.status,
+ id: action.payload.id
+ };
+ case CLEAR_MESSAGES:
+ return {
+ msg: {},
+ status: null,
+ id: null
+ };
+ default:
+ return state;
+ }
+}
diff --git a/src/reducers/tutorialReducer.js b/src/reducers/tutorialReducer.js
index c231f78..23398cd 100644
--- a/src/reducers/tutorialReducer.js
+++ b/src/reducers/tutorialReducer.js
@@ -1,4 +1,4 @@
-import { TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE, TUTORIAL_XML, TUTORIAL_ID, TUTORIAL_STEP } from '../actions/types';
+import { TUTORIAL_PROGRESS, GET_TUTORIAL, TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE, TUTORIAL_XML, TUTORIAL_ID, TUTORIAL_STEP } from '../actions/types';
import tutorials from '../data/tutorials';
@@ -40,14 +40,26 @@ const initialStatus = () => {
const initialState = {
status: initialStatus(),
- currentId: null,
+ // currentId: null,
currentIndex: null,
activeStep: 0,
- change: 0
+ change: 0,
+ tutorial: {},
+ progress: false
};
export default function (state = initialState, action) {
switch (action.type) {
+ case TUTORIAL_PROGRESS:
+ return {
+ ...state,
+ progress: !state.progress
+ }
+ case GET_TUTORIAL:
+ return {
+ ...state,
+ tutorial: action.payload
+ };
case TUTORIAL_SUCCESS:
case TUTORIAL_ERROR:
case TUTORIAL_XML:
@@ -65,7 +77,7 @@ export default function (state = initialState, action) {
case TUTORIAL_ID:
return {
...state,
- currentId: action.payload,
+ // currentId: action.payload,
currentIndex: tutorials.findIndex(tutorial => tutorial.id === action.payload)
}
case TUTORIAL_STEP: