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 9334861f11c..31524310535 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -116,7 +116,11 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }; osparc.data.Resources.fetch("studies", "getWallet", params) .then(wallet => { - if (isStudyCreation || wallet === null || osparc.desktop.credits.Utils.getWallet(wallet["walletId"]) === null) { + if ( + 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); const win = osparc.study.StudyOptions.popUpInWindow(resourceSelector); 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 a1ae4d742fa..76e9f628829 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -364,7 +364,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const resourceData = this.__resourceData; if (osparc.utils.Resources.isStudy(resourceData)) { const id = "Billing"; - const title = this.tr("Billing Settings"); + const title = this.tr("Tier Settings"); const iconSrc = "@FontAwesome5Solid/cogs/22"; const page = this.__billingSettings = new osparc.dashboard.resources.pages.BasePage(title, iconSrc, id); this.__addOpenButton(page); 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 7349d7d46b5..288290b06df 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -374,12 +374,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { newWorkspaceCard.setCardKey("new-workspace"); newWorkspaceCard.subscribeToFilterGroup("searchBarFilter"); [ - "createWorkspace", - "updateWorkspace" + "workspaceCreated", + "workspaceDeleted", + "workspaceUpdated", ].forEach(e => { - newWorkspaceCard.addListener(e, () => { - this.__reloadWorkspaces(); - }); + newWorkspaceCard.addListener(e, () => this.__reloadWorkspaces()); }); this._resourcesContainer.addNewWorkspaceCard(newWorkspaceCard); }, @@ -1170,7 +1169,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __newStudyBtnClicked: function(button) { button.setValue(false); const minStudyData = osparc.data.model.Study.createMinStudyObject(); - const title = osparc.utils.Utils.getUniqueStudyName(minStudyData.name, this._resourcesList); + const existingNames = this._resourcesList.map(study => study["name"]); + const title = osparc.utils.Utils.getUniqueName(minStudyData.name, existingNames); minStudyData["name"] = title; minStudyData["workspaceId"] = this.getCurrentWorkspaceId(); minStudyData["folderId"] = this.getCurrentFolderId(); @@ -1190,7 +1190,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __newPlanBtnClicked: function(templateData, newStudyName) { // do not override cached template data const templateCopyData = osparc.utils.Utils.deepCloneObject(templateData); - const title = osparc.utils.Utils.getUniqueStudyName(newStudyName, this._resourcesList); + const existingNames = this._resourcesList.map(study => study["name"]); + const title = osparc.utils.Utils.getUniqueName(newStudyName, existingNames); templateCopyData.name = title; this._showLoadingPage(this.tr("Creating ") + (newStudyName || osparc.product.Utils.getStudyAlias())); const contextProps = { @@ -1411,7 +1412,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __getBillingMenuButton: function(card) { - const text = osparc.utils.Utils.capitalize(this.tr("Billing Settings...")); + const text = osparc.utils.Utils.capitalize(this.tr("Tier Settings...")); const studyBillingSettingsButton = new qx.ui.menu.Button(text); studyBillingSettingsButton["billingSettingsButton"] = true; studyBillingSettingsButton.addListener("tap", () => card.openBilling(), this); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js index 9e2ca51b434..87a6a366b58 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js @@ -339,10 +339,10 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", { __editWorkspace: function() { const workspace = osparc.store.Workspaces.getInstance().getWorkspace(this.getCurrentWorkspaceId()); - const permissionsView = new osparc.editor.WorkspaceEditor(workspace); + const workspaceEditor = new osparc.editor.WorkspaceEditor(workspace); const title = this.tr("Edit Workspace"); - const win = osparc.ui.window.Window.popUpInWindow(permissionsView, title, 300, 200); - permissionsView.addListener("workspaceUpdated", () => { + const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 150); + workspaceEditor.addListener("workspaceUpdated", () => { win.close(); this.__buildLayout(); }, this); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js index 5581ec3212b..4d5253410bf 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js @@ -185,7 +185,7 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { const workspace = this.getWorkspace(); const workspaceEditor = new osparc.editor.WorkspaceEditor(workspace); const title = this.tr("Edit Workspace"); - const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 200); + const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 300, 150); workspaceEditor.addListener("workspaceUpdated", () => { win.close(); this.fireDataEvent("workspaceUpdated", workspace.getWorkspaceId()); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js index fc1526b387d..ac87579355e 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js @@ -46,26 +46,29 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonNew", { }, events: { - "createWorkspace": "qx.event.type.Data", - "updateWorkspace": "qx.event.type.Data" + "workspaceCreated": "qx.event.type.Event", + "workspaceDeleted": "qx.event.type.Event", + "workspaceUpdated": "qx.event.type.Event", }, members: { __itemSelected: function(newVal) { if (newVal) { - const workspaceCreator = new osparc.editor.WorkspaceEditor(); + const workspaceEditor = new osparc.editor.WorkspaceEditor(); const title = this.tr("New Workspace"); - const win = osparc.ui.window.Window.popUpInWindow(workspaceCreator, title, 300, 200); - workspaceCreator.addListener("workspaceCreated", e => { - win.close(); - const newWorkspace = e.getData(); - this.fireDataEvent("createWorkspace", newWorkspace.getWorkspaceId(), this); - const permissionsView = new osparc.share.CollaboratorsWorkspace(newWorkspace); - const title2 = qx.locale.Manager.tr("Share Workspace"); - osparc.ui.window.Window.popUpInWindow(permissionsView, title2, 500, 500); - permissionsView.addListener("updateAccessRights", () => this.fireDataEvent("updateWorkspace", newWorkspace.getWorkspaceId()), this); + const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 500, 500).set({ + modal: true, + clickAwayClose: false, }); - workspaceCreator.addListener("cancel", () => win.close()); + workspaceEditor.addListener("workspaceCreated", () => this.fireEvent("workspaceCreated")); + workspaceEditor.addListener("workspaceDeleted", () => this.fireEvent("workspaceDeleted")); + workspaceEditor.addListener("workspaceUpdated", () => { + win.close(); + this.fireEvent("workspaceUpdated"); + }, this); + workspaceEditor.addListener("updateAccessRights", () => this.fireEvent("workspaceUpdated")); + win.getChildControl("close-button").addListener("tap", () => workspaceEditor.cancel()); + workspaceEditor.addListener("cancel", () => win.close()); } this.setValue(false); } diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js index c65318bfcd3..7f35c3ff320 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js @@ -300,7 +300,7 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { if (oldParentFolderId === undefined) { // it was removed, not moved // remove it from the cached models - const modelFound = this.__getModel(folder.getWorkspaceId(), folder.getParentFolderId()); + const modelFound = this.__getModel(folder.getWorkspaceId(), folder.getFolderId()); if (modelFound) { const index = this.__models.indexOf(modelFound); if (index > -1) { // only splice array when item is found diff --git a/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationDetails.js b/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationDetails.js index 6871348d8a0..c9d0501c0cd 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationDetails.js +++ b/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationDetails.js @@ -94,17 +94,9 @@ qx.Class.define("osparc.desktop.organizations.OrganizationDetails", { __openEditOrganization: function() { const org = this.__orgModel; - - const newOrg = false; - const orgEditor = new osparc.editor.OrganizationEditor(newOrg); - org.bind("gid", orgEditor, "gid"); - org.bind("label", orgEditor, "label"); - org.bind("description", orgEditor, "description"); - org.bind("thumbnail", orgEditor, "thumbnail", { - converter: val => val ? val : "" - }); const title = this.tr("Organization Details Editor"); - const win = osparc.ui.window.Window.popUpInWindow(orgEditor, title, 400, 250); + const orgEditor = new osparc.editor.OrganizationEditor(org); + const win = osparc.ui.window.Window.popUpInWindow(orgEditor, title, 400, 200); orgEditor.addListener("updateOrg", () => { this.__updateOrganization(win, orgEditor.getChildControl("save"), orgEditor); }); diff --git a/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js b/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js index 740f54211fa..c2f8656ed83 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/organizations/OrganizationsList.js @@ -99,10 +99,9 @@ qx.Class.define("osparc.desktop.organizations.OrganizationsList", { allowGrowX: false }); createOrgBtn.addListener("execute", function() { - const newOrg = true; - const orgEditor = new osparc.editor.OrganizationEditor(newOrg); const title = this.tr("New Organization"); - const win = osparc.ui.window.Window.popUpInWindow(orgEditor, title, 400, 250); + const orgEditor = new osparc.editor.OrganizationEditor(); + const win = osparc.ui.window.Window.popUpInWindow(orgEditor, title, 400, 200); orgEditor.addListener("createOrg", () => { this.__createOrganization(win, orgEditor.getChildControl("create"), orgEditor); }); @@ -176,7 +175,7 @@ qx.Class.define("osparc.desktop.organizations.OrganizationsList", { } }, - reloadOrganizations: function() { + reloadOrganizations: function(orgId) { this.__orgsUIList.resetSelection(); const orgsModel = this.__orgsModel; orgsModel.removeAll(); @@ -199,6 +198,9 @@ qx.Class.define("osparc.desktop.organizations.OrganizationsList", { orgsList.sort(this.self().sortOrganizations); orgsList.forEach(org => orgsModel.append(qx.data.marshal.Json.createModel(org))); this.setOrganizationsLoaded(true); + if (orgId) { + this.fireDataEvent("organizationSelected", orgId); + } }); }, @@ -208,16 +210,9 @@ qx.Class.define("osparc.desktop.organizations.OrganizationsList", { return; } - const newOrg = false; - const orgEditor = new osparc.editor.OrganizationEditor(newOrg); - org.bind("gid", orgEditor, "gid"); - org.bind("label", orgEditor, "label"); - org.bind("description", orgEditor, "description"); - org.bind("thumbnail", orgEditor, "thumbnail", { - converter: val => val ? val : "" - }); const title = this.tr("Organization Details Editor"); - const win = osparc.ui.window.Window.popUpInWindow(orgEditor, title, 400, 250); + const orgEditor = new osparc.editor.OrganizationEditor(org); + const win = osparc.ui.window.Window.popUpInWindow(orgEditor, title, 400, 200); orgEditor.addListener("updateOrg", () => { this.__updateOrganization(win, orgEditor.getChildControl("save"), orgEditor); }); @@ -287,14 +282,15 @@ qx.Class.define("osparc.desktop.organizations.OrganizationsList", { } }; osparc.data.Resources.fetch("organizations", "post", params) - .then(() => { + .then(org => { osparc.FlashMessenger.getInstance().logAs(name + this.tr(" successfully created")); button.setFetching(false); osparc.store.Store.getInstance().reset("organizations"); // reload "profile", "organizations" are part of the information in this endpoint osparc.data.Resources.getOne("profile", {}, null, false) .then(() => { - this.reloadOrganizations(); + // open it + this.reloadOrganizations(org["gid"]); }); }) .catch(err => { diff --git a/services/static-webserver/client/source/class/osparc/editor/OrganizationEditor.js b/services/static-webserver/client/source/class/osparc/editor/OrganizationEditor.js index f4be5233d2f..b528e760c01 100644 --- a/services/static-webserver/client/source/class/osparc/editor/OrganizationEditor.js +++ b/services/static-webserver/client/source/class/osparc/editor/OrganizationEditor.js @@ -18,7 +18,7 @@ qx.Class.define("osparc.editor.OrganizationEditor", { extend: qx.ui.core.Widget, - construct: function(newOrg = true) { + construct: function(organization) { this.base(arguments); this._setLayout(new qx.ui.layout.VBox(8)); @@ -29,7 +29,27 @@ qx.Class.define("osparc.editor.OrganizationEditor", { manager.add(title); this.getChildControl("description"); this.getChildControl("thumbnail"); - newOrg ? this.getChildControl("create") : this.getChildControl("save"); + organization ? this.getChildControl("save") : this.getChildControl("create"); + + if (organization) { + organization.bind("gid", this, "gid"); + organization.bind("label", this, "label"); + organization.bind("description", this, "description"); + organization.bind("thumbnail", this, "thumbnail", { + converter: val => val ? val : "" + }); + } else { + osparc.store.Store.getInstance().getGroupsOrganizations() + .then(orgs => { + const existingNames = orgs.map(org => org["label"]); + const defaultName = osparc.utils.Utils.getUniqueName("New Organization", existingNames) + title.setValue(defaultName); + }) + .catch(err => { + console.error(err); + title.setValue("New Organization"); + }); + } this.addListener("appear", () => { title.focus(); @@ -82,7 +102,7 @@ qx.Class.define("osparc.editor.OrganizationEditor", { font: "text-14", backgroundColor: "background-main", placeholder: this.tr("Title"), - height: 35 + height: 30, }); this.bind("label", control, "value"); control.bind("value", this, "label"); @@ -90,12 +110,10 @@ qx.Class.define("osparc.editor.OrganizationEditor", { break; } case "description": { - control = new qx.ui.form.TextArea().set({ + control = new qx.ui.form.TextField().set({ font: "text-14", placeholder: this.tr("Description"), - autoSize: true, - minHeight: 70, - maxHeight: 140 + height: 30, }); this.bind("description", control, "value"); control.bind("value", this, "description"); @@ -106,7 +124,7 @@ qx.Class.define("osparc.editor.OrganizationEditor", { control = new qx.ui.form.TextField().set({ font: "text-14", placeholder: this.tr("Thumbnail"), - height: 35 + height: 30, }); this.bind("thumbnail", control, "value"); control.bind("value", this, "thumbnail"); diff --git a/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js b/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js index 6b89ee2af78..dab5a9807c3 100644 --- a/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js +++ b/services/static-webserver/client/source/class/osparc/editor/WorkspaceEditor.js @@ -33,20 +33,33 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { manager.add(title); this.getChildControl("description"); this.getChildControl("thumbnail"); - workspace ? this.getChildControl("save") : this.getChildControl("create"); + this.getChildControl("cancel"); + this.getChildControl("save"); if (workspace) { - this.__workspaceId = workspace.getWorkspaceId(); - this.set({ - label: workspace.getName(), - description: workspace.getDescription(), - thumbnail: workspace.getThumbnail(), - }); + // editing + this.setWorkspace(workspace); + } else { + // creating + this.__creatingWorkspace = true; + this.__createWorkspace() + .then(newWorkspace => { + this.setWorkspace(newWorkspace); + this.fireDataEvent("workspaceCreated"); + this.getChildControl("sharing"); + }); } this.addListener("appear", this.__onAppear, this); }, properties: { + workspace: { + check: "osparc.data.model.Workspace", + init: null, + nullable: false, + apply: "__applyWorkspace" + }, + label: { check: "String", init: "", @@ -70,13 +83,26 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { }, events: { - "workspaceCreated": "qx.event.type.Data", + "workspaceCreated": "qx.event.type.Event", + "workspaceDeleted": "qx.event.type.Event", "workspaceUpdated": "qx.event.type.Event", + "updateAccessRights": "qx.event.type.Event", "cancel": "qx.event.type.Event" }, + statics: { + POS: { + INTRO: 0, + TITLE: 1, + DESCRIPTION: 2, + THUMBNAIL: 3, + SHARING: 4, + BUTTONS: 5, + } + }, + members: { - __workspaceId: null, + __creatingWorkspace: null, _createChildControlImpl: function(id) { let control; @@ -89,7 +115,7 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { rich: true, wrap: true }); - this._add(control); + this._addAt(control, this.self().POS.INTRO); break; } case "title": { @@ -97,71 +123,64 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { font: "text-14", backgroundColor: "background-main", placeholder: this.tr("Title"), - minHeight: 27 + height: 30, }); this.bind("label", control, "value"); control.bind("value", this, "label"); - this._add(control); + this._addAt(control, this.self().POS.TITLE); break; } case "description": { - control = new qx.ui.form.TextArea().set({ + control = new qx.ui.form.TextField().set({ font: "text-14", placeholder: this.tr("Description"), - autoSize: true, - minHeight: 70, + height: 30, }); this.bind("description", control, "value"); control.bind("value", this, "description"); - this._add(control); + this._addAt(control, this.self().POS.DESCRIPTION); break; } case "thumbnail": { control = new qx.ui.form.TextField().set({ font: "text-14", placeholder: this.tr("Thumbnail"), + height: 30, }); this.bind("thumbnail", control, "value"); control.bind("value", this, "thumbnail"); - this._add(control); + this._addAt(control, this.self().POS.THUMBNAIL); break; } - case "create": { - const buttons = this.getChildControl("buttonsLayout"); - control = new osparc.ui.form.FetchButton(this.tr("Create")).set({ - appearance: "form-button" - }); - control.addListener("execute", () => { - if (this.__validator.validate()) { - this.__createWorkspace(control); - } - }, this); - buttons.addAt(control, 1); + case "sharing": { + control = new osparc.share.CollaboratorsWorkspace(this.getWorkspace()); + control.addListener("updateAccessRights", () => this.fireDataEvent("updateAccessRights", this.getWorkspace().getWorkspaceId()), this); + this._addAt(control, this.self().POS.SHARING); + break; + } + case "buttons-layout": { + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(8).set({ + alignX: "right" + })); + this._addAt(control, this.self().POS.BUTTONS); break; } case "save": { - const buttons = this.getChildControl("buttonsLayout"); + const buttons = this.getChildControl("buttons-layout"); control = new osparc.ui.form.FetchButton(this.tr("Save")).set({ appearance: "form-button" }); - control.addListener("execute", () => { - if (this.__validator.validate()) { - this.__editWorkspace(control); - } - }, this); + control.addListener("execute", () => this.__saveWorkspace(control), this); buttons.addAt(control, 1); break; } - case "buttonsLayout": { - control = new qx.ui.container.Composite(new qx.ui.layout.HBox(8).set({ - alignX: "right" - })); - const cancelButton = new qx.ui.form.Button(this.tr("Cancel")).set({ + case "cancel": { + const buttons = this.getChildControl("buttons-layout"); + control = new qx.ui.form.Button(this.tr("Cancel")).set({ appearance: "form-button-text" }); - cancelButton.addListener("execute", () => this.fireEvent("cancel"), this); - control.addAt(cancelButton, 0); - this._add(control); + control.addListener("execute", () => this.cancel(), this); + buttons.addAt(control, 0); break; } } @@ -169,36 +188,55 @@ qx.Class.define("osparc.editor.WorkspaceEditor", { return control || this.base(arguments, id); }, - __createWorkspace: function(createButton) { - createButton.setFetching(true); + __applyWorkspace: function(workspace) { + this.set({ + label: workspace.getName(), + description: workspace.getDescription(), + thumbnail: workspace.getThumbnail(), + }); + }, + + __createWorkspace: function() { + const workspaceStore = osparc.store.Workspaces.getInstance(); + const workspaces = workspaceStore.getWorkspaces(); + const existingNames = workspaces.map(workspace => workspace.getName()); + const defaultName = osparc.utils.Utils.getUniqueName("New Workspace", existingNames) const newWorkspaceData = { - name: this.getLabel(), + name: this.getLabel() || defaultName, description: this.getDescription(), thumbnail: this.getThumbnail(), }; - osparc.store.Workspaces.getInstance().postWorkspace(newWorkspaceData) - .then(newWorkspace => this.fireDataEvent("workspaceCreated", newWorkspace)) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }) - .finally(() => createButton.setFetching(false)); + return workspaceStore.postWorkspace(newWorkspaceData) }, - __editWorkspace: function(editButton) { - editButton.setFetching(true); - const updateData = { - name: this.getLabel(), - description: this.getDescription(), - thumbnail: this.getThumbnail(), - }; - osparc.store.Workspaces.getInstance().putWorkspace(this.__workspaceId, updateData) - .then(() => this.fireEvent("workspaceUpdated")) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }) - .finally(() => editButton.setFetching(false)); + __saveWorkspace: function(editButton) { + if (this.__validator.validate()) { + editButton.setFetching(true); + const updateData = { + name: this.getLabel(), + description: this.getDescription(), + thumbnail: this.getThumbnail(), + }; + osparc.store.Workspaces.getInstance().putWorkspace(this.getWorkspace().getWorkspaceId(), updateData) + .then(() => this.fireEvent("workspaceUpdated")) + .catch(err => { + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); + }) + .finally(() => editButton.setFetching(false)); + } + }, + + cancel: function() { + if (this.__creatingWorkspace) { + osparc.store.Workspaces.getInstance().deleteWorkspace(this.getWorkspace().getWorkspaceId()) + .then(() => this.fireEvent("workspaceDeleted")) + .catch(err => { + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); + }); + } + this.fireEvent("cancel"); }, __onAppear: function() { diff --git a/services/static-webserver/client/source/class/osparc/store/Workspaces.js b/services/static-webserver/client/source/class/osparc/store/Workspaces.js index 8d803de0af5..253ac714a1d 100644 --- a/services/static-webserver/client/source/class/osparc/store/Workspaces.js +++ b/services/static-webserver/client/source/class/osparc/store/Workspaces.js @@ -197,6 +197,10 @@ qx.Class.define("osparc.store.Workspaces", { return this.workspacesCached.find(w => w.getWorkspaceId() === workspaceId); }, + getWorkspaces: function() { + return this.workspacesCached; + }, + __addToCache: function(workspace) { const found = this.workspacesCached.find(w => w.getWorkspaceId() === workspace.getWorkspaceId()); if (!found) { 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 54ba001d6d6..9922ec017e3 100644 --- a/services/static-webserver/client/source/class/osparc/study/StudyOptions.js +++ b/services/static-webserver/client/source/class/osparc/study/StudyOptions.js @@ -23,28 +23,17 @@ qx.Class.define("osparc.study.StudyOptions", { this._setLayout(new qx.ui.layout.VBox(15)); - this.__studyId = studyId; - - const params = { - url: { - studyId - } - }; - Promise.all([ - osparc.data.Resources.getOne("studies", params), - osparc.data.Resources.fetch("studies", "getWallet", params) - ]) - .then(values => { - const studyData = values[0]; - this.__studyData = osparc.data.model.Study.deepCloneStudyObject(studyData); - if (values[1] && "walletId" in values[1]) { - this.__projectWalletId = values[1]["walletId"]; - } - this.__buildLayout(); - }); + this.setStudyId(studyId); }, properties: { + studyId: { + check: "String", + init: null, + nullable: false, + apply: "__fetchStudy" + }, + wallet: { check: "osparc.data.model.Wallet", init: null, @@ -93,9 +82,8 @@ qx.Class.define("osparc.study.StudyOptions", { }, members: { - __studyId: null, __studyData: null, - __projectWalletId: null, + __studyWalletId: null, _createChildControlImpl: function(id) { let control; @@ -105,7 +93,7 @@ qx.Class.define("osparc.study.StudyOptions", { this._addAt(control, 0); break; case "title-field": - control = new qx.ui.form.TextField(this.__studyData["name"]).set({ + control = new qx.ui.form.TextField().set({ maxWidth: 220 }); this.getChildControl("title-layout").add(control); @@ -192,6 +180,27 @@ qx.Class.define("osparc.study.StudyOptions", { return control || this.base(arguments, id); }, + __fetchStudy: function(studyId) { + const params = { + url: { + studyId + } + }; + Promise.all([ + osparc.data.Resources.getOne("studies", params), + osparc.data.Resources.fetch("studies", "getWallet", params) + ]) + .then(values => { + const studyData = values[0]; + this.__studyData = osparc.data.model.Study.deepCloneStudyObject(studyData); + + if (values[1] && "walletId" in values[1]) { + this.__studyWalletId = values[1]["walletId"]; + } + this.__buildLayout(); + }); + }, + __applyWallet: function(wallet) { if (wallet) { const walletSelector = this.getChildControl("wallet-selector"); @@ -214,15 +223,16 @@ qx.Class.define("osparc.study.StudyOptions", { __buildTopSummaryLayout: function() { const store = osparc.store.Store.getInstance(); - this._createChildControlImpl("title-label"); const titleField = this.getChildControl("title-field"); + if (this.__studyData) { + titleField.setValue(this.__studyData["name"]); + } titleField.addListener("appear", () => { titleField.focus(); titleField.activate(); }); // Wallet Selector - this._createChildControlImpl("wallet-selector-label"); const walletSelector = this.getChildControl("wallet-selector"); const wallets = store.getWallets(); @@ -241,8 +251,8 @@ qx.Class.define("osparc.study.StudyOptions", { } }); const preferredWallet = store.getPreferredWallet(); - if (wallets.find(wallet => wallet.getWalletId() === parseInt(this.__projectWalletId))) { - selectWallet(this.__projectWalletId); + if (wallets.find(wallet => wallet.getWalletId() === parseInt(this.__studyWalletId))) { + selectWallet(this.__studyWalletId); } else if (preferredWallet) { selectWallet(preferredWallet.getWalletId()); } else if (!osparc.desktop.credits.Utils.autoSelectActiveWallet(walletSelector)) { @@ -283,17 +293,18 @@ qx.Class.define("osparc.study.StudyOptions", { // first, update the name if necessary const titleSelection = this.getChildControl("title-field").getValue(); - if (this.__studyData["name"] !== titleSelection) { + if (this.__studyData && this.__studyData["name"] !== titleSelection) { await this.__updateName(this.__studyData, titleSelection); } // second, update the wallet if necessary const store = osparc.store.Store.getInstance(); const walletSelection = this.getChildControl("wallet-selector").getSelection(); - if (walletSelection.length && walletSelection[0]["walletId"]) { + const studyId = this.getStudyId(); + if (studyId && walletSelection.length && walletSelection[0]["walletId"]) { const params = { url: { - "studyId": this.__studyData["uuid"], + studyId, "walletId": walletSelection[0]["walletId"] } }; 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 dab2bd53bd8..0240d263e47 100644 --- a/services/static-webserver/client/source/class/osparc/study/Utils.js +++ b/services/static-webserver/client/source/class/osparc/study/Utils.js @@ -116,7 +116,8 @@ qx.Class.define("osparc.study.Utils", { newStudyLabel = metadata["name"]; } if (existingStudies) { - const title = osparc.utils.Utils.getUniqueStudyName(newStudyLabel, existingStudies); + const existingNames = existingStudies.map(study => study["name"]); + const title = osparc.utils.Utils.getUniqueName(newStudyLabel, existingNames); minStudyData["name"] = title; } else { minStudyData["name"] = newStudyLabel; @@ -234,7 +235,7 @@ qx.Class.define("osparc.study.Utils", { // update task osparc.widget.ProgressSequence.updateTaskProgress(existingTask, { value: percent, - progressLabel: percent*100 + "%" + progressLabel: parseFloat((percent*100).toFixed(2)) + "%" }); } else { // new task 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 5c751c2ee8f..b095d95eee2 100644 --- a/services/static-webserver/client/source/class/osparc/utils/Utils.js +++ b/services/static-webserver/client/source/class/osparc/utils/Utils.js @@ -277,12 +277,11 @@ qx.Class.define("osparc.utils.Utils", { return reloadButton; }, - getUniqueStudyName: function(preferredName, list) { + getUniqueName: function(preferredName, existingNames) { let title = preferredName; - const existingTitles = list.map(study => study.name); - if (existingTitles.includes(title)) { + if (existingNames.includes(title)) { let cont = 1; - while (existingTitles.includes(`${title} (${cont})`)) { + while (existingNames.includes(`${title} (${cont})`)) { cont++; } title += ` (${cont})`; 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 cacb9ffb83d..492544fa598 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 @@ -7,7 +7,7 @@ "steps": [{ "anchorEl": "osparc-test-id=dashboardTabs", "title": "Dashboard Menu", - "text": "The menu tabs give you quick access to a set of core elements of the platform, namely Projects, Tutorials, Services and Data.", + "text": "The menu tabs give you quick access to a set of core elements of the platform, namely Projects, Tutorials and Services.", "placement": "bottom" }, { "beforeClick": { @@ -28,7 +28,7 @@ "selector": "osparc-test-id=servicesTabBtn" }, "anchorEl": "osparc-test-id=servicesTabBtn", - "text": "Every Project in Sim4Life is composed of at lease one so-called Service.
Services 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 Project.", + "text": "Every Project in Sim4Life is composed of at lease 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" }] },