Merge pull request #98 from sensebox/remove-mybadges-integration
remove mybadges integration
This commit is contained in:
		
						commit
						f2d50a33c1
					
				
							
								
								
									
										3
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								.env
									
									
									
									
									
								
							| @ -2,8 +2,5 @@ REACT_APP_COMPILER_URL=https://compiler.sensebox.de | ||||
| REACT_APP_BOARD=sensebox-mcu | ||||
| REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de | ||||
| 
 | ||||
| REACT_APP_MYBADGES=https://mybadges.org | ||||
| REACT_APP_MYBADGES_API=https://mybadges.org/api/v1 | ||||
| 
 | ||||
| # in days | ||||
| REACT_APP_SHARE_LINK_EXPIRES=30 | ||||
|  | ||||
| @ -38,7 +38,7 @@ | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "start": "react-scripts start", | ||||
|     "dev": "set \"REACT_APP_BLOCKLY_API=http://localhost:8080\" && set \"REACT_APP_MYBADGES_API=http://localhost:3001/api/v1\"&& npm start", | ||||
|     "dev": "set \"REACT_APP_BLOCKLY_API=http://localhost:8080\" && npm start", | ||||
|     "build": "react-scripts build", | ||||
|     "test": "react-scripts test", | ||||
|     "eject": "react-scripts eject" | ||||
|  | ||||
| @ -1,245 +1,211 @@ | ||||
| import { MYBADGES_CONNECT, MYBADGES_DISCONNECT, GET_STATUS, USER_LOADED, USER_LOADING, AUTH_ERROR, LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT_SUCCESS, LOGOUT_FAIL, REFRESH_TOKEN_SUCCESS } from '../actions/types'; | ||||
| import { | ||||
|   GET_STATUS, | ||||
|   USER_LOADED, | ||||
|   USER_LOADING, | ||||
|   AUTH_ERROR, | ||||
|   LOGIN_SUCCESS, | ||||
|   LOGIN_FAIL, | ||||
|   LOGOUT_SUCCESS, | ||||
|   LOGOUT_FAIL, | ||||
|   REFRESH_TOKEN_SUCCESS, | ||||
| } from "../actions/types"; | ||||
| 
 | ||||
| import axios from 'axios'; | ||||
| import { returnErrors, returnSuccess } from './messageActions'; | ||||
| import { setLanguage } from './generalActions'; | ||||
| import axios from "axios"; | ||||
| import { returnErrors, returnSuccess } from "./messageActions"; | ||||
| import { setLanguage } from "./generalActions"; | ||||
| 
 | ||||
| // Check token & load user
 | ||||
