diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index 6d74de34376..4f99235ec6e 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -352,7 +352,7 @@ qx.Class.define("osparc.dashboard.CardBase", { }, uiMode: { - check: ["workbench", "guided", "app"], + check: ["workbench", "guided", "app", "standalone"], // "guided" is no longer used nullable: true, apply: "__applyUiMode" }, @@ -582,21 +582,16 @@ qx.Class.define("osparc.dashboard.CardBase", { }, __applyUiMode: function(uiMode) { - let source = null; - let toolTipText = null; switch (uiMode) { case "guided": - case "app": - source = osparc.dashboard.CardBase.MODE_APP; - toolTipText = this.tr("App mode"); + case "app": { + const uiModeIcon = this.getChildControl("workbench-mode"); + uiModeIcon.set({ + source: osparc.dashboard.CardBase.MODE_APP, + toolTipText: this.tr("App mode"), + }); break; - } - if (source) { - const uiModeIcon = this.getChildControl("workbench-mode"); - uiModeIcon.set({ - source, - toolTipText, - }); + } } }, @@ -882,6 +877,10 @@ qx.Class.define("osparc.dashboard.CardBase", { if (duplicateButton) { duplicateButton.setEnabled(osparc.study.Utils.canBeDuplicated(resourceData)); } + const convertToPipelineButton = menuButtons.find(menuBtn => "convertToPipelineButton" in menuBtn); + if (convertToPipelineButton) { + convertToPipelineButton.setEnabled(osparc.study.Utils.canBeDuplicated(resourceData)); + } const exportCMISButton = menuButtons.find(menuBtn => "exportCMISButton" in menuBtn); if (exportCMISButton) { exportCMISButton.setEnabled(osparc.study.Utils.canBeExported(resourceData)); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 5f6bb97a02e..1881ee00feb 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -431,7 +431,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { if ( osparc.utils.Resources.isService(resourceData) || !osparc.product.Utils.showStudyPreview() || - osparc.data.model.Study.getUiMode(resourceData) === "app" + !osparc.data.model.Study.getUiMode(resourceData) === "workbench" ) { // there is no pipelining or don't show it return null; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 9c04ac3ef7d..22321e4b164 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -1119,17 +1119,17 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { studiesMoveButton.set({ visibility: selection.length && currentContext === "studiesAndFolders" ? "visible" : "excluded", - label: this.tr("Move") + (selection.length > 1 ? this.tr(" selected ") + `(${selection.length})` : ""), + label: this.tr("Move") + (selection.length > 1 ? ` (${selection.length})` : ""), }); studiesTrashButton.set({ visibility: selection.length && currentContext === "studiesAndFolders" ? "visible" : "excluded", - label: this.tr("Move to Bin") + (selection.length > 1 ? this.tr(" selected ") + `(${selection.length})` : ""), + label: this.tr("Move to Bin") + (selection.length > 1 ? ` (${selection.length})` : ""), }); studiesDeleteButton.set({ visibility: selection.length && currentContext === "trash" ? "visible" : "excluded", - label: this.tr("Delete permanently") + (selection.length > 1 ? this.tr(" selected ") + `(${selection.length})` : ""), + label: this.tr("Delete permanently") + (selection.length > 1 ? ` (${selection.length})` : ""), }); }); @@ -1648,6 +1648,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const duplicateStudyButton = this.__getDuplicateMenuButton(studyData); menu.add(duplicateStudyButton); + const convertToPipelineButton = this.__getConvertToPipelineMenuButton(studyData); + menu.add(convertToPipelineButton); + if (osparc.product.Utils.isProduct("osparc")) { const exportStudyButton = this.__getExportMenuButton(studyData); menu.add(exportStudyButton); @@ -1722,6 +1725,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return renameButton; }, + __updateName: function(studyData, name) { + osparc.info.StudyUtils.patchStudyData(studyData, "name", name) + .then(() => this._updateStudyData(studyData)) + .catch(err => { + console.error(err); + const msg = err.message || this.tr("Something went wrong Renaming"); + osparc.FlashMessenger.logAs(msg, "ERROR"); + }); + }, + __getThumbnailStudyMenuButton: function(studyData) { const thumbButton = new qx.ui.menu.Button(this.tr("Thumbnail..."), "@FontAwesome5Solid/image/12"); thumbButton.addListener("execute", () => { @@ -1743,16 +1756,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return thumbButton; }, - __updateName: function(studyData, name) { - osparc.info.StudyUtils.patchStudyData(studyData, "name", name) - .then(() => this._updateStudyData(studyData)) - .catch(err => { - console.error(err); - const msg = err.message || this.tr("Something went wrong Renaming"); - osparc.FlashMessenger.logAs(msg, "ERROR"); - }); - }, - __updateThumbnail: function(studyData, url) { osparc.info.StudyUtils.patchStudyData(studyData, "thumbnail", url) .then(() => this._updateStudyData(studyData)) @@ -1860,6 +1863,29 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return duplicateButton; }, + __getConvertToPipelineMenuButton: function(studyData) { + const convertToPipelineButton = new qx.ui.menu.Button(this.tr("Convert to Pipeline"), null); + convertToPipelineButton["convertToPipelineButton"] = true; + const uiMode = osparc.data.model.Study.getUiMode(studyData); + convertToPipelineButton.setVisibility(uiMode === "standalone" ? "visible" : "excluded"); + convertToPipelineButton.addListener("execute", () => { + this.__updateUIMode(studyData, "workbench") + .catch(err => { + console.error(err); + const msg = err.message || this.tr("Something went wrong Converting to Pipeline"); + osparc.FlashMessenger.logAs(msg, "ERROR"); + }); + }, this); + return convertToPipelineButton; + }, + + __updateUIMode: function(studyData, uiMode) { + const studyUI = osparc.utils.Utils.deepCloneObject(studyData["ui"]); + studyUI["mode"] = uiMode; + return osparc.info.StudyUtils.patchStudyData(studyData, "ui", studyUI) + .then(() => this._updateStudyData(studyData)) + }, + __getExportMenuButton: function(studyData) { const exportButton = new qx.ui.menu.Button(this.tr("Export cMIS"), "@FontAwesome5Solid/cloud-download-alt/12"); exportButton["exportCMISButton"] = true; diff --git a/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js b/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js index 2ca74c47274..1c521de8d9d 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js +++ b/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js @@ -94,18 +94,27 @@ qx.Class.define("osparc.data.model.IframeHandler", { osparc.utils.Utils.setIdToWidget(iframe.getIframe(), "iframe_"+this.getNode().getNodeId()); if (osparc.product.Utils.isProduct("s4llite")) { iframe.setShowToolbar(false); + } else { + this.getStudy().getUi().bind("mode", iframe, "showToolbar", { + converter: mode => mode !== "standalone" + }); } - iframe.addListener("restart", () => this.__restartIFrame(), this); + iframe.addListener("restart", () => this.restartIFrame(), this); iframe.getDiskUsageIndicator().setCurrentNode(this.getNode()) this.setIFrame(iframe); }, __initLoadingPage: function() { - const showZoomMaximizeButton = !osparc.product.Utils.isProduct("s4llite"); - const loadingPage = new osparc.ui.message.Loading(showZoomMaximizeButton); - loadingPage.set({ + const loadingPage = new osparc.ui.message.Loading().set({ header: this.__getLoadingPageHeader() }); + if (osparc.product.Utils.isProduct("s4llite")) { + loadingPage.setShowToolbar(false); + } else { + this.getStudy().getUi().bind("mode", loadingPage, "showToolbar", { + converter: mode => mode !== "standalone" + }); + } const node = this.getNode(); const thumbnail = node.getMetaData()["thumbnail"]; @@ -115,7 +124,9 @@ qx.Class.define("osparc.data.model.IframeHandler", { node.addListener("changeLabel", () => loadingPage.setHeader(this.__getLoadingPageHeader()), this); const nodeStatus = node.getStatus(); - const sequenceWidget = nodeStatus.getProgressSequence().getWidgetForLoadingPage(); + const sequenceWidget = nodeStatus.getProgressSequence().getWidgetForLoadingPage().set({ + width: 400 + }); nodeStatus.bind("interactive", sequenceWidget, "visibility", { converter: state => ["pending", "pulling", "starting", "connecting"].includes(state) ? "visible" : "excluded" }); @@ -352,7 +363,7 @@ qx.Class.define("osparc.data.model.IframeHandler", { node.fireDataEvent("showInLogger", msgData); // will switch to iframe's content - this.__restartIFrame(); + this.restartIFrame(); if (!node.isDynamicV2()) { node.callRetrieveInputs(); } @@ -374,7 +385,7 @@ qx.Class.define("osparc.data.model.IframeHandler", { } }, - __restartIFrame: function() { + restartIFrame: function() { const node = this.getNode(); if (node.getServiceUrl() !== null) { // restart button pushed diff --git a/services/static-webserver/client/source/class/osparc/data/model/Study.js b/services/static-webserver/client/source/class/osparc/data/model/Study.js index 6a511f726b7..1da17af04d2 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Study.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Study.js @@ -637,6 +637,19 @@ qx.Class.define("osparc.data.model.Study", { return parameters; }, + getNonFrontendNodes: function() { + const nodes = this.getWorkbench().getNodes(); + return Object.values(nodes).filter(node => node.isComputational() || node.isDynamic()); + }, + + isOnlyNodeDynamic: function() { + const validNodes = this.getNonFrontendNodes(); + if (validNodes.length === 1) { + return validNodes[0].isDynamic(); + } + return null; + }, + hasSlideshow: function() { return !this.getUi().getSlideshow().isEmpty(); }, diff --git a/services/static-webserver/client/source/class/osparc/data/model/StudyUI.js b/services/static-webserver/client/source/class/osparc/data/model/StudyUI.js index d6451f158e3..fcc2f7bd9f8 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/StudyUI.js +++ b/services/static-webserver/client/source/class/osparc/data/model/StudyUI.js @@ -63,7 +63,7 @@ qx.Class.define("osparc.data.model.StudyUI", { }, mode: { - check: ["workbench", "guided", "app"], + check: ["workbench", "guided", "app", "standalone"], // "guided" is no longer used init: "workbench", nullable: true, event: "changeMode", diff --git a/services/static-webserver/client/source/class/osparc/desktop/SlideshowView.js b/services/static-webserver/client/source/class/osparc/desktop/SlideshowView.js index e05a37f56a1..52c33d94be9 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/SlideshowView.js +++ b/services/static-webserver/client/source/class/osparc/desktop/SlideshowView.js @@ -72,7 +72,7 @@ qx.Class.define("osparc.desktop.SlideshowView", { const nodeId = e.getData(); this.__hideNode(nodeId); }, this); - slideshowToolbar.addListener("slidesStop", () => this.fireEvent("slidesStop"), this); + slideshowToolbar.addListener("slidesStop", () => this.getStudy().getUi().setMode("workbench"), this); this._add(slideshowToolbar); const mainView = this.__mainView = new qx.ui.container.Composite(new qx.ui.layout.HBox().set({ @@ -109,7 +109,6 @@ qx.Class.define("osparc.desktop.SlideshowView", { }, events: { - "slidesStop": "qx.event.type.Event", "startPartialPipeline": "qx.event.type.Data", "stopPipeline": "qx.event.type.Event", "backToDashboardPressed": "qx.event.type.Event", @@ -131,12 +130,6 @@ qx.Class.define("osparc.desktop.SlideshowView", { apply: "__applyMaximized", event: "changeMaximized" }, - - pageContext: { - check: ["guided", "app"], - nullable: false, - init: "guided" - } }, statics: { @@ -270,9 +263,6 @@ qx.Class.define("osparc.desktop.SlideshowView", { view = new osparc.node.slideshow.NodeView(); } view.setNode(node); - if (node.isDynamic()) { - view.getSettingsLayout().setVisibility(this.getPageContext() === "app" ? "excluded" : "visible"); - } } this.__connectMaximizeEvents(node); this.__styleView(node, view); @@ -377,7 +367,6 @@ qx.Class.define("osparc.desktop.SlideshowView", { }); } } - this.setPageContext("app"); this.__slideshowToolbar.populateButtons(true); const currentNodeId = this.getStudy().getUi().getCurrentNodeId(); const isValid = slideshow.getPosition(currentNodeId) !== -1; diff --git a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js index 3e6a0c943e2..9d7f33e55e3 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js +++ b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js @@ -37,8 +37,6 @@ qx.Class.define("osparc.desktop.StudyEditor", { }); workbenchView.addListener("slidesEdit", () => this.__editSlides(), this); - workbenchView.addListener("slidesAppStart", () => this.setPageContext(osparc.navigation.NavigationBar.PAGE_CONTEXT[2]), this); - slideshowView.addListener("slidesStop", () => this.setPageContext(osparc.navigation.NavigationBar.PAGE_CONTEXT[1]), this); workbenchView.addListener("takeSnapshot", () => this.__takeSnapshot(), this); workbenchView.addListener("takeSnapshot", () => this.__takeSnapshot(), this); @@ -72,7 +70,7 @@ qx.Class.define("osparc.desktop.StudyEditor", { const startStopButtons = workbenchView.getStartStopButtons(); startStopButtons.addListener("startPipeline", () => this.__startPipeline([]), this); startStopButtons.addListener("startPartialPipeline", () => { - const partialPipeline = this.getPageContext() === "workbench" ? this.__workbenchView.getSelectedNodeIDs() : this.__slideshowView.getSelectedNodeIDs(); + const partialPipeline = this.getStudy().getUi().getMode() === "app" ? this.__slideshowView.getSelectedNodeIDs() : this.__workbenchView.getSelectedNodeIDs(); this.__startPipeline(partialPipeline); }, this); startStopButtons.addListener("stopPipeline", () => this.__stopPipeline(), this); @@ -103,23 +101,14 @@ qx.Class.define("osparc.desktop.StudyEditor", { apply: "__applyStudy", event: "changeStudy" }, - - pageContext: { - check: ["workbench", "guided", "app"], - init: null, - nullable: false, - event: "changePageContext", - apply: "__applyPageContext" - } }, statics: { AUTO_SAVE_INTERVAL: 3000, - READ_ONLY_TEXT: qx.locale.Manager.tr("You do not have writing permissions.
Your changes will not be saved.") + READ_ONLY_TEXT: qx.locale.Manager.tr("You do not have writing permissions.
Your changes will not be saved."), }, members: { - __study: null, __settingStudy: null, __viewsStack: null, __workbenchView: null, @@ -179,6 +168,10 @@ qx.Class.define("osparc.desktop.StudyEditor", { } }, this); } + + study.getUi().addListener("changeMode", e => { + this.__uiModeChanged(e.getData(), e.getOldData()); + }); }) .catch(err => { console.error(err); @@ -251,21 +244,9 @@ qx.Class.define("osparc.desktop.StudyEditor", { osparc.FlashMessenger.getInstance().logAs(msg, "WARNING"); } - const pageContext = study.getUi().getMode(); - switch (pageContext) { - case "guided": - case "app": - this.__slideshowView.startSlides(); - break; - default: - this.__workbenchView.openFirstNode(); - break; - } - this.addListener("changePageContext", e => { - const pageCxt = e.getData(); - study.getUi().setMode(pageCxt); - }); - this.setPageContext(pageContext); + + const uiMode = study.getUi().getMode(); + this.__uiModeChanged(uiMode); const workbench = study.getWorkbench(); workbench.addListener("retrieveInputs", e => { @@ -556,7 +537,8 @@ qx.Class.define("osparc.desktop.StudyEditor", { }, __editSlides: function() { - if (this.getPageContext() !== osparc.navigation.NavigationBar.PAGE_CONTEXT[1]) { + if (this.getStudy().getUi().getMode() !== "workbench") { + // if the user is not in "workbench" mode, return return; } @@ -719,21 +701,40 @@ qx.Class.define("osparc.desktop.StudyEditor", { return this.__workbenchView.getLogger(); }, - __applyPageContext: function(newCtxt) { - switch (newCtxt) { - case "workbench": - this.__viewsStack.setSelection([this.__workbenchView]); - if (this.getStudy() && this.getStudy().getUi()) { - this.__workbenchView.nodeSelected(this.getStudy().getUi().getCurrentNodeId()); - } - break; + __uiModeChanged: function(newUIMode, oldUIMode) { + switch (newUIMode) { case "guided": case "app": this.__viewsStack.setSelection([this.__slideshowView]); - if (this.getStudy() && this.getStudy().getUi()) { - this.__slideshowView.startSlides(); + this.__slideshowView.startSlides(); + break; + case "standalone": { + this.__viewsStack.setSelection([this.__workbenchView]); + this.__workbenchView.openFirstNode(); + break; + } + case "workbench": + default: { + this.__viewsStack.setSelection([this.__workbenchView]); + if (oldUIMode === "standalone") { + // in this transition, show workbenchUI + this.__workbenchView.setMaximized(false); + this.__workbenchView.showPipeline(); + } else { + const currentNodeId = this.getStudy().getUi().getCurrentNodeId(); + if (currentNodeId) { + const node = this.getStudy().getWorkbench().getNode(currentNodeId); + if (node && node.isDynamic()) { + this.__workbenchView.fullscreenNode(currentNodeId); + } else { + this.__workbenchView.nodeSelected(currentNodeId); + } + } else { + this.__workbenchView.openFirstNode(); + } } break; + } } }, diff --git a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js index a735b2532d6..f1999ebb99f 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js +++ b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js @@ -70,7 +70,6 @@ qx.Class.define("osparc.desktop.WorkbenchView", { "expandNavBar": "qx.event.type.Event", "backToDashboardPressed": "qx.event.type.Event", "slidesEdit": "qx.event.type.Event", - "slidesAppStart": "qx.event.type.Event", "annotationRectStart": "qx.event.type.Event", "takeSnapshot": "qx.event.type.Event", "showSnapshots": "qx.event.type.Event", @@ -81,7 +80,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { properties: { study: { check: "osparc.data.model.Study", - apply: "_applyStudy", + apply: "__applyStudy", nullable: false }, @@ -244,7 +243,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { return sidePanelsNewWidth; }, - _applyStudy: function(study) { + __applyStudy: function(study) { if (study) { this.__initViews(); this.__connectEvents(); @@ -302,9 +301,12 @@ qx.Class.define("osparc.desktop.WorkbenchView", { if (study === null) { return; } + this.__initPrimaryColumn(); this.__initSecondaryColumn(); this.__initMainView(); + + this.setMaximized(false); }, __initPrimaryColumn: function() { @@ -448,7 +450,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { marginTop: 7, ...osparc.navigation.NavigationBar.BUTTON_OPTIONS }); - startAppButtonTB.addListener("execute", () => this.fireEvent("slidesAppStart")); + startAppButtonTB.addListener("execute", () => study.getUi().setMode("app")); topBar.add(startAppButtonTB); const collapseWithUserMenu = this.__collapseWithUserMenu = new osparc.desktop.CollapseWithUserMenu(); @@ -497,10 +499,9 @@ qx.Class.define("osparc.desktop.WorkbenchView", { studyTreeItem.addListener("changeSelectedNode", () => { nodesTree.resetSelection(); - this.__populateSecondaryColumn(this.getStudy()); - this.__evalIframe(); - this.__openWorkbenchTab(); - this.__loggerView.setCurrentNodeId(null); + this.showPipeline(); + + this.getStudy().getUi().setCurrentNodeId(this.getStudy().getUuid()); }); nodesTree.addListener("changeSelectedNode", e => { studyTreeItem.resetSelection(); @@ -514,6 +515,8 @@ qx.Class.define("osparc.desktop.WorkbenchView", { this.__loggerView.setCurrentNodeId(nodeId); this.__workbenchUI.nodeSelected(nodeId); this.fireDataEvent("changeSelectedNode", nodeId); + + this.getStudy().getUi().setCurrentNodeId(nodeId); }); if (this.__workbenchUIConnected === null) { @@ -529,9 +532,13 @@ qx.Class.define("osparc.desktop.WorkbenchView", { this.__evalIframe(node); this.__loggerView.setCurrentNodeId(nodeId); this.fireDataEvent("changeSelectedNode", nodeId); + + this.getStudy().getUi().setCurrentNodeId(nodeId); } else { // empty selection this.__studyTreeItem.selectStudyItem(); + + this.getStudy().getUi().setCurrentNodeId(this.getStudy().getUuid()); } }); workbenchUI.addListener("nodeSelected", e => { @@ -545,6 +552,8 @@ qx.Class.define("osparc.desktop.WorkbenchView", { this.__populateSecondaryColumn(node); this.__openIframeTab(node); this.__loggerView.setCurrentNodeId(nodeId); + + this.getStudy().getUi().setCurrentNodeId(nodeId); } }, this); } @@ -553,16 +562,8 @@ qx.Class.define("osparc.desktop.WorkbenchView", { const nodeId = e.getData(); if (nodeId) { studyTreeItem.resetSelection(); - const workbench = this.getStudy().getWorkbench(); - const node = workbench.getNode(nodeId); - if (node) { - this.__populateSecondaryColumn(node); - this.__openIframeTab(node); - node.getLoadingPage().maximizeIFrame(true); - node.getIFrame().maximizeIFrame(true); - } - this.__loggerView.setCurrentNodeId(nodeId); - this.__workbenchUI.nodeSelected(nodeId); + this.fullscreenNode(nodeId); + this.getStudy().getUi().setCurrentNodeId(nodeId); } }, this); nodesTree.addListener("removeNode", e => { @@ -843,7 +844,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { toolTipText: this.tr("Start App Mode"), height: buttonsHeight }); - startAppBtn.addListener("execute", () => this.fireEvent("slidesAppStart"), this); + startAppBtn.addListener("execute", () => this.getStudy().getUi().setMode("app"), this); slideshowButtons.add(startAppBtn); this.__evalSlidesButtons(); @@ -1183,21 +1184,38 @@ qx.Class.define("osparc.desktop.WorkbenchView", { this.__nodesTree.nodeSelected(this.__currentNodeId); }, + showPipeline: function() { + this.__populateSecondaryColumn(this.getStudy()); + this.__evalIframe(); + this.__openWorkbenchTab(); + this.__loggerView.setCurrentNodeId(null); + + this.getStudy().getUi().setCurrentNodeId(this.getStudy().getUuid()); + }, + + fullscreenNode: function(nodeId) { + const node = this.getStudy().getWorkbench().getNode(nodeId); + if (node && node.isDynamic()) { + qx.event.Timer.once(() => { + this.__populateSecondaryColumn(node); + this.__openIframeTab(node); + node.getIFrame().maximizeIFrame(true); + }, this, 10); + } + this.__loggerView.setCurrentNodeId(nodeId); + this.__workbenchUI.nodeSelected(nodeId); + }, + openFirstNode: function() { - const nodes = this.getStudy().getWorkbench().getNodes(); - const validNodes = Object.values(nodes).filter(node => node.isComputational() || node.isDynamic()); + const validNodes = this.getStudy().getNonFrontendNodes(); if (validNodes.length === 1 && validNodes[0].isDynamic()) { const dynamicNode = validNodes[0]; - this.nodeSelected(dynamicNode.getNodeId()); - qx.event.Timer.once(() => { - this.__openIframeTab(dynamicNode); - dynamicNode.getLoadingPage().maximizeIFrame(true); - dynamicNode.getIFrame().maximizeIFrame(true); - }, this, 10); - return; + this.fullscreenNode(dynamicNode.getNodeId()); + this.getStudy().getUi().setCurrentNodeId(dynamicNode.getNodeId()); + } else { + this.setMaximized(false); + this.nodeSelected(this.getStudy().getUuid()); } - this.setMaximized(false); - this.nodeSelected(this.getStudy().getUuid()); } } }); diff --git a/services/static-webserver/client/source/class/osparc/file/FilePicker.js b/services/static-webserver/client/source/class/osparc/file/FilePicker.js index fdce6e4aec9..4437b773064 100644 --- a/services/static-webserver/client/source/class/osparc/file/FilePicker.js +++ b/services/static-webserver/client/source/class/osparc/file/FilePicker.js @@ -39,28 +39,22 @@ qx.Class.define("osparc.file.FilePicker", { /** * @param node {osparc.data.model.Node} Node owning the widget */ - construct: function(node, pageContext = "workbench") { + construct: function(node, viewContext = "workbench") { this.base(arguments); this._setLayout(new qx.ui.layout.VBox(20)); this.set({ node, - pageContext }); - this.__buildLayout(); + this.__buildLayout(viewContext); }, properties: { node: { check: "osparc.data.model.Node" }, - - pageContext: { - check: ["workbench", "guided", "app"], - nullable: false - } }, events: { @@ -284,15 +278,14 @@ qx.Class.define("osparc.file.FilePicker", { } }, - __buildLayout: function() { + __buildLayout: function(viewContext) { this._removeAll(); const hasOutput = osparc.file.FilePicker.hasOutputAssigned(this.getNode().getOutputs()); if (hasOutput) { this.__buildInfoLayout(); } else { this.__addProgressBar(); - const isWorkbenchContext = this.getPageContext() === "workbench"; - if (isWorkbenchContext) { + if (viewContext === "workbench") { this.__buildWorkbenchLayout(); } else { this.setMargin(10); @@ -545,8 +538,8 @@ qx.Class.define("osparc.file.FilePicker", { flex: 1 }); treeFolderLayout.add(treeLayout, 0); - const allowMultiselection = false; - const folderViewer = new osparc.file.FolderViewer(allowMultiselection); + const allowMultiSelection = false; + const folderViewer = new osparc.file.FolderViewer(allowMultiSelection); treeFolderLayout.add(folderViewer, 1); filesTree.addListener("selectionChanged", () => { diff --git a/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js b/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js index e8d252a5f82..faeb6f21832 100644 --- a/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js +++ b/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js @@ -80,12 +80,6 @@ qx.Class.define("osparc.navigation.NavigationBar", { minWidth: 30, minHeight: 30 }, - - PAGE_CONTEXT: { - 0: "dashboard", - 1: "workbench", - 2: "app" - } }, members: { diff --git a/services/static-webserver/client/source/class/osparc/navigation/StudyTitleWOptions.js b/services/static-webserver/client/source/class/osparc/navigation/StudyTitleWOptions.js index 342f0de8cee..4147444812b 100644 --- a/services/static-webserver/client/source/class/osparc/navigation/StudyTitleWOptions.js +++ b/services/static-webserver/client/source/class/osparc/navigation/StudyTitleWOptions.js @@ -68,6 +68,31 @@ qx.Class.define("osparc.navigation.StudyTitleWOptions", { }); }); break; + case "study-menu-reload": + control = new qx.ui.menu.Button().set({ + label: this.tr("Reload"), + icon: "@FontAwesome5Solid/redo-alt/12", + }); + control.addListener("execute", () => this.__reloadIFrame(), this); + break; + case "study-menu-convert-to-pipeline": + control = new qx.ui.menu.Button().set({ + label: this.tr("Convert to Pipeline"), + icon: null, + }); + control.addListener("execute", () => { + this.getStudy().getUi().setMode("workbench"); + }); + break; + case "study-menu-convert-to-standalone": + control = new qx.ui.menu.Button().set({ + label: this.tr("Convert to Standalone"), + icon: null, + }); + control.addListener("execute", () => { + this.getStudy().getUi().setMode("standalone"); + }); + break; case "study-menu-download-logs": control = new qx.ui.menu.Button().set({ label: this.tr("Download logs"), @@ -77,13 +102,18 @@ qx.Class.define("osparc.navigation.StudyTitleWOptions", { break; case "study-menu-button": { const optionsMenu = new qx.ui.menu.Menu(); + optionsMenu.setAppearance("menu-wider"); optionsMenu.add(this.getChildControl("study-menu-info")); + optionsMenu.add(this.getChildControl("study-menu-reload")); + optionsMenu.add(this.getChildControl("study-menu-convert-to-pipeline")); + optionsMenu.add(this.getChildControl("study-menu-convert-to-standalone")); optionsMenu.add(this.getChildControl("study-menu-download-logs")); control = new qx.ui.form.MenuButton().set({ appearance: "fab-button", menu: optionsMenu, icon: "@FontAwesome5Solid/ellipsis-v/14", - allowGrowY: false + allowGrowY: false, + width: 24, }); this._add(control); break; @@ -104,9 +134,35 @@ qx.Class.define("osparc.navigation.StudyTitleWOptions", { return control || this.base(arguments, id); }, + __reloadIFrame: function() { + const nodes = this.getStudy().getWorkbench().getNodes(); + if (Object.keys(nodes).length === 1) { + Object.values(nodes)[0].getIframeHandler().restartIFrame(); + } + }, + __applyStudy: function(study) { if (study) { - study.bind("name", this.getChildControl("edit-title-label"), "value"); + const editTitle = this.getChildControl("edit-title-label"); + study.bind("name", editTitle, "value"); + + const reloadButton = this.getChildControl("study-menu-reload"); + study.getUi().bind("mode", reloadButton, "visibility", { + converter: mode => mode === "standalone" ? "visible" : "excluded" + }); + + const convertToPipelineButton = this.getChildControl("study-menu-convert-to-pipeline"); + study.getUi().bind("mode", convertToPipelineButton, "visibility", { + converter: mode => mode === "standalone" ? "visible" : "excluded" + }); + + const convertToStandaloneButton = this.getChildControl("study-menu-convert-to-standalone"); + const evaluateConvertToPipelineButton = () => { + // exclude until we have the export to standalone backend functionality + convertToStandaloneButton.exclude(); + }; + study.getWorkbench().addListener("pipelineChanged", () => evaluateConvertToPipelineButton()); + study.getUi().addListener("changeMode", () => evaluateConvertToPipelineButton()); } else { this.exclude(); } diff --git a/services/static-webserver/client/source/class/osparc/study/StudyPreview.js b/services/static-webserver/client/source/class/osparc/study/StudyPreview.js index 62d5d01470f..c1d6af633be 100644 --- a/services/static-webserver/client/source/class/osparc/study/StudyPreview.js +++ b/services/static-webserver/client/source/class/osparc/study/StudyPreview.js @@ -37,7 +37,7 @@ qx.Class.define("osparc.study.StudyPreview", { __buildPreview: function() { const study = this.__study; const uiMode = study.getUi().getMode(); - if (uiMode !== "app" && !study.isPipelineEmpty()) { + if (uiMode === "workbench" && !study.isPipelineEmpty()) { const workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview(); workbenchUIPreview.setStudy(study); workbenchUIPreview.loadModel(study.getWorkbench()); diff --git a/services/static-webserver/client/source/class/osparc/study/Utils.js b/services/static-webserver/client/source/class/osparc/study/Utils.js index 445b2f82c31..8097b4b8e0e 100644 --- a/services/static-webserver/client/source/class/osparc/study/Utils.js +++ b/services/static-webserver/client/source/class/osparc/study/Utils.js @@ -142,6 +142,10 @@ qx.Class.define("osparc.study.Utils", { "y": 100 } }; + // maybe check it's dynamic + if (!("mode" in minStudyData["ui"])) { + minStudyData["ui"]["mode"] = "standalone"; + } const inaccessibleServices = this.getInaccessibleServices(minStudyData["workbench"]) if (inaccessibleServices.length) { const msg = this.getInaccessibleServicesMsg(inaccessibleServices, minStudyData["workbench"]); diff --git a/services/static-webserver/client/source/class/osparc/theme/Appearance.js b/services/static-webserver/client/source/class/osparc/theme/Appearance.js index 8af7db982cb..28f4fb4a3ec 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Appearance.js +++ b/services/static-webserver/client/source/class/osparc/theme/Appearance.js @@ -719,6 +719,17 @@ qx.Theme.define("osparc.theme.Appearance", { } }, + "menu-wider": { + include: "menu", + style: () => { + return { + decorator: "border-simple", + font: "text-14", + padding: 4, + }; + } + }, + "menu-button": { alias: "atom", diff --git a/services/static-webserver/client/source/class/osparc/ui/message/Loading.js b/services/static-webserver/client/source/class/osparc/ui/message/Loading.js index bfd238cd9e2..e222ead4cbf 100644 --- a/services/static-webserver/client/source/class/osparc/ui/message/Loading.js +++ b/services/static-webserver/client/source/class/osparc/ui/message/Loading.js @@ -19,6 +19,7 @@ * The loading page * * ----------------------- + * | [] | * | | * | oSparc/service logo | * | spinner + header | @@ -31,19 +32,20 @@ qx.Class.define("osparc.ui.message.Loading", { extend: qx.ui.core.Widget, - /** - * Constructor for the Loading widget. - * - * @param {Boolean} showMaximizeButton - */ - construct: function(showMaximizeButton = false) { + construct: function() { this.base(arguments); - this._setLayout(new qx.ui.layout.HBox()); - this.set({ - alignX: "center" - }); - this.__buildLayout(showMaximizeButton); + const layout = new qx.ui.layout.Grid(20, 20); + layout.setRowFlex(this.self().GRID_POS.SPACER_TOP, 1); + layout.setRowFlex(this.self().GRID_POS.SPACER_BOTTOM, 1); + layout.setColumnFlex(0, 1); + layout.setColumnMaxWidth(1, 400); + layout.setColumnAlign(1, "center", "middle"); + layout.setColumnFlex(2, 1); + layout.setColumnAlign(2, "right", "middle"); + this._setLayout(layout); + + this.__buildLayout(); }, properties: { @@ -64,15 +66,21 @@ qx.Class.define("osparc.ui.message.Loading", { check: "Array", nullable: true, apply: "__applyMessages" + }, + + /** + * Show Restart-Maximize Toolbar + */ + showToolbar: { + check: "Boolean", + init: false, + event: "changeShowToolbar", } }, - // from osparc.widget.PersistentIframe events: { - /** Fired if the iframe is restored from a minimized or maximized state */ "restore" : "qx.event.type.Event", - /** Fired if the iframe is maximized */ - "maximize" : "qx.event.type.Event" + "maximize" : "qx.event.type.Event", }, statics: { @@ -82,42 +90,38 @@ qx.Class.define("osparc.ui.message.Loading", { STATUS_ICON_SIZE: 20, GRID_POS: { - LOGO: 1, - WAITING: 2, - MESSAGES: 3, - EXTRA_WIDGETS: 4 + TOOLBAR: 0, + SPACER_TOP: 1, + LOGO: 2, + WAITING: 3, + MESSAGES: 4, + EXTRA_WIDGETS: 5, + SPACER_BOTTOM: 6, } }, members: { - __mainLayout: null, __thumbnail: null, __header: null, __messagesContainer: null, __extraWidgets: null, __maxButton: null, - __buildLayout: function(showMaximizeButton) { - this.__createMainLayout(); - this.__createMaximizeButton(showMaximizeButton); - }, - - __createMainLayout: function() { - const layout = new qx.ui.layout.Grid(20, 20); - layout.setColumnFlex(0, 1); - const mainLayout = this.__mainLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(20).set({ - alignX: "center", - alignY: "middle" - })).set({ - width: 400, - padding: 0 - }); + __buildLayout: function() { this._add(new qx.ui.core.Widget(), { - flex: 1 + column: 0, + row: 0 }); - this._add(mainLayout); - this._add(new qx.ui.core.Widget(), { - flex: 1 + + const maxLayout = this.__createMaximizeToolbar(); + this._add(maxLayout, { + column: 2, + row: this.self().GRID_POS.TOOLBAR + }); + + this._add(new qx.ui.core.Spacer(), { + column: 1, + row: this.self().GRID_POS.SPACER_TOP }); const productLogoPath = osparc.product.Utils.getLogoPath(); @@ -137,8 +141,8 @@ qx.Class.define("osparc.ui.message.Loading", { height: logoHeight }); } - mainLayout.addAt(thumbnail, { - column: 0, + this._add(thumbnail, { + column: 1, row: this.self().GRID_POS.LOGO }); @@ -150,53 +154,76 @@ qx.Class.define("osparc.ui.message.Loading", { gap: 15, allowGrowX: false }); + const icon = waitingHeader.getChildControl("icon"); + osparc.service.StatusUI.updateCircleAnimation(icon); const label = waitingHeader.getChildControl("label"); label.set({ rich: true, - wrap: true + wrap: true, + alignX: "center", }); - const icon = waitingHeader.getChildControl("icon"); - osparc.service.StatusUI.updateCircleAnimation(icon); - mainLayout.addAt(waitingHeader, { - column: 0, + this._add(waitingHeader, { + column: 1, row: this.self().GRID_POS.WAITING }); const messages = this.__messagesContainer = new qx.ui.container.Composite(new qx.ui.layout.VBox(10).set({ alignX: "center" })); - mainLayout.addAt(messages, { - column: 0, + this._add(messages, { + column: 1, row: this.self().GRID_POS.MESSAGES }); const extraWidgets = this.__extraWidgets = new qx.ui.container.Composite(new qx.ui.layout.VBox(10).set({ alignX: "center" })); - mainLayout.addAt(extraWidgets, { - column: 0, + this._add(extraWidgets, { + column: 1, row: this.self().GRID_POS.EXTRA_WIDGETS }); + + this._add(new qx.ui.core.Spacer(), { + column: 1, + row: this.self().GRID_POS.SPACER_BOTTOM + }); + }, + + maximizeIFrame: function(maximize) { + if (maximize) { + this.fireEvent("maximize"); + this.addState("maximized"); + } else { + this.fireEvent("restore"); + this.removeState("maximized"); + } + const maxButton = this.__maxButton; + maxButton.set({ + label: osparc.widget.PersistentIframe.getZoomLabel(maximize), + icon: osparc.widget.PersistentIframe.getZoomIcon(maximize) + }); + osparc.utils.Utils.setIdToWidget(maxButton, osparc.widget.PersistentIframe.getMaximizeWidgetId(maximize)); + qx.event.message.Bus.getInstance().dispatchByName("maximizeIframe", this.hasState("maximized")); }, - __createMaximizeButton: function(showMaximizeButton) { + __createMaximizeToolbar: function() { const maximize = false; const maxButton = this.__maxButton = osparc.widget.PersistentIframe.createToolbarButton(maximize).set({ + maxHeight: 25, label: osparc.widget.PersistentIframe.getZoomLabel(maximize), icon: osparc.widget.PersistentIframe.getZoomIcon(maximize), - visibility: showMaximizeButton ? "visible" : "excluded" }); osparc.utils.Utils.setIdToWidget(maxButton, osparc.widget.PersistentIframe.getMaximizeWidgetId(maximize)); maxButton.addListener("execute", () => this.maximizeIFrame(!this.hasState("maximized")), this); - const maximizeLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox()).set({ - maxWidth: 100 - }); - maximizeLayout.add(maxButton); - maximizeLayout.add(new qx.ui.core.Widget(), { - flex: 1 + const toolbarLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox().set({ + alignX: "right", + })); + this.bind("showToolbar", toolbarLayout, "visibility", { + converter: showToolbar => showToolbar ? "visible" : "hidden" }); - this._add(maximizeLayout); + toolbarLayout.add(maxButton); + return toolbarLayout; }, __applyLogo: function(newLogo) { @@ -264,23 +291,5 @@ qx.Class.define("osparc.ui.message.Loading", { addExtraWidget: function(widget) { this.__extraWidgets.add(widget); }, - - // from osparc.widget.PersistentIframe - maximizeIFrame: function(maximize) { - if (maximize) { - this.fireEvent("maximize"); - this.addState("maximized"); - } else { - this.fireEvent("restore"); - this.removeState("maximized"); - } - const maxButton = this.__maxButton; - maxButton.set({ - label: osparc.widget.PersistentIframe.getZoomLabel(maximize), - icon: osparc.widget.PersistentIframe.getZoomIcon(maximize) - }); - osparc.utils.Utils.setIdToWidget(maxButton, osparc.widget.PersistentIframe.getMaximizeWidgetId(maximize)); - qx.event.message.Bus.getInstance().dispatchByName("maximizeIframe", this.hasState("maximized")); - } } }); diff --git a/services/static-webserver/client/source/class/osparc/utils/Utils.js b/services/static-webserver/client/source/class/osparc/utils/Utils.js index 6f8bcec17f6..54647719ad9 100644 --- a/services/static-webserver/client/source/class/osparc/utils/Utils.js +++ b/services/static-webserver/client/source/class/osparc/utils/Utils.js @@ -253,19 +253,12 @@ qx.Class.define("osparc.utils.Utils", { }, prettifyMenu: function(menu) { - menu.set({ - font: "text-14", - padding: 4 - }); + menu.setAppearance("menu-wider"); menu.getChildren().forEach(menuItem => { if (menuItem.classname !== "qx.ui.menu.Separator") { menuItem.setPadding(4); } }); - - menu.getContentElement().setStyles({ - "border-radius": "4px" - }); }, hardRefresh: function() { diff --git a/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js b/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js index f5f48cf30d8..96ecc5c505d 100644 --- a/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js +++ b/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js @@ -68,6 +68,7 @@ qx.Class.define("osparc.widget.PersistentIframe", { showToolbar: { check: "Boolean", init: true, + event: "changeShowToolbar", apply: "__applyShowToolbar" } }, @@ -84,14 +85,14 @@ qx.Class.define("osparc.widget.PersistentIframe", { members: { __iframe: null, __syncScheduled: null, - __buttonContainer: null, + __buttonsContainer: null, __diskUsageIndicator: null, __reloadButton: null, __zoomButton: null, // override _createContentElement : function() { - let iframe = this.__iframe = new qx.ui.embed.Iframe(this.getSource()); + const iframe = this.__iframe = new qx.ui.embed.Iframe(this.getSource()); const persistentIframe = this; iframe.addListener("load", () => { const currentTheme = qx.theme.manager.Meta.getInstance().getTheme(); @@ -111,7 +112,7 @@ qx.Class.define("osparc.widget.PersistentIframe", { const host = window.location.host; iframeEl.setAttribute("allow", `clipboard-read; clipboard-write; from *.services.${host}`); - const buttonContainer = this.__buttonContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ + const buttonsContainer = this.__buttonsContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ alignX: "right", alignY: "middle" })); @@ -120,7 +121,7 @@ qx.Class.define("osparc.widget.PersistentIframe", { diskUsageIndicator.getChildControl("disk-indicator").set({ margin: 0 }); - buttonContainer.add(diskUsageIndicator); + buttonsContainer.add(diskUsageIndicator); const reloadButton = this.__reloadButton = this.self().createToolbarButton().set({ label: this.tr("Reload"), @@ -132,7 +133,7 @@ qx.Class.define("osparc.widget.PersistentIframe", { this.fireEvent("restart"); }, this); osparc.utils.Utils.setIdToWidget(reloadButton, "iFrameRestartBtn"); - buttonContainer.add(reloadButton); + buttonsContainer.add(reloadButton); const zoomButton = this.__zoomButton = this.self().createToolbarButton().set({ label: this.self().getZoomLabel(false), @@ -142,9 +143,9 @@ qx.Class.define("osparc.widget.PersistentIframe", { zoomButton.addListener("execute", e => { this.maximizeIFrame(!this.hasState("maximized")); }, this); - buttonContainer.add(zoomButton); + buttonsContainer.add(zoomButton); - appRoot.add(buttonContainer, { + appRoot.add(buttonsContainer, { top: this.self().HIDDEN_TOP }); standin.addListener("appear", e => { @@ -154,10 +155,11 @@ qx.Class.define("osparc.widget.PersistentIframe", { iframe.setLayoutProperties({ top: this.self().HIDDEN_TOP }); - buttonContainer.setLayoutProperties({ + buttonsContainer.setLayoutProperties({ top: this.self().HIDDEN_TOP }); }); + this.addListener("move", e => { // got to let the new layout render first or we don't see it this.__syncIframePos(); @@ -227,12 +229,12 @@ qx.Class.define("osparc.widget.PersistentIframe", { height: divSize.height - this.getToolbarHeight() }); - this.__buttonContainer.setLayoutProperties({ + this.__buttonsContainer.setLayoutProperties({ top: (divPos.top - iframeParentPos.top), right: (iframeParentPos.right - iframeParentPos.left - divPos.right) }); - this.__buttonContainer.setVisibility(this.isShowToolbar() ? "visible" : "excluded"); + this.__buttonsContainer.setVisibility(this.isShowToolbar() ? "visible" : "excluded"); }, 0); }, @@ -297,12 +299,12 @@ qx.Class.define("osparc.widget.PersistentIframe", { }, __handleIframeMessage: function(data, nodeId) { - if (data["type"] && data["message"]) { + if (data["type"]) { switch (data["type"]) { case "theme": { // switch theme driven by the iframe const message = data["message"]; - if (message.includes("osparc;theme=")) { + if (message && message.includes("osparc;theme=")) { const themeName = message.replace("osparc;theme=", ""); const validThemes = osparc.ui.switch.ThemeSwitcher.getValidThemes(); const themeFound = validThemes.find(theme => theme.basename === themeName); @@ -314,8 +316,16 @@ qx.Class.define("osparc.widget.PersistentIframe", { break; } case "openMarket": { - const category = data["message"] && data["message"]["category"]; - osparc.vipMarket.MarketWindow.openWindow(nodeId, category); + if (osparc.product.Utils.showS4LStore()) { + const category = data["message"] && data["message"]["category"]; + setTimeout(() => osparc.vipMarket.MarketWindow.openWindow(nodeId, category), 100); + } + break; + } + case "openWallets": { + if (osparc.desktop.credits.Utils.areWalletsEnabled()) { + setTimeout(() => osparc.desktop.credits.BillingCenterWindow.openWindow(), 100); + } break; } } diff --git a/services/static-webserver/client/source/resource/osparc/new_studies.json b/services/static-webserver/client/source/resource/osparc/new_studies.json index ed29e1145d5..7770b61dce2 100644 --- a/services/static-webserver/client/source/resource/osparc/new_studies.json +++ b/services/static-webserver/client/source/resource/osparc/new_studies.json @@ -148,7 +148,7 @@ "resourceType": "study", "icon": "@FontAwesome5Solid/file/18", "title": "Empty Pipeline", - "newStudyLabel": "New Project", + "newStudyLabel": "New Pipeline", "idToWidget": "emptyStudyBtn" }] }, @@ -185,7 +185,7 @@ "resourceType": "study", "icon": "@FontAwesome5Solid/file/18", "title": "Empty Pipeline", - "newStudyLabel": "New Project", + "newStudyLabel": "New Pipeline", "idToWidget": "emptyStudyBtn" }] },