diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/AppBrowser.js similarity index 77% rename from services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js rename to services/static-webserver/client/source/class/osparc/dashboard/AppBrowser.js index 50305eb14085..1aeea174faa5 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/AppBrowser.js @@ -22,7 +22,7 @@ * @ignore(fetch) */ -qx.Class.define("osparc.dashboard.ServiceBrowser", { +qx.Class.define("osparc.dashboard.AppBrowser", { extend: osparc.dashboard.ResourceBrowserBase, construct: function() { @@ -43,8 +43,12 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { this._resourcesInitialized = true; this._resourcesList = []; - osparc.store.Services.getServicesLatest() - .then(services => { + Promise.all([ + osparc.store.Services.getServicesLatest(), + osparc.store.Templates.getTemplatesHypertools(), + ]) + .then(resps => { + const services = resps[0]; // Show "Contact Us" message if services.length === 0 // Most probably is a product-stranger user (it can also be that the catalog is down) if (Object.keys(services).length === 0) { @@ -65,30 +69,52 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { reloadResources: function() { this.__loadServices(); + this.__loadHypertools(); }, __loadServices: function() { const excludeFrontend = true; const excludeDeprecated = true osparc.store.Services.getServicesLatestList(excludeFrontend, excludeDeprecated) - .then(servicesList => this.__setServicesToList(servicesList.filter(service => service !== null))); + .then(servicesList => { + servicesList.forEach(service => service["resourceType"] = "service"); + this._resourcesList.push(...servicesList.filter(service => service !== null)); + this.__sortAndReload(); + }); + }, + + __loadHypertools: function() { + osparc.store.Templates.getTemplatesHypertools() + .then(hypertoolsList => { + hypertoolsList.forEach(hypertool => hypertool["resourceType"] = "hypertool"); + this._resourcesList.push(...hypertoolsList.filter(hypertool => hypertool !== null)); + this.__sortAndReload(); + }); + }, + + __sortAndReload: function() { + osparc.service.Utils.sortObjectsBasedOn(this._resourcesList, this.__sortBy); + this._reloadCards(); }, _updateServiceData: function(serviceData) { serviceData["resourceType"] = "service"; - const servicesList = this._resourcesList; - const index = servicesList.findIndex(service => service["key"] === serviceData["key"] && service["version"] === serviceData["version"]); + const appsList = this._resourcesList; + const index = appsList.findIndex(service => service["key"] === serviceData["key"] && service["version"] === serviceData["version"]); if (index !== -1) { - servicesList[index] = serviceData; + appsList[index] = serviceData; this._reloadCards(); } }, - __setServicesToList: function(servicesList) { - servicesList.forEach(service => service["resourceType"] = "service"); - osparc.service.Utils.sortObjectsBasedOn(servicesList, this.__sortBy); - this._resourcesList = servicesList; - this._reloadCards(); + _updateHypertoolData: function(hypertoolData) { + hypertoolData["resourceType"] = "hypertool"; + const appsList = this._resourcesList; + const index = appsList.findIndex(service => service["uuid"] === hypertoolData["uuid"]); + if (index !== -1) { + appsList[index] = hypertoolData; + this._reloadCards(); + } }, _reloadCards: function() { @@ -103,8 +129,8 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { }, __itemClicked: function(card) { - const serviceData = card.getResourceData(); - this._openResourceDetails(serviceData); + const appData = card.getResourceData(); + this._openResourceDetails(appData); this.resetSelection(); }, @@ -126,6 +152,28 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { return this._resourcesContainer; }, + __addSortingButtons: function() { + const containerSortButtons = new osparc.service.SortServicesButtons(); + containerSortButtons.set({ + appearance: "form-button-outlined" + }); + containerSortButtons.addListener("sortBy", e => { + this.__sortBy = e.getData(); + this.__sortAndReload(); + }, this); + this._toolbar.add(containerSortButtons); + }, + + _populateCardMenu: function(card) { + const menu = card.getMenu(); + const appData = card.getResourceData(); + + const openButton = this._getOpenMenuButton(appData); + if (openButton) { + menu.add(openButton); + } + }, + __addNewServiceButtons: function() { const platformName = osparc.store.StaticInfo.getInstance().getPlatformName(); const hasRights = osparc.data.Permissions.getInstance().canDo("studies.template.create.productAll"); @@ -140,7 +188,7 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { this._toolbar.add(testDataButton); } - const addServiceButton = new qx.ui.form.Button(this.tr("Submit new service"), "@FontAwesome5Solid/plus-circle/14"); + const addServiceButton = new qx.ui.form.Button(this.tr("Submit new app"), "@FontAwesome5Solid/plus-circle/14"); addServiceButton.set({ appearance: "form-button-outlined", visibility: hasRights ? "visible" : "excluded" @@ -149,30 +197,8 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { this._toolbar.add(addServiceButton); }, - __addSortingButtons: function() { - const containerSortButtons = new osparc.service.SortServicesButtons(); - containerSortButtons.set({ - appearance: "form-button-outlined" - }); - containerSortButtons.addListener("sortBy", e => { - this.__sortBy = e.getData(); - this.__setServicesToList(this._resourcesList); - }, this); - this._toolbar.add(containerSortButtons); - }, - - _populateCardMenu: function(card) { - const menu = card.getMenu(); - const serviceData = card.getResourceData(); - - const openButton = this._getOpenMenuButton(serviceData); - if (openButton) { - menu.add(openButton); - } - }, - __displayServiceSubmissionForm: function(formData) { - const addServiceWindow = new osparc.ui.window.Window(this.tr("Submit a new service")).set({ + const addServiceWindow = new osparc.ui.window.Window(this.tr("Submit a new app")).set({ modal: true, autoDestroy: true, showMinimize: false, 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 63eead024d00..94338bb42b3b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -41,6 +41,7 @@ qx.Class.define("osparc.dashboard.CardBase", { "updateStudy": "qx.event.type.Data", "updateTemplate": "qx.event.type.Data", "updateService": "qx.event.type.Data", + "updateHypertool": "qx.event.type.Data", "publishTemplate": "qx.event.type.Data", "tagClicked": "qx.event.type.Data", "emptyStudyClicked": "qx.event.type.Data" @@ -160,10 +161,10 @@ qx.Class.define("osparc.dashboard.CardBase", { return false; }, - filterServiceType: function(resourceType, metadata, serviceType) { - if (serviceType && resourceType === "service") { + filterAppType: function(resourceType, metadata, appType) { + if (appType && ["service", "hypertool"].includes(resourceType)) { if (metadata && metadata.type) { - const matches = metadata.type === serviceType; + const matches = metadata.type === appType; return !matches; } return false; @@ -221,11 +222,11 @@ qx.Class.define("osparc.dashboard.CardBase", { const organizations = groupsStore.getOrganizations(); const myGroupId = groupsStore.getMyGroupId(); - const sharedGrps = []; const groups = []; groups.push(groupEveryone); groups.push(groupProductEveryone); groups.push(...Object.values(organizations)); + const sharedGrps = []; groups.forEach(group => { const idx = gids.indexOf(group.getGroupId()); if (idx > -1) { @@ -260,7 +261,10 @@ qx.Class.define("osparc.dashboard.CardBase", { sharedGrpLabels.push("..."); break; } - const sharedGrpLabel = sharedGrps[i].getLabel(); + let sharedGrpLabel = sharedGrps[i].getLabel(); + if ([groupEveryone, groupProductEveryone].includes(sharedGrps[i])) { + sharedGrpLabel = "Public"; + } if (!sharedGrpLabels.includes(sharedGrpLabel)) { sharedGrpLabels.push(sharedGrpLabel); } @@ -315,7 +319,12 @@ qx.Class.define("osparc.dashboard.CardBase", { }, resourceType: { - check: ["study", "template", "service"], + check: [ + "study", + "template", + "service", + "hypertool", + ], init: true, nullable: true, event: "changeResourceType" @@ -506,11 +515,8 @@ qx.Class.define("osparc.dashboard.CardBase", { let icon = null; switch (resourceData["resourceType"]) { case "study": - uuid = resourceData.uuid ? resourceData.uuid : null; - owner = resourceData.prjOwner ? resourceData.prjOwner : ""; - workbench = resourceData.workbench ? resourceData.workbench : {}; - break; case "template": + case "hypertool": uuid = resourceData.uuid ? resourceData.uuid : null; owner = resourceData.prjOwner ? resourceData.prjOwner : ""; workbench = resourceData.workbench ? resourceData.workbench : {}; @@ -543,7 +549,7 @@ qx.Class.define("osparc.dashboard.CardBase", { workbench }); - if (resourceData["resourceType"] === "study" || resourceData["resourceType"] === "template") { + if (["study", "template", "hypertool"].includes(resourceData["resourceType"])) { osparc.store.Services.getStudyServices(resourceData.uuid) .then(resp => { const services = resp["services"]; @@ -993,7 +999,8 @@ qx.Class.define("osparc.dashboard.CardBase", { [ "updateStudy", "updateTemplate", - "updateService" + "updateService", + "updateHypertool", ].forEach(ev => { resourceDetails.addListener(ev, e => this.fireDataEvent(ev, e.getData())); }); @@ -1045,6 +1052,8 @@ qx.Class.define("osparc.dashboard.CardBase", { toolTipText += osparc.product.Utils.getStudyAlias(); } else if (this.isResourceType("template")) { toolTipText += osparc.product.Utils.getTemplateAlias(); + } else if (this.isResourceType("hypertool")) { + toolTipText += osparc.product.Utils.getAppAlias(); } const control = new qx.ui.basic.Image().set({ source: "@FontAwesome5Solid/times-circle/14", @@ -1103,10 +1112,10 @@ qx.Class.define("osparc.dashboard.CardBase", { return this.self().filterSharedWith(checks, sharedWith); }, - _filterServiceType: function(serviceType) { + __filterAppType: function(appType) { const resourceType = this.getResourceType(); const resourceData = this.getResourceData(); - return this.self().filterServiceType(resourceType, resourceData, serviceType); + return this.self().filterAppType(resourceType, resourceData, appType); }, _filterClassifiers: function(classifiers) { @@ -1117,7 +1126,14 @@ qx.Class.define("osparc.dashboard.CardBase", { _shouldApplyFilter: function(data) { let filterId = "searchBarFilter"; if (this.isPropertyInitialized("resourceType")) { - filterId += "-" + this.getResourceType(); + switch (this.getResourceType()) { + case "hypertool": + filterId += "-service"; + break; + default: + filterId += "-" + this.getResourceType(); + break; + } } data = filterId in data ? data[filterId] : data; if (this._filterText(data.text)) { @@ -1129,7 +1145,7 @@ qx.Class.define("osparc.dashboard.CardBase", { if (this._filterSharedWith(data.sharedWith)) { return true; } - if (this._filterServiceType(data.serviceType)) { + if (this.__filterAppType(data.appType)) { return true; } if (this._filterClassifiers(data.classifiers)) { @@ -1141,7 +1157,14 @@ qx.Class.define("osparc.dashboard.CardBase", { _shouldReactToFilter: function(data) { let filterId = "searchBarFilter"; if (this.isPropertyInitialized("resourceType")) { - filterId += "-" + this.getResourceType(); + switch (this.getResourceType()) { + case "hypertool": + filterId += "-service"; + break; + default: + filterId += "-" + this.getResourceType(); + break; + } } data = filterId in data ? data[filterId] : data; if (data.text && data.text.length > 1) { @@ -1153,7 +1176,7 @@ qx.Class.define("osparc.dashboard.CardBase", { if (data.sharedWith) { return true; } - if ("serviceType" in data) { + if ("appType" in data) { return true; } if (data.classifiers && data.classifiers.length) { 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 63b9566d3540..7c600e351f7c 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js @@ -19,7 +19,7 @@ * Widget containing a TabView including: * - StudyBrowser * - TemplateBrowser - * - ServiceBrowser + * - AppBrowser * - DataBrowser * * *Example* @@ -79,8 +79,7 @@ qx.Class.define("osparc.dashboard.Dashboard", { members: { __studyBrowser: null, __templateBrowser: null, - __hypertoolBrowser: null, - __serviceBrowser: null, + __appBrowser: null, __dataBrowser: null, getStudyBrowser: function() { @@ -91,12 +90,8 @@ qx.Class.define("osparc.dashboard.Dashboard", { return this.__templateBrowser; }, - getHypertoolBrowser: function() { - return this.__hypertoolBrowser; - }, - - getServiceBrowser: function() { - return this.__serviceBrowser; + getAppBrowser: function() { + return this.__appBrowser; }, __createMainViewLayout: function() { @@ -123,24 +118,14 @@ qx.Class.define("osparc.dashboard.Dashboard", { icon: "@FontAwesome5Solid/copy/"+tabIconSize, buildLayout: this.__createTemplateBrowser }); - if (osparc.product.Utils.isS4LProduct() && osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled()) { - tabs.push({ - id: "hypertoolsTab", - buttonId: "hypertoolsTabBtn", - label: this.tr("HYPERTOOLS"), - icon: "@FontAwesome5Solid/copy/"+tabIconSize, - // initVisibility: "excluded", - buildLayout: this.__createHypertoolsBrowser - }); - } } if (permissions.canDo("dashboard.services.read")) { tabs.push({ - id: "servicesTab", - buttonId: "servicesTabBtn", - label: this.tr("SERVICES"), + id: "appsTab", + buttonId: "appsTabBtn", + label: this.tr("APPS"), icon: "@FontAwesome5Solid/cogs/"+tabIconSize, - buildLayout: this.__createServiceBrowser + buildLayout: this.__createAppBrowser }); } if (permissions.canDo("dashboard.data.read")) { @@ -152,7 +137,7 @@ qx.Class.define("osparc.dashboard.Dashboard", { buildLayout: this.__createDataBrowser }); } - tabs.forEach(({id, buttonId, label, icon, initVisibility, buildLayout}) => { + tabs.forEach(({id, buttonId, label, icon, buildLayout}) => { const tabPage = new qx.ui.tabview.Page(label, icon).set({ appearance: "dashboard-page" }); @@ -161,7 +146,6 @@ qx.Class.define("osparc.dashboard.Dashboard", { tabButton.set({ minWidth: 50, maxHeight: 36, - visibility: initVisibility ? initVisibility : "visible", }); tabButton.ttt = label; tabButton.getChildControl("label").set({ @@ -206,8 +190,8 @@ qx.Class.define("osparc.dashboard.Dashboard", { if (this.__studyBrowser) { this.__studyBrowser.initResources(); } - if (this.__serviceBrowser) { - this.__serviceBrowser.initResources(); + if (this.__appBrowser) { + this.__appBrowser.initResources(); } if (this.__dataBrowser) { this.__dataBrowser.initResources(); @@ -233,15 +217,9 @@ qx.Class.define("osparc.dashboard.Dashboard", { return templatesView; }, - __createHypertoolsBrowser: function() { - const templateType = osparc.data.model.StudyUI.HYPERTOOL_TYPE; - const hypertoolsView = this.__hypertoolBrowser = new osparc.dashboard.TemplateBrowser(templateType); - return hypertoolsView; - }, - - __createServiceBrowser: function() { - const servicesView = this.__serviceBrowser = new osparc.dashboard.ServiceBrowser(); - return servicesView; + __createAppBrowser: function() { + const appsView = this.__appBrowser = new osparc.dashboard.AppBrowser(); + return appsView; }, __createDataBrowser: function() { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js index ad0bf2ec3ae7..10d755424cd8 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js @@ -185,7 +185,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { // overridden _applyLastChangeDate: function(value, old) { if (value) { - if (this.isResourceType("study") || this.isResourceType("template")) { + if (["study", "template", "hypertool"].includes(this.getResourceType())) { const dateBy = this.getChildControl("date-by"); dateBy.set({ date: value, @@ -198,7 +198,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { // overridden _applyTrashedAt: function(value) { if (value && value.getTime() !== new Date(0).getTime()) { - if (this.isResourceType("study") || this.isResourceType("template")) { + if (["study", "template"].includes(this.getResourceType())) { const dateBy = this.getChildControl("date-by"); dateBy.set({ date: value, @@ -211,7 +211,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { // overridden _applyTrashedBy: function(gid) { if (gid) { - if (this.isResourceType("study") || this.isResourceType("template")) { + if (["study", "template"].includes(this.getResourceType())) { const dateBy = this.getChildControl("date-by"); dateBy.setGroupId(gid); } @@ -220,7 +220,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { __createOwner: function(label) { if (label === osparc.auth.Data.getInstance().getEmail()) { - const resourceAlias = osparc.utils.Utils.resourceTypeToAlias(this.getResourceType()); + const resourceAlias = osparc.product.Utils.resourceTypeToAlias(this.getResourceType(), {firstUpperCase: true}); return qx.locale.Manager.tr(`My ${resourceAlias}`); } return osparc.utils.Utils.getNameFromEmail(label); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js index 3cfb70d3b7d7..d79717bb8f72 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js @@ -186,7 +186,7 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { // overridden _applyLastChangeDate: function(value, old) { if (value) { - if (this.isResourceType("study") || this.isResourceType("template")) { + if (["study", "template", "hypertool"].includes(this.getResourceType())) { const dateBy = this.getChildControl("date-by"); dateBy.set({ date: value, @@ -199,7 +199,7 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { // overridden _applyTrashedAt: function(value) { if (value && value.getTime() !== new Date(0).getTime()) { - if (this.isResourceType("study") || this.isResourceType("template")) { + if (["study", "template"].includes(this.getResourceType())) { const dateBy = this.getChildControl("date-by"); dateBy.set({ date: value, @@ -212,7 +212,7 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { // overridden _applyTrashedBy: function(gid) { if (gid) { - if (this.isResourceType("study") || this.isResourceType("template")) { + if (["study", "template"].includes(this.getResourceType())) { const dateBy = this.getChildControl("date-by"); dateBy.setGroupId(gid); } @@ -221,7 +221,7 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { __createOwner: function(label) { if (label === osparc.auth.Data.getInstance().getEmail()) { - const resourceAlias = osparc.utils.Utils.resourceTypeToAlias(this.getResourceType()); + const resourceAlias = osparc.product.Utils.resourceTypeToAlias(this.getResourceType(), {firstUpperCase: true}); return qx.locale.Manager.tr(`My ${resourceAlias}`); } return osparc.utils.Utils.getNameFromEmail(label); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/NewPlusMenu.js b/services/static-webserver/client/source/class/osparc/dashboard/NewPlusMenu.js index 3e4592eac750..f75eb12cedf2 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/NewPlusMenu.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/NewPlusMenu.js @@ -160,12 +160,10 @@ qx.Class.define("osparc.dashboard.NewPlusMenu", { __addItems: function() { this.__addUIConfigItems(); - if (osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled()) { - if (osparc.product.Utils.isS4LProduct()) { - this.__addHypertools(); - } - this.__addOtherTabsAccess(); + if (osparc.product.Utils.isS4LProduct()) { + this.__addHypertools(); } + this.__addOtherTabsAccess(); this.getChildControl("new-folder"); }, @@ -235,7 +233,7 @@ qx.Class.define("osparc.dashboard.NewPlusMenu", { if (permissions.canDo("dashboard.services.read")) { const servicesButton = this.self().createMenuButton("@FontAwesome5Solid/cog/16", this.tr("Services...")); - servicesButton.addListener("execute", () => this.fireDataEvent("changeTab", "servicesTab"), this); + servicesButton.addListener("execute", () => this.fireDataEvent("changeTab", "appsTab"), this); moreMenu.add(servicesButton); } moreMenuButton.setVisibility(moreMenu.getChildren().length ? "visible" : "excluded"); 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 d244902d3511..5ff218af4518 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -288,6 +288,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourcesContainer.addListener("updateStudy", e => this._updateStudyData(e.getData())); resourcesContainer.addListener("updateTemplate", e => this._updateTemplateData(e.getData())); resourcesContainer.addListener("updateService", e => this._updateServiceData(e.getData())); + resourcesContainer.addListener("updateHypertool", e => this._updateHypertoolData(e.getData())); resourcesContainer.addListener("publishTemplate", e => this.fireDataEvent("publishTemplate", e.getData())); resourcesContainer.addListener("tagClicked", e => this._searchBarFilter.addTagActiveFilter(e.getData())); resourcesContainer.addListener("emptyStudyClicked", e => this._deleteResourceRequested(e.getData())); @@ -411,7 +412,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }, _addResourceFilter: function() { - const resourceFilter = this._resourceFilter = new osparc.dashboard.ResourceFilter(this._resourceType).set({ + const resourceFilter = this._resourceFilter = new osparc.dashboard.ResourceBrowserFilter(this._resourceType).set({ marginTop: 20, maxWidth: this.self().SIDE_SPACER_WIDTH, width: this.self().SIDE_SPACER_WIDTH @@ -432,9 +433,9 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { this._searchBarFilter.setTagsActiveFilter(selectedTagIds); }, this); - resourceFilter.addListener("changeServiceType", e => { - const serviceType = e.getData(); - this._searchBarFilter.setServiceTypeActiveFilter(serviceType.id, serviceType.label); + resourceFilter.addListener("changeAppType", e => { + const appType = e.getData(); + this._searchBarFilter.setAppTypeActiveFilter(appType.appType, appType.label); }, this); this._searchBarFilter.addListener("filterChanged", e => { @@ -529,6 +530,10 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { throw new Error("Abstract method called!"); }, + _updateHypertoolData: function(serviceData) { + throw new Error("Abstract method called!"); + }, + _startStudyById: function(studyId, openCB, cancelCB, isStudyCreation = false) { if (isStudyCreation) { this.fireDataEvent("changeTab", "studiesTab"); @@ -744,6 +749,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { break; } case "template": + case "hypertool": this._createStudyFromTemplate(resourceData); break; case "service": @@ -760,6 +766,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourceDetails.addListener("updateStudy", e => this._updateStudyData(e.getData())); resourceDetails.addListener("updateTemplate", e => this._updateTemplateData(e.getData())); resourceDetails.addListener("updateService", e => this._updateServiceData(e.getData())); + resourceDetails.addListener("updateHypertool", e => this._updateHypertoolData(e.getData())); resourceDetails.addListener("publishTemplate", e => { win.close(); this.fireDataEvent("publishTemplate", e.getData()); @@ -775,6 +782,11 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { const templateData = e.getData(); this._createStudyFromTemplate(templateData); }); + resourceDetails.addListener("openHypertool", e => { + win.close(); + const templateData = e.getData(); + this._createStudyFromTemplate(templateData); + }); resourceDetails.addListener("openService", e => { win.close(); const openServiceData = e.getData(); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js similarity index 75% rename from services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js rename to services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js index 352e45955a6d..29a98376cf71 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js @@ -16,7 +16,7 @@ ************************************************************************ */ -qx.Class.define("osparc.dashboard.ResourceFilter", { +qx.Class.define("osparc.dashboard.ResourceBrowserFilter", { extend: qx.ui.core.Widget, construct: function(resourceType) { @@ -27,7 +27,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { this.__resourceType = resourceType; this.__sharedWithButtons = []; this.__tagButtons = []; - this.__serviceTypeButtons = []; + this.__appTypeButtons = []; this._setLayout(new qx.ui.layout.VBox(15)); this.__buildLayout(); @@ -40,7 +40,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { "trashFolderRequested": "qx.event.type.Data", "changeSharedWith": "qx.event.type.Data", "changeSelectedTags": "qx.event.type.Data", - "changeServiceType": "qx.event.type.Data" + "changeAppType": "qx.event.type.Data", }, members: { @@ -49,7 +49,7 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { __trashButton: null, __sharedWithButtons: null, __tagButtons: null, - __serviceTypeButtons: null, + __appTypeButtons: null, __buildLayout: function() { const filtersSpacer = new qx.ui.core.Spacer(10, 10); @@ -57,7 +57,6 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { case "study": { this._add(this.__createWorkspacesAndFoldersTree()); this._add(this.__createTrashBin()); - // this._add(this.__createResourceTypeContextButtons()); this._add(filtersSpacer); const scrollView = new qx.ui.container.Scroll(); scrollView.add(this.__createTagsFilterLayout()); @@ -67,7 +66,6 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { break; } case "template": { - // this._add(this.__createResourceTypeContextButtons()); this._add(filtersSpacer); this._add(this.__createSharedWithFilterLayout()); const scrollView = new qx.ui.container.Scroll(); @@ -78,10 +76,9 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { break; } case "service": - // this._add(this.__createResourceTypeContextButtons()); this._add(filtersSpacer); this._add(this.__createSharedWithFilterLayout()); - this._add(this.__createServiceTypeFilterLayout()); + this._add(this.__createAppTypeFilterLayout()); break; } }, @@ -222,91 +219,6 @@ 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(2)); @@ -442,39 +354,44 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { /* /TAGS */ /* SERVICE TYPE */ - __createServiceTypeFilterLayout: function() { + __createAppTypeFilterLayout: function() { const layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(2)); const radioGroup = new qx.ui.form.RadioGroup(); radioGroup.setAllowEmptySelection(true); + const iconSize = 20; const serviceTypes = osparc.service.Utils.TYPES; Object.keys(serviceTypes).forEach(serviceId => { if (!["computational", "dynamic"].includes(serviceId)) { return; } const serviceType = serviceTypes[serviceId]; - const iconSize = 20; const button = new qx.ui.toolbar.RadioButton(serviceType.label, serviceType.icon+iconSize); - button.id = serviceId; + button.appType = serviceId; osparc.utils.Utils.setIdToWidget(button, this.__resourceType + "-serviceTypeFilterItem"); - button.set({ + this.__appTypeButtons.push(button); + }); + + // hypertools filter + const button = new qx.ui.toolbar.RadioButton("Hypertools", "@FontAwesome5Solid/wrench/"+iconSize); + button.appType = "hypertool"; + this.__appTypeButtons.push(button); + + this.__appTypeButtons.forEach(btn => { + btn.set({ appearance: "filter-toggle-button", value: false }); - - layout.add(button); - radioGroup.add(button); - - button.addListener("execute", () => { - const checked = button.getValue(); - this.fireDataEvent("changeServiceType", { - id: checked ? serviceId : null, - label: checked ? serviceType.label : null + layout.add(btn); + radioGroup.add(btn); + btn.addListener("execute", () => { + const checked = btn.getValue(); + this.fireDataEvent("changeAppType", { + appType: checked ? btn.appType : null, + label: checked ? btn.getLabel() : null }); }, this); - - this.__serviceTypeButtons.push(button); }); return layout; @@ -493,9 +410,9 @@ qx.Class.define("osparc.dashboard.ResourceFilter", { btn.setValue(filterData["tags"].includes(btn.id)); }); } - if ("serviceType" in filterData) { - this.__serviceTypeButtons.forEach(btn => { - btn.setValue(filterData["serviceType"] === btn.id); + if ("appType" in filterData) { + this.__appTypeButtons.forEach(btn => { + btn.setValue(filterData["appType"] === btn.appType); }); } } diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index 6f89b6abd086..6ca49e333c83 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -66,6 +66,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "updateStudy": "qx.event.type.Data", "updateTemplate": "qx.event.type.Data", "updateService": "qx.event.type.Data", + "updateHypertool": "qx.event.type.Data", "publishTemplate": "qx.event.type.Data", "tagClicked": "qx.event.type.Data", "emptyStudyClicked": "qx.event.type.Data", @@ -238,6 +239,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "updateStudy", "updateTemplate", "updateService", + "updateHypertool", "publishTemplate", "tagClicked", "emptyStudyClicked" 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 d543206295de..d066ff1ab103 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -26,7 +26,8 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { let latestPromise = null; switch (resourceData["resourceType"]) { case "study": - case "template": { + case "template": + case "hypertool": { const params = { url: { "studyId": resourceData["uuid"] @@ -47,7 +48,8 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { this.__resourceData["resourceType"] = resourceData["resourceType"]; switch (resourceData["resourceType"]) { case "study": - case "template": { + case "template": + case "hypertool": osparc.store.Services.getStudyServicesMetadata(latestResourceData) .finally(() => { this.__resourceModel = new osparc.data.model.Study(latestResourceData); @@ -56,7 +58,6 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { this.__addPages(); }) break; - } case "service": { this.__resourceModel = new osparc.data.model.Service(latestResourceData); this.__resourceModel["resourceType"] = resourceData["resourceType"]; @@ -71,10 +72,12 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { events: { "pagesAdded": "qx.event.type.Event", "openTemplate": "qx.event.type.Data", + "openHypertool": "qx.event.type.Data", "openService": "qx.event.type.Data", "updateStudy": "qx.event.type.Data", "updateTemplate": "qx.event.type.Data", "updateService": "qx.event.type.Data", + "updateHypertool": "qx.event.type.Data", "publishTemplate": "qx.event.type.Data", }, @@ -85,7 +88,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { popUpInWindow: function(resourceDetails) { // eslint-disable-next-line no-underscore-dangle - const resourceAlias = osparc.utils.Utils.resourceTypeToAlias(resourceDetails.__resourceData["resourceType"]); + const resourceAlias = osparc.product.Utils.resourceTypeToAlias(resourceDetails.__resourceData["resourceType"], {firstUpperCase: true}); // eslint-disable-next-line no-underscore-dangle const title = `${resourceAlias} ${qx.locale.Manager.tr("Details")} - ${resourceDetails.__resourceData.name}`; const win = osparc.ui.window.Window.popUpInWindow(resourceDetails, title, this.WIDTH, this.HEIGHT).set({ @@ -240,6 +243,9 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { case "template": this.fireDataEvent("openTemplate", this.__resourceData); break; + case "hypertool": + this.fireDataEvent("openHypertool", this.__resourceData); + break; case "service": this.fireDataEvent("openService", this.__resourceData); break; @@ -382,6 +388,23 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { this.fireEvent("pagesAdded"); }, + __fireUpdateEvent: function(resourceData, updatedData) { + switch (resourceData["resourceType"]) { + case "study": + this.fireDataEvent("updateStudy", updatedData); + break; + case "template": + this.fireDataEvent("updateTemplate", updatedData); + break; + case "hypertool": + this.fireDataEvent("updateHypertool", updatedData); + break; + case "service": + this.fireDataEvent("updateService", updatedData); + break; + } + }, + __getInfoPage: function() { const id = "Information"; const title = this.tr("Overview"); @@ -397,19 +420,13 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { infoCard = new osparc.info.ServiceLarge(resourceData, null, false); infoCard.addListener("updateService", e => { const updatedData = e.getData(); - if (osparc.utils.Resources.isService(resourceData)) { - this.fireDataEvent("updateService", updatedData); - } + this.__fireUpdateEvent(resourceData, updatedData); }); } else { infoCard = new osparc.info.StudyLarge(resourceModel, false); infoCard.addListener("updateStudy", e => { const updatedData = e.getData(); - if (osparc.utils.Resources.isStudy(resourceData)) { - this.fireDataEvent("updateStudy", updatedData); - } else if (osparc.utils.Resources.isTemplate(resourceData)) { - this.fireDataEvent("updateTemplate", updatedData); - } + this.__fireUpdateEvent(resourceData, updatedData); }); infoCard.addListener("openTags", () => this.openTags()); } @@ -545,9 +562,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { collaboratorsView = new osparc.share.CollaboratorsService(resourceData); collaboratorsView.addListener("updateAccessRights", e => { const updatedData = e.getData(); - if (osparc.utils.Resources.isService(resourceData)) { - this.fireDataEvent("updateService", updatedData); - } + this.__fireUpdateEvent(resourceData, updatedData); }, this); } else { collaboratorsView = new osparc.share.CollaboratorsStudy(resourceData); @@ -558,11 +573,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { } collaboratorsView.addListener("updateAccessRights", e => { const updatedData = e.getData(); - if (osparc.utils.Resources.isStudy(resourceData)) { - this.fireDataEvent("updateStudy", updatedData); - } else if (osparc.utils.Resources.isTemplate(resourceData)) { - this.fireDataEvent("updateTemplate", updatedData); - } + this.__fireUpdateEvent(resourceData, updatedData); }, this); } page.addToContent(collaboratorsView); @@ -595,13 +606,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { classifiers = new osparc.metadata.ClassifiersEditor(resourceData); classifiers.addListener("updateClassifiers", e => { const updatedData = e.getData(); - if (osparc.utils.Resources.isStudy(resourceData)) { - this.fireDataEvent("updateStudy", updatedData); - } else if (osparc.utils.Resources.isTemplate(resourceData)) { - this.fireDataEvent("updateTemplate", updatedData); - } else if (osparc.utils.Resources.isService(resourceData)) { - this.fireDataEvent("updateService", updatedData); - } + this.__fireUpdateEvent(resourceData, updatedData); }, this); } else { classifiers = new osparc.metadata.ClassifiersViewer(resourceData); @@ -633,11 +638,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const qualityEditor = new osparc.metadata.QualityEditor(resourceData); qualityEditor.addListener("updateQuality", e => { const updatedData = e.getData(); - if (osparc.utils.Resources.isStudy(resourceData)) { - this.fireDataEvent("updateStudy", updatedData); - } else if (osparc.utils.Resources.isTemplate(resourceData)) { - this.fireDataEvent("updateTemplate", updatedData); - } + this.__fireUpdateEvent(updatedData); }); page.addToContent(qualityEditor); } @@ -668,11 +669,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { tagManager.addListener("updateTags", e => { const updatedData = e.getData(); tagManager.setStudyData(updatedData); - if (osparc.utils.Resources.isStudy(resourceData)) { - this.fireDataEvent("updateStudy", updatedData); - } else if (osparc.utils.Resources.isTemplate(resourceData)) { - this.fireDataEvent("updateTemplate", updatedData); - } + this.__fireUpdateEvent(resourceData, updatedData); }, this); page.addToContent(tagManager); } @@ -703,11 +700,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const servicesUpdate = new osparc.metadata.ServicesInStudyUpdate(resourceData); servicesUpdate.addListener("updateService", e => { const updatedData = e.getData(); - if (osparc.utils.Resources.isStudy(resourceData)) { - this.fireDataEvent("updateStudy", updatedData); - } else if (osparc.utils.Resources.isTemplate(resourceData)) { - this.fireDataEvent("updateTemplate", updatedData); - } + this.__fireUpdateEvent(resourceData, updatedData); }); page.addToContent(servicesUpdate); } @@ -741,11 +734,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const servicesBootOpts = new osparc.metadata.ServicesInStudyBootOpts(resourceData); servicesBootOpts.addListener("updateService", e => { const updatedData = e.getData(); - if (osparc.utils.Resources.isStudy(resourceData)) { - this.fireDataEvent("updateStudy", updatedData); - } else if (osparc.utils.Resources.isTemplate(resourceData)) { - this.fireDataEvent("updateTemplate", updatedData); - } + this.__fireUpdateEvent(resourceData, updatedData); }); page.addToContent(servicesBootOpts); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js index 062c963ef8f2..5886834d6ad0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/SearchBarFilter.js @@ -45,6 +45,9 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", { HEIGHT: 36, getSharedWithOptions: function(resourceType) { + if (resourceType === "service") { + resourceType = "app"; + } return [{ id: "show-all", label: qx.locale.Manager.tr("All") + " " + osparc.product.Utils.resourceTypeToAlias(resourceType, { @@ -147,7 +150,7 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", { this.__addSharedWith(sharedWithButton); menu.add(sharedWithButton); - if (this.__resourceType !== "service") { + if (["study", "template"].includes(this.__resourceType)) { const tagsButton = new qx.ui.menu.Button(this.tr("Tags"), "@FontAwesome5Solid/tags/12"); osparc.utils.Utils.setIdToWidget(tagsButton, "searchBarFilter-tags-button"); this.__addTags(tagsButton); @@ -159,9 +162,9 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", { } if (this.__resourceType === "service") { - const serviceTypeButton = new qx.ui.menu.Button(this.tr("Service Type"), "@FontAwesome5Solid/cogs/12"); - this.__addServiceTypes(serviceTypeButton); - menu.add(serviceTypeButton); + const appTypeButton = new qx.ui.menu.Button(this.tr("App Type"), "@FontAwesome5Solid/cogs/12"); + this.__addAppTypes(appTypeButton); + menu.add(appTypeButton); } }, @@ -258,20 +261,26 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", { } }, - __addServiceTypes: function(menuButton) { + __addAppTypes: function(menuButton) { const serviceTypeMenu = new qx.ui.menu.Menu(); menuButton.setMenu(serviceTypeMenu); + + const iconSize = 12; const serviceTypes = osparc.service.Utils.TYPES; Object.keys(serviceTypes).forEach(serviceId => { if (!["computational", "dynamic"].includes(serviceId)) { return; } const serviceType = serviceTypes[serviceId]; - const iconSize = 12; const serviceTypeButton = new qx.ui.menu.Button(serviceType.label, serviceType.icon+iconSize); serviceTypeMenu.add(serviceTypeButton); - serviceTypeButton.addListener("execute", () => this.__addChip("service-type", serviceId, serviceType.label), this); + serviceTypeButton.addListener("execute", () => this.__addChip("app-type", serviceId, serviceType.label), this); }); + + // hypertools filter + const hypertoolTypeButton = new qx.ui.menu.Button("Hypertools", "@FontAwesome5Solid/wrench/"+iconSize); + serviceTypeMenu.add(hypertoolTypeButton); + hypertoolTypeButton.addListener("execute", () => this.__addChip("app-type", "hypertool", "Hypertools"), this); }, addTagActiveFilter: function(tag) { @@ -300,10 +309,10 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", { } }, - setServiceTypeActiveFilter: function(optionId, optionLabel) { - this.__removeChips("service-type"); - if (optionId && optionLabel) { - this.__addChip("service-type", optionId, optionLabel); + setAppTypeActiveFilter: function(appType, optionLabel) { + this.__removeChips("app-type"); + if (appType && optionLabel) { + this.__addChip("app-type", appType, optionLabel); } else { this.__filter(); } @@ -369,7 +378,7 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", { tags: [], classifiers: [], sharedWith: null, - serviceType: null, + appType: null, text: "" }; const textFilter = this.getTextFilterValue(); @@ -385,8 +394,8 @@ qx.Class.define("osparc.dashboard.SearchBarFilter", { case "shared-with": filterData.sharedWith = chip.id === "show-all" ? null : chip.id; break; - case "service-type": - filterData.serviceType = chip.id; + case "app-type": + filterData.appType = chip.id; break; } }); 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 27d9e6ec4112..e08c03584069 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -1516,10 +1516,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const duplicateStudyButton = this.__getDuplicateMenuButton(studyData); menu.add(duplicateStudyButton); - if (osparc.product.Utils.hasConvertToPipelineEnabled()) { - const convertToPipelineButton = this.__getConvertToPipelineMenuButton(studyData); - menu.add(convertToPipelineButton); - } + const convertToPipelineButton = this.__getConvertToPipelineMenuButton(studyData); + menu.add(convertToPipelineButton); if (osparc.product.Utils.hasExportCMisEnabled()) { const exportStudyButton = this.__getExportCMisMenuButton(studyData); diff --git a/services/static-webserver/client/source/class/osparc/data/SubJob.js b/services/static-webserver/client/source/class/osparc/data/SubJob.js index e6143c2dc5db..6642827635a5 100644 --- a/services/static-webserver/client/source/class/osparc/data/SubJob.js +++ b/services/static-webserver/client/source/class/osparc/data/SubJob.js @@ -24,12 +24,14 @@ qx.Class.define("osparc.data.SubJob", { this.set({ projectUuid: subJobData["projectUuid"], nodeId: subJobData["nodeId"], - nodeName: subJobData["nodeId"], + nodeName: subJobData["nodeName"], state: subJobData["state"], progress: subJobData["progress"], startedAt: subJobData["startedAt"] ? new Date(subJobData["startedAt"]) : null, endedAt: subJobData["endedAt"] ? new Date(subJobData["endedAt"]) : null, + osparcCredits: subJobData["osparcCredits"] || null, image: subJobData["image"] || {}, + logDownloadLink: subJobData["logDownloadLink"] || null, }); }, @@ -76,11 +78,23 @@ qx.Class.define("osparc.data.SubJob", { nullable: true, }, + osparcCredits: { + check: "Number", + nullable: true, + init: null, + }, + image: { check: "Object", nullable: false, init: null, }, + + logDownloadLink: { + check: "String", + nullable: true, + init: null, + }, }, members: { 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 8204b8250d1b..6d6095b2d4aa 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js +++ b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js @@ -27,7 +27,7 @@ * - Dashboard Stack * - StudyBrowser * - TemplateBrowser - * - ServiceBrowser + * - AppBrowser * - DataManager * - StudyEditor * @@ -69,9 +69,7 @@ qx.Class.define("osparc.desktop.MainPage", { preloadPromises.push(osparc.store.Tags.getInstance().fetchTags()); preloadPromises.push(osparc.store.Products.getInstance().fetchUiConfig()); preloadPromises.push(osparc.store.PollTasks.getInstance().fetchTasks()); - if (osparc.utils.DisabledPlugins.isJobsEnabled()) { - preloadPromises.push(osparc.store.Jobs.getInstance().fetchJobs()); - } + preloadPromises.push(osparc.store.Jobs.getInstance().fetchJobs()); Promise.all(preloadPromises) .then(() => { const mainStack = this.__createMainStack(); @@ -251,7 +249,7 @@ qx.Class.define("osparc.desktop.MainPage", { pollTasks.createPollingTask(fetchPromise) .then(task => { const templateBrowser = this.__dashboard.getTemplateBrowser(); - const hypertoolBrowser = this.__dashboard.getHypertoolBrowser(); + const appBrowser = this.__dashboard.getAppBrowser(); if (templateBrowser) { templateBrowser.taskToTemplateReceived(task, studyName); } @@ -267,8 +265,9 @@ qx.Class.define("osparc.desktop.MainPage", { if (templateBrowser) { templateBrowser.reloadResources(); } - if (hypertoolBrowser) { - hypertoolBrowser.reloadResources(); + if (appBrowser) { + // OM: reload hypertools only + appBrowser.reloadResources(); } }); } 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 b7021976228b..ade394646951 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js +++ b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js @@ -438,18 +438,16 @@ qx.Class.define("osparc.desktop.WorkbenchView", { this.__addTopBarSpacer(topBar); - if (osparc.utils.DisabledPlugins.isConversationEnabled()) { - const commentsButton = new qx.ui.form.Button().set({ - appearance: "form-button-outlined", - toolTipText: this.tr("Conversations"), - icon: "@FontAwesome5Solid/comments/16", - marginRight: 10, - marginTop: 7, - ...osparc.navigation.NavigationBar.BUTTON_OPTIONS - }); - commentsButton.addListener("execute", () => osparc.study.Conversations.popUpInWindow(study.serialize())); - topBar.add(commentsButton); - } + const commentsButton = new qx.ui.form.Button().set({ + appearance: "form-button-outlined", + toolTipText: this.tr("Conversations"), + icon: "@FontAwesome5Solid/comments/16", + marginRight: 10, + marginTop: 7, + ...osparc.navigation.NavigationBar.BUTTON_OPTIONS + }); + commentsButton.addListener("execute", () => osparc.study.Conversations.popUpInWindow(study.serialize())); + topBar.add(commentsButton); const startAppButtonTB = this.__startAppButtonTB = new qx.ui.form.Button().set({ appearance: "form-button-outlined", diff --git a/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js b/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js index d87c450c2de4..6a9274cea418 100644 --- a/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js +++ b/services/static-webserver/client/source/class/osparc/filter/CollaboratorToggleButton.js @@ -15,6 +15,7 @@ qx.Class.define("osparc.filter.CollaboratorToggleButton", { construct: function(collaborator) { this.base(arguments); + this._setLayout(new qx.ui.layout.HBox(8).set({ alignY: "middle" })); @@ -23,11 +24,15 @@ qx.Class.define("osparc.filter.CollaboratorToggleButton", { appearance: "tagbutton" }); - let label = collaborator.getLabel(); - if ("getEmail" in collaborator && collaborator.getEmail()) { - label += ` (${collaborator.getEmail()})`; + if (collaborator["collabType"] === 0) { + this.setLabel(this.tr("Public")); + } else { + let label = collaborator.getLabel(); + if ("getEmail" in collaborator && collaborator.getEmail()) { + label += ` (${collaborator.getEmail()})`; + } + this.setLabel(label); } - this.setLabel(label); if (collaborator.getDescription()) { const ttt = collaborator.getLabel() + "
" + collaborator.getDescription(); diff --git a/services/static-webserver/client/source/class/osparc/jobs/RunsWindow.js b/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js similarity index 70% rename from services/static-webserver/client/source/class/osparc/jobs/RunsWindow.js rename to services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js index f10b604ebdfd..cd56d3198f6d 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/RunsWindow.js +++ b/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js @@ -15,11 +15,11 @@ ************************************************************************ */ -qx.Class.define("osparc.jobs.RunsWindow", { +qx.Class.define("osparc.jobs.ActivityCenterWindow", { extend: osparc.ui.window.SingletonWindow, construct: function() { - this.base(arguments, "runs", this.tr("Runs and Clusters")); + this.base(arguments, "runs", this.tr("Activity Center")); this.set({ layout: new qx.ui.layout.VBox(), @@ -35,7 +35,7 @@ qx.Class.define("osparc.jobs.RunsWindow", { statics: { openWindow: function() { - const runsWindow = new osparc.jobs.RunsWindow(); + const runsWindow = new osparc.jobs.ActivityCenterWindow(); runsWindow.center(); runsWindow.open(); return runsWindow; @@ -49,12 +49,12 @@ qx.Class.define("osparc.jobs.RunsWindow", { flex: 1 }); - const runsAndClusters = new osparc.jobs.RunsAndClusters(); + const runsBrowser = new osparc.jobs.RunsBrowser(); const subRunsBrowser = new osparc.jobs.SubRunsBrowser(); - stack.add(runsAndClusters); + stack.add(runsBrowser); stack.add(subRunsBrowser); - runsAndClusters.addListener("runSelected", e => { + runsBrowser.addListener("runSelected", e => { const project = e.getData(); subRunsBrowser.setProject(project); this.getChildControl("title").setValue(this.tr("Runs")); @@ -62,13 +62,13 @@ qx.Class.define("osparc.jobs.RunsWindow", { }); subRunsBrowser.addListener("backToRuns", () => { - runsAndClusters.getChildControl("runs-browser").reloadRuns(); - this.getChildControl("title").setValue(this.tr("Runs and Clusters")); - stack.setSelection([runsAndClusters]); + runsBrowser.reloadRuns(); + this.getChildControl("title").setValue(this.tr("Activity Center")); + stack.setSelection([runsBrowser]); }); this.addListener("close", () => { - runsAndClusters.getChildControl("runs-browser").stopInterval(); + runsBrowser.stopInterval(); subRunsBrowser.stopInterval(); }); }, diff --git a/services/static-webserver/client/source/class/osparc/jobs/JobsButton.js b/services/static-webserver/client/source/class/osparc/jobs/JobsButton.js index f3b4d0fac0df..c0df3017ee9c 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/JobsButton.js +++ b/services/static-webserver/client/source/class/osparc/jobs/JobsButton.js @@ -28,10 +28,10 @@ qx.Class.define("osparc.jobs.JobsButton", { alignX: "center", cursor: "pointer", visibility: "excluded", - toolTipText: this.tr("Runs and Clusters"), + toolTipText: this.tr("Activity Center"), }); - this.addListener("tap", () => osparc.jobs.RunsWindow.openWindow(), this); + this.addListener("tap", () => osparc.jobs.ActivityCenterWindow.openWindow(), this); const jobsStore = osparc.store.Jobs.getInstance(); jobsStore.addListener("changeJobs", e => this.__updateJobsButton(), this); diff --git a/services/static-webserver/client/source/class/osparc/jobs/RunsAndClusters.js b/services/static-webserver/client/source/class/osparc/jobs/RunsAndClusters.js deleted file mode 100644 index ebc68a6cd0de..000000000000 --- a/services/static-webserver/client/source/class/osparc/jobs/RunsAndClusters.js +++ /dev/null @@ -1,91 +0,0 @@ -/* ************************************************************************ - - 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.jobs.RunsAndClusters", { - extend: qx.ui.tabview.TabView, - - construct: function() { - this.base(arguments); - - this.set({ - contentPadding: 5, - barPosition: "top", - }); - - const runsBrowser = this.getChildControl("runs-browser"); - runsBrowser.addListener("runSelected", e => this.fireDataEvent("runSelected", e.getData())); - - this.getChildControl("clusters-browser"); - }, - - events: { - "runSelected": "qx.event.type.Data", - }, - - statics: { - popUpInWindow: function(jobsAndClusters) { - if (!jobsAndClusters) { - jobsAndClusters = new osparc.jobs.RunsAndClusters(); - } - const title = qx.locale.Manager.tr("Runs and Clusters"); - const win = osparc.ui.window.Window.popUpInWindow(jobsAndClusters, title, 1100, 500); - win.open(); - return win; - } - }, - - members: { - _createChildControlImpl: function(id) { - let control; - switch (id) { - case "jobs-page": - control = new qx.ui.tabview.Page(this.tr("Runs")).set({ - layout: new qx.ui.layout.VBox(10) - }); - this.add(control); - break; - case "runs-browser": { - control = new osparc.jobs.RunsBrowser(); - const scroller = new qx.ui.container.Scroll(); - scroller.add(control); - this.getChildControl("jobs-page").add(scroller); - break; - } - case "clusters-page": - control = new qx.ui.tabview.Page(this.tr("Clusters")).set({ - layout: new qx.ui.layout.VBox(10) - }); - this.add(control); - break; - case "clusters-browser": { - control = new osparc.jobs.ClustersBrowser(); - const scroller = new qx.ui.container.Scroll(); - scroller.add(control); - this.getChildControl("clusters-page").add(scroller); - break; - } - } - return control || this.base(arguments, id); - }, - - reloadRuns: function() { - const runsBrowser = this.getChildControl("runs-browser"); - runsBrowser.reloadRuns(); - }, - } -}); diff --git a/services/static-webserver/client/source/class/osparc/jobs/RunsBrowser.js b/services/static-webserver/client/source/class/osparc/jobs/RunsBrowser.js index 4b75bdb13bac..ff9e5414b840 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/RunsBrowser.js +++ b/services/static-webserver/client/source/class/osparc/jobs/RunsBrowser.js @@ -25,7 +25,6 @@ qx.Class.define("osparc.jobs.RunsBrowser", { this._setLayout(new qx.ui.layout.VBox(10)); const jobsFilter = this.getChildControl("jobs-filter"); - this.getChildControl("jobs-ongoing"); const jobsTable = this.getChildControl("runs-table"); jobsFilter.getChildControl("textfield").addListener("input", e => { @@ -61,14 +60,6 @@ qx.Class.define("osparc.jobs.RunsBrowser", { flex: 1 }); break; - case "jobs-ongoing": - control = new qx.ui.form.CheckBox().set({ - label: "Hide finished jobs", - value: true, - enabled: false, - }); - this.getChildControl("header-filter").add(control); - break; case "runs-table": control = new osparc.jobs.RunsTable(); control.addListener("runSelected", e => this.fireDataEvent("runSelected", e.getData())); diff --git a/services/static-webserver/client/source/class/osparc/jobs/RunsTable.js b/services/static-webserver/client/source/class/osparc/jobs/RunsTable.js index cf7c68e00bfa..41fc06821a0c 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/RunsTable.js +++ b/services/static-webserver/client/source/class/osparc/jobs/RunsTable.js @@ -36,10 +36,6 @@ qx.Class.define("osparc.jobs.RunsTable", { Object.values(this.self().COLS).forEach(col => columnModel.setColumnWidth(col.column, col.width)); - const iconPathRun = "osparc/circle-play-text.svg"; - const fontButtonRendererRun = new osparc.ui.table.cellrenderer.ImageButtonRenderer("run", iconPathRun); - columnModel.setDataCellRenderer(this.self().COLS.ACTION_RUN.column, fontButtonRendererRun); - const iconPathStop = "osparc/circle-stop-text.svg"; const fontButtonRendererStop = new osparc.ui.table.cellrenderer.ImageButtonRenderer("stop", iconPathStop); columnModel.setDataCellRenderer(this.self().COLS.ACTION_STOP.column, fontButtonRendererStop); @@ -62,7 +58,7 @@ qx.Class.define("osparc.jobs.RunsTable", { PROJECT_NAME: { id: "projectName", column: 1, - label: qx.locale.Manager.tr("Project Name"), + label: qx.locale.Manager.tr("Project"), width: 170, sortable: true }, @@ -75,7 +71,7 @@ qx.Class.define("osparc.jobs.RunsTable", { SUBMIT: { id: "submit", column: 3, - label: qx.locale.Manager.tr("Submitted"), + label: qx.locale.Manager.tr("Queued"), width: 130, sortable: true }, @@ -93,15 +89,9 @@ qx.Class.define("osparc.jobs.RunsTable", { width: 130, sortable: true }, - ACTION_RUN: { - id: "action_run", - column: 6, - label: "", - width: 40 - }, ACTION_STOP: { id: "action_stop", - column: 7, + column: 6, label: "", width: 40 }, diff --git a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js index 28f2806200d4..2f457bb3efbb 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js +++ b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js @@ -39,7 +39,11 @@ qx.Class.define("osparc.jobs.SubRunsTable", { const iconPathInfo = "osparc/circle-info-text.svg"; const fontButtonRendererInfo = new osparc.ui.table.cellrenderer.ImageButtonRenderer("info", iconPathInfo); - columnModel.setDataCellRenderer(this.self().COLS.IMAGE.column, fontButtonRendererInfo); + columnModel.setDataCellRenderer(this.self().COLS.INFO.column, fontButtonRendererInfo); + + const iconPathLogs = "osparc/logs-text.svg"; + const fontButtonRendererLogs = new osparc.ui.table.cellrenderer.ImageButtonRenderer("logs", iconPathLogs); + columnModel.setDataCellRenderer(this.self().COLS.LOGS.column, fontButtonRendererLogs); this.__attachHandlers(); }, @@ -61,13 +65,13 @@ qx.Class.define("osparc.jobs.SubRunsTable", { NODE_NAME: { id: "nodeName", column: 2, - label: qx.locale.Manager.tr("Node Name"), + label: qx.locale.Manager.tr("Node"), width: 170 }, - SOLVER: { - id: "solver", + APP: { + id: "app", column: 3, - label: qx.locale.Manager.tr("Solver"), + label: qx.locale.Manager.tr("App"), width: 150 }, STATE: { @@ -100,12 +104,24 @@ qx.Class.define("osparc.jobs.SubRunsTable", { label: qx.locale.Manager.tr("Duration"), width: 70 }, - IMAGE: { - id: "image", + CREDITS: { + id: "credits", column: 9, + label: qx.locale.Manager.tr("Credits"), + width: 70 + }, + INFO: { + id: "info", + column: 10, label: qx.locale.Manager.tr("Info"), width: 40 }, + LOGS: { + id: "logs", + column: 11, + label: qx.locale.Manager.tr("Logs"), + width: 40 + }, } }, diff --git a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js index 4c9077d60180..33f65f03a85d 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js +++ b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js @@ -73,8 +73,14 @@ qx.Class.define("osparc.jobs.SubRunsTableModel", { const data = []; const subJobsCols = osparc.jobs.SubRunsTable.COLS; subJobs.forEach(subJob => { - const nodeName = subJob.getImage()["name"].split("/").pop(); - const version = osparc.store.Services.getVersionDisplay(subJob.getImage()["name"], subJob.getImage()["tag"]) || subJob.getImage()["tag"]; + const serviceKey = subJob.getImage()["name"]; + const serviceVersion = subJob.getImage()["tag"]; + const serviceMetadata = osparc.store.Services.getLatest(serviceKey); + let appName = serviceKey.split("/").pop(); + if (serviceMetadata) { + appName = serviceMetadata["name"]; + } + const displayVersion = osparc.store.Services.getVersionDisplay(serviceKey, serviceVersion) || serviceVersion; const startedAt = subJob.getStartedAt(); const endedAt = subJob.getEndedAt(); let duration = "-"; @@ -89,12 +95,13 @@ qx.Class.define("osparc.jobs.SubRunsTableModel", { [subJobsCols.PROJECT_UUID.id]: subJob.getProjectUuid(), [subJobsCols.NODE_ID.id]: subJob.getNodeId(), [subJobsCols.NODE_NAME.id]: subJob.getNodeName(), - [subJobsCols.SOLVER.id]: nodeName + ":" + version, + [subJobsCols.APP.id]: appName + ":" + displayVersion, [subJobsCols.STATE.id]: subJob.getState(), [subJobsCols.PROGRESS.id]: subJob.getProgress() * 100 + "%", [subJobsCols.START.id]: startedAt ? osparc.utils.Utils.formatDateAndTime(startedAt) : "-", [subJobsCols.END.id]: endedAt ? osparc.utils.Utils.formatDateAndTime(endedAt) : "-", [subJobsCols.DURATION.id]: duration, + [subJobsCols.CREDITS.id]: subJob.getOsparcCredits() === null ? "-" : subJob.getOsparcCredits(), }); }); return data; 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 4d593c765292..1b86bfab8564 100644 --- a/services/static-webserver/client/source/class/osparc/navigation/StudyTitleWOptions.js +++ b/services/static-webserver/client/source/class/osparc/navigation/StudyTitleWOptions.js @@ -110,12 +110,8 @@ qx.Class.define("osparc.navigation.StudyTitleWOptions", { optionsMenu.setAppearance("menu-wider"); optionsMenu.add(this.getChildControl("study-menu-info")); optionsMenu.add(this.getChildControl("study-menu-reload")); - if (osparc.utils.DisabledPlugins.isConversationEnabled()) { - optionsMenu.add(this.getChildControl("study-menu-conversations")); - } - if (osparc.product.Utils.hasConvertToPipelineEnabled()) { - optionsMenu.add(this.getChildControl("study-menu-convert-to-pipeline")); - } + optionsMenu.add(this.getChildControl("study-menu-conversations")); + optionsMenu.add(this.getChildControl("study-menu-convert-to-pipeline")); optionsMenu.add(this.getChildControl("study-menu-restore")); optionsMenu.add(this.getChildControl("study-menu-open-logger")); control = new qx.ui.form.MenuButton().set({ @@ -161,19 +157,15 @@ qx.Class.define("osparc.navigation.StudyTitleWOptions", { converter: mode => mode === "standalone" ? "visible" : "excluded" }); - if (osparc.utils.DisabledPlugins.isConversationEnabled()) { - const conversationsButton = this.getChildControl("study-menu-conversations"); - study.getUi().bind("mode", conversationsButton, "visibility", { - converter: mode => mode === "standalone" ? "visible" : "excluded" - }); - } + const conversationsButton = this.getChildControl("study-menu-conversations"); + study.getUi().bind("mode", conversationsButton, "visibility", { + converter: mode => mode === "standalone" ? "visible" : "excluded" + }); - if (osparc.product.Utils.hasConvertToPipelineEnabled()) { - const convertToPipelineButton = this.getChildControl("study-menu-convert-to-pipeline"); - study.getUi().bind("mode", convertToPipelineButton, "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 restoreButton = this.getChildControl("study-menu-restore"); study.getUi().bind("mode", restoreButton, "visibility", { 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 249f7575cf63..987ee0ef5f30 100644 --- a/services/static-webserver/client/source/class/osparc/product/Utils.js +++ b/services/static-webserver/client/source/class/osparc/product/Utils.js @@ -102,6 +102,36 @@ qx.Class.define("osparc.product.Utils", { return alias; }, + getHypertoolAlias: function(options = {}) { + let alias = qx.locale.Manager.tr("hypertool"); + if (options.plural) { + alias = qx.locale.Manager.tr("hypertools"); + } + + if (options.firstUpperCase) { + alias = osparc.utils.Utils.capitalize(alias); + } else if (options.allUpperCase) { + alias = alias.toUpperCase(); + } + + return alias; + }, + + getAppAlias: function(options = {}) { + let alias = qx.locale.Manager.tr("app"); + if (options.plural) { + alias = qx.locale.Manager.tr("Apps"); + } + + if (options.firstUpperCase) { + alias = osparc.utils.Utils.capitalize(alias); + } else if (options.allUpperCase) { + alias = alias.toUpperCase(); + } + + return alias; + }, + resourceTypeToAlias: function(resourceType, options) { switch (resourceType) { case "study": @@ -110,6 +140,10 @@ qx.Class.define("osparc.product.Utils", { return this.getTemplateAlias(options); case "service": return this.getServiceAlias(options); + case "hypertool": + return this.getHypertoolAlias(options); + case "app": + return this.getAppAlias(options); } return resourceType; }, @@ -188,10 +222,6 @@ qx.Class.define("osparc.product.Utils", { return "REGISTER"; }, - hasConvertToPipelineEnabled: function() { - return osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled(); - }, - // oSPARC only hasExportCMisEnabled: function() { const product = this.getProductName(); diff --git a/services/static-webserver/client/source/class/osparc/service/Utils.js b/services/static-webserver/client/source/class/osparc/service/Utils.js index b7499816eac4..82d7ec021b97 100644 --- a/services/static-webserver/client/source/class/osparc/service/Utils.js +++ b/services/static-webserver/client/source/class/osparc/service/Utils.js @@ -98,11 +98,14 @@ qx.Class.define("osparc.service.Utils", { } servicesArray.sort((a, b) => { if (basedOn.sort === "hits") { - if (a[basedOn.sort] !== b[basedOn.sort]) { + const aHits = a["hits"] === undefined ? -1 : a["hits"]; // Treat undefined hits as -1 + const bHits = b["hits"] === undefined ? -1 : b["hits"]; // Treat undefined hits as -1 + + if (aHits !== bHits) { if (basedOn.order === "down") { - return b[basedOn.sort] - a[basedOn.sort]; + return bHits - aHits; } - return a[basedOn.sort] - b[basedOn.sort]; + return aHits - bHits; } return a["name"].localeCompare(b["name"]); } else if (basedOn.sort === "name") { diff --git a/services/static-webserver/client/source/class/osparc/share/Collaborators.js b/services/static-webserver/client/source/class/osparc/share/Collaborators.js index 2ee15429b167..ca26799687ad 100644 --- a/services/static-webserver/client/source/class/osparc/share/Collaborators.js +++ b/services/static-webserver/client/source/class/osparc/share/Collaborators.js @@ -194,6 +194,7 @@ qx.Class.define("osparc.share.Collaborators", { switch (this._resourceType) { case "study": case "template": + case "hypertool": canIShare = osparc.data.model.Study.canIWrite(this._serializedDataCopy["accessRights"]); break; case "service": @@ -218,6 +219,7 @@ qx.Class.define("osparc.share.Collaborators", { switch (this._resourceType) { case "study": case "template": + case "hypertool": fullOptions = osparc.data.model.Study.canIDelete(this._serializedDataCopy["accessRights"]); break; case "service": @@ -238,6 +240,7 @@ qx.Class.define("osparc.share.Collaborators", { switch (this._resourceType) { case "study": case "template": + case "hypertool": rolesLayout = osparc.data.Roles.createRolesStudyInfo(); break; case "service": @@ -342,7 +345,7 @@ qx.Class.define("osparc.share.Collaborators", { item.addListener("removeMember", e => { const orgMember = e.getData(); if ( - ["study", "template"].includes(this._resourceType) && + ["study", "template", "hypertool"].includes(this._resourceType) && !osparc.share.CollaboratorsStudy.canCollaboratorBeRemoved(this._serializedDataCopy, orgMember["gid"]) ) { let msg = this.tr("Collaborator can't be removed:"); @@ -370,7 +373,7 @@ qx.Class.define("osparc.share.Collaborators", { __getLeaveStudyButton: function() { const myGid = osparc.auth.Data.getInstance().getGroupId(); if ( - ["study", "template"].includes(this._resourceType) && + ["study", "template", "hypertool"].includes(this._resourceType) && osparc.share.CollaboratorsStudy.canCollaboratorBeRemoved(this._serializedDataCopy, myGid) ) { const leaveText = this.tr("Leave") + " " + osparc.product.Utils.getStudyAlias({ diff --git a/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js b/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js index aed7b56d89ce..71655ea452f5 100644 --- a/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js +++ b/services/static-webserver/client/source/class/osparc/share/NewCollaboratorsManager.js @@ -34,10 +34,7 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { this.__potentialCollaborators = {}; this.__reloadPotentialCollaborators(); - this.__shareWithEmailEnabled = false; - if (this.__resourceData["resourceType"] === "study") { - this.__shareWithEmailEnabled = osparc.utils.DisabledPlugins.isShareWithEmailEnabled(); - } + this.__shareWithEmailEnabled = this.__resourceData["resourceType"] === "study"; if (preselectCollaboratorGids && preselectCollaboratorGids.length) { preselectCollaboratorGids.forEach(preselectCollaboratorGid => { @@ -256,6 +253,9 @@ qx.Class.define("osparc.share.NewCollaboratorsManager", { } else if (this.__resourceData && this.__resourceData["resourceType"] === "service") { // all users can share services with ProductEveryone showProductEveryone = true; + } else if (this.__resourceData && this.__resourceData["resourceType"] === "hypertool") { + // all users can share hypertool with ProductEveryone + showProductEveryone = true; } return showProductEveryone; }, diff --git a/services/static-webserver/client/source/class/osparc/store/Templates.js b/services/static-webserver/client/source/class/osparc/store/Templates.js index 6a0a2483849e..8b6deb996880 100644 --- a/services/static-webserver/client/source/class/osparc/store/Templates.js +++ b/services/static-webserver/client/source/class/osparc/store/Templates.js @@ -58,7 +58,10 @@ qx.Class.define("osparc.store.Templates", { getTemplatesHypertools: function() { return this.getTemplates() .then(templates => { - return templates.filter(t => osparc.study.Utils.extractTemplateType(t) === osparc.data.model.StudyUI.HYPERTOOL_TYPE); + const hypertools = templates.filter(t => osparc.study.Utils.extractTemplateType(t) === osparc.data.model.StudyUI.HYPERTOOL_TYPE); + // required for filtering + hypertools.forEach(hypertool => hypertool.type = osparc.data.model.StudyUI.HYPERTOOL_TYPE); + return hypertools; }); }, diff --git a/services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js b/services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js index 8fde68b678fc..c29755d08a1c 100644 --- a/services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js +++ b/services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js @@ -56,7 +56,7 @@ qx.Class.define("osparc.study.SaveAsTemplate", { }); form.add(publishWithData, this.tr("Publish with data"), null, "publishWithData"); - if (osparc.utils.DisabledPlugins.isHypertoolsEnabled()) { + if (osparc.product.Utils.isS4LProduct()) { const templateTypeSB = new qx.ui.form.SelectBox().set({ allowGrowX: false, }); 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 cdaa3ca712db..d4e5d59d4cd0 100644 --- a/services/static-webserver/client/source/class/osparc/study/Utils.js +++ b/services/static-webserver/client/source/class/osparc/study/Utils.js @@ -258,10 +258,6 @@ qx.Class.define("osparc.study.Utils", { }, canCreateFunction: function(workbench) { - if (!osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled()) { - return false; - } - // in order to create a function, the pipeline needs: // - at least one parameter (or file-picker (file type parameter)) // - at least one probe 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 a1bc3cd837b1..84467571190c 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Appearance.js +++ b/services/static-webserver/client/source/class/osparc/theme/Appearance.js @@ -125,6 +125,16 @@ qx.Theme.define("osparc.theme.Appearance", { } }, + "pb-hypertool": { + include: "pb-listitem", + style: function(states) { + const style = { + backgroundColor: "pb-template" + }; + return style; + } + }, + "pb-dynamic": { include: "pb-listitem", style: function(states) { diff --git a/services/static-webserver/client/source/class/osparc/ui/list/CollaboratorListItem.js b/services/static-webserver/client/source/class/osparc/ui/list/CollaboratorListItem.js index cc6b575d06f2..8962431206a9 100644 --- a/services/static-webserver/client/source/class/osparc/ui/list/CollaboratorListItem.js +++ b/services/static-webserver/client/source/class/osparc/ui/list/CollaboratorListItem.js @@ -76,7 +76,7 @@ qx.Class.define("osparc.ui.list.CollaboratorListItem", { members: { __getRoleInfo: function(id) { const resource = this.getResourceType(); - if (resource === "study" || resource === "template") { + if (["study", "template", "hypertool"].includes(resource)) { return osparc.data.Roles.STUDY[id]; } else if (resource === "service") { return osparc.data.Roles.SERVICES[id]; @@ -113,6 +113,24 @@ qx.Class.define("osparc.ui.list.CollaboratorListItem", { return control || this.base(arguments, id); }, + // overridden + _applyTitle: function(value) { + if (value === null) { + return; + } + const groupsStore = osparc.store.Groups.getInstance(); + const everyoneGroupIds = [ + groupsStore.getEveryoneProductGroup().getGroupId(), + groupsStore.getEveryoneGroup().getGroupId(), + ]; + const label = this.getChildControl("title"); + if (everyoneGroupIds.includes(this.getModel())) { + label.setValue(this.tr("Public")); + } else { + label.setValue(value); + } + }, + // overridden _applyThumbnail: function(value) { if (value === null) { diff --git a/services/static-webserver/client/source/class/osparc/ui/list/ListItem.js b/services/static-webserver/client/source/class/osparc/ui/list/ListItem.js index 5b45f066022c..89f0d7c87b7c 100644 --- a/services/static-webserver/client/source/class/osparc/ui/list/ListItem.js +++ b/services/static-webserver/client/source/class/osparc/ui/list/ListItem.js @@ -90,7 +90,7 @@ qx.Class.define("osparc.ui.list.ListItem", { title: { check : "String", - apply : "__applyTitle", + apply : "_applyTitle", nullable : true }, @@ -228,7 +228,7 @@ qx.Class.define("osparc.ui.list.ListItem", { thumbnail.setSource(value); }, - __applyTitle: function(value) { + _applyTitle: function(value) { if (value === null) { return; } diff --git a/services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js b/services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js index 225e775f047b..0578f4b73f9a 100644 --- a/services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js +++ b/services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js @@ -52,25 +52,6 @@ qx.Class.define("osparc.utils.DisabledPlugins", { return this.__isPluginDisabled(this.LICENSES); }, - isShareWithEmailEnabled: function() { - return osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled(); - }, - - isJobsEnabled: function() { - return osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled(); - }, - - isHypertoolsEnabled: function() { - if (osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled() && osparc.product.Utils.isS4LProduct()) { - return true; - } - return false; - }, - - isConversationEnabled: function() { - return osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled(); - }, - __isPluginDisabled: function(key) { const statics = osparc.store.Store.getInstance().get("statics"); if (statics) { diff --git a/services/static-webserver/client/source/class/osparc/utils/Resources.js b/services/static-webserver/client/source/class/osparc/utils/Resources.js index 701480b0d7e4..b5045092a918 100644 --- a/services/static-webserver/client/source/class/osparc/utils/Resources.js +++ b/services/static-webserver/client/source/class/osparc/utils/Resources.js @@ -31,8 +31,12 @@ qx.Class.define("osparc.utils.Resources", { return ((templateData["resourceType"] === "template") && ("uuid" in templateData)); }, + isHypertool: function(hypertoolData) { + return ((hypertoolData["resourceType"] === "hypertool") && ("uuid" in hypertoolData)); + }, + isService: function(serviceData) { return ((serviceData["resourceType"] === "service") && ("key" in serviceData) && ("version" in serviceData)); - } + }, } }); 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 ea48b14120f1..9d57a035e29c 100644 --- a/services/static-webserver/client/source/class/osparc/utils/Utils.js +++ b/services/static-webserver/client/source/class/osparc/utils/Utils.js @@ -457,21 +457,6 @@ qx.Class.define("osparc.utils.Utils", { return (["dev", "master"].includes(platformName)); }, - resourceTypeToAlias: function(resourceType) { - switch (resourceType) { - case "study": - resourceType = osparc.product.Utils.getStudyAlias({firstUpperCase: true}); - break; - case "template": - resourceType = osparc.product.Utils.getTemplateAlias({firstUpperCase: true}); - break; - case "service": - resourceType = qx.locale.Manager.tr("Service"); - break; - } - return resourceType; - }, - getEditButton: function(isVisible = true) { return new qx.ui.form.Button(null, "@FontAwesome5Solid/pencil-alt/12").set({ appearance: "form-button-outlined", diff --git a/services/static-webserver/client/source/resource/osparc/circle-play-text.svg b/services/static-webserver/client/source/resource/osparc/circle-play-text.svg deleted file mode 100644 index 922e5c5d6f0b..000000000000 --- a/services/static-webserver/client/source/resource/osparc/circle-play-text.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/services/static-webserver/client/source/resource/osparc/tours/osparc_tours.json b/services/static-webserver/client/source/resource/osparc/tours/osparc_tours.json index 24ff1e63ef10..1d0d098e8f25 100644 --- a/services/static-webserver/client/source/resource/osparc/tours/osparc_tours.json +++ b/services/static-webserver/client/source/resource/osparc/tours/osparc_tours.json @@ -60,9 +60,9 @@ "placement": "bottom" }, { "beforeClick": { - "selector": "osparc-test-id=servicesTabBtn" + "selector": "osparc-test-id=appsTabBtn" }, - "anchorEl": "osparc-test-id=servicesTabBtn", + "anchorEl": "osparc-test-id=appsTabBtn", "text": "Every Study in oSparc is composed of so-called Services.
These are building blocks for Studies and can provide data/files, visualize results (2D, 3D), implement code in Jupyter notebooks or perform computations to execute simulations within a Study.", "placement": "bottom" }, { diff --git a/services/static-webserver/client/source/resource/osparc/tours/s4l_tours.json b/services/static-webserver/client/source/resource/osparc/tours/s4l_tours.json index 0e5f056a68ce..f08e7937c80d 100644 --- a/services/static-webserver/client/source/resource/osparc/tours/s4l_tours.json +++ b/services/static-webserver/client/source/resource/osparc/tours/s4l_tours.json @@ -60,9 +60,9 @@ "placement": "bottom" }, { "beforeClick": { - "selector": "osparc-test-id=servicesTabBtn" + "selector": "osparc-test-id=appsTabBtn" }, - "anchorEl": "osparc-test-id=servicesTabBtn", + "anchorEl": "osparc-test-id=appsTabBtn", "text": "Every Project in Sim4Life is composed of at least one so-called Service.
Services are building blocks for Projects and can provide data/files, visualize results (2D, 3D), implement code in Jupyter notebooks or perform computations to execute simulations within a Project.", "placement": "bottom" }] diff --git a/tests/e2e-frontend/tests/navigationBar/navigationBar.spec.js b/tests/e2e-frontend/tests/navigationBar/navigationBar.spec.js index c6bb8752f334..8f77bff20165 100644 --- a/tests/e2e-frontend/tests/navigationBar/navigationBar.spec.js +++ b/tests/e2e-frontend/tests/navigationBar/navigationBar.spec.js @@ -175,7 +175,7 @@ for (const product in products) { }; await checkButton("templatesTabBtn", isTemplatesVisible, templatesLabel); - await checkButton("servicesTabBtn", isServicesVisible, servicesLabel); + await checkButton("appsTabBtn", isServicesVisible, servicesLabel); await checkButton("dataTabBtn", isDataVisible, dataLabel); }); diff --git a/tests/e2e-frontend/tests/serviceBrowser/leftFilters.spec.js b/tests/e2e-frontend/tests/serviceBrowser/leftFilters.spec.js index 247f5def5822..eb08072dd1f4 100644 --- a/tests/e2e-frontend/tests/serviceBrowser/leftFilters.spec.js +++ b/tests/e2e-frontend/tests/serviceBrowser/leftFilters.spec.js @@ -28,7 +28,7 @@ test.describe.serial(`Left Filters:`, () => { await responsePromise; - await page.getByTestId("servicesTabBtn").click(); + await page.getByTestId("appsTabBtn").click(); }); test.afterAll(async ({ browser }) => { diff --git a/tests/e2e-frontend/tests/serviceBrowser/mainView.spec.js b/tests/e2e-frontend/tests/serviceBrowser/mainView.spec.js index c3077e467e70..b7ac9c8cc4db 100644 --- a/tests/e2e-frontend/tests/serviceBrowser/mainView.spec.js +++ b/tests/e2e-frontend/tests/serviceBrowser/mainView.spec.js @@ -7,7 +7,7 @@ import { LoginPage } from '../fixtures/loginPage'; import products from '../products.json'; import users from '../users.json'; -const servicesTabExposed = { +const appsTabExposed = { "osparc": { "areServicesExposed": true, }, @@ -29,8 +29,8 @@ const servicesTabExposed = { } for (const product in products) { - expect(servicesTabExposed[product]).toBeDefined(); - if (!servicesTabExposed[product]["areServicesExposed"]) { + expect(appsTabExposed[product]).toBeDefined(); + if (!appsTabExposed[product]["areServicesExposed"]) { continue; } @@ -62,7 +62,7 @@ for (const product in products) { expect("data" in resp && "_meta" in resp["data"] && "total" in resp["data"]["_meta"]); console.log("N Services in Response:", resp["data"]["_meta"]["total"]); - await page.getByTestId("servicesTabBtn").click(); + await page.getByTestId("appsTabBtn").click(); }); test.afterAll(async ({ browser }) => { diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 60e92755190e..45c200022290 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -663,7 +663,7 @@ def _( service_key_prefix: str | None, ) -> None: with log_context(logging.INFO, f"Finding {service_name=} in dashboard"): - page.get_by_test_id("servicesTabBtn").click() + page.get_by_test_id("appsTabBtn").click() _textbox = page.get_by_test_id("searchBarFilter-textField-service") _textbox.fill(service_name) _textbox.press("Enter") diff --git a/tests/e2e/utils/auto.js b/tests/e2e/utils/auto.js index f9a723059d5b..25c11b8c6637 100644 --- a/tests/e2e/utils/auto.js +++ b/tests/e2e/utils/auto.js @@ -101,7 +101,7 @@ async function __dashboardTemplatesBrowser(page) { async function __dashboardServicesBrowser(page) { console.log("Navigating through Services"); - await utils.waitAndClick(page, '[osparc-test-id="servicesTabBtn"]'); + await utils.waitAndClick(page, '[osparc-test-id="appsTabBtn"]'); } async function dashboardNewTIPlan(page) {