Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd6358ea2b | ||
|
|
912440f64c | ||
|
|
77b338ffb6 | ||
|
|
8571f0430f | ||
|
|
1bfe1a26ba | ||
|
|
564dcc4d4a | ||
|
|
50b70b4ee3 | ||
|
|
00310a49dc | ||
|
|
ec1eea110c | ||
|
|
22d11773e9 | ||
|
|
2de54c0e9b | ||
|
|
5974608fd8 | ||
|
|
6fcaff9cc3 | ||
|
|
f95b1b36ca | ||
|
|
a31195f71e | ||
|
|
506ddef7e9 | ||
|
|
9ac98aa06d | ||
|
|
a8704316ac | ||
|
|
c950dbbd86 | ||
|
|
e00617ac78 | ||
|
|
d61d0cc5c4 |
@@ -14,6 +14,7 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/react-fontawesome": "^0.1.19",
|
||||
"@material-ui/core": "^4.12.4",
|
||||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@monaco-editor/react": "^4.3.1",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
@@ -41,6 +42,7 @@
|
||||
"reactour": "^1.18.7",
|
||||
"redux": "^4.2.0",
|
||||
"redux-thunk": "^2.4.1",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"remark-gemoji": "^7.0.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"styled-components": "^4.4.1",
|
||||
|
||||
+10
@@ -62,3 +62,13 @@ td {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:root {
|
||||
--url: url('./data/mcu_opacity.png');
|
||||
}
|
||||
|
||||
.blocklySvg {
|
||||
background-image: var(--url);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
import {
|
||||
BOARD,
|
||||
} from "./types";
|
||||
} from "./types";
|
||||
import mini_opacity from "../data/mini_opacity.png"
|
||||
import mcu_opacity from "../data/mcu_opacity.png"
|
||||
|
||||
export const setBoard = (board) => (dispatch) => {
|
||||
window.sessionStorage.setItem("board", board);
|
||||
const root = document.querySelector(':root');
|
||||
root.style.setProperty('--url', `url(${board === "mcu" ? mcu_opacity : mini_opacity})`);
|
||||
dispatch({
|
||||
type: BOARD,
|
||||
payload: board,
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const fetchSensorWikiSuccess = sensors => ({
|
||||
type: 'FETCH_SENSORWIKI_SUCCESS',
|
||||
payload: { sensors }
|
||||
})
|
||||
|
||||
export const fetchSensors = () => {
|
||||
return async dispatch => {
|
||||
try {
|
||||
let sensors = await axios.get('https://api.sensors.wiki/sensors/all')
|
||||
dispatch(fetchSensorWikiSuccess(sensors.data))
|
||||
}
|
||||
catch(e){
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,12 +301,21 @@ export const resetTutorial = () => (dispatch, getState) => {
|
||||
hardware: [],
|
||||
requirements: [],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: "finalpage",
|
||||
headline: "",
|
||||
text: "",
|
||||
samplesolutions: false,
|
||||
solutions: [],
|
||||
furthertutorials: false,
|
||||
},
|
||||
];
|
||||
dispatch(tutorialSteps(steps));
|
||||
dispatch({
|
||||
type: BUILDER_ERROR,
|
||||
payload: {
|
||||
steps: [{}],
|
||||
steps: [{}, {}],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -20,9 +20,15 @@ export const onChangeCode = () => (dispatch, getState) => {
|
||||
if (selectedBlock !== null) {
|
||||
code.helpurl = selectedBlock.helpUrl
|
||||
code.tooltip = selectedBlock.tooltip
|
||||
if (selectedBlock.data) {
|
||||
code.data = selectedBlock.data
|
||||
} else {
|
||||
code.data = null
|
||||
}
|
||||
} else if (selectedBlock === null) {
|
||||
code.tooltip = Blockly.Msg.tooltip_hint
|
||||
code.helpurl = ''
|
||||
code.data = null
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ Blockly.Blocks["sensebox_sensor_temp_hum"] = {
|
||||
this.setColour(getColour().sensebox);
|
||||
this.setTooltip(Blockly.Msg.senseBox_temp_hum_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_temp_hum_helpurl);
|
||||
this.data = {name: "hdc1080", connection: "I2C"};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -51,6 +52,7 @@ Blockly.Blocks["sensebox_sensor_uv_light"] = {
|
||||
this.setColour(getColour().sensebox);
|
||||
this.setTooltip(Blockly.Msg.senseBox_uv_light_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_uv_light_helpurl);
|
||||
this.data = {name: "veml6070"};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -91,6 +93,7 @@ Blockly.Blocks["sensebox_sensor_bmx055_accelerometer"] = {
|
||||
this.setColour(getColour().sensebox);
|
||||
this.setTooltip(Blockly.Msg.senseBox_bmx055_accelerometer_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_bmx055_helpurl);
|
||||
this.data = {name: "bmx055"};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -122,6 +125,7 @@ Blockly.Blocks["sensebox_sensor_sds011"] = {
|
||||
this.setColour(getColour().sensebox);
|
||||
this.setTooltip(Blockly.Msg.senseBox_sds011_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_sds011_helpurl);
|
||||
this.data = {name: "sds011"};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -155,6 +159,7 @@ Blockly.Blocks["sensebox_sensor_pressure"] = {
|
||||
this.setOutput(true, Types.DECIMAL.typeName);
|
||||
this.setTooltip(Blockly.Msg.senseBox_pressure_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_pressure_helpurl);
|
||||
this.data = {name: "bmp280"};
|
||||
this.getField("NAME").setValidator(
|
||||
function (val) {
|
||||
this.updateShape_(val === "Altitude");
|
||||
@@ -201,6 +206,7 @@ Blockly.Blocks["sensebox_sensor_bme680_bsec"] = {
|
||||
this.setColour(getColour().sensebox);
|
||||
this.setTooltip(Blockly.Msg.senseBox_bme_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_bme680_helpurl);
|
||||
this.data = {name: "bme680"};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -245,6 +251,7 @@ Blockly.Blocks["sensebox_sensor_ultrasonic_ranger"] = {
|
||||
this.setOutput(true, Types.NUMBER.typeName);
|
||||
this.setTooltip(Blockly.Msg.senseBox_ultrasonic_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_ultrasonic_helpurl);
|
||||
this.data = {name: "hc-sr04"};
|
||||
},
|
||||
/**
|
||||
* Parse XML to restore the number of pins available.
|
||||
@@ -382,6 +389,7 @@ Blockly.Blocks["sensebox_scd30"] = {
|
||||
this.setColour(getColour().sensebox);
|
||||
this.setTooltip(Blockly.Msg.senseBox_scd_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_scd_helpurl);
|
||||
this.data = {name: "scd30"};
|
||||
},
|
||||
onchange: function (e) {
|
||||
var dropdown = this.getFieldValue("dropdown");
|
||||
@@ -448,6 +456,7 @@ Blockly.Blocks["sensebox_sensor_truebner_smt50"] = {
|
||||
this.setOutput(true, Types.NUMBER.typeName);
|
||||
this.setTooltip(Blockly.Msg.senseBox_smt50_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_smt50_helpurl);
|
||||
this.data = {name: "smt50"};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -470,6 +479,7 @@ Blockly.Blocks["sensebox_sensor_watertemperature"] = {
|
||||
.appendField(new Blockly.FieldDropdown(dropdownOptions), "Port");
|
||||
this.setOutput(true, Types.NUMBER.typeName);
|
||||
this.setTooltip(Blockly.Msg.senseBox_watertemperature_tip);
|
||||
this.data = {name: "ds18b20"};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -542,6 +552,7 @@ Blockly.Blocks["sensebox_sensor_dps310"] = {
|
||||
this.setOutput(true, Types.DECIMAL.typeName);
|
||||
this.setTooltip(Blockly.Msg.senseBox_sensor_dps310_tooltip);
|
||||
this.setHelpUrl(Blockly.Msg.senseBox_sensor_dps310_helpurl);
|
||||
this.data = {name: "dps310"};
|
||||
this.getField("NAME").setValidator(
|
||||
function (val) {
|
||||
this.updateShape_(val === "Altitude");
|
||||
|
||||
@@ -9,10 +9,10 @@ Blockly.Blocks["variables_set_dynamic"] = {
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.appendValueInput("VALUE")
|
||||
.appendField("set", "set")
|
||||
.appendField("", "type")
|
||||
.appendField(Blockly.Msg.variables_set, Blockly.Msg.variables_set)
|
||||
// .appendField("", "type")
|
||||
.appendField(new Blockly.FieldVariable("VAR"), "VAR")
|
||||
.appendField("to");
|
||||
.appendField(Blockly.Msg.variables_to);
|
||||
},
|
||||
onchange: function (e) {
|
||||
let variableID = this.getFieldValue("VAR");
|
||||
|
||||
@@ -19,10 +19,12 @@ export const UI = {
|
||||
variable_DECIMAL: "Kommazahl (float)",
|
||||
variables_TEXT: "Text (string)",
|
||||
variables_ARRAY: "Array (array)",
|
||||
variables_CHARACTER: "char (char)",
|
||||
variables_CHARACTER: "Buchstabe (char)",
|
||||
variables_BOOLEAN: "Boolean (boolean)",
|
||||
variables_NULL: "void (void)",
|
||||
variables_UNDEF: "undefined",
|
||||
variables_set: "Schreibe",
|
||||
variables_to: "",
|
||||
|
||||
/**
|
||||
* Tooltips
|
||||
@@ -150,6 +152,7 @@ export const UI = {
|
||||
button_next: "nächster Schritt",
|
||||
button_tutorial_overview: "Tutorial Übersicht",
|
||||
button_login: "Anmelden",
|
||||
button_createVariable: "Typisierte Variable erstellen",
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -311,5 +314,15 @@ export const UI = {
|
||||
deviceselection_head: "Welches Board benutzt du?",
|
||||
deviceselection_keep_selection: "Speichere meine Auswahl fürs nächste Mal (Du kannst das Board später in den Einstellungen wechseln)",
|
||||
deviceselection_footnote: "Hier kommst du zur alten Blockly Version für den ",
|
||||
deviceselection_footnote_02: "oder die"
|
||||
deviceselection_footnote_02: "oder die",
|
||||
|
||||
/**
|
||||
* Sensor Markdown Information
|
||||
*/
|
||||
sensorinfo_info: "Informationen zum Sensor",
|
||||
sensorinfo_description: "Beschreibung",
|
||||
sensorinfo_measurable_phenos: "Messbare Phänomene",
|
||||
sensorinfo_manufacturer: "Hersteller",
|
||||
sensorinfo_lifetime: "Lebensdauer",
|
||||
sensorinfo_explanation: "Diese Informationen wurden aus [sensors.wiki](https://sensors.wiki) entnommen. Für weitere Informationen besuchen Sie den Abschnitt über diesen Sensor "
|
||||
};
|
||||
|
||||
@@ -15,14 +15,16 @@ export const UI = {
|
||||
toolbox_motors: "Motors",
|
||||
variable_NUMBER: "Number (int)",
|
||||
variable_SHORT_NUMBER: "char",
|
||||
variable_LONG: " Zahl (long)",
|
||||
variable_LONG: "Big number (long)",
|
||||
variable_DECIMAL: "Decimal (float)",
|
||||
variables_TEXT: "Text (string)",
|
||||
variables_ARRAY: "Array (array)",
|
||||
variables_CHARACTER: "char (char)",
|
||||
variables_CHARACTER: "Character (char)",
|
||||
variables_BOOLEAN: "Boolean (boolean)",
|
||||
variables_NULL: "void (void)",
|
||||
variables_UNDEF: "undefined",
|
||||
variables_set: "set",
|
||||
variables_to: "to",
|
||||
|
||||
/**
|
||||
* Tooltips
|
||||
@@ -145,6 +147,7 @@ export const UI = {
|
||||
button_next: "Next step",
|
||||
button_tutorial_overview: "Tutorial overview",
|
||||
button_login: "Login",
|
||||
button_createVariable: "Create Typed Variable",
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -308,5 +311,13 @@ export const UI = {
|
||||
deviceselection_footnote: "Here you can access the old blockly Version for the",
|
||||
deviceselection_footnote_02: "or the",
|
||||
|
||||
|
||||
/**
|
||||
* Sensor Markdown Information
|
||||
* */
|
||||
sensorinfo_info: "Informationen regarding the Sensor",
|
||||
sensorinfo_description: "Description",
|
||||
sensorinfo_measurable_phenos: "Measurable Phenomena",
|
||||
sensorinfo_manufacturer: "Manufacturer",
|
||||
sensorinfo_lifetime: "Lifetime",
|
||||
sensorinfo_explanation: "This information was fetched from [sensors.wiki](https://sensors.wiki). For more information visit the section on this sensor ",
|
||||
};
|
||||
|
||||
@@ -16,7 +16,6 @@ class Toolbox extends React.Component {
|
||||
this.props.workspace,
|
||||
"callbackName",
|
||||
[
|
||||
[`${Blockly.Msg.variable_SHORT_NUMBER}`, "char"],
|
||||
[`${Blockly.Msg.variable_NUMBER}`, "int"],
|
||||
[`${Blockly.Msg.variable_LONG}`, "long"],
|
||||
[`${Blockly.Msg.variable_DECIMAL}`, "float"],
|
||||
@@ -33,7 +32,7 @@ class Toolbox extends React.Component {
|
||||
|
||||
// Add your button and give it a callback name.
|
||||
const button = document.createElement("button");
|
||||
button.setAttribute("text", "Create Typed Variable");
|
||||
button.setAttribute("text", Blockly.Msg.button_createVariable);
|
||||
button.setAttribute("callbackKey", "callbackName");
|
||||
|
||||
xmlList.push(button);
|
||||
@@ -41,6 +40,7 @@ class Toolbox extends React.Component {
|
||||
// This gets all the variables that the user creates and adds them to the
|
||||
// flyout.
|
||||
const blockList = Blockly.VariablesDynamic.flyoutCategoryBlocks(workspace);
|
||||
console.log(blockList);
|
||||
xmlList = xmlList.concat(blockList);
|
||||
return xmlList;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import Sidebar from "./Sidebar";
|
||||
import Dialog from "../Dialog";
|
||||
import SaveIcon from "./SaveIcon";
|
||||
import store from "../../store";
|
||||
import DeviceSelection from "../DeviceSelection";
|
||||
|
||||
const CodeEditor = (props) => {
|
||||
//const [filehandle, setFileHandle] = useState();
|
||||
@@ -192,6 +193,7 @@ void loop() {
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<DeviceSelection />
|
||||
<Grid item lg={4} md={4}>
|
||||
<Button
|
||||
style={{ padding: "1rem", margin: "1rem" }}
|
||||
|
||||
@@ -4,8 +4,6 @@ import { connect } from "react-redux";
|
||||
import Dialog from "./Dialog";
|
||||
|
||||
import { withStyles } from "@material-ui/core/styles";
|
||||
import Checkbox from "@material-ui/core/Checkbox";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import * as Blockly from "blockly";
|
||||
import { IconButton, Grid, Avatar, Typography } from "@material-ui/core";
|
||||
import { setBoard } from "../actions/boardAction";
|
||||
@@ -27,41 +25,31 @@ const styles = (theme) => ({
|
||||
|
||||
class DeviceSeclection extends Component {
|
||||
constructor(props) {
|
||||
var previousPageWasAnotherDomain = props.pageVisits === 0;
|
||||
var userWantToKeepBoard = window.localStorage.getItem("board")
|
||||
? true
|
||||
: false;
|
||||
super(props);
|
||||
this.state = {
|
||||
open: userWantToKeepBoard
|
||||
? !userWantToKeepBoard
|
||||
: previousPageWasAnotherDomain,
|
||||
selectedBoard : "",
|
||||
open: props.selectedBoard ? false : true,
|
||||
selectedBoard: "",
|
||||
saveSettings: false,
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
toggleDialog = () => {
|
||||
this.setState({ open: !this.state });
|
||||
if(this.state.saveSettings){
|
||||
window.localStorage.setItem("board", this.state.selectedBoard)
|
||||
}
|
||||
this.props.setBoard(this.state.selectedBoard)
|
||||
|
||||
};
|
||||
|
||||
onChange = (e) => {
|
||||
if (e.target.checked) {
|
||||
this.setState({ saveSettings: true});
|
||||
this.setState({ saveSettings: true });
|
||||
} else {
|
||||
this.setState({ saveSettings: false});
|
||||
this.setState({ saveSettings: false });
|
||||
}
|
||||
};
|
||||
|
||||
onclick = (e, value) => {
|
||||
console.log(e, value)
|
||||
this.setState({selectedBoard: value})
|
||||
this.setState({ selectedBoard: value })
|
||||
this.props.setBoard(value)
|
||||
this.setState({ open: !this.state });
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -74,26 +62,24 @@ class DeviceSeclection extends Component {
|
||||
title={Blockly.Msg.deviceselection_head}
|
||||
content={""}
|
||||
onClick={this.toggleDialog}
|
||||
button={Blockly.Msg.button_accept}
|
||||
disabled={this.state.selectedBoard===""}
|
||||
disabled={this.state.selectedBoard === ""}
|
||||
>
|
||||
<div>
|
||||
<Grid container spacing={2} style={{ textAlign : "center" }}>
|
||||
<Grid container spacing={2} style={{ textAlign: "center" }}>
|
||||
<Grid item xs={6}>
|
||||
<IconButton onClick={(e) => this.onclick(e, "mcu")}>
|
||||
<Avatar
|
||||
alt="Sensebox MCU"
|
||||
src="/media/hardware/senseboxmcu.png"
|
||||
style={{
|
||||
border: this.state.selectedBoard === "mcu" ? 'medium solid DeepSkyBlue': "0.1px solid lightgray",
|
||||
width:"20vw",
|
||||
border: this.state.selectedBoard === "mcu" ? 'medium solid DeepSkyBlue' : "0.1px solid lightgray",
|
||||
width: "20vw",
|
||||
height: "20vw"
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
<p>senseBox MCU</p>
|
||||
</Grid>
|
||||
|
||||
{/* <Grid item xs={4}>
|
||||
<IconButton onClick={(e) => this.onclick(e, "esp")}>
|
||||
<Avatar
|
||||
@@ -114,8 +100,8 @@ class DeviceSeclection extends Component {
|
||||
alt="Sensebox Mini"
|
||||
src="/media/hardware/senseboxmcumini.png"
|
||||
style={{
|
||||
border: this.state.selectedBoard === "mini" ? 'medium solid DeepSkyBlue': "0.1px solid lightgray",
|
||||
width:"20vw",
|
||||
border: this.state.selectedBoard === "mini" ? 'medium solid DeepSkyBlue' : "0.1px solid lightgray",
|
||||
width: "20vw",
|
||||
height: "20vw"
|
||||
}}
|
||||
/>
|
||||
@@ -124,21 +110,6 @@ class DeviceSeclection extends Component {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
<FormControlLabel
|
||||
style={{ marginTop: "20px" }}
|
||||
classes={{ label: this.props.classes.label }}
|
||||
control={
|
||||
<Checkbox
|
||||
size={"small"}
|
||||
value={true}
|
||||
checked={this.state.checked}
|
||||
onChange={(e) => this.onChange(e)}
|
||||
name="dialog"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label={Blockly.Msg.deviceselection_keep_selection}
|
||||
/>
|
||||
<Typography variant="body1" >
|
||||
{Blockly.Msg.deviceselection_footnote} <a href="https://sensebox.github.io/blockly/">Arduino UNO</a> {Blockly.Msg.deviceselection_footnote_02} <a href="https://sensebox-blockly.netlify.app/ardublockly/?board=sensebox-mcu">senseBox MCU</a>
|
||||
</Typography>
|
||||
@@ -158,5 +129,5 @@ const mapStateToProps = (state) => ({
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{setBoard}
|
||||
{ setBoard }
|
||||
)(withStyles(styles, { withTheme: true })(DeviceSeclection));
|
||||
|
||||
@@ -8,6 +8,7 @@ import Button from "@material-ui/core/Button";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import * as Blockly from "blockly";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import Container from "@material-ui/core/Container";
|
||||
import ExpansionPanel from "@material-ui/core/ExpansionPanel";
|
||||
import ExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary";
|
||||
@@ -61,6 +62,7 @@ class Faq extends Component {
|
||||
className="news"
|
||||
allowDangerousHtml="true"
|
||||
children={object.answer}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
></ReactMarkdown>
|
||||
</Typography>
|
||||
</ExpansionPanelDetails>
|
||||
|
||||
@@ -18,6 +18,7 @@ import Divider from "@material-ui/core/Divider";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Backdrop from "@material-ui/core/Backdrop";
|
||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||
import DeviceSelection from "../DeviceSelection";
|
||||
|
||||
const styles = (theme) => ({
|
||||
link: {
|
||||
@@ -60,8 +61,7 @@ class ProjectHome extends Component {
|
||||
this.setState({
|
||||
snackbar: true,
|
||||
key: Date.now(),
|
||||
message: `Dein angefragtes ${
|
||||
type === "gallery" ? "Galerie-" : ""
|
||||
message: `Dein angefragtes ${type === "gallery" ? "Galerie-" : ""
|
||||
}Projekt konnte nicht gefunden werden.`,
|
||||
type: "error",
|
||||
});
|
||||
@@ -108,6 +108,7 @@ class ProjectHome extends Component {
|
||||
/>
|
||||
|
||||
<h1>{data}</h1>
|
||||
<DeviceSelection />
|
||||
{this.props.progress ? (
|
||||
<Backdrop open invisible>
|
||||
<CircularProgress color="primary" />
|
||||
@@ -127,8 +128,7 @@ class ProjectHome extends Component {
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
to={`/${
|
||||
data === "Projekte" ? "project" : "gallery"
|
||||
to={`/${data === "Projekte" ? "project" : "gallery"
|
||||
}/${project._id}`}
|
||||
style={{ textDecoration: "none", color: "inherit" }}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from "react";
|
||||
import Box from "@material-ui/core/Box";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import TabContext from "@material-ui/lab/TabContext";
|
||||
import TabList from "@material-ui/lab/TabList";
|
||||
import TabPanel from "@material-ui/lab/TabPanel";
|
||||
import store from "../store";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import * as Blockly from "blockly";
|
||||
|
||||
|
||||
export default function LabTabs() {
|
||||
const [value, setValue] = React.useState('1');
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
// get the description in the current language if no lang is give return english or the first one
|
||||
function filterLanguage(options) {
|
||||
var lang;
|
||||
if (window.localStorage.getItem("locale")) {
|
||||
lang = window.localStorage.getItem("locale").split("_")[0];
|
||||
}
|
||||
else {
|
||||
lang = "en";
|
||||
}
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
if (options[i].languageCode === lang) {
|
||||
return options[i].text;
|
||||
}
|
||||
}
|
||||
return options[0].text;
|
||||
}
|
||||
var currentStore = store.getState();
|
||||
// ALL SENSOR DATA FROM WIKI
|
||||
var sensorData = currentStore.sensorwiki;
|
||||
// NAME OF SELECTED BLOCK
|
||||
var sensorName = currentStore.workspace.code.data.name;
|
||||
// SEARCH ALL DATA FOR SELECTED BLOCK NAME
|
||||
var sensorInfo = sensorData.find(function (element) {
|
||||
return element.slug === sensorName;
|
||||
});
|
||||
if (sensorInfo) {
|
||||
sensorInfo.markdown = "Coming soon...";
|
||||
|
||||
// GET DESCRIPTION OF SELECTED BLOCK
|
||||
sensorInfo.details = `# ${sensorName.toUpperCase()}
|
||||
${Blockly.Msg.sensorinfo_explanation} [${sensorName.toUpperCase()}](https://sensors.wiki/sensor/detail/${sensorName})
|
||||
## ${Blockly.Msg.sensorinfo_description}
|
||||
${filterLanguage(sensorInfo.description.item)}
|
||||
## ${Blockly.Msg.sensorinfo_measurable_phenos}
|
||||
## ${Blockly.Msg.sensorinfo_manufacturer}
|
||||
${sensorInfo.manufacturer}
|
||||
## ${Blockly.Msg.sensorinfo_lifetime}
|
||||
${sensorInfo.lifePeriod}`
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%', typography: 'body1' }}>
|
||||
<TabContext value={value}>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<TabList onChange={handleChange}>
|
||||
<Tab label="Basic Information" value="1" />
|
||||
<Tab label="Details" value="2" />
|
||||
</TabList>
|
||||
</Box>
|
||||
<TabPanel value="1">
|
||||
{sensorInfo ? <ReactMarkdown>{sensorInfo.markdown}</ReactMarkdown> : "No data available"}
|
||||
</TabPanel>
|
||||
<TabPanel value="2">
|
||||
{sensorInfo ? <ReactMarkdown>{sensorInfo.details}</ReactMarkdown> : "No data available"}
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +1,41 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import withWidth from "@material-ui/core/withWidth";
|
||||
|
||||
import { Card } from "@material-ui/core";
|
||||
import { Button, Card } from "@material-ui/core";
|
||||
import * as Blockly from "blockly";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogActions from "@material-ui/core/DialogActions";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import Slide from "@material-ui/core/Slide";
|
||||
import SensorInfo from "./SensorInfo";
|
||||
import store from "../store";
|
||||
|
||||
const Transition = React.forwardRef(function Transition(props, ref) {
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
class TooltipViewer extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.inputRef = React.createRef();
|
||||
this.state = {
|
||||
open: false,
|
||||
};
|
||||
}
|
||||
|
||||
toggleDialog = () => {
|
||||
this.setState({ open: !this.state });
|
||||
};
|
||||
|
||||
openDialog = () => {
|
||||
this.setState({ open: true });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Card
|
||||
@@ -29,14 +53,46 @@ class TooltipViewer extends Component {
|
||||
{Blockly.Msg.tooltip_viewer}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" component="p">
|
||||
<ReactMarkdown linkTarget="_blank">
|
||||
{this.props.tooltip}
|
||||
</ReactMarkdown>
|
||||
|
||||
{this.props.helpurl !== "" ? (
|
||||
{store.getState().workspace.code.data ? (
|
||||
<Button
|
||||
label="Mehr"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
this.openDialog();
|
||||
}}
|
||||
>
|
||||
Sensor Informationen
|
||||
</Button>
|
||||
) : (
|
||||
<ReactMarkdown>{`${Blockly.Msg.tooltip_moreInformation} [${Blockly.Msg.labels_here}](${this.props.helpurl})`}</ReactMarkdown>
|
||||
) : null}
|
||||
)}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
{store.getState().workspace.code.data ? (<Dialog
|
||||
open={this.state.open}
|
||||
TransitionComponent={Transition}
|
||||
keepMounted
|
||||
aria-describedby="alert-dialog-slide-description"
|
||||
onClose={() => {
|
||||
this.toggleDialog();
|
||||
}}
|
||||
maxWidth={"md"}
|
||||
fullWidth={true}
|
||||
>
|
||||
<DialogContent>
|
||||
<SensorInfo></SensorInfo>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => {
|
||||
this.toggleDialog();
|
||||
}}>Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>) : (null)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ class Builder extends Component {
|
||||
window.scrollTo(0, 0);
|
||||
};
|
||||
|
||||
submit = () => {
|
||||
submit = () => {;
|
||||
var isError = this.props.checkError();
|
||||
if (isError) {
|
||||
this.setState({
|
||||
@@ -273,6 +273,8 @@ class Builder extends Component {
|
||||
} else {
|
||||
// export steps without attribute 'url'
|
||||
var steps = this.props.steps;
|
||||
// console.log(steps);
|
||||
var length = steps.length;
|
||||
var newTutorial = new FormData();
|
||||
newTutorial.append("title", this.props.title);
|
||||
newTutorial.append("difficulty", this.props.difficulty);
|
||||
@@ -299,11 +301,30 @@ class Builder extends Component {
|
||||
newTutorial.append(`steps[${i}][hardware][${j}]`, hardware);
|
||||
});
|
||||
}
|
||||
if (i === length-1 && step.type === "finalpage") {
|
||||
newTutorial.append(`steps[${i}][samplesolutions]`, step.samplesolutions);
|
||||
newTutorial.append(`steps[${i}][furthertutorials]`, step.furthertutorials);
|
||||
|
||||
if (step.samplesolutions === true) {
|
||||
var solutionindex = 0;
|
||||
steps.forEach((solutionstep) => {
|
||||
if (solutionstep.type === "task"&& solutionstep.xml) {
|
||||
newTutorial.append(`steps[${i}][solutions][${solutionindex}][type]`, solutionstep.type);
|
||||
newTutorial.append(`steps[${i}][solutions][${solutionindex}][headline]`, solutionstep.headline);
|
||||
newTutorial.append(`steps[${i}][solutions][${solutionindex}][xml]`, solutionstep.xml);
|
||||
solutionindex++;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (step.xml) {
|
||||
// optional
|
||||
newTutorial.append(`steps[${i}][xml]`, step.xml);
|
||||
}
|
||||
});
|
||||
for (const pair of newTutorial.entries()) {
|
||||
console.log(`${pair[0]}, ${pair[1]}`);
|
||||
}
|
||||
return newTutorial;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { changeContent } from "../../../actions/tutorialBuilderActions";
|
||||
|
||||
import { withStyles } from "@material-ui/core/styles";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
import FormGroup from '@material-ui/core/FormGroup';
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
|
||||
const styles = (theme) => ({
|
||||
errorColor: {
|
||||
color: theme.palette.error.dark,
|
||||
},
|
||||
errorBorder: {
|
||||
border: `1px solid ${theme.palette.error.dark}`,
|
||||
},
|
||||
errorButton: {
|
||||
marginTop: "5px",
|
||||
height: "40px",
|
||||
backgroundColor: theme.palette.error.dark,
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.error.dark,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
class FinalPageOptions extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
checkedSampleSolutions: props.checkedSampleSolutions,
|
||||
checkedFurtherTutorials: props.checkedFurtherTutorials,
|
||||
};
|
||||
}
|
||||
|
||||
onChangeSampleSolutions = (value) => {
|
||||
var oldValue = this.state.checked;
|
||||
this.setState({ checked: value });
|
||||
if (oldValue !== value) {
|
||||
this.props.changeContent(value, this.props.index, "samplesolutions");
|
||||
}
|
||||
};
|
||||
|
||||
onChangeFurtherTutorials = (value) => {
|
||||
var oldValue = this.state.checked;
|
||||
this.setState({ checked: value });
|
||||
if (oldValue !== value) {
|
||||
this.props.changeContent(value, this.props.index, "furthertutorials");
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
var steps = this.props.steps;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
marginBottom: "10px",
|
||||
padding: "18.5px 14px",
|
||||
borderRadius: "25px",
|
||||
border: "1px solid lightgrey",
|
||||
width: "calc(100% - 28px)",
|
||||
}}
|
||||
>
|
||||
<FormGroup>
|
||||
<FormControlLabel
|
||||
labelPlacement="end"
|
||||
label={
|
||||
"Musterlösung(en) der Aufgabe(n) auf der Abschlussseite anzeigen"
|
||||
}
|
||||
control={
|
||||
<Switch
|
||||
checked={this.state.checked}
|
||||
onChange={(e) => this.onChangeSampleSolutions(e.target.checked)}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormControlLabel
|
||||
labelPlacement="end"
|
||||
label={
|
||||
"Vorschläge für weitere Tutorials auf der Abschlussseite anzeigen"
|
||||
}
|
||||
control={
|
||||
<Switch
|
||||
checked={this.state.checked}
|
||||
onChange={(e) => this.onChangeFurtherTutorials(e.target.checked)}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FinalPageOptions.propTypes = {
|
||||
changeContent: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
changeContent,
|
||||
})(withStyles(styles, { withTheme: true })(FinalPageOptions));
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import MarkdownEditor from "./MarkdownEditor";
|
||||
import FinalPageOptions from "./FinalPageOptions";
|
||||
|
||||
const styles = (theme) => ({
|
||||
button: {
|
||||
@@ -55,6 +56,8 @@ class Step extends Component {
|
||||
render() {
|
||||
var index = this.props.index;
|
||||
var steps = this.props.steps;
|
||||
// console.log(this.props.steps);
|
||||
// console.log(this.props.step);
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -64,12 +67,21 @@ class Step extends Component {
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
{this.props.step.type !== "finalpage" ? (
|
||||
<Typography
|
||||
variant="h6"
|
||||
style={{ marginBottom: "10px", marginLeft: "4px" }}
|
||||
>
|
||||
Schritt {index + 1}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography
|
||||
variant="h6"
|
||||
style={{ marginBottom: "10px", marginLeft: "4px" }}
|
||||
>
|
||||
Abschlussseite
|
||||
</Typography>
|
||||
)}
|
||||
<div style={{ display: "flex", position: "relative" }}>
|
||||
<div
|
||||
style={{
|
||||
@@ -80,6 +92,8 @@ class Step extends Component {
|
||||
bottom: "10px",
|
||||
}}
|
||||
>
|
||||
{this.props.step.type !== "finalpage" ? (
|
||||
<div>
|
||||
<Tooltip title="Schritt hinzufügen" arrow>
|
||||
<IconButton
|
||||
className={this.props.classes.button}
|
||||
@@ -109,7 +123,7 @@ class Step extends Component {
|
||||
arrow
|
||||
>
|
||||
<IconButton
|
||||
disabled={index === steps.length - 1}
|
||||
disabled={index >= this.props.steps.length - 2}
|
||||
className={this.props.classes.button}
|
||||
style={{ marginBottom: "5px" }}
|
||||
onClick={() => this.props.changeStepIndex(index, index + 1)}
|
||||
@@ -119,7 +133,7 @@ class Step extends Component {
|
||||
</Tooltip>
|
||||
<Tooltip title={`Schritt ${index + 1} löschen`} arrow>
|
||||
<IconButton
|
||||
disabled={index === 0}
|
||||
disabled={index === 0 && index === steps.length - 1}
|
||||
className={clsx(
|
||||
this.props.classes.button,
|
||||
this.props.classes.delete
|
||||
@@ -132,8 +146,12 @@ class Step extends Component {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div style={{ width: "100%", marginLeft: "54px" }}>
|
||||
{this.props.step.type !== "finalpage" ? (
|
||||
<StepType value={this.props.step.type} index={index} />
|
||||
) : null}
|
||||
<Textfield
|
||||
value={this.props.step.headline}
|
||||
property={"headline"}
|
||||
@@ -178,12 +196,21 @@ class Step extends Component {
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{this.props.step.type !== "finalpage" ? (
|
||||
<BlocklyExample
|
||||
value={this.props.step.xml}
|
||||
index={index}
|
||||
task={this.props.step.type === "task"}
|
||||
error={this.props.error.steps[index].xml ? true : false}
|
||||
/>
|
||||
) : (
|
||||
<FinalPageOptions
|
||||
steps={steps}
|
||||
checkedSampleSolutions={this.props.step.samplesolutions}
|
||||
checkedFurtherTutorials={this.props.step.furthertutorials}
|
||||
index={index}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import axios from "axios";
|
||||
|
||||
import BlocklyWindow from "../Blockly/BlocklyWindow";
|
||||
|
||||
import clsx from "clsx";
|
||||
import { alpha } from "@material-ui/core/styles";
|
||||
import { withStyles } from "@material-ui/core/styles";
|
||||
|
||||
import {
|
||||
faCheck,
|
||||
faTimes,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link } from "react-router-dom";
|
||||
import ReactStars from "react-rating-stars-component";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkGemoji from "remark-gemoji";
|
||||
import Dialog from "../Dialog";
|
||||
import { IconButton } from "@material-ui/core";
|
||||
import { clearMessages } from "../../actions/messageActions";
|
||||
|
||||
import * as Blockly from "blockly/core";
|
||||
|
||||
// import { getTutorials,
|
||||
// tutorialProgress,
|
||||
// resetTutorial
|
||||
// } from "../../actions/tutorialActions";
|
||||
|
||||
|
||||
|
||||
const styles = (theme) => ({
|
||||
outerDiv: {
|
||||
position: "absolute",
|
||||
right: "-30px",
|
||||
bottom: "-30px",
|
||||
width: "160px",
|
||||
height: "160px",
|
||||
color: alpha(theme.palette.secondary.main, 0.6),
|
||||
},
|
||||
outerDivError: {
|
||||
stroke: alpha(theme.palette.error.dark, 0.6),
|
||||
color: alpha(theme.palette.error.dark, 0.6),
|
||||
},
|
||||
outerDivSuccess: {
|
||||
stroke: alpha(theme.palette.primary.main, 0.6),
|
||||
color: alpha(theme.palette.primary.main, 0.6),
|
||||
},
|
||||
outerDivOther: {
|
||||
stroke: alpha(theme.palette.secondary.main, 0.6),
|
||||
},
|
||||
innerDiv: {
|
||||
width: "inherit",
|
||||
height: "inherit",
|
||||
display: "table-cell",
|
||||
verticalAlign: "middle",
|
||||
textAlign: "center",
|
||||
},
|
||||
// button: {
|
||||
// backgroundColor: theme.palette.primary.main,
|
||||
// color: theme.palette.primary.contrastText,
|
||||
// width: "40px",
|
||||
// height: "40px",
|
||||
// "&:hover": {
|
||||
// backgroundColor: theme.palette.primary.main,
|
||||
// color: theme.palette.primary.contrastText,
|
||||
// },
|
||||
// },
|
||||
// link: {
|
||||
// color: theme.palette.primary.main,
|
||||
// textDecoration: "none",
|
||||
// "&:hover": {
|
||||
// color: theme.palette.primary.main,
|
||||
// textDecoration: "underline",
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
class FinalPage extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
open: false,
|
||||
title: "",
|
||||
content: "",
|
||||
tutorials: [],
|
||||
randomtutorials: [],
|
||||
activeXML: "",
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
if (this.props.step.furthertutorials === true) {
|
||||
axios
|
||||
.get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial`)
|
||||
.then((res) => {
|
||||
this.setState({ tutorials: res.data.tutorials });
|
||||
this.setState({ randomtutorials: this.getRandomTutorials(this.state.tutorials, 3) });
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
toggleDialog = (solution) => {
|
||||
console.log(solution)
|
||||
if (this.state.open === true) {
|
||||
this.setState({ open: !this.state.open, title: "", activeXML: "" });
|
||||
} else if (this.state.open === false) {
|
||||
this.setState({ open: !this.state.open, title: "Task - " + solution.headline, activeXML: solution.xml });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
getRandomTutorials = (tutorials, n) => {
|
||||
var length = tutorials.length;
|
||||
var taken = new Array(length);
|
||||
var result = new Array(n);
|
||||
if (n > length)
|
||||
throw new RangeError("getRandom: more elements taken than available");
|
||||
while (n--) {
|
||||
var x = Math.floor(Math.random() * length);
|
||||
result[n] = tutorials[x in taken ? taken[x] : x];
|
||||
taken[x] = --length in taken ? taken[length] : length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// toggleDialog = (solution) => {
|
||||
// this.setState({ open: !this.state.open });
|
||||
// // if (solution) {
|
||||
// // this.setState({ open: !this.state.open, activeSolution: solution });
|
||||
// // } else {
|
||||
// // this.setState({ open: !this.state.open, activeSolution: null });
|
||||
// // }
|
||||
// };
|
||||
|
||||
render() {
|
||||
var step = this.props.step;
|
||||
var tutorials = this.state.tutorials;
|
||||
var randomtutorials = this.state.randomtutorials;
|
||||
// var randomtutorials = this.getRandomTutorials(tutorials, 3);
|
||||
// console.log(randomtutorials)
|
||||
return (
|
||||
<div>
|
||||
<Typography>
|
||||
<ReactMarkdown
|
||||
className={"tutorial"}
|
||||
linkTarget={"_blank"}
|
||||
skipHtml={false}
|
||||
allowDangerousHtml={true}
|
||||
remarkPlugins={[remarkGfm, remarkGemoji]}
|
||||
>
|
||||
{step.text}
|
||||
</ReactMarkdown>
|
||||
</Typography>
|
||||
{step.media ? (
|
||||
step.media.picture ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={`${process.env.REACT_APP_BLOCKLY_API}/media/${step.media.picture.path}`}
|
||||
alt=""
|
||||
style={{ maxHeight: "40vH", maxWidth: "100%" }}
|
||||
/>
|
||||
</div>
|
||||
) : step.media.youtube ? (
|
||||
/*16:9; width: 800px; height: width/16*9=450px*/
|
||||
<div style={{ maxWidth: "800px", margin: "auto" }}>
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
height: 0,
|
||||
paddingBottom: "calc(100% / 16 * 9)",
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
title={step.media.youtube}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "0",
|
||||
left: "0",
|
||||
width: "100%",
|
||||
maxWidth: "800px",
|
||||
height: "100%",
|
||||
maxHeight: "450px",
|
||||
}}
|
||||
src={`https://www.youtube.com/embed/${step.media.youtube}`}
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
) : null}
|
||||
{step.samplesolutions === true ? (
|
||||
<div>
|
||||
<h2 style={{ justifyContent: "center" }}>Musterlösungen</h2>
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
style={{ marginBottom: "5px", justifyContent: "space-evenly" }}
|
||||
>
|
||||
{step.solutions.map((solution) => {
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
sm={6}
|
||||
md={4}
|
||||
xl={3}
|
||||
style={{ display: "flex", justifyContent: "center" }}
|
||||
// onclick={this.toggleDialog(solution)}
|
||||
>
|
||||
<IconButton
|
||||
onClick={() => this.toggleDialog(solution)}
|
||||
>
|
||||
<Paper
|
||||
style={{
|
||||
padding: "10px",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<h3>{"Task - " + solution.headline}</h3>
|
||||
<BlocklyWindow
|
||||
svg
|
||||
blockDisabled
|
||||
initialXml={solution.xml}
|
||||
/>
|
||||
</Paper>
|
||||
</IconButton>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
|
||||
<Dialog
|
||||
open={this.state.open}
|
||||
onClose={this.toggleDialog}
|
||||
onClick={this.toggleDialog}
|
||||
title={this.state.title}
|
||||
button={Blockly.Msg.button_close}
|
||||
>
|
||||
<BlocklyWindow
|
||||
svg
|
||||
blockDisabled
|
||||
initialXml={this.state.activeXML}
|
||||
/>
|
||||
</Dialog>
|
||||
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
||||
|
||||
{step.furthertutorials === true ? (
|
||||
<div>
|
||||
<h2 style={{ justifyContent: "center" }}>Weitere Tutorials</h2>
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
style={{ justifyContent: "space-evenly" }}
|
||||
>
|
||||
{randomtutorials.map((tutorial, i) => {
|
||||
var status = this.props.status.filter(
|
||||
(status) => status._id === tutorial._id
|
||||
)[0];
|
||||
var tasks = status.tasks;
|
||||
var error =
|
||||
status.tasks.filter((task) => task.type === "error").length > 0;
|
||||
var success =
|
||||
status.tasks.filter((task) => task.type === "success").length /
|
||||
tasks.length;
|
||||
var tutorialStatus =
|
||||
success === 1 ? "Success" : error ? "Error" : "Other";
|
||||
const firstExample = {
|
||||
size: 30,
|
||||
value: tutorial.difficulty,
|
||||
edit: false,
|
||||
isHalf: true,
|
||||
};
|
||||
return (
|
||||
<Grid item xs={12} sm={6} md={4} xl={3} key={i} style={{}}>
|
||||
<Link
|
||||
to={`/tutorial/${tutorial._id}`}
|
||||
style={{ textDecoration: "none", color: "inherit" }}
|
||||
>
|
||||
<Paper
|
||||
style={{
|
||||
height: "150px",
|
||||
padding: "10px",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{tutorial.title}
|
||||
<ReactStars {...firstExample} />
|
||||
<div
|
||||
className={clsx(this.props.classes.outerDiv)}
|
||||
style={{ width: "160px", height: "160px", border: 0 }}
|
||||
>
|
||||
<svg style={{ width: "100%", height: "100%" }}>
|
||||
{error || success === 1 ? (
|
||||
<circle
|
||||
className={
|
||||
error
|
||||
? this.props.classes.outerDivError
|
||||
: this.props.classes.outerDivSuccess
|
||||
}
|
||||
style={{
|
||||
transform: "rotate(-44deg)",
|
||||
transformOrigin: "50% 50%",
|
||||
}}
|
||||
r="75"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
fill="none"
|
||||
stroke-width="10"
|
||||
></circle>
|
||||
) : (
|
||||
<circle
|
||||
className={this.props.classes.outerDivOther}
|
||||
style={{
|
||||
transform: "rotate(-44deg)",
|
||||
transformOrigin: "50% 50%",
|
||||
}}
|
||||
r="75"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
fill="none"
|
||||
stroke-width="10"
|
||||
stroke-dashoffset={`${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
}`}
|
||||
stroke-dasharray={`${75 * 2 * Math.PI * (1 - (50 / 100 - success / 2))
|
||||
} ${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
}`}
|
||||
></circle>
|
||||
)}
|
||||
{success < 1 && !error ? (
|
||||
<circle
|
||||
className={this.props.classes.outerDivSuccess}
|
||||
style={{
|
||||
transform: "rotate(-44deg)",
|
||||
transformOrigin: "50% 50%",
|
||||
}}
|
||||
r="75"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
fill="none"
|
||||
stroke-width="10"
|
||||
stroke-dashoffset={`${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
}`}
|
||||
stroke-dasharray={`${75 * 2 * Math.PI}`}
|
||||
></circle>
|
||||
) : null}
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
this.props.classes.outerDiv,
|
||||
tutorialStatus === "Error"
|
||||
? this.props.classes.outerDivError
|
||||
: tutorialStatus === "Success"
|
||||
? this.props.classes.outerDivSuccess
|
||||
: null
|
||||
)}
|
||||
>
|
||||
<div className={this.props.classes.innerDiv}>
|
||||
{error || success === 1 ? (
|
||||
<FontAwesomeIcon
|
||||
size="4x"
|
||||
icon={
|
||||
tutorialStatus === "Success" ? faCheck : faTimes
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Typography
|
||||
variant="h3"
|
||||
className={
|
||||
success > 0
|
||||
? this.props.classes.outerDivSuccess
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{Math.round(success * 100)}%
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</Link>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
tutorials: state.tutorial.tutorials,
|
||||
status: state.tutorial.status,
|
||||
isLoading: state.tutorial.progress,
|
||||
message: state.message,
|
||||
progress: state.auth.progress,
|
||||
user: state.auth.user,
|
||||
authProgress: state.auth.progress,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
// getTutorials,
|
||||
// resetTutorial,
|
||||
// tutorialProgress,
|
||||
clearMessages,
|
||||
})(withStyles(styles, { withTheme: true })(FinalPage));
|
||||
@@ -31,6 +31,9 @@ const styles = (theme) => ({
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
},
|
||||
stepIconSuccess: {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
stepIconLargeSuccess: {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
@@ -130,12 +133,25 @@ class StepperVertical extends Component {
|
||||
"stepIconLarge" + taskStatus
|
||||
]
|
||||
)
|
||||
: i === activeStep
|
||||
: step.type === "instruction"
|
||||
? i === activeStep
|
||||
? clsx(
|
||||
this.props.classes.stepIcon,
|
||||
this.props.classes.stepIconActiveOther
|
||||
)
|
||||
: clsx(this.props.classes.stepIcon),
|
||||
: clsx(this.props.classes.stepIcon)
|
||||
: step.type === "finalpage"
|
||||
? i === activeStep
|
||||
? clsx(
|
||||
this.props.classes.stepIcon,
|
||||
this.props.classes.stepIconSuccess,
|
||||
this.props.classes.stepIconActiveSuccess
|
||||
)
|
||||
: clsx(
|
||||
this.props.classes.stepIcon,
|
||||
this.props.classes.stepIconSuccess
|
||||
)
|
||||
: null,
|
||||
}}
|
||||
></StepLabel>
|
||||
</div>
|
||||
|
||||
@@ -17,6 +17,7 @@ import StepperHorizontal from "./StepperHorizontal";
|
||||
import StepperVertical from "./StepperVertical";
|
||||
import Instruction from "./Instruction";
|
||||
import Assessment from "./Assessment";
|
||||
import FinalPage from "./FinalPage";
|
||||
import NotFound from "../NotFound";
|
||||
import * as Blockly from "blockly";
|
||||
import { detectWhitespacesAndReturnReadableResult } from "../../helpers/whitespace";
|
||||
@@ -74,7 +75,9 @@ class Tutorial extends Component {
|
||||
(() => {
|
||||
var tutorial = this.props.tutorial;
|
||||
var steps = this.props.tutorial.steps;
|
||||
console.log(steps)
|
||||
var step = steps[this.props.activeStep];
|
||||
console.log(step)
|
||||
var name = `${detectWhitespacesAndReturnReadableResult(
|
||||
tutorial.title
|
||||
)}_${detectWhitespacesAndReturnReadableResult(step.headline)}`;
|
||||
@@ -107,8 +110,10 @@ class Tutorial extends Component {
|
||||
{step ? (
|
||||
step.type === "instruction" ? (
|
||||
<Instruction step={step} />
|
||||
) : (
|
||||
) : step.type === "task" ? (
|
||||
<Assessment step={step} name={name} />
|
||||
) : (
|
||||
<FinalPage step={step} />
|
||||
) // if step.type === 'assessment'
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import Tooltip from "@material-ui/core/Tooltip";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import Snackbar from "../Snackbar";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import DeviceSelection from "../DeviceSelection";
|
||||
|
||||
const styles = (theme) => ({
|
||||
outerDiv: {
|
||||
@@ -243,13 +244,10 @@ class TutorialHome extends Component {
|
||||
cy="50%"
|
||||
fill="none"
|
||||
stroke-width="10"
|
||||
stroke-dashoffset={`${
|
||||
75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
stroke-dashoffset={`${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
}`}
|
||||
stroke-dasharray={`${
|
||||
75 * 2 * Math.PI * (1 - (50 / 100 - success / 2))
|
||||
} ${
|
||||
75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
stroke-dasharray={`${75 * 2 * Math.PI * (1 - (50 / 100 - success / 2))
|
||||
} ${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
}`}
|
||||
></circle>
|
||||
)}
|
||||
@@ -265,8 +263,7 @@ class TutorialHome extends Component {
|
||||
cy="50%"
|
||||
fill="none"
|
||||
stroke-width="10"
|
||||
stroke-dashoffset={`${
|
||||
75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
stroke-dashoffset={`${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
|
||||
}`}
|
||||
stroke-dasharray={`${75 * 2 * Math.PI}`}
|
||||
></circle>
|
||||
@@ -311,6 +308,7 @@ class TutorialHome extends Component {
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
<DeviceSelection />
|
||||
{this.props.user ? (
|
||||
<div>
|
||||
<h2>User Tutorials</h2>
|
||||
|
||||
@@ -38,6 +38,19 @@ export class Login extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.message.id === "LOGIN_FAIL") {
|
||||
this.setState({
|
||||
email: "",
|
||||
password: "",
|
||||
snackbar: true,
|
||||
key: Date.now(),
|
||||
message: Blockly.Msg.messages_LOGIN_FAIL,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(props) {
|
||||
const { message } = this.props;
|
||||
if (message !== props.message) {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 167 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
@@ -15,11 +15,13 @@ const compareNumberOfBlocks = (originalBlocks, userBlocks) => {
|
||||
var blocks;
|
||||
if(originalBlocks.length > userBlocks.length){
|
||||
blocks = originalBlocks.length-userBlocks.length;
|
||||
return {text: `Es wurde${blocks === 1 ? '' : 'n'} ${blocks} Bl${blocks === 1 ? 'ock' : 'öcke'} zu wenig verwendet.`, type: 'error'};
|
||||
//return {text: `Es wurde${blocks === 1 ? '' : 'n'} ${blocks} Bl${blocks === 1 ? 'ock' : 'öcke'} zu wenig verwendet.`, type: 'error'};
|
||||
return {text: `Du kannst deinen Programmcode kompilieren, aber es wurde${blocks === 1 ? '' : 'n'} ${blocks} Bl${blocks === 1 ? 'ock' : 'öcke'} weniger verwendet als in der hinterlegten Lösung.`, type: 'success'};
|
||||
}
|
||||
else {
|
||||
blocks = userBlocks.length-originalBlocks.length;
|
||||
return {text: `Es wurde${blocks === 1 ? '' : 'n'} ${blocks} Bl${blocks === 1 ? 'ock' : 'öcke'} zu viel verwendet.`, type: 'error'};
|
||||
//return {text: `Es wurde${blocks === 1 ? '' : 'n'} ${blocks} Bl${blocks === 1 ? 'ock' : 'öcke'} zu viel verwendet.`, type: 'error'};
|
||||
return {text: `Du kannst deinen Programmcode kompilieren, aber es wurde${blocks === 1 ? '' : 'n'} ${blocks} Bl${blocks === 1 ? 'ock' : 'öcke'} mehr verwendet als in der hinterlegten Lösung.`, type: 'success'};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,12 @@ import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
import store from "./store";
|
||||
import { fetchSensors } from "./actions/sensorwikiActions";
|
||||
|
||||
store.dispatch(fetchSensors());
|
||||
|
||||
fetchSensors();
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import { BOARD } from '../actions/types';
|
||||
import mini_opacity from "../data/mini_opacity.png"
|
||||
import mcu_opacity from "../data/mcu_opacity.png"
|
||||
|
||||
const initialValue = () => {
|
||||
if (window.localStorage.getItem("board")) {
|
||||
return window.localStorage.getItem("board");
|
||||
if (window.sessionStorage.getItem("board")) {
|
||||
setBackgroundImage(window.sessionStorage.getItem("board"));
|
||||
return window.sessionStorage.getItem("board");
|
||||
}
|
||||
return "bla";
|
||||
return null;
|
||||
};
|
||||
|
||||
const setBackgroundImage = (board) => {
|
||||
const root = document.querySelector(':root');
|
||||
root.style.setProperty('--url', `url(${board === "mcu" ? mcu_opacity : mini_opacity})`);
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
board: initialValue()
|
||||
};
|
||||
|
||||
@@ -6,7 +6,8 @@ import generalReducer from './generalReducer';
|
||||
import projectReducer from './projectReducer';
|
||||
import messageReducer from './messageReducer';
|
||||
import authReducer from './authReducer';
|
||||
import boardReducer from './boardReducer'
|
||||
import boardReducer from './boardReducer';
|
||||
import sensorwikiReducer from './sensorwikiReducer';
|
||||
|
||||
export default combineReducers({
|
||||
auth: authReducer,
|
||||
@@ -16,5 +17,6 @@ export default combineReducers({
|
||||
builder: tutorialBuilderReducer,
|
||||
project: projectReducer,
|
||||
general: generalReducer,
|
||||
message: messageReducer
|
||||
message: messageReducer,
|
||||
sensorwiki: sensorwikiReducer
|
||||
});
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
const sensors = (state = [] , action) => {
|
||||
switch(action.type) {
|
||||
case 'FETCH_SENSORWIKI_SUCCESS':
|
||||
return action.payload.sensors
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export default sensors
|
||||
@@ -33,9 +33,18 @@ const initialState = {
|
||||
hardware: [],
|
||||
requirements: [],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: "finalpage",
|
||||
headline: "",
|
||||
text: "",
|
||||
samplesolutions: false,
|
||||
solutions: [],
|
||||
furthertutorials: false,
|
||||
},
|
||||
],
|
||||
error: {
|
||||
steps: [{}],
|
||||
steps: [{},{}],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user