| export const loadUser = () => (dispatch) => { | ||||
|   // user loading
 | ||||
|   dispatch({ | ||||
|     type: USER_LOADING | ||||
|     type: USER_LOADING, | ||||
|   }); | ||||
|   const config = { | ||||
|     success: res => { | ||||
|     success: (res) => { | ||||
|       dispatch({ | ||||
|         type: GET_STATUS, | ||||
|         payload: res.data.user.status | ||||
|         payload: res.data.user.status, | ||||
|       }); | ||||
|       dispatch(setLanguage(res.data.user.language)); | ||||
|       dispatch({ | ||||
|         type: USER_LOADED, | ||||
|         payload: res.data.user | ||||
|         payload: res.data.user, | ||||
|       }); | ||||
|     }, | ||||
|     error: err => { | ||||
|       if(err.response){ | ||||
|     error: (err) => { | ||||
|       if (err.response) { | ||||
|         dispatch(returnErrors(err.response.data.message, err.response.status)); | ||||
|       } | ||||
|       var status = []; | ||||
|       if (window.localStorage.getItem('status')) { | ||||
|         status = JSON.parse(window.localStorage.getItem('status')); | ||||
|       if (window.localStorage.getItem("status")) { | ||||
|         status = JSON.parse(window.localStorage.getItem("status")); | ||||
|       } | ||||
|       dispatch({ | ||||
|         type: GET_STATUS, | ||||
|         payload: status | ||||
|         payload: status, | ||||
|       }); | ||||
|       dispatch({ | ||||
|         type: AUTH_ERROR | ||||
|         type: AUTH_ERROR, | ||||
|       }); | ||||
|     } | ||||
|     }, | ||||
|   }; | ||||
|   axios.get(`${process.env.REACT_APP_BLOCKLY_API}/user`, config, dispatch(authInterceptor())) | ||||
|     .then(res => { | ||||
|   axios | ||||
|     .get( | ||||
|       `${process.env.REACT_APP_BLOCKLY_API}/user`, | ||||
|       config, | ||||
|       dispatch(authInterceptor()) | ||||
|     ) | ||||
|     .then((res) => { | ||||
|       res.config.success(res); | ||||
|     }) | ||||
|     .catch(err => { | ||||
|     .catch((err) => { | ||||
|       err.config.error(err); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| var logoutTimerId; | ||||
| const timeToLogout = 14.9*60*1000; // nearly 15 minutes corresponding to the API
 | ||||
| const timeToLogout = 14.9 * 60 * 1000; // nearly 15 minutes corresponding to the API
 | ||||
| 
 | ||||
| // Login user
 | ||||
| export const login = ({ email, password }) => (dispatch) => { | ||||
| export const login = | ||||
|   ({ email, password }) => | ||||
|   (dispatch) => { | ||||
|     dispatch({ | ||||
|     type: USER_LOADING | ||||
|       type: USER_LOADING, | ||||
|     }); | ||||
|     // Headers
 | ||||
|     const config = { | ||||
|       headers: { | ||||
|       'Content-Type': 'application/json' | ||||
|     } | ||||
|         "Content-Type": "application/json", | ||||
|       }, | ||||
|     }; | ||||
|     // Request Body
 | ||||
|     const body = JSON.stringify({ email, password }); | ||||
|   axios.post(`${process.env.REACT_APP_BLOCKLY_API}/user`, body, config) | ||||
|   .then(res => { | ||||
|     axios | ||||
|       .post(`${process.env.REACT_APP_BLOCKLY_API}/user`, body, config) | ||||
|       .then((res) => { | ||||
|         // Logout automatically if refreshToken "expired"
 | ||||
|     const logoutTimer = () => setTimeout( | ||||
|       () => dispatch(logout()), | ||||
|       timeToLogout | ||||
|     ); | ||||
|         const logoutTimer = () => | ||||
|           setTimeout(() => dispatch(logout()), timeToLogout); | ||||
|         logoutTimerId = logoutTimer(); | ||||
|         dispatch(setLanguage(res.data.user.language)); | ||||
|         dispatch({ | ||||
|           type: LOGIN_SUCCESS, | ||||
|       payload: res.data | ||||
|           payload: res.data, | ||||
|         }); | ||||
|         dispatch({ | ||||
|           type: GET_STATUS, | ||||
|       payload: res.data.user.status | ||||
|           payload: res.data.user.status, | ||||
|         }); | ||||
|     dispatch(returnSuccess(res.data.message, res.status, 'LOGIN_SUCCESS')); | ||||
|         dispatch(returnSuccess(res.data.message, res.status, "LOGIN_SUCCESS")); | ||||
|       }) | ||||
|   .catch(err => { | ||||
|     dispatch(returnErrors(err.response.data.message, err.response.status, 'LOGIN_FAIL')); | ||||
|       .catch((err) => { | ||||
|         dispatch( | ||||
|           returnErrors( | ||||
|             err.response.data.message, | ||||
|             err.response.status, | ||||
|             "LOGIN_FAIL" | ||||
|           ) | ||||
|         ); | ||||
|         dispatch({ | ||||
|       type: LOGIN_FAIL | ||||
|           type: LOGIN_FAIL, | ||||
|         }); | ||||
|         var status = []; | ||||
|     if (window.localStorage.getItem('status')) { | ||||
|       status = JSON.parse(window.localStorage.getItem('status')); | ||||
|         if (window.localStorage.getItem("status")) { | ||||
|           status = JSON.parse(window.localStorage.getItem("status")); | ||||
|         } | ||||
|         dispatch({ | ||||
|           type: GET_STATUS, | ||||
|       payload: status | ||||
|           payload: status, | ||||
|         }); | ||||
|       }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| // Connect to MyBadges-Account
 | ||||
| export const connectMyBadges = ({ username, password }) => (dispatch, getState) => { | ||||
|   const config = { | ||||
|     success: res => { | ||||
|       var user = getState().auth.user; | ||||
|       user.badge = res.data.account; | ||||
|       user.badges = res.data.badges; | ||||
|       dispatch({ | ||||
|         type: MYBADGES_CONNECT, | ||||
|         payload: user | ||||
|       }); | ||||
|       dispatch(returnSuccess(res.data.message, res.status, 'MYBADGES_CONNECT_SUCCESS')); | ||||
|     }, | ||||
|     error: err => { | ||||
|       dispatch(returnErrors(err.response.data.message, err.response.status, 'MYBADGES_CONNECT_FAIL')); | ||||
|     } | ||||
|   }; | ||||
|   // Request Body
 | ||||
|   const body = JSON.stringify({ username, password }); | ||||
|   axios.post(`${process.env.REACT_APP_BLOCKLY_API}/user/badge`, body, config) | ||||
|   .then(res => { | ||||
|     res.config.success(res); | ||||
|   }) | ||||
|   .catch(err => { | ||||
|     if(err.response && err.response.status !== 401){ | ||||
|       err.config.error(err); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| // Disconnect MyBadges-Account
 | ||||
| export const disconnectMyBadges = () => (dispatch, getState) => { | ||||
|   const config = { | ||||
|     success: res => { | ||||
|       var user = getState().auth.user; | ||||
|       user.badge = null; | ||||
|       user.badges = null; | ||||
|       dispatch({ | ||||
|         type: MYBADGES_DISCONNECT, | ||||
|         payload: user | ||||
|       }); | ||||
|       dispatch(returnSuccess(res.data.message, res.status, 'MYBADGES_DISCONNECT_SUCCESS')); | ||||
|     }, | ||||
|     error: err => { | ||||
|       dispatch(returnErrors(err.response.data.message, err.response.status, 'MYBADGES_DISCONNECT_FAIL')); | ||||
|     } | ||||
|   }; | ||||
|   axios.put(`${process.env.REACT_APP_BLOCKLY_API}/user/badge`, {}, config) | ||||
|   .then(res => { | ||||
|     res.config.success(res); | ||||
|   }) | ||||
|   .catch(err => { | ||||
|     if(err.response && err.response.status !== 401){ | ||||
|       err.config.error(err); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| // Logout User
 | ||||
| export const logout = () => (dispatch) => { | ||||
|   const config = { | ||||
|     success: res => { | ||||
|     success: (res) => { | ||||
|       dispatch({ | ||||
|         type: LOGOUT_SUCCESS | ||||
|         type: LOGOUT_SUCCESS, | ||||
|       }); | ||||
|       var status = []; | ||||
|       if (window.localStorage.getItem('status')) { | ||||
|         status = JSON.parse(window.localStorage.getItem('status')); | ||||
|       if (window.localStorage.getItem("status")) { | ||||
|         status = JSON.parse(window.localStorage.getItem("status")); | ||||
|       } | ||||
|       dispatch({ | ||||
|         type: GET_STATUS, | ||||
|         payload: status | ||||
|         payload: status, | ||||
|       }); | ||||
|       var locale = 'en_US'; | ||||
|       if (window.localStorage.getItem('locale')) { | ||||
|         locale = window.localStorage.getItem('locale'); | ||||
|       } | ||||
|       else if (navigator.language === 'de-DE'){ | ||||
|         locale = 'de_DE'; | ||||
|       var locale = "en_US"; | ||||
|       if (window.localStorage.getItem("locale")) { | ||||
|         locale = window.localStorage.getItem("locale"); | ||||
|       } else if (navigator.language === "de-DE") { | ||||
|         locale = "de_DE"; | ||||
|       } | ||||
|       dispatch(setLanguage(locale)); | ||||
|       dispatch(returnSuccess(res.data.message, res.status, 'LOGOUT_SUCCESS')); | ||||
|       dispatch(returnSuccess(res.data.message, res.status, "LOGOUT_SUCCESS")); | ||||
|       clearTimeout(logoutTimerId); | ||||
|     }, | ||||
|     error: err => { | ||||
|       dispatch(returnErrors(err.response.data.message, err.response.status, 'LOGOUT_FAIL')); | ||||
|     error: (err) => { | ||||
|       dispatch( | ||||
|         returnErrors( | ||||
|           err.response.data.message, | ||||
|           err.response.status, | ||||
|           "LOGOUT_FAIL" | ||||
|         ) | ||||
|       ); | ||||
|       dispatch({ | ||||
|         type: LOGOUT_FAIL | ||||
|         type: LOGOUT_FAIL, | ||||
|       }); | ||||
|       var status = []; | ||||
|       if (window.localStorage.getItem('status')) { | ||||
|         status = JSON.parse(window.localStorage.getItem('status')); | ||||
|       if (window.localStorage.getItem("status")) { | ||||
|         status = JSON.parse(window.localStorage.getItem("status")); | ||||
|       } | ||||
|       dispatch({ | ||||
|         type: GET_STATUS, | ||||
|         payload: status | ||||
|         payload: status, | ||||
|       }); | ||||
|       clearTimeout(logoutTimerId); | ||||
|     } | ||||
|     }, | ||||
|   }; | ||||
|   axios.post('https://api.opensensemap.org/users/sign-out', {}, config) | ||||
|   .then(res => { | ||||
|   axios | ||||
|     .post("https://api.opensensemap.org/users/sign-out", {}, config) | ||||
|     .then((res) => { | ||||
|       res.config.success(res); | ||||
|     }) | ||||
|   .catch(err => { | ||||
|     if(err.response && err.response.status !== 401){ | ||||
|     .catch((err) => { | ||||
|       if (err.response && err.response.status !== 401) { | ||||
|         err.config.error(err); | ||||
|       } | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| export const authInterceptor = () => (dispatch, getState) => { | ||||
|   // Add a request interceptor
 | ||||
|   axios.interceptors.request.use( | ||||
|     config => { | ||||
|       config.headers['Content-Type'] = 'application/json'; | ||||
|     (config) => { | ||||
|       config.headers["Content-Type"] = "application/json"; | ||||
|       const token = getState().auth.token; | ||||
|       if (token) { | ||||
|        config.headers['Authorization'] = `Bearer ${token}`; | ||||
|         config.headers["Authorization"] = `Bearer ${token}`; | ||||
|       } | ||||
|       return config; | ||||
|     }, | ||||
|    error => { | ||||
|     (error) => { | ||||
|       Promise.reject(error); | ||||
|     } | ||||
|   ); | ||||
| 
 | ||||
|   // Add a response interceptor
 | ||||
|   axios.interceptors.response.use( | ||||
|     response => { | ||||
|     (response) => { | ||||
|       // request was successfull
 | ||||
|       return response; | ||||
|     }, | ||||
|     error => { | ||||
|     (error) => { | ||||
|       const originalRequest = error.config; | ||||
|       const refreshToken = getState().auth.refreshToken; | ||||
|       if(refreshToken){ | ||||
|       if (refreshToken) { | ||||
|         // try to refresh the token failed
 | ||||
|         if (error.response.status === 401 && originalRequest._retry) { | ||||
|           // router.push('/login');
 | ||||
| @ -250,38 +216,42 @@ export const authInterceptor = () => (dispatch, getState) => { | ||||
|           originalRequest._retry = true; | ||||
|           const refreshToken = getState().auth.refreshToken; | ||||
|           // request to refresh the token, in request-body is the refreshToken
 | ||||
|           axios.post('https://api.opensensemap.org/users/refresh-auth', {"token": refreshToken}) | ||||
|                .then(res => { | ||||
|           axios | ||||
|             .post("https://api.opensensemap.org/users/refresh-auth", { | ||||
|               token: refreshToken, | ||||
|             }) | ||||
|             .then((res) => { | ||||
|               if (res.status === 200) { | ||||
|                 clearTimeout(logoutTimerId); | ||||
|                    const logoutTimer = () => setTimeout( | ||||
|                      () => dispatch(logout()), | ||||
|                      timeToLogout | ||||
|                    ); | ||||
|                 const logoutTimer = () => | ||||
|                   setTimeout(() => dispatch(logout()), timeToLogout); | ||||
|                 logoutTimerId = logoutTimer(); | ||||
|                 dispatch({ | ||||
|                   type: REFRESH_TOKEN_SUCCESS, | ||||
|                      payload: res.data | ||||
|                   payload: res.data, | ||||
|                 }); | ||||
|                    axios.defaults.headers.common['Authorization'] = 'Bearer ' + getState().auth.token; | ||||
|                 axios.defaults.headers.common["Authorization"] = | ||||
|                   "Bearer " + getState().auth.token; | ||||
|                 // request was successfull, new request with the old parameters and the refreshed token
 | ||||
|                 return axios(originalRequest) | ||||
|                           .then(res => { | ||||
|                   .then((res) => { | ||||
|                     originalRequest.success(res); | ||||
|                   }) | ||||
|                            .catch(err => { | ||||
|                   .catch((err) => { | ||||
|                     originalRequest.error(err); | ||||
|                   }); | ||||
|               } | ||||
|               return Promise.reject(error); | ||||
|             }) | ||||
|                .catch(err => { | ||||
|             .catch((err) => { | ||||
|               // request failed, token could not be refreshed
 | ||||
|                  if(err.response){ | ||||
|                    dispatch(returnErrors(err.response.data.message, err.response.status)); | ||||
|               if (err.response) { | ||||
|                 dispatch( | ||||
|                   returnErrors(err.response.data.message, err.response.status) | ||||
|                 ); | ||||
|               } | ||||
|               dispatch({ | ||||
|                    type: AUTH_ERROR | ||||
|                 type: AUTH_ERROR, | ||||
|               }); | ||||
|               return Promise.reject(error); | ||||
|             }); | ||||
|  | ||||
| @ -1,107 +1,105 @@ | ||||
| import { MYBADGES_DISCONNECT, TUTORIAL_PROGRESS, GET_TUTORIAL, GET_TUTORIALS, TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE, TUTORIAL_XML, TUTORIAL_STEP } from './types'; | ||||
| import { | ||||
|   TUTORIAL_PROGRESS, | ||||
|   GET_TUTORIAL, | ||||
|   GET_TUTORIALS, | ||||
|   TUTORIAL_SUCCESS, | ||||
|   TUTORIAL_ERROR, | ||||
|   TUTORIAL_CHANGE, | ||||
|   TUTORIAL_XML, | ||||
|   TUTORIAL_STEP, | ||||
| } from "./types"; | ||||
| 
 | ||||
| import axios from 'axios'; | ||||
| import { returnErrors, returnSuccess } from './messageActions'; | ||||
| import axios from "axios"; | ||||
| import { returnErrors, returnSuccess } from "./messageActions"; | ||||
| 
 | ||||
| export const tutorialProgress = () => (dispatch) => { | ||||
|   dispatch({type: TUTORIAL_PROGRESS}); | ||||
|   dispatch({ type: TUTORIAL_PROGRESS }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| export const getTutorial = (id) => (dispatch, getState) => { | ||||
|   axios.get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/${id}`) | ||||
|     .then(res => { | ||||
|   axios | ||||
|     .get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/${id}`) | ||||
|     .then((res) => { | ||||
|       var tutorial = res.data.tutorial; | ||||
|       existingTutorial(tutorial, getState().tutorial.status).then(status => { | ||||
|       existingTutorial(tutorial, getState().tutorial.status).then((status) => { | ||||
|         dispatch({ | ||||
|           type: TUTORIAL_SUCCESS, | ||||
|           payload: status | ||||
|           payload: status, | ||||
|         }); | ||||
|         dispatch(updateStatus(status)); | ||||
|         dispatch({ | ||||
|           type: GET_TUTORIAL, | ||||
|           payload: tutorial | ||||
|           payload: tutorial, | ||||
|         }); | ||||
|         dispatch({type: TUTORIAL_PROGRESS}); | ||||
|         dispatch({ type: TUTORIAL_PROGRESS }); | ||||
|         dispatch(returnSuccess(res.data.message, res.status)); | ||||
|       }); | ||||
|     }) | ||||
|     .catch(err => { | ||||
|     .catch((err) => { | ||||
|       if (err.response) { | ||||
|         dispatch(returnErrors(err.response.data.message, err.response.status, 'GET_TUTORIAL_FAIL')); | ||||
|         dispatch( | ||||
|           returnErrors( | ||||
|             err.response.data.message, | ||||
|             err.response.status, | ||||
|             "GET_TUTORIAL_FAIL" | ||||
|           ) | ||||
|         ); | ||||
|       } | ||||
|       dispatch({ type: TUTORIAL_PROGRESS }); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| export const getTutorials = () => (dispatch, getState) => { | ||||
|   axios.get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial`) | ||||
|     .then(res => { | ||||
|   axios | ||||
|     .get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial`) | ||||
|     .then((res) => { | ||||
|       var tutorials = res.data.tutorials; | ||||
|       existingTutorials(tutorials, getState().tutorial.status).then(status => { | ||||
|       existingTutorials(tutorials, getState().tutorial.status).then( | ||||
|         (status) => { | ||||
|           dispatch({ | ||||
|             type: TUTORIAL_SUCCESS, | ||||
|           payload: status | ||||
|             payload: status, | ||||
|           }); | ||||
|           dispatch(updateStatus(status)); | ||||
|           dispatch({ | ||||
|             type: GET_TUTORIALS, | ||||
|           payload: tutorials | ||||
|             payload: tutorials, | ||||
|           }); | ||||
|           dispatch({ type: TUTORIAL_PROGRESS }); | ||||
|           dispatch(returnSuccess(res.data.message, res.status)); | ||||
|       }); | ||||
|         } | ||||
|       ); | ||||
|     }) | ||||
|     .catch(err => { | ||||
|     .catch((err) => { | ||||
|       if (err.response) { | ||||
|         dispatch(returnErrors(err.response.data.message, err.response.status, 'GET_TUTORIALS_FAIL')); | ||||
|         dispatch( | ||||
|           returnErrors( | ||||
|             err.response.data.message, | ||||
|             err.response.status, | ||||
|             "GET_TUTORIALS_FAIL" | ||||
|           ) | ||||
|         ); | ||||
|       } | ||||
|       dispatch({ type: TUTORIAL_PROGRESS }); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| export const assigneBadge = (id) => (dispatch, getState) => { | ||||
|   const config = { | ||||
|     success: res => { | ||||
|       var badge = res.data.badge; | ||||
|       var user = getState().auth.user; | ||||
|       user.badges.push(badge._id); | ||||
|       dispatch({ | ||||
|         type: MYBADGES_DISCONNECT, | ||||
|         payload: user | ||||
|       }); | ||||
|       dispatch(returnSuccess(badge, res.status, 'ASSIGNE_BADGE_SUCCESS')); | ||||
|     }, | ||||
|     error: err => { | ||||
|       dispatch(returnErrors(err.response.data.message, err.response.status, 'ASSIGNE_BADGE_FAIL')); | ||||
|     } | ||||
|   }; | ||||
|   axios.put(`${process.env.REACT_APP_BLOCKLY_API}/user/badge/${id}`, {}, config) | ||||
|     .then(res => { | ||||
|       res.config.success(res); | ||||
|     }) | ||||
|     .catch(err => { | ||||
|       if(err.response && err.response.status !== 401){ | ||||
|         err.config.error(err); | ||||
|       } | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| export const updateStatus = (status) => (dispatch, getState) => { | ||||
|   if(getState().auth.isAuthenticated){ | ||||
|   if (getState().auth.isAuthenticated) { | ||||
|     // update user account in database - sync with redux store
 | ||||
|     axios.put(`${process.env.REACT_APP_BLOCKLY_API}/user/status`, {status: status}) | ||||
|       .then(res => { | ||||
|         // dispatch(returnSuccess(badge, res.status, 'UPDATE_STATUS_SUCCESS'));
 | ||||
|     axios | ||||
|       .put(`${process.env.REACT_APP_BLOCKLY_API}/user/status`, { | ||||
|         status: status, | ||||
|       }) | ||||
|       .catch(err => { | ||||
|         if(err.response){ | ||||
|       .then((res) => {}) | ||||
|       .catch((err) => { | ||||
|         if (err.response) { | ||||
|           // dispatch(returnErrors(err.response.data.message, err.response.status, 'UPDATE_STATUS_FAIL'));
 | ||||
|         } | ||||
|       }); | ||||
|   } else { | ||||
|     // update locale storage - sync with redux store
 | ||||
|     window.localStorage.setItem('status', JSON.stringify(status)); | ||||
|     window.localStorage.setItem("status", JSON.stringify(status)); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| @ -109,65 +107,77 @@ export const deleteTutorial = (id) => (dispatch, getState) => { | ||||
|   var tutorial = getState().tutorial; | ||||
|   var id = getState().builder.id; | ||||
|   const config = { | ||||
|     success: res => { | ||||
|     success: (res) => { | ||||
|       var tutorials = tutorial.tutorials; | ||||
|       var index = tutorials.findIndex(res => res._id === id); | ||||
|       tutorials.splice(index, 1) | ||||
|       var index = tutorials.findIndex((res) => res._id === id); | ||||
|       tutorials.splice(index, 1); | ||||
|       dispatch({ | ||||
|         type: GET_TUTORIALS, | ||||
|         payload: tutorials | ||||
|         payload: tutorials, | ||||
|       }); | ||||
|       dispatch(returnSuccess(res.data.message, res.status, 'TUTORIAL_DELETE_SUCCESS')); | ||||
|       dispatch( | ||||
|         returnSuccess(res.data.message, res.status, "TUTORIAL_DELETE_SUCCESS") | ||||
|       ); | ||||
|     }, | ||||
|     error: (err) => { | ||||
|       dispatch( | ||||
|         returnErrors( | ||||
|           err.response.data.message, | ||||
|           err.response.status, | ||||
|           "TUTORIAL_DELETE_FAIL" | ||||
|         ) | ||||
|       ); | ||||
|     }, | ||||
|     error: err => { | ||||
|       dispatch(returnErrors(err.response.data.message, err.response.status, 'TUTORIAL_DELETE_FAIL')); | ||||
|     } | ||||
|   }; | ||||
|   axios.delete(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/${id}`, config) | ||||
|     .then(res => { | ||||
|   axios | ||||
|     .delete(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/${id}`, config) | ||||
|     .then((res) => { | ||||
|       res.config.success(res); | ||||
|     }) | ||||
|     .catch(err => { | ||||
|       if(err.response && err.response.status !== 401){ | ||||
|     .catch((err) => { | ||||
|       if (err.response && err.response.status !== 401) { | ||||
|         err.config.error(err); | ||||
|       } | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| export const resetTutorial = () => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: GET_TUTORIALS, | ||||
|     payload: [] | ||||
|     payload: [], | ||||
|   }); | ||||
|   dispatch({ | ||||
|     type: TUTORIAL_STEP, | ||||
|     payload: 0 | ||||
|     payload: 0, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const tutorialChange = () => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: TUTORIAL_CHANGE | ||||
|     type: TUTORIAL_CHANGE, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const tutorialCheck = (status, step) => (dispatch, getState) => { | ||||
|   var tutorialsStatus = getState().tutorial.status; | ||||
|   var id = getState().tutorial.tutorials[0]._id; | ||||
|   var tutorialsStatusIndex = tutorialsStatus.findIndex(tutorialStatus => tutorialStatus._id === id); | ||||
|   var tasksIndex = tutorialsStatus[tutorialsStatusIndex].tasks.findIndex(task => task._id === step._id); | ||||
|   var tutorialsStatusIndex = tutorialsStatus.findIndex( | ||||
|     (tutorialStatus) => tutorialStatus._id === id | ||||
|   ); | ||||
|   var tasksIndex = tutorialsStatus[tutorialsStatusIndex].tasks.findIndex( | ||||
|     (task) => task._id === step._id | ||||
|   ); | ||||
|   tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex] = { | ||||
|     ...tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex], | ||||
|     type: status | ||||
|     type: status, | ||||
|   }; | ||||
|   dispatch({ | ||||
|     type: status === 'success' ? TUTORIAL_SUCCESS : TUTORIAL_ERROR, | ||||
|     payload: tutorialsStatus | ||||
|     type: status === "success" ? TUTORIAL_SUCCESS : TUTORIAL_ERROR, | ||||
|     payload: tutorialsStatus, | ||||
|   }); | ||||
|   dispatch(updateStatus(tutorialsStatus)); | ||||
|   dispatch(tutorialChange()); | ||||
|   dispatch(returnSuccess('', '', 'TUTORIAL_CHECK_SUCCESS')); | ||||
|   dispatch(returnSuccess("", "", "TUTORIAL_CHECK_SUCCESS")); | ||||
| }; | ||||
| 
 | ||||
| export const storeTutorialXml = (code) => (dispatch, getState) => { | ||||
| @ -176,17 +186,21 @@ export const storeTutorialXml = (code) => (dispatch, getState) => { | ||||
|     var id = tutorial._id; | ||||
|     var activeStep = getState().tutorial.activeStep; | ||||
|     var steps = tutorial.steps; | ||||
|     if (steps && steps[activeStep].type === 'task') { | ||||
|     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); | ||||
|       var tutorialsStatusIndex = tutorialsStatus.findIndex( | ||||
|         (tutorialStatus) => tutorialStatus._id === id | ||||
|       ); | ||||
|       var tasksIndex = tutorialsStatus[tutorialsStatusIndex].tasks.findIndex( | ||||
|         (task) => task._id === steps[activeStep]._id | ||||
|       ); | ||||
|       tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex] = { | ||||
|         ...tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex], | ||||
|         xml: code | ||||
|         xml: code, | ||||
|       }; | ||||
|       dispatch({ | ||||
|         type: TUTORIAL_XML, | ||||
|         payload: tutorialsStatus | ||||
|         payload: tutorialsStatus, | ||||
|       }); | ||||
|       dispatch(updateStatus(tutorialsStatus)); | ||||
|     } | ||||
| @ -196,38 +210,45 @@ export const storeTutorialXml = (code) => (dispatch, getState) => { | ||||
| export const tutorialStep = (step) => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: TUTORIAL_STEP, | ||||
|     payload: step | ||||
|     payload: step, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| const existingTutorials = (tutorials, status) => new Promise(function (resolve, reject) { | ||||
| const existingTutorials = (tutorials, status) => | ||||
|   new Promise(function (resolve, reject) { | ||||
|     var newstatus; | ||||
|     new Promise(function (resolve, reject) { | ||||
|       var existingTutorialIds = tutorials.map((tutorial, i) => { | ||||
|       existingTutorial(tutorial, status).then(status => { | ||||
|         existingTutorial(tutorial, status).then((status) => { | ||||
|           newstatus = status; | ||||
|         }); | ||||
|         return tutorial._id; | ||||
|       }); | ||||
|     resolve(existingTutorialIds) | ||||
|   }).then(existingTutorialIds => { | ||||
|       resolve(existingTutorialIds); | ||||
|     }).then((existingTutorialIds) => { | ||||
|       // deleting old tutorials which do not longer exist
 | ||||
|       if (existingTutorialIds.length > 0) { | ||||
|       status = newstatus.filter(status => existingTutorialIds.indexOf(status._id) > -1); | ||||
|         status = newstatus.filter( | ||||
|           (status) => existingTutorialIds.indexOf(status._id) > -1 | ||||
|         ); | ||||
|       } | ||||
|       resolve(status); | ||||
|     }); | ||||
| }); | ||||
|   }); | ||||
| 
 | ||||
| const existingTutorial = (tutorial, status) => new Promise(function(resolve, reject){ | ||||
| const existingTutorial = (tutorial, status) => | ||||
|   new Promise(function (resolve, reject) { | ||||
|     var tutorialsId = tutorial._id; | ||||
|   var statusIndex = status.findIndex(status => status._id === tutorialsId); | ||||
|     var statusIndex = status.findIndex((status) => status._id === tutorialsId); | ||||
|     if (statusIndex > -1) { | ||||
|     var tasks = tutorial.steps.filter(step => step.type === 'task'); | ||||
|       var tasks = tutorial.steps.filter((step) => step.type === "task"); | ||||
|       var existingTaskIds = tasks.map((task, j) => { | ||||
|         var tasksId = task._id; | ||||
|       if (status[statusIndex].tasks.findIndex(task => task._id === tasksId) === -1) { | ||||
|         if ( | ||||
|           status[statusIndex].tasks.findIndex( | ||||
|             (task) => task._id === tasksId | ||||
|           ) === -1 | ||||
|         ) { | ||||
|           // task does not exist
 | ||||
|           status[statusIndex].tasks.push({ _id: tasksId }); | ||||
|         } | ||||
| @ -235,11 +256,19 @@ const existingTutorial = (tutorial, status) => new Promise(function(resolve, rej | ||||
|       }); | ||||
|       // deleting old tasks which do not longer exist
 | ||||
|       if (existingTaskIds.length > 0) { | ||||
|       status[statusIndex].tasks = status[statusIndex].tasks.filter(task => existingTaskIds.indexOf(task._id) > -1); | ||||
|         status[statusIndex].tasks = status[statusIndex].tasks.filter( | ||||
|           (task) => existingTaskIds.indexOf(task._id) > -1 | ||||
|         ); | ||||
|       } | ||||
|   } | ||||
|   else { | ||||
|     status.push({ _id: tutorialsId, tasks: tutorial.steps.filter(step => step.type === 'task').map(task => { return { _id: task._id }; }) }); | ||||
|     } else { | ||||
|       status.push({ | ||||
|         _id: tutorialsId, | ||||
|         tasks: tutorial.steps | ||||
|           .filter((step) => step.type === "task") | ||||
|           .map((task) => { | ||||
|             return { _id: task._id }; | ||||
|           }), | ||||
|       }); | ||||
|     } | ||||
|     resolve(status); | ||||
| }); | ||||
|   }); | ||||
|  | ||||
| @ -1,24 +1,36 @@ | ||||
| import { PROGRESS, JSON_STRING, BUILDER_CHANGE, BUILDER_ERROR, BUILDER_TITLE, BUILDER_ID, BUILDER_BADGE, BUILDER_ADD_STEP, BUILDER_DELETE_STEP, BUILDER_CHANGE_STEP, BUILDER_CHANGE_ORDER, BUILDER_DELETE_PROPERTY } from './types'; | ||||
| import { | ||||
|   PROGRESS, | ||||
|   JSON_STRING, | ||||
|   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 data from '../data/hardware.json'; | ||||
| import data from "../data/hardware.json"; | ||||
| 
 | ||||
| export const changeTutorialBuilder = () => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: BUILDER_CHANGE | ||||
|     type: BUILDER_CHANGE, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const jsonString = (json) => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: JSON_STRING, | ||||
|     payload: json | ||||
|     payload: json, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const tutorialTitle = (title) => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: BUILDER_TITLE, | ||||
|     payload: title | ||||
|     payload: title, | ||||
|   }); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
| @ -26,7 +38,7 @@ export const tutorialTitle = (title) => (dispatch) => { | ||||
| export const tutorialSteps = (steps) => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: BUILDER_ADD_STEP, | ||||
|     payload: steps | ||||
|     payload: steps, | ||||
|   }); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
| @ -34,15 +46,7 @@ export const tutorialSteps = (steps) => (dispatch) => { | ||||
| export const tutorialId = (id) => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: BUILDER_ID, | ||||
|     payload: id | ||||
|   }); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
| 
 | ||||
| export const tutorialBadge = (badge) => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: BUILDER_BADGE, | ||||
|     payload: badge | ||||
|     payload: id, | ||||
|   }); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
| @ -51,14 +55,14 @@ export const addStep = (index) => (dispatch, getState) => { | ||||
|   var steps = getState().builder.steps; | ||||
|   var step = { | ||||
|     id: index + 1, | ||||
|     type: 'instruction', | ||||
|     headline: '', | ||||
|     text: '' | ||||
|     type: "instruction", | ||||
|     headline: "", | ||||
|     text: "", | ||||
|   }; | ||||
|   steps.splice(index, 0, step); | ||||
|   dispatch({ | ||||
|     type: BUILDER_ADD_STEP, | ||||
|     payload: steps | ||||
|     payload: steps, | ||||
|   }); | ||||
|   dispatch(addErrorStep(index)); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| @ -69,7 +73,7 @@ export const addErrorStep = (index) => (dispatch, getState) => { | ||||
|   error.steps.splice(index, 0, {}); | ||||
|   dispatch({ | ||||
|     type: BUILDER_ERROR, | ||||
|     payload: error | ||||
|     payload: error, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| @ -78,7 +82,7 @@ export const removeStep = (index) => (dispatch, getState) => { | ||||
|   steps.splice(index, 1); | ||||
|   dispatch({ | ||||
|     type: BUILDER_DELETE_STEP, | ||||
|     payload: steps | ||||
|     payload: steps, | ||||
|   }); | ||||
|   dispatch(removeErrorStep(index)); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| @ -89,11 +93,12 @@ export const removeErrorStep = (index) => (dispatch, getState) => { | ||||
|   error.steps.splice(index, 1); | ||||
|   dispatch({ | ||||
|     type: BUILDER_ERROR, | ||||
|     payload: error | ||||
|     payload: error, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const changeContent = (content, index, property1, property2) => (dispatch, getState) => { | ||||
| export const changeContent = | ||||
|   (content, index, property1, property2) => (dispatch, getState) => { | ||||
|     var steps = getState().builder.steps; | ||||
|     var step = steps[index]; | ||||
|     if (property2) { | ||||
| @ -107,12 +112,13 @@ export const changeContent = (content, index, property1, property2) => (dispatch | ||||
|     } | ||||
|     dispatch({ | ||||
|       type: BUILDER_CHANGE_STEP, | ||||
|     payload: steps | ||||
|       payload: steps, | ||||
|     }); | ||||
|     dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
|   }; | ||||
| 
 | ||||
| export const deleteProperty = (index, property1, property2) => (dispatch, getState) => { | ||||
| export const deleteProperty = | ||||
|   (index, property1, property2) => (dispatch, getState) => { | ||||
|     var steps = getState().builder.steps; | ||||
|     var step = steps[index]; | ||||
|     if (property2) { | ||||
| @ -124,10 +130,10 @@ export const deleteProperty = (index, property1, property2) => (dispatch, getSta | ||||
|     } | ||||
|     dispatch({ | ||||
|       type: BUILDER_DELETE_PROPERTY, | ||||
|     payload: steps | ||||
|       payload: steps, | ||||
|     }); | ||||
|     dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
|   }; | ||||
| 
 | ||||
| export const changeStepIndex = (fromIndex, toIndex) => (dispatch, getState) => { | ||||
|   var steps = getState().builder.steps; | ||||
| @ -136,34 +142,34 @@ export const changeStepIndex = (fromIndex, toIndex) => (dispatch, getState) => { | ||||
|   steps.splice(toIndex, 0, step); | ||||
|   dispatch({ | ||||
|     type: BUILDER_CHANGE_ORDER, | ||||
|     payload: steps | ||||
|     payload: steps, | ||||
|   }); | ||||
|   dispatch(changeErrorStepIndex(fromIndex, toIndex)); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
| 
 | ||||
| export const changeErrorStepIndex = (fromIndex, toIndex) => (dispatch, getState) => { | ||||
| export const changeErrorStepIndex = | ||||
|   (fromIndex, toIndex) => (dispatch, getState) => { | ||||
|     var error = getState().builder.error; | ||||
|     var errorStep = error.steps[fromIndex]; | ||||
|     error.steps.splice(fromIndex, 1); | ||||
|     error.steps.splice(toIndex, 0, errorStep); | ||||
|     dispatch({ | ||||
|       type: BUILDER_ERROR, | ||||
|     payload: error | ||||
|       payload: error, | ||||
|     }); | ||||
| }; | ||||
|   }; | ||||
| 
 | ||||
| export const setError = (index, property) => (dispatch, getState) => { | ||||
|   var error = getState().builder.error; | ||||
|   if (index !== undefined) { | ||||
|     error.steps[index][property] = true; | ||||
|   } | ||||
|   else { | ||||
|   } else { | ||||
|     error[property] = true; | ||||
|   } | ||||
|   dispatch({ | ||||
|     type: BUILDER_ERROR, | ||||
|     payload: error | ||||
|     payload: error, | ||||
|   }); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
| @ -172,13 +178,12 @@ export const deleteError = (index, property) => (dispatch, getState) => { | ||||
|   var error = getState().builder.error; | ||||
|   if (index !== undefined) { | ||||
|     delete error.steps[index][property]; | ||||
|   } | ||||
|   else { | ||||
|   } else { | ||||
|     delete error[property]; | ||||
|   } | ||||
|   dispatch({ | ||||
|     type: BUILDER_ERROR, | ||||
|     payload: error | ||||
|     payload: error, | ||||
|   }); | ||||
|   dispatch(changeTutorialBuilder()); | ||||
| }; | ||||
| @ -188,11 +193,11 @@ export const setSubmitError = () => (dispatch, getState) => { | ||||
|   // if(builder.id === undefined || builder.id === ''){
 | ||||
|   //   dispatch(setError(undefined, 'id'));
 | ||||
|   // }
 | ||||
|   if (builder.title === '') { | ||||
|     dispatch(setError(undefined, 'title')); | ||||
|   if (builder.title === "") { | ||||
|     dispatch(setError(undefined, "title")); | ||||
|   } | ||||
|   if (builder.title === null) { | ||||
|     dispatch(setError(undefined, 'badge')); | ||||
|     dispatch(setError(undefined, "title")); | ||||
|   } | ||||
|   var type = builder.steps.map((step, i) => { | ||||
|     // media and xml are directly checked for errors in their components and
 | ||||
| @ -200,76 +205,82 @@ export const setSubmitError = () => (dispatch, getState) => { | ||||
|     step.id = i + 1; | ||||
|     if (i === 0) { | ||||
|       if (step.requirements && step.requirements.length > 0) { | ||||
|         var requirements = step.requirements.filter(requirement => /^[0-9a-fA-F]{24}$/.test(requirement)); | ||||
|         var requirements = step.requirements.filter((requirement) => | ||||
|           /^[0-9a-fA-F]{24}$/.test(requirement) | ||||
|         ); | ||||
|         if (requirements.length < step.requirements.length) { | ||||
|           dispatch(changeContent(requirements, i, 'requirements')); | ||||
|           dispatch(changeContent(requirements, i, "requirements")); | ||||
|         } | ||||
|       } | ||||
|       if (step.hardware === undefined || step.hardware.length < 1) { | ||||
|         dispatch(setError(i, 'hardware')); | ||||
|       } | ||||
|       else { | ||||
|         var hardwareIds = data.map(hardware => hardware.id); | ||||
|         var hardware = step.hardware.filter(hardware => hardwareIds.includes(hardware)); | ||||
|         dispatch(setError(i, "hardware")); | ||||
|       } else { | ||||
|         var hardwareIds = data.map((hardware) => hardware.id); | ||||
|         var hardware = step.hardware.filter((hardware) => | ||||
|           hardwareIds.includes(hardware) | ||||
|         ); | ||||
|         if (hardware.length < step.hardware.length) { | ||||
|           dispatch(changeContent(hardware, i, 'hardware')); | ||||
|           dispatch(changeContent(hardware, i, "hardware")); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     if (step.headline === undefined || step.headline === '') { | ||||
|       dispatch(setError(i, 'headline')); | ||||
|     if (step.headline === undefined || step.headline === "") { | ||||
|       dispatch(setError(i, "headline")); | ||||
|     } | ||||
|     if (step.text === undefined || step.text === '') { | ||||
|       dispatch(setError(i, 'text')); | ||||
|     if (step.text === undefined || step.text === "") { | ||||
|       dispatch(setError(i, "text")); | ||||
|     } | ||||
|     return step.type; | ||||
|   }); | ||||
|   if (!(type.filter(item => item === 'task').length > 0 && type.filter(item => item === 'instruction').length > 0)) { | ||||
|     dispatch(setError(undefined, 'type')); | ||||
|   if ( | ||||
|     !( | ||||
|       type.filter((item) => item === "task").length > 0 && | ||||
|       type.filter((item) => item === "instruction").length > 0 | ||||
|     ) | ||||
|   ) { | ||||
|     dispatch(setError(undefined, "type")); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| export const checkError = () => (dispatch, getState) => { | ||||
|   dispatch(setSubmitError()); | ||||
|   var error = getState().builder.error; | ||||
|   if (error.id || error.title || error.badge ||error.type) { | ||||
|   if (error.id || error.title || error.type) { | ||||
|     return true; | ||||
|   } | ||||
|   for (var i = 0; i < error.steps.length; i++) { | ||||
|     if (Object.keys(error.steps[i]).length > 0) { | ||||
|       return true | ||||
|       return true; | ||||
|     } | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| }; | ||||
| 
 | ||||
| export const progress = (inProgress) => (dispatch) => { | ||||
|   dispatch({ | ||||
|     type: PROGRESS, | ||||
|     payload: inProgress | ||||
|   }) | ||||
|     payload: inProgress, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const resetTutorial = () => (dispatch, getState) => { | ||||
|   dispatch(jsonString('')); | ||||
|   dispatch(tutorialTitle('')); | ||||
|   dispatch(tutorialBadge(undefined)); | ||||
|   dispatch(jsonString("")); | ||||
|   dispatch(tutorialTitle("")); | ||||
|   var steps = [ | ||||
|     { | ||||
|       type: 'instruction', | ||||
|       headline: '', | ||||
|       text: '', | ||||
|       type: "instruction", | ||||
|       headline: "", | ||||
|       text: "", | ||||
|       hardware: [], | ||||
|       requirements: [] | ||||
|     } | ||||
|       requirements: [], | ||||
|     }, | ||||
|   ]; | ||||
|   dispatch(tutorialSteps(steps)); | ||||
|   dispatch({ | ||||
|     type: BUILDER_ERROR, | ||||
|     payload: { | ||||
|       steps: [{}] | ||||
|     } | ||||
|       steps: [{}], | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| @ -278,8 +289,10 @@ export const readJSON = (json) => (dispatch, getState) => { | ||||
|   dispatch({ | ||||
|     type: BUILDER_ERROR, | ||||
|     payload: { | ||||
|       steps: json.steps.map(() => { return {}; }) | ||||
|     } | ||||
|       steps: json.steps.map(() => { | ||||
|         return {}; | ||||
|       }), | ||||
|     }, | ||||
|   }); | ||||
|   // accept only valid attributes
 | ||||
|   var steps = json.steps.map((step, i) => { | ||||
| @ -287,7 +300,7 @@ export const readJSON = (json) => (dispatch, getState) => { | ||||
|       _id: step._id, | ||||
|       type: step.type, | ||||
|       headline: step.headline, | ||||
|       text: step.text | ||||
|       text: step.text, | ||||
|     }; | ||||
|     if (i === 0) { | ||||
|       object.hardware = step.hardware; | ||||
| @ -296,19 +309,17 @@ export const readJSON = (json) => (dispatch, getState) => { | ||||
|     if (step.xml) { | ||||
|       object.xml = step.xml; | ||||
|     } | ||||
|     if (step.media && step.type === 'instruction') { | ||||
|     if (step.media && step.type === "instruction") { | ||||
|       object.media = {}; | ||||
|       if (step.media.picture) { | ||||
|         object.media.picture = step.media.picture; | ||||
|       } | ||||
|       else if (step.media.youtube) { | ||||
|       } else if (step.media.youtube) { | ||||
|         object.media.youtube = step.media.youtube; | ||||
|       } | ||||
|     } | ||||
|     return object; | ||||
|   }); | ||||
|   dispatch(tutorialTitle(json.title)); | ||||
|   dispatch(tutorialBadge(json.badge)); | ||||
|   dispatch(tutorialSteps(steps)); | ||||
|   dispatch(setSubmitError()); | ||||
|   dispatch(progress(false)); | ||||
|  | ||||
| @ -1,65 +1,59 @@ | ||||
| // authentication
 | ||||
| export const USER_LOADING = 'USER_LOADING'; | ||||
| export const USER_LOADED = 'USER_LOADED'; | ||||
| export const AUTH_ERROR = 'AUTH_ERROR'; | ||||
| export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; | ||||
| export const LOGIN_FAIL = 'LOGIN_FAIL'; | ||||
| export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; | ||||
| export const LOGOUT_FAIL = 'LOGOUT_FAIL'; | ||||
| export const REFRESH_TOKEN_FAIL = 'REFRESH_TOKEN_FAIL'; | ||||
| export const REFRESH_TOKEN_SUCCESS = 'REFRESH_TOKEN_SUCCESS'; | ||||
| export const MYBADGES_CONNECT = 'MYBADGES_CONNECT'; | ||||
| export const MYBADGES_DISCONNECT = 'MYBADGES_DISCONNECT'; | ||||
| export const USER_LOADING = "USER_LOADING"; | ||||
| export const USER_LOADED = "USER_LOADED"; | ||||
| export const AUTH_ERROR = "AUTH_ERROR"; | ||||
| export const LOGIN_SUCCESS = "LOGIN_SUCCESS"; | ||||
| export const LOGIN_FAIL = "LOGIN_FAIL"; | ||||
| export const LOGOUT_SUCCESS = "LOGOUT_SUCCESS"; | ||||
| export const LOGOUT_FAIL = "LOGOUT_FAIL"; | ||||
| export const REFRESH_TOKEN_FAIL = "REFRESH_TOKEN_FAIL"; | ||||
| export const REFRESH_TOKEN_SUCCESS = "REFRESH_TOKEN_SUCCESS"; | ||||
| 
 | ||||
| export const NEW_CODE = 'NEW_CODE'; | ||||
| export const CHANGE_WORKSPACE = 'CHANGE_WORKSPACE'; | ||||
| export const CREATE_BLOCK = 'CREATE_BLOCK'; | ||||
| export const MOVE_BLOCK = 'MOVE_BLOCK'; | ||||
| export const CHANGE_BLOCK = 'CHANGE_BLOCK'; | ||||
| export const DELETE_BLOCK = 'DELETE_BLOCK'; | ||||
| export const CLEAR_STATS = 'CLEAR_STATS'; | ||||
| export const NAME = 'NAME'; | ||||
| export const NEW_CODE = "NEW_CODE"; | ||||
| export const CHANGE_WORKSPACE = "CHANGE_WORKSPACE"; | ||||
| export const CREATE_BLOCK = "CREATE_BLOCK"; | ||||
| export const MOVE_BLOCK = "MOVE_BLOCK"; | ||||
| export const CHANGE_BLOCK = "CHANGE_BLOCK"; | ||||
| 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 GET_TUTORIALS = 'GET_TUTORIALS'; | ||||
| export const GET_STATUS = 'GET_STATUS'; | ||||
| export const TUTORIAL_SUCCESS = 'TUTORIAL_SUCCESS'; | ||||
| export const TUTORIAL_ERROR = 'TUTORIAL_ERROR'; | ||||
| export const TUTORIAL_CHANGE = 'TUTORIAL_CHANGE'; | ||||
| export const TUTORIAL_XML = 'TUTORIAL_XML'; | ||||
| export const TUTORIAL_ID = 'TUTORIAL_ID'; | ||||
| export const TUTORIAL_STEP = 'TUTORIAL_STEP'; | ||||
| export const JSON_STRING = 'JSON_STRING'; | ||||
| export const TUTORIAL_PROGRESS = "TUTORIAL_PROGRESS"; | ||||
| export const GET_TUTORIAL = "GET_TUTORIAL"; | ||||
| export const GET_TUTORIALS = "GET_TUTORIALS"; | ||||
| export const GET_STATUS = "GET_STATUS"; | ||||
| export const TUTORIAL_SUCCESS = "TUTORIAL_SUCCESS"; | ||||
| export const TUTORIAL_ERROR = "TUTORIAL_ERROR"; | ||||
| export const TUTORIAL_CHANGE = "TUTORIAL_CHANGE"; | ||||
| export const TUTORIAL_XML = "TUTORIAL_XML"; | ||||
| export const TUTORIAL_ID = "TUTORIAL_ID"; | ||||
| export const TUTORIAL_STEP = "TUTORIAL_STEP"; | ||||
| export const JSON_STRING = "JSON_STRING"; | ||||
| 
 | ||||
| export const BUILDER_CHANGE = "BUILDER_CHANGE"; | ||||
| export const BUILDER_TITLE = "BUILDER_TITLE"; | ||||
| export const BUILDER_ID = "BUILDER_ID"; | ||||
| export const BUILDER_ADD_STEP = "BUILDER_ADD_STEP"; | ||||
| export const BUILDER_DELETE_STEP = "BUILDER_DELETE_STEP"; | ||||
| 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"; | ||||
| 
 | ||||
| export const BUILDER_CHANGE = 'BUILDER_CHANGE'; | ||||
| export const BUILDER_TITLE = 'BUILDER_TITLE'; | ||||
| export const BUILDER_BADGE = 'BUILDER_BADGE'; | ||||
| export const BUILDER_ID = 'BUILDER_ID'; | ||||
| export const BUILDER_ADD_STEP = 'BUILDER_ADD_STEP'; | ||||
| export const BUILDER_DELETE_STEP = 'BUILDER_DELETE_STEP'; | ||||
| 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'; | ||||
| 
 | ||||
| 
 | ||||
| export const VISIT = 'VISIT'; | ||||
| export const LANGUAGE = 'LANGUAGE'; | ||||
| export const RENDERER = 'RENDERER'; | ||||
| export const STATISTICS = 'STATISTICS'; | ||||
| export const VISIT = "VISIT"; | ||||
| export const LANGUAGE = "LANGUAGE"; | ||||
| export const RENDERER = "RENDERER"; | ||||
| export const STATISTICS = "STATISTICS"; | ||||
| 
 | ||||
| // messages
 | ||||
| export const GET_ERRORS = 'GET_ERRORS'; | ||||
| export const GET_SUCCESS = 'GET_SUCCESS'; | ||||
| export const CLEAR_MESSAGES = 'CLEAR_MESSAGES'; | ||||
| 
 | ||||
| export const GET_ERRORS = "GET_ERRORS"; | ||||
| export const GET_SUCCESS = "GET_SUCCESS"; | ||||
| export const CLEAR_MESSAGES = "CLEAR_MESSAGES"; | ||||
| 
 | ||||
| // projects: share, gallery, project
 | ||||
| export const PROJECT_PROGRESS = 'PROJECT_PROGRESS'; | ||||
| export const GET_PROJECT = 'GET_PROJECT'; | ||||
| export const GET_PROJECTS = 'GET_PROJECTS'; | ||||
| export const PROJECT_TYPE = 'PROJECT_TYPE'; | ||||
| export const PROJECT_DESCRIPTION = 'PROJECT_DESCRIPTION'; | ||||
| export const PROJECT_PROGRESS = "PROJECT_PROGRESS"; | ||||
| export const GET_PROJECT = "GET_PROJECT"; | ||||
| export const GET_PROJECTS = "GET_PROJECTS"; | ||||
| export const PROJECT_TYPE = "PROJECT_TYPE"; | ||||
| export const PROJECT_DESCRIPTION = "PROJECT_DESCRIPTION"; | ||||
|  | ||||
| @ -172,14 +172,6 @@ export const UI = { | ||||
|   labels_username: "E-Mail oder Nutzername", | ||||
|   labels_password: "Passwort", | ||||
| 
 | ||||
|   /** | ||||
|    * Badges | ||||
|    */ | ||||
| 
 | ||||
|   badges_explaination: | ||||
|     "Eine Übersicht über alle erhaltenen Badges im Kontext Blockly for senseBox findest du ", | ||||
|   badges_ASSIGNE_BADGE_SUCCESS_01: "Herzlichen Glückwunsch! Du hast den Badge ", | ||||
|   badges_ASSIGNE_BADGE_SUCCESS_02: " erhalten.", | ||||
|   /** | ||||
|    * Tutorials | ||||
|    */ | ||||
| @ -228,7 +220,6 @@ export const UI = { | ||||
| 
 | ||||
|   navbar_menu: "Menü", | ||||
|   navbar_login: "Einloggen", | ||||
|   navbar_mybadges: "myBadges", | ||||
|   navbar_account: "Konto", | ||||
|   navbar_logout: "Abmelden", | ||||
|   navbar_settings: "Einstellungen", | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -165,15 +165,6 @@ export const UI = { | ||||
|   labels_here: "here", | ||||
|   labels_username: "Email or username", | ||||
|   labels_password: "Password", | ||||
|   /** | ||||
|    * Badges | ||||
|    */ | ||||
| 
 | ||||
|   badges_explaination: | ||||
|     "An overview of all badges received in the Blockly for senseBox context can be found ", | ||||
|   badges_ASSIGNE_BADGE_SUCCESS_01: | ||||
|     "Congratulations! You have received the badge ", | ||||
|   badges_ASSIGNE_BADGE_SUCCESS_02: ".", | ||||
| 
 | ||||
|   /** | ||||
|    * Tutorials | ||||
| @ -225,7 +216,6 @@ export const UI = { | ||||
| 
 | ||||
|   navbar_menu: "Menu", | ||||
|   navbar_login: "Login", | ||||
|   navbar_mybadges: "myBadges", | ||||
|   navbar_account: "Account", | ||||
|   navbar_logout: "Logout", | ||||
|   navbar_settings: "Settings", | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,136 +1,177 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { Link } from 'react-router-dom'; | ||||
| import { logout } from '../actions/authActions'; | ||||
| import React, { Component } from "react"; | ||||
| import PropTypes from "prop-types"; | ||||
| import { connect } from "react-redux"; | ||||
| import { Link } from "react-router-dom"; | ||||
| import { logout } from "../actions/authActions"; | ||||
| 
 | ||||
| import senseboxLogo from './sensebox_logo.svg'; | ||||
| import senseboxLogo from "./sensebox_logo.svg"; | ||||
| 
 | ||||
| import { withRouter } from 'react-router-dom'; | ||||
| import { withRouter } from "react-router-dom"; | ||||
| 
 | ||||
| import { withStyles } from '@material-ui/core/styles'; | ||||
| import Drawer from '@material-ui/core/Drawer'; | ||||
| import AppBar from '@material-ui/core/AppBar'; | ||||
| import Toolbar from '@material-ui/core/Toolbar'; | ||||
| import List from '@material-ui/core/List'; | ||||
| import Typography from '@material-ui/core/Typography'; | ||||
| import Divider from '@material-ui/core/Divider'; | ||||
| import IconButton from '@material-ui/core/IconButton'; | ||||
| import ListItem from '@material-ui/core/ListItem'; | ||||
| import ListItemIcon from '@material-ui/core/ListItemIcon'; | ||||
| import ListItemText from '@material-ui/core/ListItemText'; | ||||
| import LinearProgress from '@material-ui/core/LinearProgress'; | ||||
| import Tour from 'reactour' | ||||
| import { home, assessment } from './Tour'; | ||||
| import { faBars, faChevronLeft, faLayerGroup, faSignInAlt, faSignOutAlt, faCertificate, faUserCircle, faQuestionCircle, faCog, faChalkboardTeacher, faTools, faLightbulb } from "@fortawesome/free-solid-svg-icons"; | ||||
| import { withStyles } from "@material-ui/core/styles"; | ||||
| import Drawer from "@material-ui/core/Drawer"; | ||||
| import AppBar from "@material-ui/core/AppBar"; | ||||
| import Toolbar from "@material-ui/core/Toolbar"; | ||||
| import List from "@material-ui/core/List"; | ||||
| import Typography from "@material-ui/core/Typography"; | ||||
| import Divider from "@material-ui/core/Divider"; | ||||
| import IconButton from "@material-ui/core/IconButton"; | ||||
| import ListItem from "@material-ui/core/ListItem"; | ||||
| import ListItemIcon from "@material-ui/core/ListItemIcon"; | ||||
| import ListItemText from "@material-ui/core/ListItemText"; | ||||
| import LinearProgress from "@material-ui/core/LinearProgress"; | ||||
| import Tour from "reactour"; | ||||
| import { home, assessment } from "./Tour"; | ||||
| import { | ||||
|   faBars, | ||||
|   faChevronLeft, | ||||
|   faLayerGroup, | ||||
|   faSignInAlt, | ||||
|   faSignOutAlt, | ||||
|   faUserCircle, | ||||
|   faQuestionCircle, | ||||
|   faCog, | ||||
|   faChalkboardTeacher, | ||||
|   faTools, | ||||
|   faLightbulb, | ||||
| } from "@fortawesome/free-solid-svg-icons"; | ||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||
| import * as Blockly from 'blockly' | ||||
| import Tooltip from '@material-ui/core/Tooltip'; | ||||
| 
 | ||||
| import * as Blockly from "blockly"; | ||||
| import Tooltip from "@material-ui/core/Tooltip"; | ||||
| 
 | ||||
| const styles = (theme) => ({ | ||||
|   drawerWidth: { | ||||
|     // color: theme.palette.primary.main,
 | ||||
|     width: window.innerWidth < 600 ? '100%' : '240px', | ||||
|     borderRight: `1px solid ${theme.palette.primary.main}` | ||||
|     width: window.innerWidth < 600 ? "100%" : "240px", | ||||
|     borderRight: `1px solid ${theme.palette.primary.main}`, | ||||
|   }, | ||||
|   appBarColor: { | ||||
|     backgroundColor: theme.palette.primary.main | ||||
|     backgroundColor: theme.palette.primary.main, | ||||
|   }, | ||||
|   tourButton: { | ||||
|     marginleft: 'auto', | ||||
|     marginright: '30px', | ||||
|   } | ||||
|     marginleft: "auto", | ||||
|     marginright: "30px", | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| class Navbar extends Component { | ||||
| 
 | ||||
|   constructor(props) { | ||||
|     super(props); | ||||
|     this.state = { | ||||
|       open: false, | ||||
|       isTourOpen: false | ||||
|       isTourOpen: false, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   toggleDrawer = () => { | ||||
|     this.setState({ open: !this.state.open }); | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   openTour = () => { | ||||
|     this.setState({ isTourOpen: true }); | ||||
| 
 | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   closeTour = () => { | ||||
|     this.setState({ isTourOpen: false }); | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   render() { | ||||
|     var isHome = /^\/(\/.*$|$)/g.test(this.props.location.pathname); | ||||
|     var isTutorial = /^\/tutorial(\/.*$|$)/g.test(this.props.location.pathname); | ||||
|     var isAssessment = /^\/tutorial\/.{1,}$/g.test(this.props.location.pathname) && | ||||
|       !this.props.tutorialIsLoading && this.props.tutorial && | ||||
|       this.props.tutorial.steps[this.props.activeStep].type === 'task'; | ||||
|     var isAssessment = | ||||
|       /^\/tutorial\/.{1,}$/g.test(this.props.location.pathname) && | ||||
|       !this.props.tutorialIsLoading && | ||||
|       this.props.tutorial && | ||||
|       this.props.tutorial.steps[this.props.activeStep].type === "task"; | ||||
|     return ( | ||||
|       <div> | ||||
|         <AppBar | ||||
|           position="relative" | ||||
|           style={{ height: '50px', marginBottom: this.props.tutorialIsLoading || this.props.projectIsLoading ? '0px' : '30px', boxShadow: this.props.tutorialIsLoading || this.props.projectIsLoading ? 'none' : '0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)' }} | ||||
|           style={{ | ||||
|             height: "50px", | ||||
|             marginBottom: | ||||
|               this.props.tutorialIsLoading || this.props.projectIsLoading | ||||
|                 ? "0px" | ||||
|                 : "30px", | ||||
|             boxShadow: | ||||
|               this.props.tutorialIsLoading || this.props.projectIsLoading | ||||
|                 ? "none" | ||||
|                 : "0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)", | ||||
|           }} | ||||
|           classes={{ root: this.props.classes.appBarColor }} | ||||
|         > | ||||
|           <Toolbar style={{ height: '50px', minHeight: '50px', padding: 0, color: 'white' }}> | ||||
|           <Toolbar | ||||
|             style={{ | ||||
|               height: "50px", | ||||
|               minHeight: "50px", | ||||
|               padding: 0, | ||||
|               color: "white", | ||||
|             }} | ||||
|           > | ||||
|             <IconButton | ||||
|               color="inherit" | ||||
|               onClick={this.toggleDrawer} | ||||
|               style={{ margin: '0 10px' }} | ||||
|               style={{ margin: "0 10px" }} | ||||
|               className="MenuButton" | ||||
|             > | ||||
|               <FontAwesomeIcon icon={faBars} /> | ||||
|             </IconButton> | ||||
|             <Link to={"/"} style={{ textDecoration: 'none', color: 'inherit' }}> | ||||
|             <Link to={"/"} style={{ textDecoration: "none", color: "inherit" }}> | ||||
|               <Typography variant="h6" noWrap> | ||||
|                 senseBox Blockly | ||||
|               </Typography> | ||||
|             </Link> | ||||
|             <Link to={"/"} style={{ marginLeft: '10px' }}> | ||||
|             <Link to={"/"} style={{ marginLeft: "10px" }}> | ||||
|               <img src={senseboxLogo} alt="senseBox-Logo" width="30" /> | ||||
|             </Link> | ||||
|             {isTutorial ? | ||||
|               <Link to={"/tutorial"} style={{ textDecoration: 'none', color: 'inherit', marginLeft: '10px' }}> | ||||
|             {isTutorial ? ( | ||||
|               <Link | ||||
|                 to={"/tutorial"} | ||||
|                 style={{ | ||||
|                   textDecoration: "none", | ||||
|                   color: "inherit", | ||||
|                   marginLeft: "10px", | ||||
|                 }} | ||||
|               > | ||||
|                 <Typography variant="h6" noWrap> | ||||
|                   Tutorial | ||||
|                 </Typography> | ||||
|               </Link> : null} | ||||
|             {isHome ? | ||||
|               <Tooltip title='Hilfe starten' arrow> | ||||
|               </Link> | ||||
|             ) : null} | ||||
|             {isHome ? ( | ||||
|               <Tooltip title="Hilfe starten" arrow> | ||||
|                 <IconButton | ||||
|                   color="inherit" | ||||
|                   className={`openTour ${this.props.classes.button}`} | ||||
|                   onClick={() => { this.openTour(); }} | ||||
|                   style={{ margin: '0 30px 0 auto' }} | ||||
|                   onClick={() => { | ||||
|                     this.openTour(); | ||||
|                   }} | ||||
|                   style={{ margin: "0 30px 0 auto" }} | ||||
|                 > | ||||
|                   <FontAwesomeIcon icon={faQuestionCircle} /> | ||||
|                 </IconButton> | ||||
|               </Tooltip> | ||||
|               : null} | ||||
|             {isAssessment ? | ||||
|               <Tooltip title='Hilfe starten' arrow> | ||||
|             ) : null} | ||||
|             {isAssessment ? ( | ||||
|               <Tooltip title="Hilfe starten" arrow> | ||||
|                 <IconButton | ||||
|                   color="inherit" | ||||
|                   className={`openTour ${this.props.classes.button}`} | ||||
|                   onClick={() => { this.openTour(); }} | ||||
|                   style={{ margin: '0 30px 0 auto' }} | ||||
|                   onClick={() => { | ||||
|                     this.openTour(); | ||||
|                   }} | ||||
|                   style={{ margin: "0 30px 0 auto" }} | ||||
|                 > | ||||
|                   <FontAwesomeIcon icon={faQuestionCircle} /> | ||||
|                 </IconButton> | ||||
|               </Tooltip> | ||||
|               : null} | ||||
|             ) : null} | ||||
|             <Tour | ||||
|               steps={isHome ? home() : assessment()} | ||||
|               isOpen={this.state.isTourOpen} | ||||
|               onRequestClose={() => { this.closeTour(); }} | ||||
|               onRequestClose={() => { | ||||
|                 this.closeTour(); | ||||
|               }} | ||||
|             /> | ||||
|           </Toolbar> | ||||
|         </AppBar> | ||||
| @ -142,71 +183,161 @@ class Navbar extends Component { | ||||
|           classes={{ paper: this.props.classes.drawerWidth }} | ||||
|           ModalProps={{ keepMounted: true }} // Better open performance on mobile.
 | ||||
|         > | ||||
|           <div style={{ height: '50px', cursor: 'pointer', color: 'white', padding: '0 22px' }} className={this.props.classes.appBarColor} onClick={this.toggleDrawer}> | ||||
|             <div style={{ display: ' table-cell', verticalAlign: 'middle', height: 'inherit', width: '0.1%' }}> | ||||
|               <Typography variant="h6" style={{ display: 'inline' }}> | ||||
|           <div | ||||
|             style={{ | ||||
|               height: "50px", | ||||
|               cursor: "pointer", | ||||
|               color: "white", | ||||
|               padding: "0 22px", | ||||
|             }} | ||||
|             className={this.props.classes.appBarColor} | ||||
|             onClick={this.toggleDrawer} | ||||
|           > | ||||
|             <div | ||||
|               style={{ | ||||
|                 display: " table-cell", | ||||
|                 verticalAlign: "middle", | ||||
|                 height: "inherit", | ||||
|                 width: "0.1%", | ||||
|               }} | ||||
|             > | ||||
|               <Typography variant="h6" style={{ display: "inline" }}> | ||||
|                 {Blockly.Msg.navbar_menu} | ||||
|               </Typography> | ||||
|               <div style={{ float: 'right' }}> | ||||
|               <div style={{ float: "right" }}> | ||||
|                 <FontAwesomeIcon icon={faChevronLeft} /> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           <List> | ||||
|             {[{ text: Blockly.Msg.navbar_tutorials, icon: faChalkboardTeacher, link: "/tutorial" }, | ||||
|             { text: Blockly.Msg.navbar_tutorialbuilder, icon: faTools, link: "/tutorial/builder", restriction: this.props.user && this.props.user.blocklyRole !== 'user' && this.props.isAuthenticated }, | ||||
|             { text: Blockly.Msg.navbar_gallery, icon: faLightbulb, link: "/gallery" }, | ||||
|             { text: Blockly.Msg.navbar_projects, icon: faLayerGroup, link: "/project", restriction: this.props.isAuthenticated }].map((item, index) => { | ||||
|               if (item.restriction || Object.keys(item).filter(attribute => attribute === 'restriction').length === 0) { | ||||
|             {[ | ||||
|               { | ||||
|                 text: Blockly.Msg.navbar_tutorials, | ||||
|                 icon: faChalkboardTeacher, | ||||
|                 link: "/tutorial", | ||||
|               }, | ||||
|               { | ||||
|                 text: Blockly.Msg.navbar_tutorialbuilder, | ||||
|                 icon: faTools, | ||||
|                 link: "/tutorial/builder", | ||||
|                 restriction: | ||||
|                   this.props.user && | ||||
|                   this.props.user.blocklyRole !== "user" && | ||||
|                   this.props.isAuthenticated, | ||||
|               }, | ||||
|               { | ||||
|                 text: Blockly.Msg.navbar_gallery, | ||||
|                 icon: faLightbulb, | ||||
|                 link: "/gallery", | ||||
|               }, | ||||
|               { | ||||
|                 text: Blockly.Msg.navbar_projects, | ||||
|                 icon: faLayerGroup, | ||||
|                 link: "/project", | ||||
|                 restriction: this.props.isAuthenticated, | ||||
|               }, | ||||
|             ].map((item, index) => { | ||||
|               if ( | ||||
|                 item.restriction || | ||||
|                 Object.keys(item).filter( | ||||
|                   (attribute) => attribute === "restriction" | ||||
|                 ).length === 0 | ||||
|               ) { | ||||
|                 return ( | ||||
|                   <Link to={item.link} key={index} style={{ textDecoration: 'none', color: 'inherit' }}> | ||||
|                   <Link | ||||
|                     to={item.link} | ||||
|                     key={index} | ||||
|                     style={{ textDecoration: "none", color: "inherit" }} | ||||
|                   > | ||||
|                     <ListItem button onClick={this.toggleDrawer}> | ||||
|                       <ListItemIcon><FontAwesomeIcon icon={item.icon} /></ListItemIcon> | ||||
|                       <ListItemIcon> | ||||
|                         <FontAwesomeIcon icon={item.icon} /> | ||||
|                       </ListItemIcon> | ||||
|                       <ListItemText primary={item.text} /> | ||||
|                     </ListItem> | ||||
|                   </Link> | ||||
|                 ); | ||||
|               } else { | ||||
|                 return null; | ||||
|               } | ||||
|               else { | ||||
|                 return( | ||||
|                   null | ||||
|                   ) | ||||
|               } | ||||
|             } | ||||
|             )} | ||||
|             })} | ||||
|           </List> | ||||
|           <Divider classes={{ root: this.props.classes.appBarColor }} style={{ marginTop: 'auto' }} /> | ||||
|           <Divider | ||||
|             classes={{ root: this.props.classes.appBarColor }} | ||||
|             style={{ marginTop: "auto" }} | ||||
|           /> | ||||
|           <List> | ||||
|             {[{ text: Blockly.Msg.navbar_login, icon: faSignInAlt, link: '/user/login', restriction: !this.props.isAuthenticated }, | ||||
|             { text: Blockly.Msg.navbar_account, icon: faUserCircle, link: '/user', restriction: this.props.isAuthenticated }, | ||||
|             { text: Blockly.Msg.navbar_mybadges, icon: faCertificate, link: '/user/badge', restriction: this.props.isAuthenticated }, | ||||
|             { text: Blockly.Msg.navbar_logout, icon: faSignOutAlt, function: this.props.logout, restriction: this.props.isAuthenticated }, | ||||
|             { text: 'FAQ', icon: faQuestionCircle, link: "/faq" }, | ||||
|             { text: Blockly.Msg.navbar_settings, icon: faCog, link: "/settings" }].map((item, index) => { | ||||
|               if (item.restriction || Object.keys(item).filter(attribute => attribute === 'restriction').length === 0) { | ||||
|             {[ | ||||
|               { | ||||
|                 text: Blockly.Msg.navbar_login, | ||||
|                 icon: faSignInAlt, | ||||
|                 link: "/user/login", | ||||
|                 restriction: !this.props.isAuthenticated, | ||||
|               }, | ||||
|               { | ||||
|                 text: Blockly.Msg.navbar_account, | ||||
|                 icon: faUserCircle, | ||||
|                 link: "/user", | ||||
|                 restriction: this.props.isAuthenticated, | ||||
|               }, | ||||
|               { | ||||
|                 text: Blockly.Msg.navbar_logout, | ||||
|                 icon: faSignOutAlt, | ||||
|                 function: this.props.logout, | ||||
|                 restriction: this.props.isAuthenticated, | ||||
|               }, | ||||
|               { text: "FAQ", icon: faQuestionCircle, link: "/faq" }, | ||||
|               { | ||||
|                 text: Blockly.Msg.navbar_settings, | ||||
|                 icon: faCog, | ||||
|                 link: "/settings", | ||||
|               }, | ||||
|             ].map((item, index) => { | ||||
|               if ( | ||||
|                 item.restriction || | ||||
|                 Object.keys(item).filter( | ||||
|                   (attribute) => attribute === "restriction" | ||||
|                 ).length === 0 | ||||
|               ) { | ||||
|                 return ( | ||||
|                   <Link to={item.link} key={index} style={{ textDecoration: 'none', color: 'inherit' }}> | ||||
|                     <ListItem button onClick={item.function ? () => { item.function(); this.toggleDrawer(); } : this.toggleDrawer}> | ||||
|                       <ListItemIcon><FontAwesomeIcon icon={item.icon} /></ListItemIcon> | ||||
|                   <Link | ||||
|                     to={item.link} | ||||
|                     key={index} | ||||
|                     style={{ textDecoration: "none", color: "inherit" }} | ||||
|                   > | ||||
|                     <ListItem | ||||
|                       button | ||||
|                       onClick={ | ||||
|                         item.function | ||||
|                           ? () => { | ||||
|                               item.function(); | ||||
|                               this.toggleDrawer(); | ||||
|                             } | ||||
|                           : this.toggleDrawer | ||||
|                       } | ||||
|                     > | ||||
|                       <ListItemIcon> | ||||
|                         <FontAwesomeIcon icon={item.icon} /> | ||||
|                       </ListItemIcon> | ||||
|                       <ListItemText primary={item.text} /> | ||||
|                     </ListItem> | ||||
|                   </Link> | ||||
|                 ); | ||||
|               } else { | ||||
|                 return null; | ||||
|               } | ||||
|               else { | ||||
|                 return( | ||||
|                   null | ||||
|                   ) | ||||
|               } | ||||
|                | ||||
|             } | ||||
|              | ||||
|             )} | ||||
|             })} | ||||
|           </List> | ||||
|         </Drawer> | ||||
|         {this.props.tutorialIsLoading || this.props.projectIsLoading ? | ||||
|           <LinearProgress style={{ marginBottom: '30px', boxShadow: '0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)' }} /> | ||||
|           : null} | ||||
|         {this.props.tutorialIsLoading || this.props.projectIsLoading ? ( | ||||
|           <LinearProgress | ||||
|             style={{ | ||||
|               marginBottom: "30px", | ||||
|               boxShadow: | ||||
|                 "0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)", | ||||
|             }} | ||||
|           /> | ||||
|         ) : null} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| @ -218,10 +349,10 @@ Navbar.propTypes = { | ||||
|   isAuthenticated: PropTypes.bool.isRequired, | ||||
|   user: PropTypes.object, | ||||
|   tutorial: PropTypes.object.isRequired, | ||||
|   activeStep: PropTypes.number.isRequired | ||||
|   activeStep: PropTypes.number.isRequired, | ||||
| }; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
| const mapStateToProps = (state) => ({ | ||||
|   tutorialIsLoading: state.tutorial.progress, | ||||
|   projectIsLoading: state.project.progress, | ||||
|   isAuthenticated: state.auth.isAuthenticated, | ||||
| @ -230,4 +361,6 @@ const mapStateToProps = state => ({ | ||||
|   activeStep: state.tutorial.activeStep, | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps, { logout })(withStyles(styles, { withTheme: true })(withRouter(Navbar))); | ||||
| export default connect(mapStateToProps, { logout })( | ||||
|   withStyles(styles, { withTheme: true })(withRouter(Navbar)) | ||||
| ); | ||||
|  | ||||
| @ -1,40 +1,38 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { visitPage } from '../../actions/generalActions'; | ||||
| import React, { Component } from "react"; | ||||
| import PropTypes from "prop-types"; | ||||
| import { connect } from "react-redux"; | ||||
| import { visitPage } from "../../actions/generalActions"; | ||||
| 
 | ||||
| import { Route, Switch, withRouter } from 'react-router-dom'; | ||||
| import { Route, Switch, withRouter } from "react-router-dom"; | ||||
| 
 | ||||
| import PublicRoute from './PublicRoute'; | ||||
| import PrivateRoute from './PrivateRoute'; | ||||
| import PrivateRouteCreator from './PrivateRouteCreator'; | ||||
| import IsLoggedRoute from './IsLoggedRoute'; | ||||
| import PublicRoute from "./PublicRoute"; | ||||
| import PrivateRoute from "./PrivateRoute"; | ||||
| import PrivateRouteCreator from "./PrivateRouteCreator"; | ||||
| import IsLoggedRoute from "./IsLoggedRoute"; | ||||
| 
 | ||||
| import Home from '../Home'; | ||||
| import Tutorial from '../Tutorial/Tutorial'; | ||||
| import TutorialHome from '../Tutorial/TutorialHome'; | ||||
| import Builder from '../Tutorial/Builder/Builder'; | ||||
| import NotFound from '../NotFound'; | ||||
| import ProjectHome from '../Project/ProjectHome'; | ||||
| import Project from '../Project/Project'; | ||||
| import Settings from '../Settings/Settings'; | ||||
| import Impressum from '../Impressum'; | ||||
| import Privacy from '../Privacy'; | ||||
| import Login from '../User/Login'; | ||||
| import Account from '../User/Account'; | ||||
| import MyBadges from '../User/MyBadges'; | ||||
| import News from '../News' | ||||
| import Faq from '../Faq' | ||||
| import Home from "../Home"; | ||||
| import Tutorial from "../Tutorial/Tutorial"; | ||||
| import TutorialHome from "../Tutorial/TutorialHome"; | ||||
| import Builder from "../Tutorial/Builder/Builder"; | ||||
| import NotFound from "../NotFound"; | ||||
| import ProjectHome from "../Project/ProjectHome"; | ||||
| import Project from "../Project/Project"; | ||||
| import Settings from "../Settings/Settings"; | ||||
| import Impressum from "../Impressum"; | ||||
| import Privacy from "../Privacy"; | ||||
| import Login from "../User/Login"; | ||||
| import Account from "../User/Account"; | ||||
| import News from "../News"; | ||||
| import Faq from "../Faq"; | ||||
| 
 | ||||
| class Routes extends Component { | ||||
| 
 | ||||
|   componentDidUpdate() { | ||||
|     this.props.visitPage(); | ||||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     return ( | ||||
|       <div style={{ margin: '0 22px' }}> | ||||
|       <div style={{ margin: "0 22px" }}> | ||||
|         <Switch> | ||||
|           <PublicRoute path="/" exact> | ||||
|             <Home /> | ||||
| @ -74,9 +72,6 @@ class Routes extends Component { | ||||
|           <PrivateRoute path="/user" exact> | ||||
|             <Account /> | ||||
|           </PrivateRoute> | ||||
|           <PrivateRoute path="/user/badge" exact> | ||||
|             <MyBadges /> | ||||
|           </PrivateRoute> | ||||
|           {/* settings */} | ||||
|           <PublicRoute path="/settings" exact> | ||||
|             <Settings /> | ||||
| @ -98,7 +93,6 @@ class Routes extends Component { | ||||
|           <PublicRoute> | ||||
|             <NotFound /> | ||||
|           </PublicRoute> | ||||
| 
 | ||||
|         </Switch> | ||||
|       </div> | ||||
|     ); | ||||
| @ -106,7 +100,7 @@ class Routes extends Component { | ||||
| } | ||||
| 
 | ||||
| Home.propTypes = { | ||||
|   visitPage: PropTypes.func.isRequired | ||||
|   visitPage: PropTypes.func.isRequired, | ||||
| }; | ||||
| 
 | ||||
| export default connect(null, { visitPage })(withRouter(Routes)); | ||||
|  | ||||
| @ -1,119 +0,0 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { assigneBadge } from '../../actions/tutorialActions'; | ||||
| 
 | ||||
| import Dialog from '../Dialog'; | ||||
| 
 | ||||
| import { Link } from 'react-router-dom'; | ||||
| 
 | ||||
| import { withStyles } from '@material-ui/core/styles'; | ||||
| import Paper from '@material-ui/core/Paper'; | ||||
| import Typography from '@material-ui/core/Typography'; | ||||
| import Avatar from '@material-ui/core/Avatar'; | ||||
| import * as Blockly from 'blockly'; | ||||
| 
 | ||||
| const styles = (theme) => ({ | ||||
|   link: { | ||||
|     color: theme.palette.primary.main, | ||||
|     textDecoration: 'none', | ||||
|     '&:hover': { | ||||
|       color: theme.palette.primary.main, | ||||
|       textDecoration: 'underline' | ||||
|     } | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| class Badge extends Component { | ||||
| 
 | ||||
|   state = { | ||||
|     open: false, | ||||
|     title: '', | ||||
|     content: '' | ||||
|   }; | ||||
| 
 | ||||
|   componentDidUpdate(props) { | ||||
|     if (this.props.message.id === 'TUTORIAL_CHECK_SUCCESS') { | ||||
|       if (this.props.tutorial.badge) { | ||||
|         // is connected to MyBadges?
 | ||||
|         if (this.props.isAuthenticated && this.props.user && this.props.user.badge) { | ||||
|           if (this.props.user.badges && !this.props.user.badges.includes(this.props.tutorial.badge)) { | ||||
|             if (this.isSuccess()) { | ||||
|               this.props.assigneBadge(this.props.tutorial.badge); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     if (props.message !== this.props.message) { | ||||
|       if (this.props.message.id === 'ASSIGNE_BADGE_SUCCESS') { | ||||
|         this.setState({ title: `Badge: ${this.props.message.msg.name}`, content: `${Blockly.Msg.badges_ASSIGNE_BADGE_SUCCESS_01} ${this.props.message.msg.name} ${Blockly.Msg.badges_ASSIGNE_BADGE_SUCCESS_02}`, open: true }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   isSuccess = () => { | ||||
|     var tutorialId = this.props.tutorial._id; | ||||
|     var status = this.props.status.filter(status => status._id === tutorialId)[0]; | ||||
|     var tasks = status.tasks; | ||||
|     var success = tasks.filter(task => task.type === 'success').length / tasks.length; | ||||
|     if (success === 1) { | ||||
|       return true; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   toggleDialog = () => { | ||||
|     this.setState({ open: !this.state, title: '', content: '' }); | ||||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     return ( | ||||
|       <Dialog | ||||
|         style={{ zIndex: 99999999 }} | ||||
|         open={this.state.open} | ||||
|         title={this.state.title} | ||||
|         content={this.state.content} | ||||
|         onClose={() => { this.toggleDialog(); }} | ||||
|         onClick={() => { this.toggleDialog(); }} | ||||
|         button={Blockly.Msg.button_close} | ||||
|       > | ||||
|         <div style={{ marginTop: '10px' }}> | ||||
|           <Paper style={{ textAlign: 'center' }}> | ||||
|             {this.props.message.msg.image && this.props.message.msg.image.path ? | ||||
|               <Avatar src={`${process.env.REACT_APP_MYBADGES}/media/${this.props.message.msg.image.path}`} style={{ width: '200px', height: '200px', marginLeft: 'auto', marginRight: 'auto' }} /> | ||||
|               : <Avatar style={{ width: '200px', height: '200px', marginLeft: 'auto', marginRight: 'auto' }}></Avatar>} | ||||
|             <Typography variant='h6' style={{ display: 'flex', cursor: 'default', paddingBottom: '6px' }}> | ||||
|               <div style={{ flexGrow: 1, marginLeft: '10px', marginRight: '10px' }}>{this.props.message.msg.name}</div> | ||||
|             </Typography> | ||||
|           </Paper> | ||||
|           <Typography style={{ marginTop: '10px' }}> | ||||
|             {Blockly.Msg.badges_explaination}<Link to={'/user/badge'} className={this.props.classes.link}>{Blockly.Msg.labels_here}</Link>. | ||||
|           </Typography> | ||||
|         </div> | ||||
|       </Dialog> | ||||
|     ); | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| Badge.propTypes = { | ||||
|   assigneBadge: PropTypes.func.isRequired, | ||||
|   status: PropTypes.array.isRequired, | ||||
|   change: PropTypes.number.isRequired, | ||||
|   tutorial: PropTypes.object.isRequired, | ||||
|   user: PropTypes.object, | ||||
|   isAuthenticated: PropTypes.bool.isRequired, | ||||
|   message: PropTypes.object.isRequired | ||||
| }; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   change: state.tutorial.change, | ||||
|   status: state.tutorial.status, | ||||
|   tutorial: state.tutorial.tutorials[0], | ||||
|   user: state.auth.user, | ||||
|   isAuthenticated: state.auth.isAuthenticated, | ||||
|   message: state.message | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps, { assigneBadge })(withStyles(styles, { withTheme: true })(Badge)); | ||||
| @ -1,189 +0,0 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { tutorialBadge, deleteProperty, setError, deleteError } from '../../../actions/tutorialBuilderActions'; | ||||
| 
 | ||||
| import axios from 'axios'; | ||||
| 
 | ||||
| import { withStyles } from '@material-ui/core/styles'; | ||||
| import Switch from '@material-ui/core/Switch'; | ||||
| import FormControlLabel from '@material-ui/core/FormControlLabel'; | ||||
| import FormHelperText from '@material-ui/core/FormHelperText'; | ||||
| import List from '@material-ui/core/List'; | ||||
| import ListItem from '@material-ui/core/ListItem'; | ||||
| import ListItemText from '@material-ui/core/ListItemText'; | ||||
| import IconButton from '@material-ui/core/IconButton'; | ||||
| import OutlinedInput from '@material-ui/core/OutlinedInput'; | ||||
| import InputLabel from '@material-ui/core/InputLabel'; | ||||
| import FormControl from '@material-ui/core/FormControl'; | ||||
| 
 | ||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||
| import { faTimes } from "@fortawesome/free-solid-svg-icons"; | ||||
| 
 | ||||
| const styles = (theme) => ({ | ||||
|   errorColor: { | ||||
|     color: `${theme.palette.error.dark} !important` | ||||
|   }, | ||||
|   errorColorShrink: { | ||||
|     color: `rgba(0, 0, 0, 0.54) !important` | ||||
|   }, | ||||
|   errorBorder: { | ||||
|     borderColor: `${theme.palette.error.dark} !important` | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| class Badge extends Component { | ||||
| 
 | ||||
|   constructor(props){ | ||||
|     super(props); | ||||
|     this.state={ | ||||
|       checked: props.badge ? true : false, | ||||
|       badgeName: '', | ||||
|       filteredBadges: [], | ||||
|       badges: [] | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   componentDidMount(){ | ||||
|     this.getBadges(); | ||||
|   } | ||||
| 
 | ||||
|   componentDidUpdate(props){ | ||||
|     if(props.badge !== this.props.badge){ | ||||
|       this.setState({ checked: this.props.badge !== undefined ? true : false, badgeName: this.props.badge ? this.state.badges.filter(badge => badge._id === this.props.badge)[0].name : '' }); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getBadges = () => { | ||||
|     axios.get(`${process.env.REACT_APP_MYBADGES_API}/badge`) | ||||
|       .then(res => { | ||||
|         this.setState({badges: res.data.badges, badgeName: this.props.badge ? res.data.badges.filter(badge => badge._id === this.props.badge)[0].name : '' }); | ||||
|       }) | ||||
|       .catch(err => { | ||||
|       }); | ||||
|   }; | ||||
| 
 | ||||
|   deleteBadge = () => { | ||||
|     this.setState({ filteredBadges: [], badgeName: '' }); | ||||
|     this.props.tutorialBadge(null); | ||||
|     this.props.setError(this.props.index, 'badge'); | ||||
|   }; | ||||
| 
 | ||||
|   setBadge = (badge) => { | ||||
|     this.setState({ filteredBadges: [] }); | ||||
|     this.props.tutorialBadge(badge._id); | ||||
|     this.props.deleteError(this.props.index, 'badge'); | ||||
|   }; | ||||
| 
 | ||||
|   onChange = e => { | ||||
|     this.setState({ badgeName: e.target.value }); | ||||
|   }; | ||||
| 
 | ||||
|   onChangeBadge = e => { | ||||
|     if(e.target.value && this.props.badge === null){ | ||||
|       var filteredBadges = this.state.badges.filter(badge => new RegExp(e.target.value, 'i').test(badge.name)); | ||||
|       if(filteredBadges.length < 1){ | ||||
|         filteredBadges = ['Keine Übereinstimmung gefunden.']; | ||||
|       } | ||||
|       this.setState({filteredBadges: filteredBadges}); | ||||
|     } | ||||
|     else { | ||||
|       this.setState({filteredBadges: []}); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   onChangeSwitch = (value) => { | ||||
|     var oldValue = this.state.checked; | ||||
|     this.setState({checked: value}); | ||||
|     if(oldValue !== value){ | ||||
|       if(value){ | ||||
|         this.props.setError(this.props.index, 'badge'); | ||||
|         this.props.tutorialBadge(null); | ||||
|       } else { | ||||
|         this.props.deleteError(this.props.index, 'badge'); | ||||
|         this.props.tutorialBadge(undefined); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     return ( | ||||
|       <div style={{marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)'}}> | ||||
|         <FormControlLabel | ||||
|           labelPlacement="end" | ||||
|           label={"Badge"} | ||||
|           control={ | ||||
|             <Switch | ||||
|               checked={this.state.checked} | ||||
|               onChange={(e) => this.onChangeSwitch(e.target.checked)} | ||||
|               color="primary" | ||||
|             /> | ||||
|           } | ||||
|         /> | ||||
|         {this.state.checked ? | ||||
|           <div style={{marginTop: '10px'}}> | ||||
|             <FormControl variant="outlined" fullWidth> | ||||
|               <InputLabel | ||||
|                 htmlFor={'badge'} | ||||
|                 classes={{shrink: this.props.error ? this.props.classes.errorColorShrink : null}} | ||||
|               > | ||||
|                 {'Badge'} | ||||
|               </InputLabel> | ||||
|               <OutlinedInput | ||||
|                 style={{borderRadius: '25px'}} | ||||
|                 classes={{notchedOutline: this.props.error ? this.props.classes.errorBorder : null}} | ||||
|                 error={this.props.error} | ||||
|                 value={this.state.badgeName} | ||||
|                 label={'Badge'} | ||||
|                 id={'badge'} | ||||
|                 disabled={this.props.badge} | ||||
|                 onChange={(e) => this.onChange(e)} | ||||
|                 onInput={(e) => this.onChangeBadge(e)} | ||||
|                 fullWidth={true} | ||||
|                 endAdornment={ | ||||
|                       <IconButton | ||||
|                         onClick={this.deleteBadge} | ||||
|                         edge="end" | ||||
|                       > | ||||
|                         <FontAwesomeIcon size='xs' icon={faTimes} /> | ||||
|                       </IconButton> | ||||
|                     } | ||||
|               /> | ||||
|               {this.props.error && this.state.filteredBadges.length === 0 ? | ||||
|               <FormHelperText className={this.props.classes.errorColor}>Wähle ein Badge aus.</FormHelperText> | ||||
|               : null} | ||||
|             </FormControl> | ||||
|             <List style={{paddingTop: 0}}> | ||||
|             {this.state.filteredBadges.map((badge, i) => ( | ||||
|               badge === 'Keine Übereinstimmung gefunden.' ? | ||||
|                 <ListItem button key={i} onClick={this.deleteBadge} style={{border: '1px solid rgba(0, 0, 0, 0.23)', borderRadius: '25px'}}> | ||||
|                   <ListItemText>{badge}</ListItemText> | ||||
|                 </ListItem> | ||||
|               : | ||||
|               <ListItem button key={i} onClick={() => {this.setBadge(badge)}} style={{border: '1px solid rgba(0, 0, 0, 0.23)', borderRadius: '25px'}}> | ||||
|                 <ListItemText>{`${badge.name}`}</ListItemText> | ||||
|               </ListItem> | ||||
|             ))} | ||||
|             </List> | ||||
|           </div> | ||||
|         : null} | ||||
|       </div> | ||||
|     ); | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| Badge.propTypes = { | ||||
|   tutorialBadge: PropTypes.func.isRequired, | ||||
|   deleteProperty: PropTypes.func.isRequired, | ||||
|   setError: PropTypes.func.isRequired, | ||||
|   deleteError: PropTypes.func.isRequired, | ||||
|   badge: PropTypes.string.isRequired | ||||
| }; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   badge: state.builder.badge, | ||||
|   change: state.builder.change | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| export default connect(mapStateToProps, { tutorialBadge, deleteProperty, setError, deleteError })(withStyles(styles, {withTheme: true})(Badge)); | ||||
| @ -1,67 +1,75 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { checkError, readJSON, jsonString, progress, tutorialId, resetTutorial as resetTutorialBuilder} from '../../../actions/tutorialBuilderActions'; | ||||
| import { getTutorials, resetTutorial, deleteTutorial, tutorialProgress } from '../../../actions/tutorialActions'; | ||||
| import { clearMessages } from '../../../actions/messageActions'; | ||||
| import React, { Component } from "react"; | ||||
| import PropTypes from "prop-types"; | ||||
| import { connect } from "react-redux"; | ||||
| import { | ||||
|   checkError, | ||||
|   readJSON, | ||||
|   jsonString, | ||||
|   progress, | ||||
|   tutorialId, | ||||
|   resetTutorial as resetTutorialBuilder, | ||||
| } from "../../../actions/tutorialBuilderActions"; | ||||
| import { | ||||
|   getTutorials, | ||||
|   resetTutorial, | ||||
|   deleteTutorial, | ||||
|   tutorialProgress, | ||||
| } from "../../../actions/tutorialActions"; | ||||
| import { clearMessages } from "../../../actions/messageActions"; | ||||
| 
 | ||||
| import axios from 'axios'; | ||||
| import { withRouter } from 'react-router-dom'; | ||||
| import axios from "axios"; | ||||
| import { withRouter } from "react-router-dom"; | ||||
| 
 | ||||
| import Breadcrumbs from "../../Breadcrumbs"; | ||||
| import Textfield from "./Textfield"; | ||||
| import Step from "./Step"; | ||||
| import Dialog from "../../Dialog"; | ||||
| import Snackbar from "../../Snackbar"; | ||||
| 
 | ||||
| import Breadcrumbs from '../../Breadcrumbs'; | ||||
| import Badge from './Badge'; | ||||
| import Textfield from './Textfield'; | ||||
| import Step from './Step'; | ||||
| import Dialog from '../../Dialog'; | ||||
| import Snackbar from '../../Snackbar'; | ||||
| 
 | ||||
| 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'; | ||||
| import FormHelperText from '@material-ui/core/FormHelperText'; | ||||
| import Radio from '@material-ui/core/Radio'; | ||||
| import RadioGroup from '@material-ui/core/RadioGroup'; | ||||
| import FormControlLabel from '@material-ui/core/FormControlLabel'; | ||||
| import InputLabel from '@material-ui/core/InputLabel'; | ||||
| import MenuItem from '@material-ui/core/MenuItem'; | ||||
| import FormControl from '@material-ui/core/FormControl'; | ||||
| import Select from '@material-ui/core/Select'; | ||||
| 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"; | ||||
| import FormHelperText from "@material-ui/core/FormHelperText"; | ||||
| import Radio from "@material-ui/core/Radio"; | ||||
| import RadioGroup from "@material-ui/core/RadioGroup"; | ||||
| import FormControlLabel from "@material-ui/core/FormControlLabel"; | ||||
| import InputLabel from "@material-ui/core/InputLabel"; | ||||
| import MenuItem from "@material-ui/core/MenuItem"; | ||||
| import FormControl from "@material-ui/core/FormControl"; | ||||
| import Select from "@material-ui/core/Select"; | ||||
| 
 | ||||
| const styles = (theme) => ({ | ||||
|   backdrop: { | ||||
|     zIndex: theme.zIndex.drawer + 1, | ||||
|     color: '#fff', | ||||
|     color: "#fff", | ||||
|   }, | ||||
|   errorColor: { | ||||
|     color: theme.palette.error.dark | ||||
|     color: theme.palette.error.dark, | ||||
|   }, | ||||
|   errorButton: { | ||||
|     marginTop: '5px', | ||||
|     height: '40px', | ||||
|     marginTop: "5px", | ||||
|     height: "40px", | ||||
|     backgroundColor: theme.palette.error.dark, | ||||
|     '&:hover': { | ||||
|       backgroundColor: theme.palette.error.dark | ||||
|     } | ||||
|   } | ||||
|     "&:hover": { | ||||
|       backgroundColor: theme.palette.error.dark, | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| class Builder extends Component { | ||||
| 
 | ||||
|   constructor(props) { | ||||
|     super(props); | ||||
|     this.state = { | ||||
|       tutorial: 'new', | ||||
|       tutorial: "new", | ||||
|       open: false, | ||||
|       title: '', | ||||
|       content: '', | ||||
|       title: "", | ||||
|       content: "", | ||||
|       string: false, | ||||
|       snackbar: false, | ||||
|       key: '', | ||||
|       message: '' | ||||
|       key: "", | ||||
|       message: "", | ||||
|     }; | ||||
|     this.inputRef = React.createRef(); | ||||
|   } | ||||
| @ -70,27 +78,38 @@ class Builder extends Component { | ||||
|     this.props.tutorialProgress(); | ||||
|     // retrieve tutorials only if a potential user is loaded - authentication
 | ||||
|     // is finished (success or failed)
 | ||||
|     if(!this.props.authProgress){ | ||||
|     if (!this.props.authProgress) { | ||||
|       this.props.getTutorials(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   componentDidUpdate(props, state) { | ||||
|     if(props.authProgress !== this.props.authProgress && !this.props.authProgress){ | ||||
|     if ( | ||||
|       props.authProgress !== this.props.authProgress && | ||||
|       !this.props.authProgress | ||||
|     ) { | ||||
|       // authentication is completed
 | ||||
|       this.props.getTutorials(); | ||||
|     } | ||||
|     if(props.message !== this.props.message){ | ||||
|       if(this.props.message.id === 'GET_TUTORIALS_FAIL'){ | ||||
|     if (props.message !== this.props.message) { | ||||
|       if (this.props.message.id === "GET_TUTORIALS_FAIL") { | ||||
|         // alert(this.props.message.msg);
 | ||||
|         this.props.clearMessages(); | ||||
|       } | ||||
|       else if (this.props.message.id === 'TUTORIAL_DELETE_SUCCESS') { | ||||
|         this.onChange('new'); | ||||
|         this.setState({ snackbar: true, key: Date.now(), message: `Das Tutorial wurde erfolgreich gelöscht.`, type: 'success' }); | ||||
|       } | ||||
|       else if (this.props.message.id === 'TUTORIAL_DELETE_FAIL') { | ||||
|         this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Löschen des Tutorials. Versuche es noch einmal.`, type: 'error' }); | ||||
|       } else if (this.props.message.id === "TUTORIAL_DELETE_SUCCESS") { | ||||
|         this.onChange("new"); | ||||
|         this.setState({ | ||||
|           snackbar: true, | ||||
|           key: Date.now(), | ||||
|           message: `Das Tutorial wurde erfolgreich gelöscht.`, | ||||
|           type: "success", | ||||
|         }); | ||||
|       } else if (this.props.message.id === "TUTORIAL_DELETE_FAIL") { | ||||
|         this.setState({ | ||||
|           snackbar: true, | ||||
|           key: Date.now(), | ||||
|           message: `Fehler beim Löschen des Tutorials. Versuche es noch einmal.`, | ||||
|           type: "error", | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @ -105,22 +124,32 @@ class Builder extends Component { | ||||
| 
 | ||||
|   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.' }); | ||||
|     } | ||||
|     else { | ||||
|       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(); | ||||
|       reader.readAsText(jsonFile); | ||||
|       reader.onloadend = () => { | ||||
|         this.readJson(reader.result, true); | ||||
|       }; | ||||
|     } | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   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 { | ||||
| @ -129,173 +158,255 @@ class Builder extends Component { | ||||
|         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' }); | ||||
|       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.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.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.`,
 | ||||
|       }); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   checkSteps = (steps) => { | ||||
|     if (!(steps && steps.length > 0)) { | ||||
|       return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   toggle = () => { | ||||
|     this.setState({ open: !this.state }); | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   onChange = (value) => { | ||||
|     this.props.resetTutorialBuilder(); | ||||
|     this.props.tutorialId(''); | ||||
|     this.props.tutorialId(""); | ||||
|     this.setState({ tutorial: value }); | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   onChangeId = (value) => { | ||||
|     this.props.tutorialId(value); | ||||
|     if (this.state.tutorial === 'change') { | ||||
|     if (this.state.tutorial === "change") { | ||||
|       this.props.progress(true); | ||||
|       var tutorial = this.props.tutorials.filter(tutorial => tutorial._id === value)[0]; | ||||
|       var tutorial = this.props.tutorials.filter( | ||||
|         (tutorial) => tutorial._id === value | ||||
|       )[0]; | ||||
|       this.props.readJSON(tutorial); | ||||
|       this.setState({ snackbar: true, key: Date.now(), message: `Das ausgewählte Tutorial "${tutorial.title}" wurde erfolgreich übernommen.`, type: 'success' }); | ||||
|     } | ||||
|       this.setState({ | ||||
|         snackbar: true, | ||||
|         key: Date.now(), | ||||
|         message: `Das ausgewählte Tutorial "${tutorial.title}" wurde erfolgreich übernommen.`, | ||||
|         type: "success", | ||||
|       }); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   resetFull = () => { | ||||
|     this.props.resetTutorialBuilder(); | ||||
|     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); | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   resetTutorial = () => { | ||||
|     var tutorial = this.props.tutorials.filter(tutorial => tutorial._id === this.props.id)[0]; | ||||
|     var tutorial = this.props.tutorials.filter( | ||||
|       (tutorial) => tutorial._id === this.props.id | ||||
|     )[0]; | ||||
|     this.props.readJSON(tutorial); | ||||
|     this.setState({ snackbar: true, key: Date.now(), message: `Das Tutorial ${tutorial.title} wurde erfolgreich auf den ursprünglichen Stand zurückgesetzt.`, type: 'success' }); | ||||
|     this.setState({ | ||||
|       snackbar: true, | ||||
|       key: Date.now(), | ||||
|       message: `Das Tutorial ${tutorial.title} wurde erfolgreich auf den ursprünglichen Stand zurückgesetzt.`, | ||||
|       type: "success", | ||||
|     }); | ||||
|     window.scrollTo(0, 0); | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   submit = () => { | ||||
|     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' }); | ||||
|       this.setState({ | ||||
|         snackbar: true, | ||||
|         key: Date.now(), | ||||
|         message: `Die Angaben für das Tutorial sind nicht vollständig.`, | ||||
|         type: "error", | ||||
|       }); | ||||
|       window.scrollTo(0, 0); | ||||
|       return false; | ||||
|     } | ||||
|     else { | ||||
|     } else { | ||||
|       // export steps without attribute 'url'
 | ||||
|       var steps = this.props.steps; | ||||
|       var newTutorial = new FormData(); | ||||
|       newTutorial.append('title', this.props.title); | ||||
|       if(this.props.badge){ | ||||
|         newTutorial.append('badge', this.props.badge); | ||||
|       } | ||||
|       newTutorial.append("title", this.props.title); | ||||
|       steps.forEach((step, i) => { | ||||
|         if(step._id){ | ||||
|         if (step._id) { | ||||
|           newTutorial.append(`steps[${i}][_id]`, step._id); | ||||
|         } | ||||
|         newTutorial.append(`steps[${i}][type]`, step.type); | ||||
|         newTutorial.append(`steps[${i}][headline]`, step.headline); | ||||
|         newTutorial.append(`steps[${i}][text]`, step.text); | ||||
|         if (i === 0 && step.type === 'instruction') { | ||||
|           if (step.requirements) { // optional
 | ||||
|         if (i === 0 && step.type === "instruction") { | ||||
|           if (step.requirements) { | ||||
|             // optional
 | ||||
|             step.requirements.forEach((requirement, j) => { | ||||
|               newTutorial.append(`steps[${i}][requirements][${j}]`, requirement); | ||||
|               newTutorial.append( | ||||
|                 `steps[${i}][requirements][${j}]`, | ||||
|                 requirement | ||||
|               ); | ||||
|             }); | ||||
|           } | ||||
|           step.hardware.forEach((hardware, j) => { | ||||
|             newTutorial.append(`steps[${i}][hardware][${j}]`, hardware); | ||||
|           }); | ||||
|         } | ||||
|         if (step.xml) { // optional
 | ||||
|         if (step.xml) { | ||||
|           // optional
 | ||||
|           newTutorial.append(`steps[${i}][xml]`, step.xml); | ||||
|         } | ||||
|         if (step.media) { // optional
 | ||||
|         if (step.media) { | ||||
|           // optional
 | ||||
|           if (step.media.youtube) { | ||||
|             newTutorial.append(`steps[${i}][media][youtube]`, step.media.youtube); | ||||
|             newTutorial.append( | ||||
|               `steps[${i}][media][youtube]`, | ||||
|               step.media.youtube | ||||
|             ); | ||||
|           } | ||||
|           if (step.media.picture) { | ||||
|             newTutorial.append(`steps[${i}][media][picture]`, step.media.picture); | ||||
|             newTutorial.append( | ||||
|               `steps[${i}][media][picture]`, | ||||
|               step.media.picture | ||||
|             ); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|       return newTutorial; | ||||
|     } | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   submitNew = () => { | ||||
|     var newTutorial = this.submit(); | ||||
|     if(newTutorial){ | ||||
|     if (newTutorial) { | ||||
|       const config = { | ||||
|         success: res => { | ||||
|         success: (res) => { | ||||
|           var tutorial = res.data.tutorial; | ||||
|           this.props.history.push(`/tutorial/${tutorial._id}`); | ||||
|         }, | ||||
|         error: err => { | ||||
|           this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Erstellen des Tutorials. Versuche es noch einmal.`, type: 'error' }); | ||||
|         error: (err) => { | ||||
|           this.setState({ | ||||
|             snackbar: true, | ||||
|             key: Date.now(), | ||||
|             message: `Fehler beim Erstellen des Tutorials. Versuche es noch einmal.`, | ||||
|             type: "error", | ||||
|           }); | ||||
|           window.scrollTo(0, 0); | ||||
|         } | ||||
|         }, | ||||
|       }; | ||||
|       axios.post(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/`, newTutorial, config) | ||||
|         .then(res => { | ||||
|       axios | ||||
|         .post( | ||||
|           `${process.env.REACT_APP_BLOCKLY_API}/tutorial/`, | ||||
|           newTutorial, | ||||
|           config | ||||
|         ) | ||||
|         .then((res) => { | ||||
|           res.config.success(res); | ||||
|         }) | ||||
|         .catch(err => { | ||||
|         .catch((err) => { | ||||
|           err.config.error(err); | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   submitUpdate = () => { | ||||
|     var updatedTutorial = this.submit(); | ||||
|     if(updatedTutorial){ | ||||
|     if (updatedTutorial) { | ||||
|       const config = { | ||||
|         success: res => { | ||||
|         success: (res) => { | ||||
|           var tutorial = res.data.tutorial; | ||||
|           this.props.history.push(`/tutorial/${tutorial._id}`); | ||||
|         }, | ||||
|         error: err => { | ||||
|           this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Ändern des Tutorials. Versuche es noch einmal.`, type: 'error' }); | ||||
|         error: (err) => { | ||||
|           this.setState({ | ||||
|             snackbar: true, | ||||
|             key: Date.now(), | ||||
|             message: `Fehler beim Ändern des Tutorials. Versuche es noch einmal.`, | ||||
|             type: "error", | ||||
|           }); | ||||
|           window.scrollTo(0, 0); | ||||
|         } | ||||
|         }, | ||||
|       }; | ||||
|       axios.put(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/${this.props.id}`, updatedTutorial, config) | ||||
|         .then(res => { | ||||
|       axios | ||||
|         .put( | ||||
|           `${process.env.REACT_APP_BLOCKLY_API}/tutorial/${this.props.id}`, | ||||
|           updatedTutorial, | ||||
|           config | ||||
|         ) | ||||
|         .then((res) => { | ||||
|           res.config.success(res); | ||||
|         }) | ||||
|         .catch(err => { | ||||
|         .catch((err) => { | ||||
|           err.config.error(err); | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   render() { | ||||
|     var filteredTutorials = this.props.tutorials.filter(tutorial => tutorial.creator === this.props.user.email); | ||||
|     var filteredTutorials = this.props.tutorials.filter( | ||||
|       (tutorial) => tutorial.creator === this.props.user.email | ||||
|     ); | ||||
|     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> | ||||
| 
 | ||||
|         <RadioGroup row value={this.state.tutorial} onChange={(e) => this.onChange(e.target.value)}> | ||||
|           <FormControlLabel style={{ color: 'black' }} | ||||
|         <RadioGroup | ||||
|           row | ||||
|           value={this.state.tutorial} | ||||
|           onChange={(e) => this.onChange(e.target.value)} | ||||
|         > | ||||
|           <FormControlLabel | ||||
|             style={{ color: "black" }} | ||||
|             value="new" | ||||
|             control={<Radio color="primary" />} | ||||
|             label="neues Tutorial erstellen" | ||||
|             labelPlacement="end" | ||||
|           /> | ||||
|           {filteredTutorials.length > 0 ? | ||||
|           {filteredTutorials.length > 0 ? ( | ||||
|             <div> | ||||
|               <FormControlLabel style={{ color: 'black' }} | ||||
|               <FormControlLabel | ||||
|                 style={{ color: "black" }} | ||||
|                 disabled={this.props.index === 0} | ||||
|                 value="change" | ||||
|                 control={<Radio color="primary" />} | ||||
|                 label="bestehendes Tutorial ändern" | ||||
|                 labelPlacement="end" | ||||
|               /> | ||||
|               <FormControlLabel style={{ color: 'black' }} | ||||
|               <FormControlLabel | ||||
|                 style={{ color: "black" }} | ||||
|                 disabled={this.props.index === 0} | ||||
|                 value="delete" | ||||
|                 control={<Radio color="primary" />} | ||||
| @ -303,110 +414,196 @@ class Builder extends Component { | ||||
|                 labelPlacement="end" | ||||
|               /> | ||||
|             </div> | ||||
|             : null} | ||||
|           ) : null} | ||||
|         </RadioGroup> | ||||
| 
 | ||||
|         <Divider variant='fullWidth' style={{ margin: '10px 0 15px 0' }} /> | ||||
|         <Divider variant="fullWidth" style={{ margin: "10px 0 15px 0" }} /> | ||||
| 
 | ||||
|         {this.state.tutorial === 'new' ? | ||||
|         {this.state.tutorial === "new" ? ( | ||||
|           /*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> | ||||
|           : <FormControl variant="outlined" style={{ width: '100%' }}> | ||||
|         ) : ( | ||||
|           <FormControl variant="outlined" style={{ width: "100%" }}> | ||||
|             <InputLabel id="select-outlined-label">Tutorial</InputLabel> | ||||
|             <Select | ||||
|               color='primary' | ||||
|               color="primary" | ||||
|               labelId="select-outlined-label" | ||||
|               value={this.props.id} | ||||
|               onChange={(e) => this.onChangeId(e.target.value)} | ||||
|               label="Tutorial" | ||||
|             > | ||||
|               {filteredTutorials.map(tutorial => | ||||
|               {filteredTutorials.map((tutorial) => ( | ||||
|                 <MenuItem value={tutorial._id}>{tutorial.title}</MenuItem> | ||||
|               )} | ||||
|               ))} | ||||
|             </Select> | ||||
|           </FormControl> | ||||
|         } | ||||
| 
 | ||||
|         <Divider variant='fullWidth' style={{ margin: '10px 0 15px 0' }} /> | ||||
| 
 | ||||
|         {this.state.tutorial === 'new' || (this.state.tutorial === 'change' && this.props.id !== '') ? | ||||
|         /*Tutorial-Builder-Form*/ | ||||
|         <div> | ||||
|           {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} /> | ||||
|           <Badge error={this.props.error.badge}/> | ||||
| 
 | ||||
|           {this.props.steps.map((step, i) => | ||||
|             <Step step={step} index={i} key={i} /> | ||||
|         )} | ||||
| 
 | ||||
|           {/*submit or reset*/} | ||||
|           {this.state.tutorial !== 'delete' ? | ||||
|             <div> | ||||
|               <Divider variant='fullWidth' style={{ margin: '30px 0 10px 0' }} /> | ||||
|               {this.state.tutorial === 'new' ? | ||||
|                 <div> | ||||
|                   <Button style={{ marginRight: '10px', marginTop: '10px' }} variant='contained' color='primary' onClick={() => this.submitNew()}>Tutorial erstellen</Button> | ||||
|                   <Button style={{ marginTop: '10px' }} variant='contained' onClick={() => this.resetFull()}>Zurücksetzen</Button> | ||||
|                 </div> | ||||
|               : <div> | ||||
|                   <Button style={{ marginRight: '10px', marginTop: '10px' }} variant='contained' color='primary' onClick={() => this.submitUpdate()}>Tutorial ändern</Button> | ||||
|                   <Button style={{ marginTop: '10px' }} variant='contained' onClick={() => this.resetTutorial()}>Zurücksetzen</Button> | ||||
|                 </div> | ||||
|               } | ||||
|             </div> | ||||
|           : null} | ||||
|         <Divider variant="fullWidth" style={{ margin: "10px 0 15px 0" }} /> | ||||
| 
 | ||||
|             <Backdrop className={this.props.classes.backdrop} open={this.props.isProgress}> | ||||
|         {this.state.tutorial === "new" || | ||||
|         (this.state.tutorial === "change" && this.props.id !== "") ? ( | ||||
|           /*Tutorial-Builder-Form*/ | ||||
|           <div> | ||||
|             {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} | ||||
|             /> | ||||
| 
 | ||||
|             {this.props.steps.map((step, i) => ( | ||||
|               <Step step={step} index={i} key={i} /> | ||||
|             ))} | ||||
| 
 | ||||
|             {/*submit or reset*/} | ||||
|             {this.state.tutorial !== "delete" ? ( | ||||
|               <div> | ||||
|                 <Divider | ||||
|                   variant="fullWidth" | ||||
|                   style={{ margin: "30px 0 10px 0" }} | ||||
|                 /> | ||||
|                 {this.state.tutorial === "new" ? ( | ||||
|                   <div> | ||||
|                     <Button | ||||
|                       style={{ marginRight: "10px", marginTop: "10px" }} | ||||
|                       variant="contained" | ||||
|                       color="primary" | ||||
|                       onClick={() => this.submitNew()} | ||||
|                     > | ||||
|                       Tutorial erstellen | ||||
|                     </Button> | ||||
|                     <Button | ||||
|                       style={{ marginTop: "10px" }} | ||||
|                       variant="contained" | ||||
|                       onClick={() => this.resetFull()} | ||||
|                     > | ||||
|                       Zurücksetzen | ||||
|                     </Button> | ||||
|                   </div> | ||||
|                 ) : ( | ||||
|                   <div> | ||||
|                     <Button | ||||
|                       style={{ marginRight: "10px", marginTop: "10px" }} | ||||
|                       variant="contained" | ||||
|                       color="primary" | ||||
|                       onClick={() => this.submitUpdate()} | ||||
|                     > | ||||
|                       Tutorial ändern | ||||
|                     </Button> | ||||
|                     <Button | ||||
|                       style={{ marginTop: "10px" }} | ||||
|                       variant="contained" | ||||
|                       onClick={() => this.resetTutorial()} | ||||
|                     > | ||||
|                       Zurücksetzen | ||||
|                     </Button> | ||||
|                   </div> | ||||
|                 )} | ||||
|               </div> | ||||
|             ) : null} | ||||
| 
 | ||||
|             <Backdrop | ||||
|               className={this.props.classes.backdrop} | ||||
|               open={this.props.isProgress} | ||||
|             > | ||||
|               <CircularProgress color="inherit" /> | ||||
|             </Backdrop> | ||||
|           </div> | ||||
|           : null} | ||||
|         ) : null} | ||||
| 
 | ||||
|         {this.state.tutorial === 'delete' && this.props.id !== '' ? | ||||
|         {this.state.tutorial === "delete" && this.props.id !== "" ? ( | ||||
|           <Button | ||||
|             className={this.props.classes.errorButton} | ||||
|             variant='contained' | ||||
|             color='primary' | ||||
|             onClick={() => this.props.deleteTutorial()}>Tutorial löschen</Button> | ||||
|           : null} | ||||
|             variant="contained" | ||||
|             color="primary" | ||||
|             onClick={() => this.props.deleteTutorial()} | ||||
|           > | ||||
|             Tutorial löschen | ||||
|           </Button> | ||||
|         ) : null} | ||||
| 
 | ||||
|         <Dialog | ||||
|           open={this.state.open} | ||||
|           maxWidth={this.state.string ? 'md' : 'sm'} | ||||
|           maxWidth={this.state.string ? "md" : "sm"} | ||||
|           fullWidth={this.state.string} | ||||
|           title={this.state.title} | ||||
|           content={this.state.content} | ||||
|           onClose={this.toggle} | ||||
|           onClick={this.toggle} | ||||
|           button={'Schließen'} | ||||
|           button={"Schließen"} | ||||
|           actions={ | ||||
|             this.state.string ? | ||||
|             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> | ||||
|                 <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 | ||||
|             ) : null | ||||
|           } | ||||
|         > | ||||
|           {this.state.string ? | ||||
|             <Textfield value={this.props.json} property={'json'} label={'JSON'} multiline error={this.props.error.json} /> | ||||
|             : null} | ||||
|           {this.state.string ? ( | ||||
|             <Textfield | ||||
|               value={this.props.json} | ||||
|               property={"json"} | ||||
|               label={"JSON"} | ||||
|               multiline | ||||
|               error={this.props.error.json} | ||||
|             /> | ||||
|           ) : null} | ||||
|         </Dialog> | ||||
| 
 | ||||
|         <Snackbar | ||||
| @ -415,10 +612,9 @@ class Builder extends Component { | ||||
|           type={this.state.type} | ||||
|           key={this.state.key} | ||||
|         /> | ||||
| 
 | ||||
|       </div> | ||||
|     ); | ||||
|   }; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Builder.propTypes = { | ||||
| @ -434,7 +630,6 @@ Builder.propTypes = { | ||||
|   resetTutorialBuilder: PropTypes.func.isRequired, | ||||
|   tutorialProgress: PropTypes.func.isRequired, | ||||
|   title: PropTypes.string.isRequired, | ||||
|   badge: PropTypes.string.isRequired, | ||||
|   id: PropTypes.string.isRequired, | ||||
|   steps: PropTypes.array.isRequired, | ||||
|   change: PropTypes.number.isRequired, | ||||
| @ -444,12 +639,11 @@ Builder.propTypes = { | ||||
|   tutorials: PropTypes.array.isRequired, | ||||
|   message: PropTypes.object.isRequired, | ||||
|   user: PropTypes.object.isRequired, | ||||
|   authProgress: PropTypes.bool.isRequired | ||||
|   authProgress: PropTypes.bool.isRequired, | ||||
| }; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
| const mapStateToProps = (state) => ({ | ||||
|   title: state.builder.title, | ||||
|   badge: state.builder.badge, | ||||
|   id: state.builder.id, | ||||
|   steps: state.builder.steps, | ||||
|   change: state.builder.change, | ||||
| @ -459,7 +653,19 @@ const mapStateToProps = state => ({ | ||||
|   tutorials: state.tutorial.tutorials, | ||||
|   message: state.message, | ||||
|   user: state.auth.user, | ||||
|   authProgress: state.auth.progress | ||||
|   authProgress: state.auth.progress, | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps, { checkError, readJSON, jsonString, progress, tutorialId, resetTutorialBuilder, getTutorials, resetTutorial, tutorialProgress, clearMessages, deleteTutorial })(withStyles(styles, { withTheme: true })(withRouter(Builder))); | ||||
| export default connect(mapStateToProps, { | ||||
|   checkError, | ||||
|   readJSON, | ||||
|   jsonString, | ||||
|   progress, | ||||
|   tutorialId, | ||||
|   resetTutorialBuilder, | ||||
|   getTutorials, | ||||
|   resetTutorial, | ||||
|   tutorialProgress, | ||||
|   clearMessages, | ||||
|   deleteTutorial, | ||||
| })(withStyles(styles, { withTheme: true })(withRouter(Builder))); | ||||
|  | ||||
| @ -1,34 +1,39 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { tutorialTitle, tutorialBadge, jsonString, changeContent, setError, deleteError } from '../../../actions/tutorialBuilderActions'; | ||||
| import React, { Component } from "react"; | ||||
| import PropTypes from "prop-types"; | ||||
| import { connect } from "react-redux"; | ||||
| import { | ||||
|   tutorialTitle, | ||||
|   jsonString, | ||||
|   changeContent, | ||||
|   setError, | ||||
|   deleteError, | ||||
| } from "../../../actions/tutorialBuilderActions"; | ||||
| 
 | ||||
| import { withStyles } from '@material-ui/core/styles'; | ||||
| import OutlinedInput from '@material-ui/core/OutlinedInput'; | ||||
| import InputLabel from '@material-ui/core/InputLabel'; | ||||
| import FormControl from '@material-ui/core/FormControl'; | ||||
| import FormHelperText from '@material-ui/core/FormHelperText'; | ||||
| import { withStyles } from "@material-ui/core/styles"; | ||||
| import OutlinedInput from "@material-ui/core/OutlinedInput"; | ||||
| import InputLabel from "@material-ui/core/InputLabel"; | ||||
| import FormControl from "@material-ui/core/FormControl"; | ||||
| import FormHelperText from "@material-ui/core/FormHelperText"; | ||||
| 
 | ||||
| const styles = theme => ({ | ||||
| const styles = (theme) => ({ | ||||
|   multiline: { | ||||
|     padding: '18.5px 14px 18.5px 24px' | ||||
|     padding: "18.5px 14px 18.5px 24px", | ||||
|   }, | ||||
|   errorColor: { | ||||
|     color: `${theme.palette.error.dark} !important` | ||||
|     color: `${theme.palette.error.dark} !important`, | ||||
|   }, | ||||
|   errorColorShrink: { | ||||
|     color: `rgba(0, 0, 0, 0.54) !important` | ||||
|     color: `rgba(0, 0, 0, 0.54) !important`, | ||||
|   }, | ||||
|   errorBorder: { | ||||
|     borderColor: `${theme.palette.error.dark} !important` | ||||
|   } | ||||
|     borderColor: `${theme.palette.error.dark} !important`, | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| class Textfield extends Component { | ||||
| 
 | ||||
|   componentDidMount(){ | ||||
|     if(this.props.error){ | ||||
|       if(this.props.property !== 'media'){ | ||||
|   componentDidMount() { | ||||
|     if (this.props.error) { | ||||
|       if (this.props.property !== "media") { | ||||
|         this.props.deleteError(this.props.index, this.props.property); | ||||
|       } | ||||
|     } | ||||
| @ -36,38 +41,50 @@ class Textfield extends Component { | ||||
| 
 | ||||
|   handleChange = (e) => { | ||||
|     var value = e.target.value; | ||||
|     if(this.props.property === 'title'){ | ||||
|     if (this.props.property === "title") { | ||||
|       this.props.tutorialTitle(value); | ||||
|     } | ||||
|     else if(this.props.property === 'json'){ | ||||
|     } else if (this.props.property === "json") { | ||||
|       this.props.jsonString(value); | ||||
|     } else { | ||||
|       this.props.changeContent( | ||||
|         value, | ||||
|         this.props.index, | ||||
|         this.props.property, | ||||
|         this.props.property2 | ||||
|       ); | ||||
|     } | ||||
|     else if(this.props.property === 'badge'){ | ||||
|       this.props.tutorialBadge(value); | ||||
|     } | ||||
|     else { | ||||
|       this.props.changeContent(value, this.props.index, this.props.property, this.props.property2); | ||||
|     } | ||||
|     if(value.replace(/\s/g,'') === ''){ | ||||
|     if (value.replace(/\s/g, "") === "") { | ||||
|       this.props.setError(this.props.index, this.props.property); | ||||
|     } | ||||
|     else{ | ||||
|     } else { | ||||
|       this.props.deleteError(this.props.index, this.props.property); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   render() { | ||||
|     return ( | ||||
|       <FormControl variant="outlined" fullWidth style={{marginBottom: '10px'}}> | ||||
|       <FormControl | ||||
|         variant="outlined" | ||||
|         fullWidth | ||||
|         style={{ marginBottom: "10px" }} | ||||
|       > | ||||
|         <InputLabel | ||||
|           htmlFor={this.props.property} | ||||
|           classes={{shrink: this.props.error ? this.props.classes.errorColorShrink : null}} | ||||
|           classes={{ | ||||
|             shrink: this.props.error | ||||
|               ? this.props.classes.errorColorShrink | ||||
|               : null, | ||||
|           }} | ||||
|         > | ||||
|           {this.props.label} | ||||
|         </InputLabel> | ||||
|         <OutlinedInput | ||||
|           style={{borderRadius: '25px'}} | ||||
|           classes={{multiline: this.props.classes.multiline, notchedOutline: this.props.error ? this.props.classes.errorBorder : null}} | ||||
|           style={{ borderRadius: "25px" }} | ||||
|           classes={{ | ||||
|             multiline: this.props.classes.multiline, | ||||
|             notchedOutline: this.props.error | ||||
|               ? this.props.classes.errorBorder | ||||
|               : null, | ||||
|           }} | ||||
|           error={this.props.error} | ||||
|           value={this.props.value} | ||||
|           label={this.props.label} | ||||
| @ -77,21 +94,37 @@ class Textfield extends Component { | ||||
|           rowsMax={10} | ||||
|           onChange={(e) => this.handleChange(e)} | ||||
|         /> | ||||
|         {this.props.error ? | ||||
|           this.props.property === 'title' ? <FormHelperText className={this.props.classes.errorColor}>Gib einen Titel für das Tutorial ein.</FormHelperText> | ||||
|         : this.props.property === 'json' ? <FormHelperText className={this.props.classes.errorColor}>Gib einen JSON-String ein und bestätige diesen mit einem Klick auf den entsprechenden Button</FormHelperText> | ||||
|         : <FormHelperText className={this.props.classes.errorColor}>{this.props.errorText}</FormHelperText> | ||||
|         : null} | ||||
|         {this.props.error ? ( | ||||
|           this.props.property === "title" ? ( | ||||
|             <FormHelperText className={this.props.classes.errorColor}> | ||||
|               Gib einen Titel für das Tutorial ein. | ||||
|             </FormHelperText> | ||||
|           ) : this.props.property === "json" ? ( | ||||
|             <FormHelperText className={this.props.classes.errorColor}> | ||||
|               Gib einen JSON-String ein und bestätige diesen mit einem Klick auf | ||||
|               den entsprechenden Button | ||||
|             </FormHelperText> | ||||
|           ) : ( | ||||
|             <FormHelperText className={this.props.classes.errorColor}> | ||||
|               {this.props.errorText} | ||||
|             </FormHelperText> | ||||
|           ) | ||||
|         ) : null} | ||||
|       </FormControl> | ||||
|     ); | ||||
|   }; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Textfield.propTypes = { | ||||
|   tutorialTitle: PropTypes.func.isRequired, | ||||
|   tutorialBadge: PropTypes.func.isRequired, | ||||
|   jsonString: PropTypes.func.isRequired, | ||||
|   changeContent: PropTypes.func.isRequired, | ||||
| }; | ||||
| 
 | ||||
| export default connect(null, { tutorialTitle, tutorialBadge, jsonString, changeContent, setError, deleteError })(withStyles(styles, { withTheme: true })(Textfield)); | ||||
| export default connect(null, { | ||||
|   tutorialTitle, | ||||
|   jsonString, | ||||
|   changeContent, | ||||
|   setError, | ||||
|   deleteError, | ||||
| })(withStyles(styles, { withTheme: true })(Textfield)); | ||||
|  | ||||
| @ -1,46 +1,51 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { workspaceName } from '../../actions/workspaceActions'; | ||||
| import { clearMessages } from '../../actions/messageActions'; | ||||
| import { getTutorial, resetTutorial, tutorialStep,tutorialProgress } from '../../actions/tutorialActions'; | ||||
| import React, { Component } from "react"; | ||||
| import PropTypes from "prop-types"; | ||||
| import { connect } from "react-redux"; | ||||
| import { workspaceName } from "../../actions/workspaceActions"; | ||||
| import { clearMessages } from "../../actions/messageActions"; | ||||
| import { | ||||
|   getTutorial, | ||||
|   resetTutorial, | ||||
|   tutorialStep, | ||||
|   tutorialProgress, | ||||
| } from "../../actions/tutorialActions"; | ||||
| 
 | ||||
| import { withRouter } from 'react-router-dom'; | ||||
| import { withRouter } from "react-router-dom"; | ||||
| 
 | ||||
| import Breadcrumbs from '../Breadcrumbs'; | ||||
| import StepperHorizontal from './StepperHorizontal'; | ||||
| import StepperVertical from './StepperVertical'; | ||||
| import Instruction from './Instruction'; | ||||
| import Assessment from './Assessment'; | ||||
| import Badge from './Badge'; | ||||
| import NotFound from '../NotFound'; | ||||
| import * as Blockly from 'blockly' | ||||
| import { detectWhitespacesAndReturnReadableResult } from '../../helpers/whitespace'; | ||||
| import Breadcrumbs from "../Breadcrumbs"; | ||||
| import StepperHorizontal from "./StepperHorizontal"; | ||||
| import StepperVertical from "./StepperVertical"; | ||||
| import Instruction from "./Instruction"; | ||||
| import Assessment from "./Assessment"; | ||||
| import NotFound from "../NotFound"; | ||||
| import * as Blockly from "blockly"; | ||||
| import { detectWhitespacesAndReturnReadableResult } from "../../helpers/whitespace"; | ||||
| 
 | ||||
| 
 | ||||
| import Card from '@material-ui/core/Card'; | ||||
| import Button from '@material-ui/core/Button'; | ||||
| import Card from "@material-ui/core/Card"; | ||||
| import Button from "@material-ui/core/Button"; | ||||
| 
 | ||||
| class Tutorial extends Component { | ||||
| 
 | ||||
|   componentDidMount() { | ||||
|     this.props.tutorialProgress(); | ||||
|     // retrieve tutorial only if a potential user is loaded - authentication
 | ||||
|     // is finished (success or failed)
 | ||||
|     if(!this.props.progress){ | ||||
|     if (!this.props.progress) { | ||||
|       this.props.getTutorial(this.props.match.params.tutorialId); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   componentDidUpdate(props, state) { | ||||
|     if(props.progress !== this.props.progress && !this.props.progress){ | ||||
|     if (props.progress !== this.props.progress && !this.props.progress) { | ||||
|       // authentication is completed
 | ||||
|       this.props.getTutorial(this.props.match.params.tutorialId); | ||||
|     } | ||||
|     else if(this.props.tutorial && !this.props.isLoading && this.props.tutorial._id !== this.props.match.params.tutorialId) { | ||||
|     } else if ( | ||||
|       this.props.tutorial && | ||||
|       !this.props.isLoading && | ||||
|       this.props.tutorial._id !== this.props.match.params.tutorialId | ||||
|     ) { | ||||
|       this.props.getTutorial(this.props.match.params.tutorialId); | ||||
|     } | ||||
|     if (this.props.message.id === 'GET_TUTORIAL_FAIL') { | ||||
|     if (this.props.message.id === "GET_TUTORIAL_FAIL") { | ||||
|       alert(this.props.message.msg); | ||||
|     } | ||||
|   } | ||||
| @ -56,44 +61,97 @@ class Tutorial extends Component { | ||||
|   render() { | ||||
|     return ( | ||||
|       <div> | ||||
|         {this.props.isLoading ? null : | ||||
|           !this.props.tutorial ? | ||||
|             this.props.message.id === 'GET_TUTORIAL_FAIL' ? <NotFound button={{ title: Blockly.Msg.messages_GET_TUTORIAL_FAIL, link: '/tutorial' }} /> : null | ||||
|             : (() => { | ||||
|         {this.props.isLoading ? null : !this.props.tutorial ? ( | ||||
|           this.props.message.id === "GET_TUTORIAL_FAIL" ? ( | ||||
|             <NotFound | ||||
|               button={{ | ||||
|                 title: Blockly.Msg.messages_GET_TUTORIAL_FAIL, | ||||
|                 link: "/tutorial", | ||||
|               }} | ||||
|             /> | ||||
|           ) : null | ||||
|         ) : ( | ||||
|           (() => { | ||||
|             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)}`; | ||||
|             var name = `${detectWhitespacesAndReturnReadableResult( | ||||
|               tutorial.title | ||||
|             )}_${detectWhitespacesAndReturnReadableResult(step.headline)}`;
 | ||||
|             return ( | ||||
|               <div> | ||||
|                   <Breadcrumbs content={[{ link: '/tutorial', title: 'Tutorial' }, { link: `/tutorial/${this.props.tutorial._id}`, title: tutorial.title }]} /> | ||||
|                 <Breadcrumbs | ||||
|                   content={[ | ||||
|                     { link: "/tutorial", title: "Tutorial" }, | ||||
|                     { | ||||
|                       link: `/tutorial/${this.props.tutorial._id}`, | ||||
|                       title: tutorial.title, | ||||
|                     }, | ||||
|                   ]} | ||||
|                 /> | ||||
| 
 | ||||
|                 <StepperHorizontal /> | ||||
|                   <Badge /> | ||||
| 
 | ||||
|                   <div style={{ display: 'flex' }}> | ||||
|                 <div style={{ display: "flex" }}> | ||||
|                   <StepperVertical steps={steps} /> | ||||
|                   {/* calc(Card-padding: 10px + Button-height: 35px + Button-marginTop: 15px)*/} | ||||
|                     <Card style={{ padding: '10px 10px 60px 10px', display: 'block', position: 'relative', height: 'max-content', width: '100%' }}> | ||||
|                       {step ? | ||||
|                         step.type === 'instruction' ? | ||||
|                   <Card | ||||
|                     style={{ | ||||
|                       padding: "10px 10px 60px 10px", | ||||
|                       display: "block", | ||||
|                       position: "relative", | ||||
|                       height: "max-content", | ||||
|                       width: "100%", | ||||
|                     }} | ||||
|                   > | ||||
|                     {step ? ( | ||||
|                       step.type === "instruction" ? ( | ||||
|                         <Instruction step={step} /> | ||||
|                           : <Assessment step={step} name={name} /> // if step.type === 'assessment'
 | ||||
|                         : null} | ||||
|                       ) : ( | ||||
|                         <Assessment step={step} name={name} /> | ||||
|                       ) // if step.type === 'assessment'
 | ||||
|                     ) : null} | ||||
| 
 | ||||
|                       <div style={{ marginTop: '20px', position: 'absolute', bottom: '10px' }}> | ||||
|                         <Button style={{ marginRight: '10px', height: '35px' }} variant='contained' disabled={this.props.activeStep === 0} onClick={() => this.props.tutorialStep(this.props.activeStep - 1)}>Zurück</Button> | ||||
|                         <Button style={{ height: '35px' }} variant='contained' color='primary' disabled={this.props.activeStep === tutorial.steps.length - 1} onClick={() => this.props.tutorialStep(this.props.activeStep + 1)}>Weiter</Button> | ||||
|                     <div | ||||
|                       style={{ | ||||
|                         marginTop: "20px", | ||||
|                         position: "absolute", | ||||
|                         bottom: "10px", | ||||
|                       }} | ||||
|                     > | ||||
|                       <Button | ||||
|                         style={{ marginRight: "10px", height: "35px" }} | ||||
|                         variant="contained" | ||||
|                         disabled={this.props.activeStep === 0} | ||||
|                         onClick={() => | ||||
|                           this.props.tutorialStep(this.props.activeStep - 1) | ||||
|                         } | ||||
|                       > | ||||
|                         Zurück | ||||
|                       </Button> | ||||
|                       <Button | ||||
|                         style={{ height: "35px" }} | ||||
|                         variant="contained" | ||||
|                         color="primary" | ||||
|                         disabled={ | ||||
|                           this.props.activeStep === tutorial.steps.length - 1 | ||||
|                         } | ||||
|                         onClick={() => | ||||
|                           this.props.tutorialStep(this.props.activeStep + 1) | ||||
|                         } | ||||
|                       > | ||||
|                         Weiter | ||||
|                       </Button> | ||||
|                     </div> | ||||
|                   </Card> | ||||
|                 </div> | ||||
|               </div> | ||||
|               ) | ||||
|             ); | ||||
|           })() | ||||
|         } | ||||
|         )} | ||||
|       </div> | ||||
|     ); | ||||
|   }; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Tutorial.propTypes = { | ||||
| @ -109,17 +167,24 @@ Tutorial.propTypes = { | ||||
|   tutorial: PropTypes.object.isRequired, | ||||
|   isLoading: PropTypes.bool.isRequired, | ||||
|   message: PropTypes.object.isRequired, | ||||
|   progress: PropTypes.bool.isRequired | ||||
|   progress: PropTypes.bool.isRequired, | ||||
| }; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
| const mapStateToProps = (state) => ({ | ||||
|   change: state.tutorial.change, | ||||
|   status: state.tutorial.status, | ||||
|   activeStep: state.tutorial.activeStep, | ||||
|   tutorial: state.tutorial.tutorials[0], | ||||
|   isLoading: state.tutorial.progress, | ||||
|   message: state.message, | ||||
|   progress: state.auth.progress | ||||
|   progress: state.auth.progress, | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps, { getTutorial, resetTutorial, tutorialStep, tutorialProgress, clearMessages, workspaceName })(withRouter(Tutorial)); | ||||
| export default connect(mapStateToProps, { | ||||
|   getTutorial, | ||||
|   resetTutorial, | ||||
|   tutorialStep, | ||||
|   tutorialProgress, | ||||
|   clearMessages, | ||||
|   workspaceName, | ||||
| })(withRouter(Tutorial)); | ||||
|  | ||||
| @ -1,267 +0,0 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { connectMyBadges, disconnectMyBadges } from '../../actions/authActions'; | ||||
| 
 | ||||
| import axios from 'axios'; | ||||
| import { withRouter } from 'react-router-dom'; | ||||
| 
 | ||||
| import Breadcrumbs from '../Breadcrumbs'; | ||||
| import Alert from '../Alert'; | ||||
| 
 | ||||
| import { withStyles } from '@material-ui/core/styles'; | ||||
| import Paper from '@material-ui/core/Paper'; | ||||
| import Button from '@material-ui/core/Button'; | ||||
| import IconButton from '@material-ui/core/IconButton'; | ||||
| import TextField from '@material-ui/core/TextField'; | ||||
| import Divider from '@material-ui/core/Divider'; | ||||
| import InputAdornment from '@material-ui/core/InputAdornment'; | ||||
| import Link from '@material-ui/core/Link'; | ||||
| import Typography from '@material-ui/core/Typography'; | ||||
| import Grid from '@material-ui/core/Grid'; | ||||
| import Avatar from '@material-ui/core/Avatar'; | ||||
| 
 | ||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||
| import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; | ||||
| 
 | ||||
| const styles = (theme) => ({ | ||||
|   root: { | ||||
|     '& label.Mui-focused': { | ||||
|       color: '#aed9c8' | ||||
|     }, | ||||
|     '& .MuiOutlinedInput-root': { | ||||
|       '&.Mui-focused fieldset': { | ||||
|         borderColor: '#aed9c8' | ||||
|       }, | ||||
|       borderRadius: '0.75rem' | ||||
|     } | ||||
|   }, | ||||
|   text: { | ||||
|     fontFamily: [ | ||||
|       '"Open Sans"', | ||||
|       'BlinkMacSystemFont', | ||||
|       '"Segoe UI"', | ||||
|       'Roboto', | ||||
|       '"Helvetica Neue"', | ||||
|       'Arial', | ||||
|       'sans-serif', | ||||
|       '"Apple Color Emoji"', | ||||
|       '"Segoe UI Emoji"', | ||||
|       '"Segoe UI Symbol"', | ||||
|     ].join(','), | ||||
|     fontSize: 16 | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| export class MyBadges extends Component { | ||||
| 
 | ||||
|   constructor(props) { | ||||
|     super(props); | ||||
|     this.state = { | ||||
|       username: '', | ||||
|       password: '', | ||||
|       showPassword: false, | ||||
|       msg: '', | ||||
|       badges: [], | ||||
|       progress: false | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   componentDidMount(){ | ||||
|     if(this.props.user.badge){ | ||||
|       this.getBadges(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   componentDidUpdate(props){ | ||||
|     const { message } = this.props; | ||||
|     if (message !== props.message) { | ||||
|       // Check for login error
 | ||||
|       if(message.id === 'MYBADGES_CONNECT_FAIL'){ | ||||
|         this.setState({msg: 'Der Benutzername oder das Passwort ist nicht korrekt.', username: '', password: '', showPassword: false}); | ||||
|       } | ||||
|       else if(message.id === 'MYBADGES_CONNECT_SUCCESS'){ | ||||
|         this.getBadges(); | ||||
|       } | ||||
|       else if(message.id === 'MYBADGES_DISCONNECT_SUCCESS' || message.id === 'MYBADGES_DISCONNECT_FAIL'){ | ||||
|         this.setState({progress: false}); | ||||
|       } | ||||
|       else { | ||||
|         this.setState({msg: null}); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getBadges = () => { | ||||
|     this.setState({progress: true}); | ||||
|     const config = { | ||||
|       success: res => { | ||||
|         this.setState({badges: res.data.badges, progress: false}); | ||||
|       }, | ||||
|       error: err => { | ||||
|         this.setState({progress: false}); | ||||
|       } | ||||
|     }; | ||||
|     axios.get(`${process.env.REACT_APP_BLOCKLY_API}/user/badge`, config) | ||||
|     .then(res => { | ||||
|       res.config.success(res); | ||||
|     }) | ||||
|     .catch(err => { | ||||
|       err.config.error(err); | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   onChange = e => { | ||||
|     this.setState({ [e.target.name]: e.target.value, msg: '' }); | ||||
|   }; | ||||
| 
 | ||||
|   onSubmit = e => { | ||||
|     e.preventDefault(); | ||||
|     const {username, password} = this.state; | ||||
|     // create user object
 | ||||
|     const user = { | ||||
|       username, | ||||
|       password | ||||
|     }; | ||||
|     this.props.connectMyBadges(user); | ||||
|   }; | ||||
| 
 | ||||
|   handleClickShowPassword = () => { | ||||
|     this.setState({ showPassword: !this.state.showPassword }); | ||||
|   }; | ||||
| 
 | ||||
|   handleMouseDownPassword = (e) => { | ||||
|     e.preventDefault(); | ||||
|   }; | ||||
| 
 | ||||
|   render(){ | ||||
|     return( | ||||
|       <div> | ||||
|         <Breadcrumbs content={[{ link: '/user/badge', title: 'MyBadges' }]} /> | ||||
| 
 | ||||
|         <Grid container spacing={2}> | ||||
|           <Grid item xs={12} style={{margin: '4px'}}> | ||||
|             {!this.props.user.badge ? | ||||
|               <Alert> | ||||
|                 Du kannst dein Blockly-Konto mit deinem <Link href={`${process.env.REACT_APP_MYBADGES}`}>MyBadges</Link>-Konto verknüpfen, um Badges erwerben zu können. | ||||
|               </Alert> | ||||
|             : null} | ||||
|             <Paper style={{background: '#fffbf5'}}> | ||||
|               <div style={{display: 'flex', flexDirection: 'row', alignSelf: 'center', justifyContent: 'center', flexWrap: 'wrap'}}> | ||||
|                 <div style={!this.props.user.badge ? {margin: '15px 15px 0px 15px'} : {margin: '15px'}}> | ||||
|                   <img src={`${process.env.REACT_APP_MYBADGES}/static/media/Logo.d1c71fdf.png`} alt="My Badges" style={{maxWidth: '200px', maxHeight: '200px'}}></img> | ||||
|                 </div> | ||||
|                 {!this.props.user.badge ? | ||||
|                   <div style={{maxWidth: '500px', alignSelf: 'center', textAlign: 'center', margin: '15px'}}> | ||||
|                     {this.state.msg ? | ||||
|                       <div style={{lineHeight: 1.43, borderRadius: '0.75rem', padding: '14px 16px', marginBottom: '10px', color: 'rgb(97, 26, 21)', backgroundColor: 'rgb(253, 236, 234)', fontFamily: `"Open Sans",BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"`}}> | ||||
|                         {this.state.msg} | ||||
|                       </div> : null | ||||
|                     } | ||||
|                     <TextField | ||||
|                       style={{marginBottom: '10px'}} | ||||
|                       classes={{root: this.props.classes.root}} | ||||
|                       variant='outlined' | ||||
|                       type='text' | ||||
|                       label='Nutzername' | ||||
|                       name='username' | ||||
|                       value={this.state.username} | ||||
|                       onChange={this.onChange} | ||||
|                       fullWidth={true} | ||||
|                     /> | ||||
|                     <TextField | ||||
|                       classes={{root: this.props.classes.root}} | ||||
|                       variant='outlined' | ||||
|                       type={this.state.showPassword ? 'text' : 'password'} | ||||
|                       label='Passwort' | ||||
|                       name='password' | ||||
|                       value={this.state.password} | ||||
|                       InputProps={{ | ||||
|                         endAdornment: | ||||
|                           <InputAdornment | ||||
|                             position="end" | ||||
|                           > | ||||
|                             <IconButton | ||||
|                               onClick={this.handleClickShowPassword} | ||||
|                               onMouseDown={this.handleMouseDownPassword} | ||||
|                               edge="end" | ||||
|                             > | ||||
|                               <FontAwesomeIcon size='xs' icon={this.state.showPassword ? faEyeSlash : faEye} /> | ||||
|                             </IconButton> | ||||
|                           </InputAdornment> | ||||
|                       }} | ||||
|                       onChange={this.onChange} | ||||
|                       fullWidth={true} | ||||
|                     /> | ||||
|                     <p> | ||||
|                       <Button variant='contained' onClick={this.onSubmit} className={this.props.classes.text} style={{background: '#aed9c8', borderRadius: '0.75rem', width: '100%'}}> | ||||
|                         Anmelden | ||||
|                       </Button> | ||||
|                     </p> | ||||
|                     <p className={this.props.classes.text} style={{textAlign: 'center', fontSize: '0.8rem'}}> | ||||
|                       <Link style={{color: '#aed9c8'}} href={`${process.env.REACT_APP_MYBADGES}/user/password`}>Passwort vergessen?</Link> | ||||
|                     </p> | ||||
|                     <Divider variant='fullWidth'/> | ||||
|                     <p className={this.props.classes.text} style={{textAlign: 'center', paddingRight: "34px", paddingLeft: "34px"}}> | ||||
|                       Du hast noch kein Konto? <Link style={{color: '#aed9c8'}} href={`${process.env.REACT_APP_MYBADGES}/register`}>Registrieren</Link> | ||||
|                     </p> | ||||
|                   </div> | ||||
|                 : <div style={{margin: '15px', alignSelf: 'center'}}> | ||||
|                     <Typography style={{fontWeight: 'bold', fontSize: '1.1rem'}}>MyBadges-Konto ist erfolgreich verknüpft.</Typography> | ||||
|                     <Button variant='outlined' style={{borderColor: '#aed9c8'}} onClick={() => {this.props.disconnectMyBadges(); this.setState({badges: [], progress: true});}}>Konto trennen</Button> | ||||
|                   </div>} | ||||
|               </div> | ||||
|             </Paper> | ||||
|           </Grid> | ||||
| 
 | ||||
|           {this.props.user.badge && !this.state.progress ? | ||||
|             <Grid container item> | ||||
|               <Grid item style={{margin: '4px'}}> | ||||
|               {this.state.badges && this.state.badges.length > 0 ? | ||||
|                 <Typography style={{fontWeight: 'bold'}}> | ||||
|                   Du hast {this.state.badges.length} {this.state.badges.length === 1 ? 'Badge' : 'Badges'} im Kontext Blockly for senseBox erreicht. | ||||
|                 </Typography> | ||||
|                : null} | ||||
|               </Grid> | ||||
|               <Grid container item> | ||||
|                 {this.state.badges && this.state.badges.length > 0 ? | ||||
|                   this.state.badges.map(badge => ( | ||||
|                     <Grid item xs={12} sm={6} md={4}> | ||||
|                       <Paper style={{margin: '4px', textAlign: 'center'}}> | ||||
|                         {badge.image && badge.image.path ? | ||||
|                           <Avatar src={`${process.env.REACT_APP_MYBADGES}/media/${badge.image.path}`} style={{width: '200px', height: '200px', marginLeft: 'auto', marginRight: 'auto'}}/> | ||||
|                         : <Avatar style={{width: '200px', height: '200px', marginLeft: 'auto', marginRight: 'auto'}}></Avatar>} | ||||
|                         <Typography variant='h6' style={{display: 'flex', cursor: 'default', paddingBottom: '6px'}}> | ||||
|                           <div style={{flexGrow:1, marginLeft: '10px', marginRight: '10px'}}>{badge.name}</div> | ||||
|                         </Typography> | ||||
|                       </Paper> | ||||
|                     </Grid> | ||||
|                   )) | ||||
|                 : | ||||
|                   <Grid item style={{margin: '4px'}}> | ||||
|                     <Typography style={{fontWeight: 'bold'}}> | ||||
|                       Du hast noch keine Badges im Kontext senseBox for Blockly erreicht. | ||||
|                     </Typography> | ||||
|                   </Grid>} | ||||
|               </Grid> | ||||
|             </Grid> | ||||
|           : null} | ||||
|         </Grid> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| MyBadges.propTypes = { | ||||
|   connectMyBadges: PropTypes.func.isRequired, | ||||
|   disconnectMyBadges: PropTypes.func.isRequired, | ||||
|   message: PropTypes.object.isRequired, | ||||
|   user: PropTypes.object.isRequired | ||||
| }; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   message: state.message, | ||||
|   user: state.auth.user | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps, { connectMyBadges, disconnectMyBadges })(withStyles(styles, { withTheme: true })(withRouter(MyBadges))); | ||||
| @ -1,6 +1,4 @@ | ||||
| import { | ||||
|   MYBADGES_CONNECT, | ||||
|   MYBADGES_DISCONNECT, | ||||
|   USER_LOADED, | ||||
|   USER_LOADING, | ||||
|   AUTH_ERROR, | ||||
| @ -45,12 +43,6 @@ export default function foo(state = initialState, action) { | ||||
|         isAuthenticated: true, | ||||
|         progress: false, | ||||
|       }; | ||||
|     case MYBADGES_CONNECT: | ||||
|     case MYBADGES_DISCONNECT: | ||||
|       return { | ||||
|         ...state, | ||||
|         user: action.payload, | ||||
|       }; | ||||
|     case AUTH_ERROR: | ||||
|     case LOGIN_FAIL: | ||||
|     case LOGOUT_SUCCESS: | ||||
|  | ||||
| @ -1,47 +1,54 @@ | ||||
| import { PROGRESS, JSON_STRING, BUILDER_CHANGE, BUILDER_ERROR, BUILDER_TITLE, BUILDER_BADGE, BUILDER_ID, BUILDER_ADD_STEP, BUILDER_DELETE_STEP, BUILDER_CHANGE_STEP,BUILDER_CHANGE_ORDER, BUILDER_DELETE_PROPERTY } from '../actions/types'; | ||||
| import { | ||||
|   PROGRESS, | ||||
|   JSON_STRING, | ||||
|   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, | ||||
|   json: '', | ||||
|   title: '', | ||||
|   id: '', | ||||
|   json: "", | ||||
|   title: "", | ||||
|   id: "", | ||||
|   steps: [ | ||||
|     { | ||||
|       id: 1, | ||||
|       type: 'instruction', | ||||
|       headline: '', | ||||
|       text: '', | ||||
|       type: "instruction", | ||||
|       headline: "", | ||||
|       text: "", | ||||
|       hardware: [], | ||||
|       requirements: [] | ||||
|     } | ||||
|       requirements: [], | ||||
|     }, | ||||
|   ], | ||||
|   error: { | ||||
|     steps: [{}] | ||||
|   } | ||||
|     steps: [{}], | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export default function foo(state = initialState, action){ | ||||
|   switch(action.type){ | ||||
| export default function foo(state = initialState, action) { | ||||
|   switch (action.type) { | ||||
|     case BUILDER_CHANGE: | ||||
|       return { | ||||
|         ...state, | ||||
|         change: state.change += 1 | ||||
|         change: (state.change += 1), | ||||
|       }; | ||||
|     case BUILDER_TITLE: | ||||
|       return { | ||||
|         ...state, | ||||
|         title: action.payload | ||||
|       }; | ||||
|     case BUILDER_BADGE: | ||||
|       return { | ||||
|         ...state, | ||||
|         badge: action.payload | ||||
|         title: action.payload, | ||||
|       }; | ||||
|     case BUILDER_ID: | ||||
|       return { | ||||
|         ...state, | ||||
|         id: action.payload | ||||
|         id: action.payload, | ||||
|       }; | ||||
|     case BUILDER_ADD_STEP: | ||||
|     case BUILDER_DELETE_STEP: | ||||
| @ -50,23 +57,23 @@ export default function foo(state = initialState, action){ | ||||
|     case BUILDER_DELETE_PROPERTY: | ||||
|       return { | ||||
|         ...state, | ||||
|         steps: action.payload | ||||
|         steps: action.payload, | ||||
|       }; | ||||
|     case BUILDER_ERROR: | ||||
|       return { | ||||
|         ...state, | ||||
|         error: action.payload | ||||
|       } | ||||
|         error: action.payload, | ||||
|       }; | ||||
|     case PROGRESS: | ||||
|       return { | ||||
|         ...state, | ||||
|         progress: action.payload | ||||
|       } | ||||
|         progress: action.payload, | ||||
|       }; | ||||
|     case JSON_STRING: | ||||
|       return { | ||||
|         ...state, | ||||
|         json: action.payload | ||||
|       } | ||||
|         json: action.payload, | ||||
|       }; | ||||
|     default: | ||||
|       return state; | ||||
|   } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user