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 331899482b37..b000864caa50 100644 --- a/services/static-webserver/client/source/class/osparc/po/POCenter.js +++ b/services/static-webserver/client/source/class/osparc/po/POCenter.js @@ -27,9 +27,7 @@ qx.Class.define("osparc.po.POCenter", { this.addWidgetToTabs(miniProfile); this.__addActiveUsersPage(); - if (osparc.utils.Utils.isDevelopmentPlatform()) { - this.__addPendingUsersPage(); - } + this.__addReviewUsersPage(); this.__addPreRegistrationPage(); this.__addInvitationsPage(); this.__addProductPage(); @@ -44,8 +42,8 @@ qx.Class.define("osparc.po.POCenter", { this.addTab(title, iconSrc, users); }, - __addPendingUsersPage: function() { - const title = this.tr("Pending Users"); + __addReviewUsersPage: function() { + const title = this.tr("Review Users"); const iconSrc = "@FontAwesome5Solid/user-plus/22"; const usersPending = new osparc.po.UsersPending(); this.addTab(title, iconSrc, usersPending); diff --git a/services/static-webserver/client/source/class/osparc/po/UsersPending.js b/services/static-webserver/client/source/class/osparc/po/UsersPending.js index 8d086036bdb5..55a9d7d40ef3 100644 --- a/services/static-webserver/client/source/class/osparc/po/UsersPending.js +++ b/services/static-webserver/client/source/class/osparc/po/UsersPending.js @@ -56,77 +56,6 @@ qx.Class.define("osparc.po.UsersPending", { return form; }, - createApproveButton: function(email) { - const button = new qx.ui.form.Button(qx.locale.Manager.tr("Approve")); - button.addListener("execute", () => { - const form = this.createInvitationForm(false); - const approveBtn = new osparc.ui.form.FetchButton(qx.locale.Manager.tr("Approve")); - approveBtn.set({ - appearance: "form-button" - }); - form.addButton(approveBtn); - const layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); - const invitationForm = new qx.ui.form.renderer.Single(form); - layout.add(invitationForm); - const win = osparc.ui.window.Window.popUpInWindow(layout, email, 350, 150).set({ - clickAwayClose: false, - resizable: false, - showClose: true - }); - win.open(); - approveBtn.addListener("execute", () => { - if (!osparc.data.Permissions.getInstance().canDo("user.invitation.generate", true)) { - return; - } - if (form.validate()) { - approveBtn.setFetching(true); - const params = { - data: { - email, - }, - }; - params.data["invitation"] = {}; - const extraCreditsInUsd = form.getItems()["credits"].getValue(); - if (extraCreditsInUsd > 0) { - params.data["invitation"]["extraCreditsInUsd"] = extraCreditsInUsd; - } - if (form.getItems()["withExpiration"].getValue()) { - params.data["invitation"]["trialAccountDays"] = form.getItems()["trialDays"].getValue(); - } - 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(() => { - approveBtn.setFetching(false); - win.close(); - }); - } - }); - }); - 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", () => { @@ -147,7 +76,7 @@ qx.Class.define("osparc.po.UsersPending", { }, createInfoButton: function(infoMetadata) { - const infoButton = new qx.ui.form.Button(null, "@MaterialIcons/info_outline/16"); + const infoButton = new qx.ui.form.Button(null, "@MaterialIcons/info_outline/14"); infoButton.addListener("execute", () => { const container = new qx.ui.container.Scroll(); container.add(new osparc.ui.basic.JsonTreeWidget(infoMetadata, "pendingUserInfo")); @@ -165,10 +94,7 @@ qx.Class.define("osparc.po.UsersPending", { control = new qx.ui.form.Button(this.tr("Reload")).set({ allowGrowX: false, }); - control.addListener("execute", () => { - this.getChildControl("pending-users-layout").removeAll(); - this.__populatePendingUsersLayout(); - }); + control.addListener("execute", () => this.__reload()); this._add(control); break; case "pending-users-container": @@ -190,7 +116,7 @@ qx.Class.define("osparc.po.UsersPending", { _buildLayout: function() { this.getChildControl("reload-button"); this.getChildControl("pending-users-container"); - + this.__addHeader(); this.__populatePendingUsersLayout(); }, @@ -217,34 +143,16 @@ qx.Class.define("osparc.po.UsersPending", { row: 0, column: 2, }); - - pendingUsersLayout.add(new qx.ui.basic.Label(this.tr("Status")).set({ - font: "text-14" - }), { - row: 0, - column: 3, - }); - - pendingUsersLayout.add(new qx.ui.basic.Label(this.tr("Info")).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"); + const grid = pendingUsersLayout.getLayout(); let row = 1; pendingUsers.forEach(pendingUser => { + grid.setRowAlign(row, "left", "middle"); + pendingUsersLayout.add(new qx.ui.basic.Label(pendingUser.firstName + " " + pendingUser.lastName), { row, column: 0, @@ -253,44 +161,62 @@ qx.Class.define("osparc.po.UsersPending", { row, column: 1, }); - pendingUsersLayout.add(new qx.ui.basic.Label(pendingUser.date ? osparc.utils.Utils.formatDateAndTime(new Date(pendingUser.date)) : "-"), { + pendingUsersLayout.add(new qx.ui.basic.Label(pendingUser.accountRequestReviewedAt ? osparc.utils.Utils.formatDateAndTime(new Date(pendingUser.accountRequestReviewedAt)) : "-"), { row, column: 2, }); - pendingUsersLayout.add(new qx.ui.basic.Label(pendingUser.accountRequestStatus.toLowerCase()), { + const statusImage = new qx.ui.basic.Image(); + pendingUsersLayout.add(statusImage, { row, column: 3, }); + pendingUsersLayout.add(new qx.ui.basic.Label(pendingUser.accountRequestStatus.toLowerCase()), { + row, + column: 4, + }); const infoButton = this.self().createInfoButton(pendingUser); pendingUsersLayout.add(infoButton, { row, - column: 4, + column: 5, }); const buttonsLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)); pendingUsersLayout.add(buttonsLayout, { row, - column: 5, + column: 6, }); switch (pendingUser.accountRequestStatus) { case "PENDING": { - const approveButton = this.self().createApproveButton(pendingUser.email); + statusImage.set({ + source: "@FontAwesome5Solid/hourglass-end/14", + textColor: "warning-yellow", + }); + const approveButton = this.__createApproveButton(pendingUser.email); buttonsLayout.add(approveButton); - const rejectButton = this.self().createRejectButton(pendingUser.email); + const rejectButton = this.__createRejectButton(pendingUser.email); buttonsLayout.add(rejectButton); break; } case "REJECTED": { - const approveButton = this.self().createApproveButton(pendingUser.email); + statusImage.set({ + source: "@FontAwesome5Solid/times/14", + textColor: "danger-red", + }); + const approveButton = this.__createApproveButton(pendingUser.email); + approveButton.setEnabled(false); // avoid changing decision for now buttonsLayout.add(approveButton); break; } case "APPROVED": { - /* + statusImage.set({ + source: "@FontAwesome5Solid/check/14", + textColor: "product-color", + }); const resendEmailButton = this.self().createResendEmailButton(pendingUser.email); + resendEmailButton.setEnabled(false); buttonsLayout.add(resendEmailButton); - */ - const rejectButton = this.self().createRejectButton(pendingUser.email); + const rejectButton = this.__createRejectButton(pendingUser.email); + rejectButton.setEnabled(false); // avoid changing decision for now buttonsLayout.add(rejectButton); break; } @@ -307,12 +233,139 @@ qx.Class.define("osparc.po.UsersPending", { .then(resps => { const pendingUsers = resps[0]; const reviewedUsers = resps[1]; - const pendingUsersLayout = this.getChildControl("pending-users-layout"); - pendingUsersLayout.removeAll(); - this.__addHeader(); + const sortByDate = (a, b) => { + const dateA = a.accountRequestReviewedAt ? new Date(a.accountRequestReviewedAt) : new Date(0); + const dateB = b.accountRequestReviewedAt ? new Date(b.accountRequestReviewedAt) : new Date(0); + return dateB - dateA; // sort by most recent first + }; + pendingUsers.sort(sortByDate); + reviewedUsers.sort(sortByDate); this.__addRows(pendingUsers.concat(reviewedUsers)); }) .catch(err => osparc.FlashMessenger.logError(err)); - } + }, + + __reload: function() { + this.getChildControl("pending-users-layout").removeAll(); + this.__addHeader(); + this.__populatePendingUsersLayout(); + }, + + __createApproveButton: function(email) { + const button = new qx.ui.form.Button(qx.locale.Manager.tr("Approve")); + button.addListener("execute", () => { + const form = this.self().createInvitationForm(false); + const approveBtn = new osparc.ui.form.FetchButton(qx.locale.Manager.tr("Approve")); + approveBtn.set({ + appearance: "form-button" + }); + form.addButton(approveBtn); + const layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); + const invitationForm = new qx.ui.form.renderer.Single(form); + layout.add(invitationForm); + const win = osparc.ui.window.Window.popUpInWindow(layout, email, 350, 150).set({ + clickAwayClose: false, + resizable: false, + showClose: true + }); + win.open(); + approveBtn.addListener("execute", () => { + if (osparc.data.Permissions.getInstance().canDo("user.invitation.generate", true)) { + if (form.validate()) { + const extraCreditsInUsd = form.getItems()["credits"].getValue(); + let trialAccountDays = 0; + if (form.getItems()["withExpiration"].getValue()) { + trialAccountDays = form.getItems()["trialDays"].getValue(); + } + + let msg = `Are you sure you want to approve ${email}`; + if (extraCreditsInUsd) { + msg += ` with ${extraCreditsInUsd}$ worth credits`; + } + if (trialAccountDays > 0) { + msg += ` and ${trialAccountDays} days of trial`; + } + msg += "?"; + const confWin = new osparc.ui.window.Confirmation(msg).set({ + caption: "Approve User", + confirmText: "Approve", + confirmAction: "create" + }); + confWin.center(); + confWin.open(); + confWin.addListener("close", () => { + if (confWin.getConfirmed()) { + approveBtn.setFetching(true); + this.__approveUser(email, form) + .then(() => { + osparc.FlashMessenger.logAs("User approved", "INFO"); + this.__reload(); + }) + .catch(err => osparc.FlashMessenger.logError(err)) + .finally(() => { + approveBtn.setFetching(false); + win.close(); + }); + } + }); + } + } + }); + }); + return button; + }, + + __createRejectButton: function(email) { + const button = new osparc.ui.form.FetchButton("Reject"); + button.addListener("execute", () => { + const msg = `Are you sure you want to reject ${email}.
The operation cannot be reverted"`; + const win = new osparc.ui.window.Confirmation(msg).set({ + caption: "Reject User", + confirmText: "Reject", + confirmAction: "delete", + }); + win.center(); + win.open(); + win.addListener("close", () => { + if (win.getConfirmed()) { + button.setFetching(true); + this.__rejectUser(email) + .then(() => { + osparc.FlashMessenger.logAs(qx.locale.Manager.tr("User denied"), "INFO"); + this.__reload(); + }) + .catch(err => osparc.FlashMessenger.logError(err)) + .finally(() => button.setFetching(false)); + } + }); + }); + return button; + }, + + __approveUser: function(email, form) { + const params = { + data: { + email, + }, + }; + params.data["invitation"] = {}; + const extraCreditsInUsd = form.getItems()["credits"].getValue(); + if (extraCreditsInUsd > 0) { + params.data["invitation"]["extraCreditsInUsd"] = extraCreditsInUsd; + } + if (form.getItems()["withExpiration"].getValue()) { + params.data["invitation"]["trialAccountDays"] = form.getItems()["trialDays"].getValue(); + } + return osparc.data.Resources.fetch("poUsers", "approveUser", params); + }, + + __rejectUser: function(email) { + const params = { + data: { + email, + }, + }; + return osparc.data.Resources.fetch("poUsers", "rejectUser", params); + }, } });