diff --git a/services/static-webserver/client/source/class/osparc/data/Job.js b/services/static-webserver/client/source/class/osparc/data/Job.js index 8b0733da7ca..c163cf7ca87 100644 --- a/services/static-webserver/client/source/class/osparc/data/Job.js +++ b/services/static-webserver/client/source/class/osparc/data/Job.js @@ -28,7 +28,7 @@ qx.Class.define("osparc.data.Job", { startedAt: jobData["startedAt"] ? new Date(jobData["startedAt"]) : null, endedAt: jobData["endedAt"] ? new Date(jobData["endedAt"]) : null, info: jobData["info"] || null, - customMetadata: jobData["customMetadata"] || null, + customMetadata: jobData["projectCustomMetadata"] || null, }); if (jobData["info"] && jobData["info"]["project_name"]) { diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index 8c4413ba44a..5f8def13791 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -1061,6 +1061,22 @@ qx.Class.define("osparc.data.Resources", { method: "GET", url: statics.API + "/admin/users:search?email={email}" }, + getPendingUsers: { + method: "GET", + url: statics.API + "/admin/users?status=PENDING" + }, + approveUser: { + method: "POST", + url: statics.API + "/admin/users:approve" + }, + rejectUser: { + method: "POST", + url: statics.API + "/admin/users:reject" + }, + resendConfirmationEmail: { + method: "POST", + url: statics.API + "/admin/users:resendConfirmationEmail" + }, preRegister: { method: "POST", url: statics.API + "/admin/users:pre-register" diff --git a/services/static-webserver/client/source/class/osparc/info/ServiceLarge.js b/services/static-webserver/client/source/class/osparc/info/ServiceLarge.js index 07f5f509965..7a38aa72737 100644 --- a/services/static-webserver/client/source/class/osparc/info/ServiceLarge.js +++ b/services/static-webserver/client/source/class/osparc/info/ServiceLarge.js @@ -457,12 +457,6 @@ qx.Class.define("osparc.info.ServiceLarge", { return resourcesLayout; }, - __createRawMetadata: function() { - const container = new qx.ui.container.Scroll(); - container.add(new osparc.ui.basic.JsonTreeWidget(this.getService(), "serviceDescriptionSettings")); - return container; - }, - __openIconEditor: function() { const iconEditor = new osparc.widget.Renamer(this.getService()["icon"], null, this.tr("Edit Icon")); iconEditor.addListener("labelChanged", e => { 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 ecdb587ac82..25ec2296ebe 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js +++ b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js @@ -175,7 +175,7 @@ qx.Class.define("osparc.jobs.SubRunsTable", { if (logDownloadLink) { osparc.utils.Utils.downloadLink(logDownloadLink, "GET", rowData["nodeName"] + ".logs"); } else { - osparc.component.message.FlashMessenger.getInstance().logAsWarning(this.tr("No logs available")); + osparc.FlashMessenger.logAs(this.tr("No logs available"), "WARNING"); } break; } diff --git a/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudy.js b/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudy.js index 6b3b54b0994..bf1cc7a2773 100644 --- a/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudy.js +++ b/services/static-webserver/client/source/class/osparc/metadata/ServicesInStudy.js @@ -132,7 +132,7 @@ qx.Class.define("osparc.metadata.ServicesInStudy", { i++; const node = workbench[nodeId]; - const infoButton = new qx.ui.form.Button(null, "@MaterialIcons/info_outline/14"); + const infoButton = new qx.ui.form.Button(null, "@MaterialIcons/info_outline/16"); infoButton.addListener("execute", () => { const metadata = osparc.store.Services.getMetadata(node["key"], node["version"]); if (metadata === null) { diff --git a/services/static-webserver/client/source/class/osparc/po/POCenter.js b/services/static-webserver/client/source/class/osparc/po/POCenter.js index 39a10d9afdb..331899482b3 100644 --- a/services/static-webserver/client/source/class/osparc/po/POCenter.js +++ b/services/static-webserver/client/source/class/osparc/po/POCenter.js @@ -26,7 +26,10 @@ qx.Class.define("osparc.po.POCenter", { }); this.addWidgetToTabs(miniProfile); - this.__addUsersPage(); + this.__addActiveUsersPage(); + if (osparc.utils.Utils.isDevelopmentPlatform()) { + this.__addPendingUsersPage(); + } this.__addPreRegistrationPage(); this.__addInvitationsPage(); this.__addProductPage(); @@ -34,13 +37,20 @@ qx.Class.define("osparc.po.POCenter", { }, members: { - __addUsersPage: function() { - const title = this.tr("Users"); + __addActiveUsersPage: function() { + const title = this.tr("Active Users"); const iconSrc = "@FontAwesome5Solid/user/22"; const users = new osparc.po.Users(); this.addTab(title, iconSrc, users); }, + __addPendingUsersPage: function() { + const title = this.tr("Pending Users"); + const iconSrc = "@FontAwesome5Solid/user-plus/22"; + const usersPending = new osparc.po.UsersPending(); + this.addTab(title, iconSrc, usersPending); + }, + __addPreRegistrationPage: function() { const title = this.tr("Pre-Registration"); const iconSrc = "@FontAwesome5Solid/address-card/22"; diff --git a/services/static-webserver/client/source/class/osparc/po/POCenterWindow.js b/services/static-webserver/client/source/class/osparc/po/POCenterWindow.js index fc06000ada7..6d10de6212a 100644 --- a/services/static-webserver/client/source/class/osparc/po/POCenterWindow.js +++ b/services/static-webserver/client/source/class/osparc/po/POCenterWindow.js @@ -21,7 +21,7 @@ qx.Class.define("osparc.po.POCenterWindow", { construct: function() { this.base(arguments, "po-center", this.tr("PO Center")); - const width = 800; + const width = 900; const height = 600; this.set({ width, diff --git a/services/static-webserver/client/source/class/osparc/po/UsersPending.js b/services/static-webserver/client/source/class/osparc/po/UsersPending.js new file mode 100644 index 00000000000..5b93793f58f --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/po/UsersPending.js @@ -0,0 +1,274 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Pedro Crespo-Valero (pcrespov) + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.po.UsersPending", { + extend: osparc.po.BaseView, + + statics: { + getPendingUsers: function() { + return new Promise(resolve => { + resolve({ + data: [{ + name: "John Doe", + email: "john.doe@email.com", + date: "2025-01-01 00:00:00.702394", + status: "PENDING", + info: { + "institution": "ETH Zurich", + "department": "Department of Physics", + "position": "PhD Student", + "country": "Switzerland", + "city": "Zurich", + }, + }, { + name: "Jane Doe", + email: "jane.doe@email.com", + date: "2025-01-01 00:01:00.702394", + status: "REJECTED", + info: { + "institution": "ETH Zurich", + "department": "Department of Physics", + "position": "PhD Student", + "country": "Switzerland", + "city": "Zurich", + }, + }, { + name: "Alice Smith", + email: "alice.smith@email.com", + date: "2025-01-01 00:02:00.702394", + status: "APPROVED", + info: { + "institution": "ETH Zurich", + "department": "Department of Physics", + "position": "PhD Student", + "country": "Switzerland", + "city": "Zurich", + }, + }] + }); + }); + }, + + createApproveButton: function(email) { + const button = new osparc.ui.form.FetchButton(qx.locale.Manager.tr("Approve")); + button.addListener("execute", () => { + button.setFetching(true); + const params = { + data: { + email, + }, + }; + osparc.data.Resources.fetch("poUsers", "approveUser", params) + .then(() => { + osparc.FlashMessenger.logAs(qx.locale.Manager.tr("User approved"), "INFO"); + }) + .catch(err => osparc.FlashMessenger.logError(err)) + .finally(() => button.setFetching(false)); + }); + return button; + }, + + createRejectButton: function(email) { + const button = new osparc.ui.form.FetchButton(qx.locale.Manager.tr("Reject")); + button.addListener("execute", () => { + button.setFetching(true); + const params = { + data: { + email, + }, + }; + osparc.data.Resources.fetch("poUsers", "rejectUser", params) + .then(() => { + osparc.FlashMessenger.logAs(qx.locale.Manager.tr("User denied"), "INFO"); + }) + .catch(err => osparc.FlashMessenger.logError(err)) + .finally(() => button.setFetching(false)); + }); + return button; + }, + + createResendEmailButton: function(email) { + const button = new osparc.ui.form.FetchButton(qx.locale.Manager.tr("Resend Email")); + button.addListener("execute", () => { + button.setFetching(true); + const params = { + data: { + email, + }, + }; + osparc.data.Resources.fetch("poUsers", "resendConfirmationEmail", params) + .then(() => { + osparc.FlashMessenger.logAs(qx.locale.Manager.tr("Email sent"), "INFO"); + }) + .catch(err => osparc.FlashMessenger.logError(err)) + .finally(() => button.setFetching(false)); + }); + return button; + }, + + createInfoButton: function(infoMetadata) { + const infoButton = new qx.ui.form.Button(null, "@MaterialIcons/info_outline/16"); + infoButton.addListener("execute", () => { + const container = new qx.ui.container.Scroll(); + container.add(new osparc.ui.basic.JsonTreeWidget(infoMetadata, "pendingUserInfo")); + osparc.ui.window.Window.popUpInWindow(container, qx.locale.Manager.tr("User Info")); + }); + return infoButton; + }, + }, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "pending-users-container": + control = new qx.ui.container.Scroll(); + this._add(control, { + flex: 1 + }); + break; + case "pending-users-layout": { + const grid = new qx.ui.layout.Grid(15, 5); + control = new qx.ui.container.Composite(grid); + this.getChildControl("pending-users-container").add(control); + break; + } + } + return control || this.base(arguments, id); + }, + + _buildLayout: function() { + this.getChildControl("pending-users-container"); + + this.__populatePendingUsersLayout(); + }, + + __addHeader: function() { + const pendingUsersLayout = this.getChildControl("pending-users-layout"); + + pendingUsersLayout.add(new qx.ui.basic.Label(this.tr("Name")).set({ + font: "text-14" + }), { + row: 0, + column: 0, + }); + + pendingUsersLayout.add(new qx.ui.basic.Label(this.tr("Email")).set({ + font: "text-14" + }), { + row: 0, + column: 1, + }); + + pendingUsersLayout.add(new qx.ui.basic.Label(this.tr("Date")).set({ + font: "text-14" + }), { + row: 0, + column: 2, + }); + + pendingUsersLayout.add(new qx.ui.basic.Label(this.tr("Info")).set({ + font: "text-14" + }), { + row: 0, + column: 3, + }); + + pendingUsersLayout.add(new qx.ui.basic.Label(this.tr("Status")).set({ + font: "text-14" + }), { + row: 0, + column: 4, + }); + + pendingUsersLayout.add(new qx.ui.basic.Label(this.tr("Action")).set({ + font: "text-14" + }), { + row: 0, + column: 5, + }); + }, + + __addRows: function(pendingUsers) { + const pendingUsersLayout = this.getChildControl("pending-users-layout"); + + let row = 1; + pendingUsers.forEach(pendingUser => { + pendingUsersLayout.add(new qx.ui.basic.Label(pendingUser.name), { + row, + column: 0, + }); + pendingUsersLayout.add(new qx.ui.basic.Label(pendingUser.email), { + row, + column: 1, + }); + pendingUsersLayout.add(new qx.ui.basic.Label(osparc.utils.Utils.formatDateAndTime(new Date(pendingUser.date))), { + row, + column: 2, + }); + const infoButton = this.self().createInfoButton(pendingUser.info); + pendingUsersLayout.add(infoButton, { + row, + column: 3, + }); + pendingUsersLayout.add(new qx.ui.basic.Label(pendingUser.status.toLowerCase()), { + row, + column: 4, + }); + const buttonsLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)); + pendingUsersLayout.add(buttonsLayout, { + row, + column: 5, + }); + + switch (pendingUser.status) { + case "PENDING": { + const approveButton = this.self().createApproveButton(pendingUser.email); + buttonsLayout.add(approveButton); + const rejectButton = this.self().createRejectButton(pendingUser.email); + buttonsLayout.add(rejectButton); + break; + } + case "REJECTED": { + const approveButton = this.self().createApproveButton(pendingUser.email); + buttonsLayout.add(approveButton); + break; + } + case "APPROVED": { + const resendEmailButton = this.self().createResendEmailButton(pendingUser.email); + buttonsLayout.add(resendEmailButton); + break; + } + } + row++; + }); + }, + + __populatePendingUsersLayout: function() { + // osparc.data.Resources.fetch("poUsers", "getPendingUsers", params) + this.self().getPendingUsers() + .then(pendingUsers => { + const pendingUsersLayout = this.getChildControl("pending-users-layout"); + pendingUsersLayout.removeAll(); + this.__addHeader(); + this.__addRows(pendingUsers["data"]); + }) + .catch(err => osparc.FlashMessenger.logError(err)); + } + } +}); 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 651ca4d505e..2005ef26a3b 100644 --- a/services/static-webserver/client/source/class/osparc/product/Utils.js +++ b/services/static-webserver/client/source/class/osparc/product/Utils.js @@ -60,13 +60,7 @@ qx.Class.define("osparc.product.Utils", { getTemplateAlias: function(options = {}) { let alias = null; - if (this.getProductName().includes("s4l")) { - if (options.plural) { - alias = qx.locale.Manager.tr("tutorials"); - } else { - alias = qx.locale.Manager.tr("tutorial"); - } - } else if (options.plural) { + if (options.plural) { alias = qx.locale.Manager.tr("templates"); } else { alias = qx.locale.Manager.tr("template");