diff --git a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js index 77595025bc45..b40fd6fc1bef 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js @@ -38,8 +38,9 @@ qx.Class.define("osparc.dashboard.Dashboard", { construct: function() { this.base(arguments); - osparc.utils.Utils.setIdToWidget(this.getChildControl("bar"), "dashboardTabs"); - osparc.utils.Utils.setIdToWidget(this, "dashboard"); + this.getChildControl("bar").set({ + visibility: "excluded", + }); this.set({ contentPadding: this.self().PADDING, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/NewPlusButton.js b/services/static-webserver/client/source/class/osparc/dashboard/NewPlusButton.js new file mode 100644 index 000000000000..125844d2b1cc --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/NewPlusButton.js @@ -0,0 +1,39 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.NewPlusButton", { + extend: qx.ui.form.MenuButton, + + construct: function() { + this.base(arguments); + + this.set({ + appearance: "strong-button", + icon: osparc.dashboard.CardBase.NEW_ICON + "20", + label: this.tr("New"), + font: "text-16", + gap: 15, + padding: 15, + paddingRight: 20, + allowGrowX: false, + }); + + osparc.utils.Utils.setIdToWidget(this, "newPlusBtn"); + + this.setMenu(new osparc.dashboard.NewPlusMenu()); + }, +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/NewPlusMenu.js b/services/static-webserver/client/source/class/osparc/dashboard/NewPlusMenu.js new file mode 100644 index 000000000000..fb21a72cc616 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/NewPlusMenu.js @@ -0,0 +1,302 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.NewPlusMenu", { + extend: qx.ui.menu.Menu, + + construct: function() { + this.base(arguments); + + osparc.utils.Utils.prettifyMenu(this); + + this.set({ + position: "bottom-left", + spacingX: 20, + }); + + this.__categoryHeaders = []; + + this.__addItems(); + }, + + events: { + "createFolder": "qx.event.type.Data", + "newEmptyStudyClicked": "qx.event.type.Data", + "newStudyFromTemplateClicked": "qx.event.type.Data", + "newStudyFromServiceClicked": "qx.event.type.Data", + }, + + statics: { + createMenuButton: function(icon, title, infoText) { + title = osparc.utils.Utils.replaceTokens( + title, + "replace_me_product_name", + osparc.store.StaticInfo.getInstance().getDisplayName() + ); + const menuButton = new qx.ui.menu.Button().set({ + icon: icon || null, + label: title, + font: "text-16", + allowGrowX: true, + }); + menuButton.getChildControl("icon").set({ + alignX: "center", + }); + menuButton.getChildControl("label").set({ + rich: true, + marginRight: 20, + }); + if (infoText) { + infoText = osparc.utils.Utils.replaceTokens( + title, + "replace_me_product_name", + osparc.store.StaticInfo.getInstance().getDisplayName() + ); + const infoHint = new osparc.ui.hint.InfoHint(infoText).set({ + source: osparc.ui.hint.InfoHint.INFO_ICON + "/16", + }); + // where the shortcut is supposed to go + // eslint-disable-next-line no-underscore-dangle + menuButton._add(infoHint, {column: 2}); + } + return menuButton; + }, + + createHeader: function(icon, label, infoText) { + return this.createMenuButton(icon, label, infoText).set({ + anonymous: true, + cursor: "default", + font: "text-14", + textColor: "text-darker", + }); + }, + }, + + members: { + __categoryHeaders: null, + + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "new-folder": + control = this.self().createMenuButton( + osparc.dashboard.CardBase.NEW_ICON + "16", + this.tr("New Folder"), + ); + osparc.utils.Utils.setIdToWidget(control, "newFolderButton"); + control.addListener("tap", () => this.__createNewFolder()); + this.add(control); + break; + } + return control || this.base(arguments, id); + }, + + __addItems: async function() { + this.getChildControl("new-folder"); + this.addSeparator(); + await this.__addNewStudyItems(); + }, + + __addNewStudyItems: async function() { + await Promise.all([ + osparc.store.Products.getInstance().getNewStudyConfig(), + osparc.data.Resources.get("templates") + ]).then(values => { + const newStudiesData = values[0]; + const templates = values[1]; + if (newStudiesData["categories"]) { + this.__addCategories(newStudiesData["categories"]); + } + newStudiesData["resources"].forEach(newStudyData => { + if (newStudyData["showDisabled"]) { + this.__addDisabledButton(newStudyData); + } else if (newStudyData["resourceType"] === "study") { + this.__addEmptyStudyButton(newStudyData); + } else if (newStudyData["resourceType"] === "template") { + this.__addFromTemplateButton(newStudyData, templates); + } else if (newStudyData["resourceType"] === "service") { + this.__addFromServiceButton(newStudyData); + } + }); + }); + }, + + __getLastIdxFromCategory: function(categoryId) { + for (let i=this.getChildren().length-1; i>=0; i--) { + const child = this.getChildren()[i]; + if (child && child["categoryId"] && child["categoryId"] === categoryId) { + return i; + } + } + return null; + }, + + __addCategories: function(categories) { + categories.forEach(category => { + const categoryHeader = this.self().createHeader(null, category["title"], category["description"]); + categoryHeader["categoryId"] = category["id"]; + if (this.__categoryHeaders.length) { + // add spacing between categories + categoryHeader.setMarginTop(10); + } + this.__categoryHeaders.push(categoryHeader); + this.add(categoryHeader); + }); + }, + + __addIcon: function(menuButton, resourceInfo, resourceMetadata) { + let source = null; + if (resourceInfo && "icon" in resourceInfo) { + // first the one set in the new_studies + source = resourceInfo["icon"]; + } else if (resourceMetadata && "thumbnail" in resourceMetadata) { + // second the one from the resource + source = resourceMetadata["thumbnail"]; + } + + if (source) { + const thumbnail = new osparc.ui.basic.Thumbnail(source, 24, 24).set({ + minHeight: 24, + minWidth: 24, + }); + thumbnail.getChildControl("image").set({ + anonymous: true, + decorator: "rounded", + }); + // eslint-disable-next-line no-underscore-dangle + menuButton._add(thumbnail, {column: 0}); + } + }, + + __addFromResourceButton: function(menuButton, category) { + let idx = null; + if (category) { + idx = this.__getLastIdxFromCategory(category); + } + if (idx) { + menuButton["categoryId"] = category; + this.addAt(menuButton, idx+1); + } else { + this.add(menuButton); + } + }, + + __addDisabledButton: function(newStudyData) { + const menuButton = this.self().createMenuButton(null, newStudyData.title, newStudyData.reason); + osparc.utils.Utils.setIdToWidget(menuButton, newStudyData.idToWidget); + menuButton.setEnabled(false); + + this.__addIcon(menuButton, newStudyData); + this.__addFromResourceButton(menuButton, newStudyData.category); + }, + + __addEmptyStudyButton: function(newStudyData) { + const menuButton = this.self().createMenuButton(null, newStudyData.title); + osparc.utils.Utils.setIdToWidget(menuButton, newStudyData.idToWidget); + + menuButton.addListener("tap", () => { + this.fireDataEvent("newEmptyStudyClicked", { + newStudyLabel: newStudyData.newStudyLabel, + }); + }); + + this.__addIcon(menuButton, newStudyData); + this.__addFromResourceButton(menuButton, newStudyData.category); + }, + + __addFromTemplateButton: function(newStudyData, templates) { + const menuButton = this.self().createMenuButton(null, newStudyData.title); + osparc.utils.Utils.setIdToWidget(menuButton, newStudyData.idToWidget); + // disable it until found in templates store + menuButton.setEnabled(false); + + let templateMetadata = templates.find(t => t.name === newStudyData.expectedTemplateLabel); + if (templateMetadata) { + menuButton.setEnabled(true); + menuButton.addListener("tap", () => { + this.fireDataEvent("newStudyFromTemplateClicked", { + templateData: templateMetadata, + newStudyLabel: newStudyData.newStudyLabel, + }); + }); + this.__addIcon(menuButton, newStudyData, templateMetadata); + this.__addFromResourceButton(menuButton, newStudyData.category); + } + }, + + __addFromServiceButton: function(newStudyData) { + const menuButton = this.self().createMenuButton(null, newStudyData.title); + osparc.utils.Utils.setIdToWidget(menuButton, newStudyData.idToWidget); + // disable it until found in services store + menuButton.setEnabled(false); + + const key = newStudyData.expectedKey; + // Include deprecated versions, they should all be updatable to a non deprecated version + const versions = osparc.service.Utils.getVersions(key, false); + if (versions.length && newStudyData) { + // scale to latest compatible + const latestVersion = versions[0]; + const latestCompatible = osparc.service.Utils.getLatestCompatible(key, latestVersion); + osparc.store.Services.getService(latestCompatible["key"], latestCompatible["version"]) + .then(latestMetadata => { + // make sure this one is not deprecated + if (osparc.service.Utils.isDeprecated(latestMetadata)) { + return; + } + menuButton.setEnabled(true); + menuButton.addListener("tap", () => { + this.fireDataEvent("newStudyFromServiceClicked", { + serviceMetadata: latestMetadata, + newStudyLabel: newStudyData.newStudyLabel, + }); + }); + + const cb = e => { + this.hide(); + // so that is not consumed by the menu button itself + e.stopPropagation(); + latestMetadata["resourceType"] = "service"; + const resourceDetails = new osparc.dashboard.ResourceDetails(latestMetadata); + osparc.dashboard.ResourceDetails.popUpInWindow(resourceDetails); + } + const infoButton = new osparc.ui.basic.IconButton(osparc.ui.hint.InfoHint.INFO_ICON + "/16", cb); + // where the shortcut is supposed to go + // eslint-disable-next-line no-underscore-dangle + menuButton._add(infoButton, {column: 2}); + + this.__addIcon(menuButton, newStudyData, latestMetadata); + this.__addFromResourceButton(menuButton, newStudyData.category); + }) + } + }, + + __createNewFolder: function() { + const newFolder = true; + const folderEditor = new osparc.editor.FolderEditor(newFolder); + const title = this.tr("New Folder"); + const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 120); + folderEditor.addListener("createFolder", () => { + const name = folderEditor.getLabel(); + this.fireDataEvent("createFolder", { + name, + }); + win.close(); + }); + folderEditor.addListener("cancel", () => win.close()); + }, + }, +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js b/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js index 4f1c9c9d736b..a4c961b82fc7 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js @@ -82,7 +82,9 @@ qx.Class.define("osparc.dashboard.NewStudies", { this._add(noGroupContainer); Array.from(this.__groups).forEach(group => { - const groupContainer = this.__createGroupContainer(group.id, group.label, "transparent"); + let headerLabel = group.title; + headerLabel += "description" in group ? (". " + group["description"]) : ""; + const groupContainer = this.__createGroupContainer(group.id, headerLabel, "transparent"); this._add(groupContainer); }); } else { @@ -106,7 +108,18 @@ qx.Class.define("osparc.dashboard.NewStudies", { this.__newStudies.forEach(resourceData => { const cards = this.__resourceToCards(resourceData); cards.forEach(newCard => { - newCard.setEnabled(!(resourceData.showDisabled)); + if (resourceData.showDisabled) { + newCard.setEnabled(false); + if (resourceData.reason) { + const reason = osparc.utils.Utils.replaceTokens( + resourceData.reason, + "replace_me_product_name", + osparc.store.StaticInfo.getInstance().getDisplayName() + ); + const descLabel = newCard.getChildControl("subtitle-text"); + descLabel.setValue(reason.toString()); + } + } newCards.push(newCard); }); }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index 8c3cfd236370..049b85416891 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -42,10 +42,10 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { const mainLayoutWithSideSpacers = new qx.ui.container.Composite(new qx.ui.layout.HBox(spacing)) this._addToMainLayout(mainLayoutWithSideSpacers); - this.__leftFilters = new qx.ui.container.Composite(new qx.ui.layout.VBox(15)).set({ + this._leftFilters = new qx.ui.container.Composite(new qx.ui.layout.VBox(15)).set({ width: leftColumnWidth }); - mainLayoutWithSideSpacers.add(this.__leftFilters); + mainLayoutWithSideSpacers.add(this._leftFilters); this.__centerLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(15)); mainLayoutWithSideSpacers.add(this.__centerLayout); @@ -95,7 +95,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { return isLogged; }, - startStudyById: function(studyId, openCB, cancelCB, showStudyOptions = false) { + startStudyById: function(studyId, openCB, cancelCB, isStudyCreation = false) { if (!osparc.dashboard.ResourceBrowserBase.checkLoggedIn()) { return; } @@ -117,12 +117,15 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { osparc.data.Resources.fetch("studies", "getWallet", params) .then(wallet => { if ( - showStudyOptions || + isStudyCreation || wallet === null || osparc.desktop.credits.Utils.getWallet(wallet["walletId"]) === null ) { // pop up study options if the study was just created or if it has no wallet assigned or user has no access to it const resourceSelector = new osparc.study.StudyOptions(studyId); + if (isStudyCreation) { + resourceSelector.getChildControl("open-button").setLabel(this.tr("New")); + } const win = osparc.study.StudyOptions.popUpInWindow(resourceSelector); win.moveItUp(); resourceSelector.addListener("startStudy", () => { @@ -190,7 +193,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }, members: { - __leftFilters: null, + _leftFilters: null, _resourceFilter: null, __centerLayout: null, _resourceType: null, @@ -395,11 +398,16 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { _addResourceFilter: function() { const resourceFilter = this._resourceFilter = new osparc.dashboard.ResourceFilter(this._resourceType).set({ - marginTop: osparc.dashboard.SearchBarFilter.HEIGHT + 10, + marginTop: 20, maxWidth: this.self().SIDE_SPACER_WIDTH, width: this.self().SIDE_SPACER_WIDTH }); + resourceFilter.addListener("changeTab", e => { + const contextTab = e.getData(); + this.fireDataEvent("changeTab", contextTab); + }, this); + resourceFilter.addListener("changeSharedWith", e => { const sharedWith = e.getData(); this._searchBarFilter.setSharedWithActiveFilter(sharedWith.id, sharedWith.label); @@ -420,7 +428,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourceFilter.filterChanged(filterData); }); - this.__leftFilters.add(resourceFilter, { + this._leftFilters.add(resourceFilter, { flex: 1 }); }, 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 5cd54f4b231d..5f6bb97a02e5 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -126,7 +126,9 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { toolbar.add(serviceVersionSelector); } - const openButton = new osparc.ui.form.FetchButton(this.tr("Open")).set({ + const studyAlias = osparc.product.Utils.getStudyAlias({firstUpperCase: true}); + const openText = (this.__resourceData["resourceType"] === "study") ? this.tr("Open") : this.tr("New") + " " + studyAlias; + const openButton = new osparc.ui.form.FetchButton(openText).set({ enabled: true }); page.openButton = openButton; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js index c9aa77b74eb3..2b35fcad22d0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js @@ -29,12 +29,13 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { this.__tagButtons = []; this.__serviceTypeButtons = []; - this._setLayout(new qx.ui.layout.VBox(20)); + this._setLayout(new qx.ui.layout.VBox(15)); this.__buildLayout(); }, events: { "trashContext": "qx.event.type.Event", + "changeTab": "qx.event.type.Data", "trashStudyRequested": "qx.event.type.Data", "trashFolderRequested": "qx.event.type.Data", "changeSharedWith": "qx.event.type.Data", @@ -51,19 +52,27 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { __serviceTypeButtons: null, __buildLayout: function() { - if (this.__resourceType === "study") { - this._add(this.__createWorkspacesAndFoldersTree()); - this._add(this.__createTrashBin()); - } else { - this._add(this.__createSharedWithFilterLayout()); - } - - if (this.__resourceType !== "service") { - this._add(this.__createTagsFilterLayout()); - } - - if (this.__resourceType === "service") { - this._add(this.__createServiceTypeFilterLayout()); + const filtersSpacer = new qx.ui.core.Spacer(10, 10); + switch (this.__resourceType) { + case "study": + this._add(this.__createWorkspacesAndFoldersTree()); + this._add(this.__createTrashBin()); + this._add(this.__createResourceTypeContextButtons()); + this._add(filtersSpacer); + this._add(this.__createTagsFilterLayout()); + break; + case "template": + this._add(this.__createResourceTypeContextButtons()); + this._add(filtersSpacer); + this._add(this.__createSharedWithFilterLayout()); + this._add(this.__createTagsFilterLayout()); + break; + case "service": + this._add(this.__createResourceTypeContextButtons()); + this._add(filtersSpacer); + this._add(this.__createSharedWithFilterLayout()); + this._add(this.__createServiceTypeFilterLayout()); + break; } }, @@ -83,14 +92,14 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { osparc.utils.Utils.setIdToWidget(workspacesAndFoldersTree, "contextTree"); // Height needs to be calculated manually to make it flexible workspacesAndFoldersTree.set({ - minHeight: 60, + minHeight: 60, // two entries maxHeight: 400, height: 60, }); workspacesAndFoldersTree.addListener("openChanged", () => { const rowConfig = workspacesAndFoldersTree.getPane().getRowConfig(); const totalHeight = rowConfig.itemCount * rowConfig.defaultItemSize; - workspacesAndFoldersTree.setHeight(totalHeight + 10); + workspacesAndFoldersTree.setHeight(totalHeight + 2); }); return workspacesAndFoldersTree; }, @@ -203,9 +212,94 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { }, /* /TRASH BIN */ + /* RESOURCE TYPE CONTEXT */ + __createResourceTypeContextButtons: function() { + const resourceTypeContextButtons = new qx.ui.container.Composite(new qx.ui.layout.VBox(2)); + + const studiesButton = this.__createStudiesButton().set({ + value: this.__resourceType === "study", + visibility: this.__resourceType === "study" ? "excluded" : "visible", + }); + resourceTypeContextButtons.add(studiesButton); + + const permissions = osparc.data.Permissions.getInstance(); + const templatesButton = this.__createTemplatesButton().set({ + value: this.__resourceType === "template", + }); + if (permissions.canDo("dashboard.templates.read")) { + resourceTypeContextButtons.add(templatesButton); + } + + const servicesButton = this.__createServicesButton().set({ + value: this.__resourceType === "service", + }); + if (permissions.canDo("dashboard.services.read")) { + resourceTypeContextButtons.add(servicesButton); + } + + return resourceTypeContextButtons; + }, + + __createStudiesButton: function() { + const studyAlias = osparc.product.Utils.getStudyAlias({ + firstUpperCase: true, + plural: true + }); + const studiesButton = new qx.ui.toolbar.RadioButton().set({ + value: false, + appearance: "filter-toggle-button", + label: studyAlias, + icon: "@FontAwesome5Solid/file/16", + paddingLeft: 10, // align it with the context + }); + osparc.utils.Utils.setIdToWidget(studiesButton, "studiesTabBtn"); + studiesButton.addListener("tap", () => { + studiesButton.setValue(this.__resourceType === "study"); + this.fireDataEvent("changeTab", "studiesTab"); + }); + return studiesButton; + }, + + __createTemplatesButton: function() { + const templateAlias = osparc.product.Utils.getTemplateAlias({ + firstUpperCase: true, + plural: true + }); + const templatesButton = new qx.ui.toolbar.RadioButton().set({ + value: false, + appearance: "filter-toggle-button", + label: templateAlias, + icon: "@FontAwesome5Solid/copy/16", + paddingLeft: 10, // align it with the context + }); + osparc.utils.Utils.setIdToWidget(templatesButton, "templatesTabBtn"); + templatesButton.addListener("tap", () => { + templatesButton.setValue(this.__resourceType === "template"); + this.fireDataEvent("changeTab", "templatesTab"); + }); + return templatesButton; + }, + + __createServicesButton: function() { + const servicesButton = new qx.ui.toolbar.RadioButton().set({ + value: false, + appearance: "filter-toggle-button", + label: this.tr("Services"), + icon: "@FontAwesome5Solid/cogs/16", + paddingLeft: 10, // align it with the context + }); + osparc.utils.Utils.setIdToWidget(servicesButton, "servicesTabBtn"); + servicesButton.addListener("tap", () => { + servicesButton.setValue(this.__resourceType === "service"); + this.fireDataEvent("changeTab", "servicesTab"); + }); + return servicesButton; + }, + /* /RESOURCE TYPE CONTEXT */ + /* SHARED WITH */ __createSharedWithFilterLayout: function() { - const sharedWithLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); + const sharedWithLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(2)); const sharedWithRadioGroup = new qx.ui.form.RadioGroup(); sharedWithRadioGroup.setAllowEmptySelection(false); @@ -338,7 +432,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { /* SERVICE TYPE */ __createServiceTypeFilterLayout: function() { - const layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); + const layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(2)); const radioGroup = new qx.ui.form.RadioGroup(); radioGroup.setAllowEmptySelection(true); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js index 7ae65ff0bd1e..fec523a23413 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js @@ -103,7 +103,9 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { return; } - this._showLoadingPage(this.tr("Creating ") + osparc.product.Utils.getStudyAlias()); + const studyAlias = osparc.product.Utils.getStudyAlias({firstUpperCase: true}); + this._showLoadingPage(this.tr("Creating ") + studyAlias); + osparc.study.Utils.createStudyFromService(key, version) .then(studyId => { const openCB = () => this._hideLoadingPage(); 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 1b4c523639ef..9c04ac3ef7d6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -251,7 +251,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }) .catch(console.error) .finally(() => { - this.__addNewFolderButton(); + // In favor of the NewPlusButton + if (!osparc.product.Utils.hasNewPlusButton()) { + this.__addNewFolderButton(); + } this.__loadingFolders = null; }); }, @@ -882,6 +885,45 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } }, + __addNewPlusButton: function() { + const newPlusButton = new osparc.dashboard.NewPlusButton(); + this._leftFilters.add(newPlusButton); + + const newPlusButtonMenu = newPlusButton.getMenu(); + + newPlusButtonMenu.addListener("createFolder", e => { + const data = e.getData(); + this.__createFolder(data); + }, this); + + newPlusButtonMenu.addListener("newEmptyStudyClicked", e => { + const { + newStudyLabel, + } = e.getData(); + this.__newEmptyStudyBtnClicked(newStudyLabel); + }, this); + + newPlusButtonMenu.addListener("newStudyFromTemplateClicked", e => { + const { + templateData, + newStudyLabel, + } = e.getData(); + if (templateData) { + this.__newPlanBtnClicked(templateData, newStudyLabel); + } + }, this); + + newPlusButtonMenu.addListener("newStudyFromServiceClicked", e => { + const { + serviceMetadata, + newStudyLabel, + } = e.getData(); + if (serviceMetadata) { + this.__newStudyFromServiceBtnClicked(serviceMetadata["key"], serviceMetadata["version"], newStudyLabel); + } + }, this); + }, + __addNewStudyButtons: function() { if (this.getCurrentContext() !== "studiesAndFolders") { return; @@ -895,19 +937,21 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } } - switch (osparc.product.Utils.getProductName()) { - case "osparc": - this.__addEmptyStudyPlusButton(); - break; - case "tis": - case "tiplite": - this.__addTIPPlusButton(); - break; - case "s4l": - case "s4lacad": - case "s4llite": - this.__addPlusButtonsFromServices(); - break; + if (!osparc.product.Utils.hasNewPlusButton()) { + switch (osparc.product.Utils.getProductName()) { + case "osparc": + this.__addEmptyStudyPlusButton(); + break; + case "tis": + case "tiplite": + this.__addTIPPlusButton(); + break; + case "s4l": + case "s4lacad": + case "s4llite": + this.__addPlusButtonsFromServices(); + break; + } } }, @@ -917,31 +961,31 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { firstUpperCase: true }) const desc = this.tr("Start with an empty study"); - const newStudyBtn = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title, desc) : new osparc.dashboard.ListButtonNew(title, desc); - newStudyBtn.setCardKey("new-study"); - newStudyBtn.subscribeToFilterGroup("searchBarFilter"); - osparc.utils.Utils.setIdToWidget(newStudyBtn, "newStudyBtn"); - newStudyBtn.addListener("tap", () => this.__newStudyBtnClicked(newStudyBtn)); - this._resourcesContainer.addNonResourceCard(newStudyBtn); + const newEmptyStudyBtn = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title, desc) : new osparc.dashboard.ListButtonNew(title, desc); + newEmptyStudyBtn.setCardKey("new-study"); + newEmptyStudyBtn.subscribeToFilterGroup("searchBarFilter"); + osparc.utils.Utils.setIdToWidget(newEmptyStudyBtn, "emptyStudyBtn"); + newEmptyStudyBtn.addListener("tap", () => this.__newEmptyStudyBtnClicked("New Study")); + this._resourcesContainer.addNonResourceCard(newEmptyStudyBtn); }, __addTIPPlusButton: function() { const mode = this._resourcesContainer.getMode(); const title = this.tr("New Plan"); - const newStudyBtn = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title) : new osparc.dashboard.ListButtonNew(title); - newStudyBtn.setCardKey("new-study"); - newStudyBtn.subscribeToFilterGroup("searchBarFilter"); - osparc.utils.Utils.setIdToWidget(newStudyBtn, "newStudyBtn"); - this._resourcesContainer.addNonResourceCard(newStudyBtn); - newStudyBtn.setEnabled(false); + const newPlansBtn = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title) : new osparc.dashboard.ListButtonNew(title); + newPlansBtn.setCardKey("new-study"); + newPlansBtn.subscribeToFilterGroup("searchBarFilter"); + osparc.utils.Utils.setIdToWidget(newPlansBtn, "newPlansBtn"); + this._resourcesContainer.addNonResourceCard(newPlansBtn); + newPlansBtn.setEnabled(false); osparc.utils.Utils.fetchJSON("/resource/osparc/new_studies.json") .then(newStudiesData => { const product = osparc.product.Utils.getProductName() if (product in newStudiesData) { - newStudyBtn.setEnabled(true); + newPlansBtn.setEnabled(true); - newStudyBtn.addListener("tap", () => { + newPlansBtn.addListener("tap", () => { osparc.data.Resources.get("templates") .then(templates => { if (templates) { @@ -990,7 +1034,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const newStudyFromServiceButton = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title, desc) : new osparc.dashboard.ListButtonNew(title, desc); newStudyFromServiceButton.setCardKey("new-"+key); osparc.utils.Utils.setIdToWidget(newStudyFromServiceButton, newButtonInfo.idToWidget); - newStudyFromServiceButton.addListener("tap", () => this.__newStudyFromServiceBtnClicked(newStudyFromServiceButton, latestMetadata["key"], latestMetadata["version"], newButtonInfo.newStudyLabel)); + newStudyFromServiceButton.addListener("tap", () => this.__newStudyFromServiceBtnClicked(latestMetadata["key"], latestMetadata["version"], newButtonInfo.newStudyLabel)); this._resourcesContainer.addNonResourceCard(newStudyFromServiceButton); }) } @@ -1044,6 +1088,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__addSortByButton(); this._addViewModeButton(); + if (osparc.product.Utils.hasNewPlusButton()) { + this.__addNewPlusButton(); + } + this._addResourceFilter(); this.__connectContexts(); @@ -1463,10 +1511,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } }, - __newStudyBtnClicked: function(button) { - const minStudyData = osparc.data.model.Study.createMinStudyObject(); + __newEmptyStudyBtnClicked: function(newStudyLabel) { const existingNames = this._resourcesList.map(study => study["name"]); - const title = osparc.utils.Utils.getUniqueName(minStudyData.name, existingNames); + const title = osparc.utils.Utils.getUniqueName(newStudyLabel, existingNames); + const minStudyData = osparc.data.model.Study.createMinStudyObject(); minStudyData["name"] = title; minStudyData["workspaceId"] = this.getCurrentWorkspaceId(); minStudyData["folderId"] = this.getCurrentFolderId(); @@ -1503,7 +1551,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); }, - __newStudyFromServiceBtnClicked: function(button, key, version, newStudyLabel) { + __newStudyFromServiceBtnClicked: function(key, version, newStudyLabel) { this._showLoadingPage(this.tr("Creating ") + osparc.product.Utils.getStudyAlias()); const contextProps = { workspaceId: this.getCurrentWorkspaceId(), diff --git a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js index fab2dc1eb94c..e9443c4ecbbc 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js @@ -144,6 +144,7 @@ qx.Class.define("osparc.dashboard.TemplateBrowser", { // they will be patched once the study is created studyOptions.setPatchStudy(false); studyOptions.setStudyData(templateData); + studyOptions.getChildControl("open-button").setLabel(this.tr("New")); const win = osparc.study.StudyOptions.popUpInWindow(studyOptions); win.moveItUp(); const cancelStudyOptions = () => { diff --git a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js index 39968f8913db..d3eff170ad6e 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js +++ b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js @@ -199,11 +199,6 @@ qx.Class.define("osparc.desktop.MainPage", { __createDashboardLayout: function() { const dashboard = this.__dashboard = new osparc.dashboard.Dashboard(); - const tabsBar = dashboard.getChildControl("bar"); - tabsBar.set({ - paddingBottom: 6 - }); - this.__navBar.addDashboardTabButtons(tabsBar); const dashboardLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)); dashboardLayout.add(dashboard, { flex: 1 diff --git a/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js b/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js index 70ddcf32bf0f..112b43d3637e 100644 --- a/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js +++ b/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js @@ -40,7 +40,7 @@ qx.Class.define("osparc.form.PortInfoHint", { } this.setHintText(text); this.set({ - source: errorMsg ? this.self().ERROR_ICON : osparc.ui.hint.InfoHint.INFO_ICON, + source: errorMsg ? this.self().ERROR_ICON : osparc.ui.hint.InfoHint.INFO_ICON + "/14", textColor: errorMsg ? "failed-red" : "text" }); } 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 f6e2688ae05c..e8d252a5f82c 100644 --- a/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js +++ b/services/static-webserver/client/source/class/osparc/navigation/NavigationBar.js @@ -330,15 +330,6 @@ qx.Class.define("osparc.navigation.NavigationBar", { return registerButton; }, - addDashboardTabButtons: function(tabButtons) { - this.__tabButtons = tabButtons; - this.getChildControl("center-items").add(tabButtons); - this.bind("study", this.__tabButtons, "visibility", { - converter: s => s ? "excluded" : "visible" - }); - this.__navBarResized(); - }, - __applyStudy: function(study) { const readOnlyInfo = this.getChildControl("read-only-info") if (study) { diff --git a/services/static-webserver/client/source/class/osparc/product/Utils.js b/services/static-webserver/client/source/class/osparc/product/Utils.js index 0a78cf5e2f04..4c77b84e0e16 100644 --- a/services/static-webserver/client/source/class/osparc/product/Utils.js +++ b/services/static-webserver/client/source/class/osparc/product/Utils.js @@ -114,23 +114,6 @@ qx.Class.define("osparc.product.Utils", { return resourceType; }, - __linkExists: function(url) { - return new Promise((resolve, reject) => { - const reqSvg = new XMLHttpRequest(); - reqSvg.open("GET", url, true); - reqSvg.onreadystatechange = () => { - if (reqSvg.readyState === 4) { - if (reqSvg.status === 404) { - reject(); - } else { - resolve(); - } - } - }; - reqSvg.send(); - }); - }, - getLogoPath: function(longLogo = true) { let logosPath = null; const colorManager = qx.theme.manager.Color.getInstance(); @@ -307,6 +290,17 @@ qx.Class.define("osparc.product.Utils", { break; } return url; - } + }, + + hasNewPlusButton: function() { + return [ + "osparc", + "s4l", + "s4lacad", + "s4llite", + // "tis", + // "tiplite", + ].includes(osparc.product.Utils.getProductName()); + }, } }); diff --git a/services/static-webserver/client/source/class/osparc/store/Products.js b/services/static-webserver/client/source/class/osparc/store/Products.js new file mode 100644 index 000000000000..4728bded6093 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/store/Products.js @@ -0,0 +1,48 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.store.Products", { + extend: qx.core.Object, + type: "singleton", + + members: { + __newStudyConfig: null, + + fetchNewStudyConfig: function() { + return osparc.utils.Utils.fetchJSON("/resource/osparc/new_studies.json") + .then(newStudiesData => { + const product = osparc.product.Utils.getProductName() + if (product in newStudiesData) { + this.__newStudyConfig = newStudiesData[product]; + return this.__newStudyConfig; + } + return {}; + }) + .catch(console.error); + }, + + getNewStudyConfig: function() { + return new Promise(resolve => { + if (this.__newStudyConfig) { + resolve(this.__newStudyConfig); + } else { + resolve(this.fetchNewStudyConfig()) + } + }); + }, + } +}); diff --git a/services/static-webserver/client/source/class/osparc/study/StudyOptions.js b/services/static-webserver/client/source/class/osparc/study/StudyOptions.js index 70576fa6c799..1ed9a4cd961d 100644 --- a/services/static-webserver/client/source/class/osparc/study/StudyOptions.js +++ b/services/static-webserver/client/source/class/osparc/study/StudyOptions.js @@ -277,6 +277,7 @@ qx.Class.define("osparc.study.StudyOptions", { walletSelector.setSelection([selectable]); } }); + osparc.utils.Utils.growSelectBox(walletSelector, 220); } }, 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 82b0c9b56661..8af7db982cb1 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Appearance.js +++ b/services/static-webserver/client/source/class/osparc/theme/Appearance.js @@ -722,13 +722,14 @@ qx.Theme.define("osparc.theme.Appearance", { "menu-button": { alias: "atom", - style: function(states) { + style: states => { return { + decorator: "rounded", cursor: states.disabled ? "not-allowed" : "pointer", - backgroundColor: states.selected ? "background-selected-dark" : undefined, + backgroundColor: states.selected || states.hovered ? "pb-new" : undefined, textColor: states.selected ? "default-button-text" : "text", - padding: [2, 6] - }; + padding: [4, 8] + } } }, diff --git a/services/static-webserver/client/source/class/osparc/ui/hint/InfoHint.js b/services/static-webserver/client/source/class/osparc/ui/hint/InfoHint.js index 050e4a6ef570..5ec7e00ca84f 100644 --- a/services/static-webserver/client/source/class/osparc/ui/hint/InfoHint.js +++ b/services/static-webserver/client/source/class/osparc/ui/hint/InfoHint.js @@ -25,7 +25,7 @@ qx.Class.define("osparc.ui.hint.InfoHint", { * @extends osparc.ui.basic.IconButton */ construct: function(hintText) { - this.base(arguments, this.self().INFO_ICON); + this.base(arguments, this.self().INFO_ICON + "/14"); this.__createHint(); @@ -39,7 +39,7 @@ qx.Class.define("osparc.ui.hint.InfoHint", { }, statics: { - INFO_ICON: "@MaterialIcons/info_outline/14" + INFO_ICON: "@MaterialIcons/info_outline" }, properties: { 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 6dcdffa2692d..6f8bcec17f63 100644 --- a/services/static-webserver/client/source/class/osparc/utils/Utils.js +++ b/services/static-webserver/client/source/class/osparc/utils/Utils.js @@ -89,7 +89,7 @@ qx.Class.define("osparc.utils.Utils", { } }, - FLOATING_Z_INDEX: 110000, + FLOATING_Z_INDEX: 1000001 + 1, updateTabName: function(name) { document.title = name; @@ -958,10 +958,10 @@ qx.Class.define("osparc.utils.Utils", { }, cookie: { - setCookie: (cname, cvalue, exdays) => { - if (exdays) { + setCookie: (cname, cvalue, expDays) => { + if (expDays) { const d = new Date(); - d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); + d.setTime(d.getTime() + (expDays * 24 * 60 * 60 * 1000)); document.cookie = cname + "=" + cvalue + ";Expires=" + d.toUTCString() + ";path=/"; } else { document.cookie = cname + "=" + cvalue + ";path=/"; diff --git a/services/static-webserver/client/source/class/osparc/widget/logger/LoggerModel.js b/services/static-webserver/client/source/class/osparc/widget/logger/LoggerModel.js index 9cb8b2249de9..edca2766438e 100644 --- a/services/static-webserver/client/source/class/osparc/widget/logger/LoggerModel.js +++ b/services/static-webserver/client/source/class/osparc/widget/logger/LoggerModel.js @@ -113,7 +113,12 @@ qx.Class.define("osparc.widget.logger.LoggerModel", { newRow["level"] = this.self().getLevelIcon(newRow.logLevel); newRow["time"] = osparc.utils.Utils.formatTime(newRow.timeStamp, true); newRow["who"] = newRow.label; - newRow["msgRich"] = newRow.msg.replace(/\n/g, "
"); + + // there might a double backslash before the n + let clean = newRow.msg.replace(/\\n/g, "
"); + clean = newRow.msg.replace(/\n/g, "
"); + newRow["msgRich"] = clean; + this.__rawData.push(newRow); }); }, 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 a13a042051f6..ed29e1145d51 100644 --- a/services/static-webserver/client/source/resource/osparc/new_studies.json +++ b/services/static-webserver/client/source/resource/osparc/new_studies.json @@ -1,149 +1,210 @@ { "tis": { - "linkedResource": "templates", + "categories": [{ + "id": "precomputed", + "title": "Precomputed" + }, { + "id": "personalized", + "title": "Personalized", + "description": "In the process, TIP will launch simulations on AWS.
The associated resource costs will be deduced from your Credits." + }], "resources": [{ + "resourceType": "template", "expectedTemplateLabel": "TI Planning Tool", "title": "Classic TI", - "description": "", "newStudyLabel": "Classic TI", "category": "precomputed", "idToWidget": "newTIPlanButton" }, { + "resourceType": "template", "expectedTemplateLabel": "mcTI Planning Tool", "title": "Multichannel TI", - "description": "", "newStudyLabel": "Multichannel TI", "category": "precomputed", "idToWidget": "newMTIPlanButton" }, { + "resourceType": "template", "expectedTemplateLabel": "pmTI Planning Tool", "title": "Phase-modulation TI", - "description": "", "newStudyLabel": "Phase-modulation TI", "category": "precomputed", "idToWidget": "newPMTIPlanButton" }, { + "resourceType": "template", "expectedTemplateLabel": "personalized TI Planning Tool", - "title": "Personalized
Classic TI", - "description": "", + "title": "Personalized Classic TI", "newStudyLabel": "Personalized Classic TI", "category": "personalized", - "idToWidget": "personalizationNewTIPlanButton", - "billable": true + "idToWidget": "personalizationNewTIPlanButton" }, { + "resourceType": "template", "expectedTemplateLabel": "personalized mcTI Planning Tool", - "title": "Personalized
Multichannel TI", - "description": "", + "title": "Personalized Multichannel TI", "newStudyLabel": "Personalized Multichannel TI", "category": "personalized", - "idToWidget": "personalizationNewMTIPlanButton", - "billable": true + "idToWidget": "personalizationNewMTIPlanButton" }, { + "resourceType": "template", "expectedTemplateLabel": "personalized pmTI Planning Tool", - "title": "Personalized
Phase-modulation TI", - "description": "", + "title": "Personalized Phase-modulation TI", "newStudyLabel": "Personalized Phase-modulation TI", "category": "personalized", - "idToWidget": "personalizationNewPMTIPlanButton", - "billable": true - }], + "idToWidget": "personalizationNewPMTIPlanButton" + }] + }, + "tiplite": { "categories": [{ "id": "precomputed", - "label": "Precomputed" + "title": "Precomputed" }, { "id": "personalized", - "label": "Personalized: In the process, TIP will launch simulations on AWS.
The associated resource costs will be deduced from your Credits." - }] - }, - "tiplite": { - "linkedResource": "templates", + "title": "Personalized", + "description": "In the process, TIP will launch simulations on AWS.
The associated resource costs will be deduced from your Credits." + }], "resources": [{ + "resourceType": "template", "expectedTemplateLabel": "TI Planning Tool", "title": "Classic TI", - "description": "", "newStudyLabel": "Classic TI", "category": "precomputed", "idToWidget": "newTIPlanButton" }, { "showDisabled": true, + "reason": "Not available in ${replace_me_product_name}", + "resourceType": "template", "expectedTemplateLabel": "mcTI Planning Tool", "title": "Multichannel TI", - "description": "Not available in ${replace_me_product_name}", "newStudyLabel": "Multichannel TI", "category": "precomputed", "idToWidget": "newMTIPlanButton" }, { "showDisabled": true, + "reason": "Not available in ${replace_me_product_name}", + "resourceType": "template", "expectedTemplateLabel": "pmTI Planning Tool", "title": "Phase-modulation TI", - "description": "Not available in ${replace_me_product_name}", "newStudyLabel": "Phase-modulation TI", "category": "precomputed", "idToWidget": "newPMTIPlanButton" }, { "showDisabled": true, + "reason": "Not available in ${replace_me_product_name}", + "resourceType": "template", "expectedTemplateLabel": "personalized TI Planning Tool", - "title": "Personalized
Classic TI", - "description": "Not available in ${replace_me_product_name}", + "title": "Personalized Classic TI", "newStudyLabel": "Personalized Classic TI", "category": "personalized", - "idToWidget": "personalizationNewTIPlanButton", - "billable": true + "idToWidget": "personalizationNewTIPlanButton" }, { "showDisabled": true, + "reason": "Not available in ${replace_me_product_name}", + "resourceType": "template", "expectedTemplateLabel": "personalized mcTI Planning Tool", - "title": "Personalized
Multichannel TI", - "description": "Not available in ${replace_me_product_name}", + "title": "Personalized Multichannel TI", "newStudyLabel": "Personalized Multichannel TI", "category": "personalized", - "idToWidget": "personalizationNewMTIPlanButton", - "billable": true + "idToWidget": "personalizationNewMTIPlanButton" }, { "showDisabled": true, + "reason": "Not available in ${replace_me_product_name}", + "resourceType": "template", "expectedTemplateLabel": "personalized pmTI Planning Tool", - "title": "Personalized
Phase-modulation TI", - "description": "Not available in ${replace_me_product_name}", + "title": "Personalized Phase-modulation TI", "newStudyLabel": "Personalized Phase-modulation TI", "category": "personalized", - "idToWidget": "personalizationNewPMTIPlanButton", - "billable": true - }], - "categories": [{ - "id": "precomputed", - "label": "Precomputed" - }, { - "id": "personalized", - "label": "Personalized: In the process, TIP will launch simulations on AWS.
The associated resource costs will be deduced from your Credits." + "idToWidget": "personalizationNewPMTIPlanButton" }] }, "s4l": { - "linkedResource": "services", + "categories": [{ + "id": "apps", + "title": "Apps" + }, { + "id": "osparc", + "title": "oSPARC" + }], "resources": [{ + "category": "apps", + "resourceType": "service", "expectedKey": "simcore/services/dynamic/s4l-ui", - "title": "Start Sim4Life", - "description": "New Sim4Life project", + "title": "Sim4Life", "newStudyLabel": "New S4L project", "idToWidget": "startS4LButton" + }, { + "category": "apps", + "resourceType": "service", + "expectedKey": "simcore/services/dynamic/iseg-web", + "title": "iSEG", + "newStudyLabel": "New iSEG project" + }, { + "category": "apps", + "resourceType": "service", + "expectedKey": "simcore/services/dynamic/s4l-jupyter", + "title": "Jupyter Lab", + "icon": "https://upload.wikimedia.org/wikipedia/commons/3/38/Jupyter_logo.svg", + "newStudyLabel": "New S4L Jupyter Lab" + }, { + "category": "osparc", + "resourceType": "study", + "icon": "@FontAwesome5Solid/file/18", + "title": "Empty Pipeline", + "newStudyLabel": "New Project", + "idToWidget": "emptyStudyBtn" }] }, "s4lacad": { - "linkedResource": "services", + "categories": [{ + "id": "apps", + "title": "Apps" + }, { + "id": "osparc", + "title": "oSPARC" + }], "resources": [{ + "category": "apps", + "resourceType": "service", "expectedKey": "simcore/services/dynamic/s4l-ui", - "title": "Start Sim4Life", - "description": "New Sim4Life project", + "title": "Sim4Life", "newStudyLabel": "New S4L project", "idToWidget": "startS4LButton" + }, { + "category": "apps", + "resourceType": "service", + "expectedKey": "simcore/services/dynamic/iseg-web", + "title": "iSEG", + "newStudyLabel": "New iSEG project" + }, { + "category": "apps", + "resourceType": "service", + "expectedKey": "simcore/services/dynamic/s4l-jupyter", + "icon": "https://upload.wikimedia.org/wikipedia/commons/3/38/Jupyter_logo.svg", + "title": "Jupyter Lab", + "newStudyLabel": "New S4L Jupyter Lab" + }, { + "category": "osparc", + "resourceType": "study", + "icon": "@FontAwesome5Solid/file/18", + "title": "Empty Pipeline", + "newStudyLabel": "New Project", + "idToWidget": "emptyStudyBtn" }] }, "s4llite": { - "linkedResource": "services", "resources": [{ + "resourceType": "service", "expectedKey": "simcore/services/dynamic/s4l-ui-lite", - "title": "Start ${replace_me_product_name}", - "description": "New project", - "newStudyLabel": "New project", + "title": "${replace_me_product_name}", + "newStudyLabel": "New Project", "idToWidget": "startS4LButton" }] + }, + "osparc": { + "resources": [{ + "resourceType": "study", + "icon": "@FontAwesome5Solid/file/18", + "title": "Empty Study", + "newStudyLabel": "New Study", + "idToWidget": "emptyStudyBtn" + }] } } diff --git a/tests/e2e-frontend/tests/navigationBar/navigationBar.spec.js b/tests/e2e-frontend/tests/navigationBar/navigationBar.spec.js index 0c981ef8777a..c6bb8752f334 100644 --- a/tests/e2e-frontend/tests/navigationBar/navigationBar.spec.js +++ b/tests/e2e-frontend/tests/navigationBar/navigationBar.spec.js @@ -10,10 +10,6 @@ import users from '../users.json'; const expectedElements = { "osparc": { "poweredByOsparc": false, - "studies": { - "visible": true, - "label": "STUDIES", - }, "templates": { "visible": true, "label": "TEMPLATES", @@ -33,10 +29,6 @@ const expectedElements = { }, "s4l": { "poweredByOsparc": true, - "studies": { - "visible": true, - "label": "PROJECTS", - }, "templates": { "visible": true, "label": "TUTORIALS", @@ -55,10 +47,6 @@ const expectedElements = { }, "s4lacad": { "poweredByOsparc": true, - "studies": { - "visible": true, - "label": "PROJECTS", - }, "templates": { "visible": true, "label": "TUTORIALS", @@ -77,10 +65,6 @@ const expectedElements = { }, "s4llite": { "poweredByOsparc": true, - "studies": { - "visible": true, - "label": "PROJECTS", - }, "templates": { "visible": true, "label": "TUTORIALS", @@ -98,10 +82,6 @@ const expectedElements = { }, "tis": { "poweredByOsparc": true, - "studies": { - "visible": true, - "label": "STUDIES", - }, "templates": { "visible": false, }, @@ -118,10 +98,6 @@ const expectedElements = { }, "tiplite": { "poweredByOsparc": true, - "studies": { - "visible": true, - "label": "STUDIES", - }, "templates": { "visible": false, }, @@ -177,13 +153,10 @@ for (const product in products) { }); test(`Check Dashboard tabs`, async () => { - expect(expectedElements[product]["studies"]).toBeDefined(); expect(expectedElements[product]["templates"]).toBeDefined(); expect(expectedElements[product]["services"]).toBeDefined(); expect(expectedElements[product]["data"]).toBeDefined(); - const isStudiesVisible = expectedElements[product]["studies"]["visible"]; - const studiesLabel = expectedElements[product]["studies"]["label"]; const isTemplatesVisible = expectedElements[product]["templates"]["visible"]; const templatesLabel = expectedElements[product]["templates"]["label"]; const isServicesVisible = expectedElements[product]["services"]["visible"]; @@ -201,7 +174,6 @@ for (const product in products) { } }; - await checkButton("studiesTabBtn", isStudiesVisible, studiesLabel); await checkButton("templatesTabBtn", isTemplatesVisible, templatesLabel); await checkButton("servicesTabBtn", isServicesVisible, servicesLabel); await checkButton("dataTabBtn", isDataVisible, dataLabel); diff --git a/tests/e2e-frontend/tests/studyBrowser/mainView.spec.js b/tests/e2e-frontend/tests/studyBrowser/mainView.spec.js index ff8890af5720..6c90a57c36d8 100644 --- a/tests/e2e-frontend/tests/studyBrowser/mainView.spec.js +++ b/tests/e2e-frontend/tests/studyBrowser/mainView.spec.js @@ -9,33 +9,51 @@ import users from '../users.json'; const expectedElements = { "osparc": { + "newButton": { + "id": "newPlusBtn" + }, "plusButton": { - "id": "newStudyBtn", + "id": "emptyStudyBtn", }, }, "s4l": { + "newButton": { + "id": "newPlusBtn" + }, "plusButton": { "id": "startS4LButton", }, }, "s4lacad": { + "newButton": { + "id": "newPlusBtn" + }, "plusButton": { "id": "startS4LButton", }, }, "s4llite": { + "newButton": { + "id": "newPlusBtn" + }, "plusButton": { "id": "startS4LButton", }, }, "tis": { + "newButton": { + "id": "newPlansBtn" + }, "plusButton": { - "id": "newStudyBtn", + "id": "newTIPlanButton", }, }, "tiplite": { + "newButton": { + "id": "newPlansBtn" + }, "plusButton": { - "id": "newStudyBtn", + "id": "newTIPlanButton", }, }, }; @@ -66,9 +84,14 @@ for (const product in products) { await browser.close(); }); - test(`Plus button`, async () => { + test(`Plus button after New button`, async () => { expect(expectedElements[product]["plusButton"]).toBeDefined(); + if (expectedElements[product]["newPlusButton"]) { + const newPlusButton = page.getByTestId("newPlusBtn"); + await newPlusButton.click(); + } + const plusButtonId = expectedElements[product]["plusButton"]["id"]; const plusButton = page.getByTestId(plusButtonId); await expect(plusButton).toBeVisible({ diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 600a314c7168..be55e580ec93 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -552,7 +552,6 @@ def wait_for_done(response): with log_context(logging.INFO, "Go back to dashboard"): page.get_by_test_id("dashboardBtn").click() page.get_by_test_id("confirmDashboardBtn").click() - page.get_by_test_id("studiesTabBtn").click() for project_uuid in created_project_uuids: with log_context( diff --git a/tests/e2e-playwright/tests/platform_CI_tests/test_platform.py b/tests/e2e-playwright/tests/platform_CI_tests/test_platform.py index 15b3a86b730a..cd18aff6bb14 100644 --- a/tests/e2e-playwright/tests/platform_CI_tests/test_platform.py +++ b/tests/e2e-playwright/tests/platform_CI_tests/test_platform.py @@ -64,6 +64,7 @@ def test_simple_folder_workflow( page.goto(f"{product_url}") page.wait_for_timeout(1000) + page.get_by_test_id("newPlusBtn").click() page.get_by_test_id("newFolderButton").click() with page.expect_response( diff --git a/tests/e2e-playwright/tests/tip/conftest.py b/tests/e2e-playwright/tests/tip/conftest.py index dbe2d6813b81..b0d979921ed0 100644 --- a/tests/e2e-playwright/tests/tip/conftest.py +++ b/tests/e2e-playwright/tests/tip/conftest.py @@ -17,8 +17,7 @@ def _( plan_name_test_id: str, ) -> None: with log_context(logging.INFO, f"Finding {plan_name_test_id=} in dashboard"): - page.get_by_test_id("studiesTabBtn").click() - page.get_by_test_id("newStudyBtn").click() + page.get_by_test_id("newPlansBtn").click() page.get_by_test_id(plan_name_test_id).click() return _ diff --git a/tests/e2e/tests/tags.tes.js b/tests/e2e/tests/tags.tes.js index 4b68209f4560..c7026220b888 100644 --- a/tests/e2e/tests/tags.tes.js +++ b/tests/e2e/tests/tags.tes.js @@ -48,7 +48,8 @@ describe('tags testing', () => { await page.goto(url); await auto.register(page, user, pass); // Create new study - await waitAndClick(page, '[osparc-test-id="newStudyBtn"]'); + await waitAndClick(page, '[osparc-test-id="newPlusBtn"]'); + await waitAndClick(page, '[osparc-test-id="emptyStudyBtn"]'); // Wait until project is created and Dashboard button is enabled await utils.sleep(4000); await auto.toDashboard(page); diff --git a/tests/e2e/tutorials/tutorialBase.js b/tests/e2e/tutorials/tutorialBase.js index 0d649cb384df..6899d3bcb269 100644 --- a/tests/e2e/tutorials/tutorialBase.js +++ b/tests/e2e/tutorials/tutorialBase.js @@ -577,7 +577,6 @@ class TutorialBase { } async removeStudy(studyId, waitFor = 5000) { - await auto.dashboardStudiesBrowser(this.__page); await this.waitFor(waitFor, 'Wait to be unlocked'); await this.takeScreenshot("deleteFirstStudy_before"); const intervalWait = 3000; diff --git a/tests/e2e/utils/auto.js b/tests/e2e/utils/auto.js index 3a745adaccd6..fb7b1367b6ac 100644 --- a/tests/e2e/utils/auto.js +++ b/tests/e2e/utils/auto.js @@ -94,11 +94,6 @@ async function dashboardPreferences(page) { await utils.waitAndClick(page, '[osparc-test-id="preferencesWindowCloseBtn"]'); } -async function dashboardStudiesBrowser(page) { - console.log("Navigating through Studies"); - await utils.waitAndClick(page, '[osparc-test-id="studiesTabBtn"]') -} - async function __dashboardTemplatesBrowser(page) { console.log("Navigating through Templates"); await utils.waitAndClick(page, '[osparc-test-id="templatesTabBtn"]'); @@ -112,15 +107,13 @@ async function __dashboardServicesBrowser(page) { async function dashboardNewTIPlan(page) { console.log("Creating New Plan"); - await dashboardStudiesBrowser(page); - await utils.waitAndClick(page, '[osparc-test-id="newStudyBtn"]'); + await utils.waitAndClick(page, '[osparc-test-id="newPlansBtn"]'); await utils.waitAndClick(page, '[osparc-test-id="newTIPlanButton"]'); } async function dashboardStartSim4LifeLite(page) { console.log("Start Sim4Lite from + button"); - await dashboardStudiesBrowser(page); await utils.waitAndClick(page, '[osparc-test-id="startS4LButton"]'); } @@ -213,7 +206,6 @@ async function __openResource(page) { } async function __filterStudiesByText(page, studyName) { - await dashboardStudiesBrowser(page); await __typeInSearchBarFilter(page, "study", studyName); } @@ -296,8 +288,6 @@ async function runStudy(page) { async function deleteFirstStudy(page, studyName) { console.log("Deleting first study") - await dashboardStudiesBrowser(page); - if (studyName) { await __filterStudiesByText(page, studyName); } @@ -405,7 +395,6 @@ module.exports = { logIn, logOut, dashboardAbout, - dashboardStudiesBrowser, dashboardPreferences, dashboardNewTIPlan, dashboardStartSim4LifeLite,