diff --git a/services/web/client/source/class/osparc/component/form/PortInfoHint.js b/services/web/client/source/class/osparc/component/form/PortInfoHint.js
new file mode 100644
index 00000000000..1068a445436
--- /dev/null
+++ b/services/web/client/source/class/osparc/component/form/PortInfoHint.js
@@ -0,0 +1,48 @@
+/* ************************************************************************
+
+ osparc - the simcore frontend
+
+ https://osparc.io
+
+ Copyright:
+ 2022 IT'IS Foundation, https://itis.swiss
+
+ License:
+ MIT: https://opensource.org/licenses/MIT
+
+ Authors:
+ * Odei Maiz (odeimaiz)
+
+************************************************************************ */
+
+qx.Class.define("osparc.component.form.PortInfoHint", {
+ extend: osparc.ui.hint.InfoHint,
+
+ statics: {
+ ERROR_ICON: "@MaterialIcons/error_outline/14"
+ },
+
+ properties: {
+ portErrorMsg: {
+ check: "String",
+ init: null,
+ nullable: true,
+ apply: "__applyPortErrorMsg"
+ }
+ },
+
+ members: {
+ __applyPortErrorMsg: function(errorMsg) {
+ let text = this.getHintText();
+ if (errorMsg) {
+ const color = qx.theme.manager.Color.getInstance().resolve("failed-red");
+ text += `
${errorMsg}`;
+ }
+ this._hint.setText(text);
+ this.set({
+ source: errorMsg ? this.self().ERROR_ICON : osparc.ui.hint.InfoHint.INFO_ICON,
+ textColor: errorMsg ? "failed-red" : "text"
+ });
+ }
+ }
+});
diff --git a/services/web/client/source/class/osparc/component/form/renderer/PropForm.js b/services/web/client/source/class/osparc/component/form/renderer/PropForm.js
index 13a0876b65c..09a273cc048 100644
--- a/services/web/client/source/class/osparc/component/form/renderer/PropForm.js
+++ b/services/web/client/source/class/osparc/component/form/renderer/PropForm.js
@@ -382,6 +382,14 @@ qx.Class.define("osparc.component.form.renderer.PropForm", {
this.fireEvent("changeChildVisibility");
},
+ setPortErrorMessage: function(portId, msg) {
+ const infoButton = this._getInfoFieldChild(portId);
+ if (infoButton && "child" in infoButton) {
+ const infoHint = infoButton.child;
+ infoHint.setPortErrorMsg(msg);
+ }
+ },
+
retrievingPortData: function(portId) {
const status = this._retrieveStatus.retrieving;
if (portId) {
diff --git a/services/web/client/source/class/osparc/component/form/renderer/PropFormBase.js b/services/web/client/source/class/osparc/component/form/renderer/PropFormBase.js
index cb455ffdd97..f7f5f4de392 100644
--- a/services/web/client/source/class/osparc/component/form/renderer/PropFormBase.js
+++ b/services/web/client/source/class/osparc/component/form/renderer/PropFormBase.js
@@ -244,7 +244,7 @@ qx.Class.define("osparc.component.form.renderer.PropFormBase", {
},
_createInfoWHint: function(hint) {
- const infoWHint = new osparc.ui.hint.InfoHint(hint);
+ const infoWHint = new osparc.component.form.PortInfoHint(hint);
return infoWHint;
},
@@ -328,6 +328,10 @@ qx.Class.define("osparc.component.form.renderer.PropFormBase", {
return this._getLayoutChild(portId, this.self().GRID_POS.LABEL);
},
+ _getInfoFieldChild: function(portId) {
+ return this._getLayoutChild(portId, this.self().GRID_POS.INFO);
+ },
+
_getCtrlFieldChild: function(portId) {
return this._getLayoutChild(portId, this.self().GRID_POS.CTRL_FIELD);
},
diff --git a/services/web/client/source/class/osparc/data/model/Node.js b/services/web/client/source/class/osparc/data/model/Node.js
index 08f043bef3e..a8fe1e36582 100644
--- a/services/web/client/source/class/osparc/data/model/Node.js
+++ b/services/web/client/source/class/osparc/data/model/Node.js
@@ -150,6 +150,14 @@ qx.Class.define("osparc.data.model.Node", {
nullable: false
},
+ errors: {
+ check: "Array",
+ init: [],
+ nullable: true,
+ event: "changeErrors",
+ apply: "__applyErrors"
+ },
+
// GUI elements //
propsForm: {
check: "osparc.component.form.renderer.PropForm",
@@ -712,29 +720,41 @@ qx.Class.define("osparc.data.model.Node", {
return outputsData;
},
- setErrors: function(errors) {
- errors.forEach(error => {
- const loc = error["loc"];
- if (loc.length < 2) {
- return;
- }
- if (loc[1] === this.getNodeId()) {
- const errorMsgData = {
- nodeId: this.getNodeId(),
- msg: error["msg"],
- level: "ERROR"
- };
- if (loc.length > 2) {
- const portKey = loc[2];
- if ("inputs" in this.getMetaData() && portKey in this.getMetaData()["inputs"]) {
- errorMsgData["msg"] = this.getMetaData()["inputs"][portKey]["label"] + ": " + errorMsgData["msg"];
- } else {
- errorMsgData["msg"] = portKey + ": " + errorMsgData["msg"];
+ __applyErrors: function(errors) {
+ if (errors && errors.length) {
+ errors.forEach(error => {
+ const loc = error["loc"];
+ if (loc.length < 2) {
+ return;
+ }
+ if (loc[1] === this.getNodeId()) {
+ const errorMsgData = {
+ nodeId: this.getNodeId(),
+ msg: error["msg"],
+ level: "ERROR"
+ };
+
+ // errors to port
+ if (loc.length > 2) {
+ const portKey = loc[2];
+ if (this.hasInputs() && portKey in this.getMetaData()["inputs"]) {
+ errorMsgData["msg"] = this.getMetaData()["inputs"][portKey]["label"] + ": " + errorMsgData["msg"];
+ } else {
+ errorMsgData["msg"] = portKey + ": " + errorMsgData["msg"];
+ }
+ this.getPropsForm().setPortErrorMessage(portKey, errorMsgData["msg"]);
}
+
+ // errors to logger
+ this.fireDataEvent("showInLogger", errorMsgData);
}
- this.fireDataEvent("showInLogger", errorMsgData);
- }
- });
+ });
+ } else if (this.hasInputs()) {
+ // reset port errors
+ Object.keys(this.getMetaData()["inputs"]).forEach(portKey => {
+ this.getPropsForm().setPortErrorMessage(portKey, null);
+ });
+ }
},
// post edge creation routine
diff --git a/services/web/client/source/class/osparc/data/model/Study.js b/services/web/client/source/class/osparc/data/model/Study.js
index 9fca6868e7f..8fa66a0f0eb 100644
--- a/services/web/client/source/class/osparc/data/model/Study.js
+++ b/services/web/client/source/class/osparc/data/model/Study.js
@@ -375,9 +375,9 @@ qx.Class.define("osparc.data.model.Study", {
}
if (node && "errors" in nodeUpdatedData) {
const errors = nodeUpdatedData["errors"];
- if (errors && errors.length) {
- node.setErrors(errors);
- }
+ node.setErrors(errors);
+ } else {
+ node.setErrors([]);
}
},
diff --git a/services/web/client/source/class/osparc/navigation/PrevNextButtons.js b/services/web/client/source/class/osparc/navigation/PrevNextButtons.js
index b5a3b4f6ec4..cf80868bcf6 100644
--- a/services/web/client/source/class/osparc/navigation/PrevNextButtons.js
+++ b/services/web/client/source/class/osparc/navigation/PrevNextButtons.js
@@ -40,7 +40,7 @@ qx.Class.define("osparc.navigation.PrevNextButtons", {
NEXT_BUTTON: "@FontAwesome5Solid/arrow-right/32",
RUN_BUTTON: "@FontAwesome5Solid/play/32",
BUSY_BUTTON: "@FontAwesome5Solid/circle-notch/32",
- SELECT_FILE_BUTTON: "@FontAwesome5Solid/arrow-right/32"
+ SELECT_FILE_BUTTON: "@FontAwesome5Solid/file-medical/32"
},
properties: {
diff --git a/services/web/client/source/class/osparc/ui/basic/NodeStatusUI.js b/services/web/client/source/class/osparc/ui/basic/NodeStatusUI.js
index 303c5c4aaca..fb15a827d9d 100644
--- a/services/web/client/source/class/osparc/ui/basic/NodeStatusUI.js
+++ b/services/web/client/source/class/osparc/ui/basic/NodeStatusUI.js
@@ -70,6 +70,15 @@ qx.Class.define("osparc.ui.basic.NodeStatusUI", {
} else {
this.__setupBlank();
}
+ node.bind("errors", this, "toolTipText", {
+ converter: errors => {
+ let errorsText = "";
+ if (errors) {
+ errors.forEach(error => errorsText += error["msg"] + "
");
+ }
+ return errorsText;
+ }
+ });
},
__setupComputational: function() {
diff --git a/services/web/client/source/class/osparc/ui/hint/InfoHint.js b/services/web/client/source/class/osparc/ui/hint/InfoHint.js
index 3deec732d72..cf3ad6e260c 100644
--- a/services/web/client/source/class/osparc/ui/hint/InfoHint.js
+++ b/services/web/client/source/class/osparc/ui/hint/InfoHint.js
@@ -25,7 +25,9 @@ qx.Class.define("osparc.ui.hint.InfoHint", {
* @extends osparc.ui.basic.IconButton
*/
construct: function(hint) {
- this.base(arguments, "@MaterialIcons/info_outline/14");
+ this.base(arguments, this.self().INFO_ICON);
+
+ this.__createHint();
this.bind("hintText", this, "visibility", {
converter: hintText => (hintText && hintText !== "") ? "visible" : "excluded"
@@ -36,6 +38,10 @@ qx.Class.define("osparc.ui.hint.InfoHint", {
}
},
+ statics: {
+ INFO_ICON: "@MaterialIcons/info_outline/14"
+ },
+
properties: {
hintText: {
check: "String",
@@ -46,35 +52,39 @@ qx.Class.define("osparc.ui.hint.InfoHint", {
},
members: {
- __applyHintText: function(hintText) {
- if (hintText && hintText !== "") {
- const hint = new osparc.ui.hint.Hint(this, hintText).set({
- active: false
- });
-
- const showHint = () => hint.show();
- const hideHint = () => hint.exclude();
-
- // Make hint "modal" when info button is clicked
- const tapListener = event => {
- if (osparc.utils.Utils.isMouseOnElement(hint, event)) {
- return;
- }
- hideHint();
- document.removeEventListener("mousedown", tapListener);
- this.addListener("mouseover", showHint);
- this.addListener("mouseout", hideHint);
- };
+ _hint: null,
+
+ __createHint: function() {
+ const hint = this._hint = new osparc.ui.hint.Hint(this).set({
+ active: false
+ });
+ const showHint = () => hint.show();
+ const hideHint = () => hint.exclude();
+
+ // Make hint "modal" when info button is clicked
+ const tapListener = event => {
+ if (osparc.utils.Utils.isMouseOnElement(hint, event)) {
+ return;
+ }
+ hideHint();
+ document.removeEventListener("mousedown", tapListener);
this.addListener("mouseover", showHint);
this.addListener("mouseout", hideHint);
- this.addListener("tap", () => {
- showHint();
- document.addEventListener("mousedown", tapListener);
- this.removeListener("mouseover", showHint);
- this.removeListener("mouseout", hideHint);
- }, this);
- }
+ };
+
+ this.addListener("mouseover", showHint);
+ this.addListener("mouseout", hideHint);
+ this.addListener("tap", () => {
+ showHint();
+ document.addEventListener("mousedown", tapListener);
+ this.removeListener("mouseover", showHint);
+ this.removeListener("mouseout", hideHint);
+ }, this);
+ },
+
+ __applyHintText: function(hintText) {
+ this._hint.setText(hintText);
}
}
});