From 244415a0d35df2a63998bb395fbd7510db8d98d5 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 14:12:20 +0100 Subject: [PATCH 01/20] Click on studyBrowser header --- .../class/osparc/dashboard/StudyBrowserHeader.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) 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 71b8d902a18..3a6ba34f6b3 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js @@ -234,13 +234,15 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", { }, __titleTapped: function() { - const workspaceId = this.getCurrentWorkspaceId(); - const folderId = null; - this.setCurrentFolderId(folderId); - this.fireDataEvent("locationChanged", { - workspaceId, - folderId, - }); + if (osparc.store.Store.getInstance().getStudyBrowserContext() === "studiesAndFolders") { + const workspaceId = this.getCurrentWorkspaceId(); + const folderId = null; + this.setCurrentFolderId(folderId); + this.fireDataEvent("locationChanged", { + workspaceId, + folderId, + }); + } }, __buildLayout: function() { From 3d3173dafa5e2bd16b2deae63276c1c500e4f8ba Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 14:28:15 +0100 Subject: [PATCH 02/20] fitToContainer --- .../class/osparc/dashboard/ResourceContainerManager.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 55ac1f85697..fe82d290aaa 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -248,15 +248,17 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { container.add(card); if (this.getMode() === "list") { + const fitToContainer = () => { + const bounds = container.getBounds() || container.getSizeHint(); + card.setWidth(bounds.width); + }; [ "appear", "resize", ].forEach(ev => { - container.addListener(ev, () => { - const bounds = container.getBounds() || container.getSizeHint(); - card.setWidth(bounds.width); - }); + container.addListener(ev, () => fitToContainer()); }); + fitToContainer(); } }, From 731023b4faecc051a7d9d5fc27f30e65d8d0604b Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 14:36:16 +0100 Subject: [PATCH 03/20] Set the Name first --- .../source/class/osparc/desktop/account/ProfilePage.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/desktop/account/ProfilePage.js b/services/static-webserver/client/source/class/osparc/desktop/account/ProfilePage.js index 732ed960c8d..7b315a79046 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/account/ProfilePage.js +++ b/services/static-webserver/client/source/class/osparc/desktop/account/ProfilePage.js @@ -271,6 +271,15 @@ qx.Class.define("osparc.desktop.account.ProfilePage", { patchData["privacy"]["hideEmail"] = model.getHideEmail(); } + if ( + "hideFullname" in patchData["privacy"] && + patchData["privacy"]["hideFullname"] === false && + this.__userProfileData["first_name"] === null + ) { + osparc.FlashMessenger.getInstance().logAs(this.tr("Set the Name first"), "WARNING"); + return; + } + if (Object.keys(patchData["privacy"]).length) { const params = { data: patchData From 9f18f939ccc5f45cba924034fa9fcd75422d0c1f Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 14:43:20 +0100 Subject: [PATCH 04/20] minor --- .../client/source/class/osparc/share/Collaborators.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 004666fcbab..3e0f50402c7 100644 --- a/services/static-webserver/client/source/class/osparc/share/Collaborators.js +++ b/services/static-webserver/client/source/class/osparc/share/Collaborators.js @@ -431,7 +431,7 @@ qx.Class.define("osparc.share.Collaborators", { "description": collab.getDescription(), }; if (!("getUserId" in collab)) { - // orgnanization + // organization if (everyoneGIds.includes(parseInt(gid))) { collaborator["thumbnail"] = "@FontAwesome5Solid/globe/32"; } else if (!collaborator["thumbnail"]) { From 9e220832d188da987a712c802a15979a0896c042 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 14:43:33 +0100 Subject: [PATCH 05/20] firstName and lastName not null --- .../client/source/class/osparc/data/model/User.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/User.js b/services/static-webserver/client/source/class/osparc/data/model/User.js index 7294987345c..a8f39a3779f 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/User.js +++ b/services/static-webserver/client/source/class/osparc/data/model/User.js @@ -32,8 +32,18 @@ qx.Class.define("osparc.data.model.User", { const groupId = ("gid" in userData) ? parseInt(userData["gid"]) : parseInt(userData["groupId"]); const username = userData["userName"]; const email = ("login" in userData) ? userData["login"] : userData["email"]; - const firstName = ("first_name" in userData) ? userData["first_name"] : userData["firstName"]; - const lastName = ("last_name" in userData) ? userData["last_name"] : userData["lastName"]; + let firstName = ""; + if (userData["first_name"]) { + firstName = userData["first_name"]; + } else if (userData["firstName"]) { + firstName = userData["firstName"]; + } + let lastName = ""; + if (userData["last_name"]) { + lastName = userData["last_name"]; + } else if (userData["lastName"]) { + lastName = userData["lastName"]; + } let description = [firstName, lastName].join(" ").trim(); // the null values will be replaced by empty strings if (email) { if (description) { From b1b631360c856fbe9db24664dd0040786d00134e Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 14:43:39 +0100 Subject: [PATCH 06/20] minor --- .../client/source/class/osparc/ui/list/CollaboratorListItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c51147869fb..81a79d37c5f 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 @@ -138,7 +138,7 @@ qx.Class.define("osparc.ui.list.CollaboratorListItem", { // highlight me const email = osparc.auth.Data.getInstance().getEmail(); - if (email === value) { + if (value.includes(email)) { this.addState("selected"); } }, From 93dc6fcd782229835c406d8f2fd75b31980c786e Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 15:22:36 +0100 Subject: [PATCH 07/20] mark name as invalid --- .../source/class/osparc/desktop/account/ProfilePage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/desktop/account/ProfilePage.js b/services/static-webserver/client/source/class/osparc/desktop/account/ProfilePage.js index 7b315a79046..9378f0c279d 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/account/ProfilePage.js +++ b/services/static-webserver/client/source/class/osparc/desktop/account/ProfilePage.js @@ -47,6 +47,7 @@ qx.Class.define("osparc.desktop.account.ProfilePage", { __userProfileModel: null, __userPrivacyData: null, __userPrivacyModel: null, + __userProfileForm: null, __fetchProfile: function() { osparc.data.Resources.getOne("profile", {}, null, false) @@ -106,7 +107,7 @@ qx.Class.define("osparc.desktop.account.ProfilePage", { readOnly: true }); - const form = new qx.ui.form.Form(); + const form = this.__userProfileForm = new qx.ui.form.Form(); form.add(username, "Username", null, "username"); form.add(firstName, "First Name", null, "firstName"); form.add(lastName, "Last Name", null, "lastName"); @@ -276,6 +277,10 @@ qx.Class.define("osparc.desktop.account.ProfilePage", { patchData["privacy"]["hideFullname"] === false && this.__userProfileData["first_name"] === null ) { + this.__userProfileForm.getItem("firstName").set({ + invalidMessage: qx.locale.Manager.tr("Name is required"), + valid: false + }); osparc.FlashMessenger.getInstance().logAs(this.tr("Set the Name first"), "WARNING"); return; } From 86776a71698b36a54a63cb8ad4f72516ab8c60d0 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 15:39:50 +0100 Subject: [PATCH 08/20] minor --- .../client/source/class/osparc/theme/Decoration.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/theme/Decoration.js b/services/static-webserver/client/source/class/osparc/theme/Decoration.js index a1381421494..2a32cae2559 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Decoration.js +++ b/services/static-webserver/client/source/class/osparc/theme/Decoration.js @@ -101,6 +101,10 @@ qx.Theme.define("osparc.theme.Decoration", { } }, + "form-input-focused-invalid": { + include: "form-input-invalid" + }, + "form-array-container": { style: { radius: 2, From 2cf13e1b250453a5786eba34c557b47cf73bf64a Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 16:08:18 +0100 Subject: [PATCH 09/20] LicensedItem model --- .../class/osparc/data/model/LicensedItem.js | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js diff --git a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js new file mode 100644 index 00000000000..487b3ff39e7 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js @@ -0,0 +1,149 @@ +/* ************************************************************************ + + 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.data.model.LicensedItem", { + extend: qx.core.Object, + + /** + * @param licensedItemData {Object} Object containing the serialized LicensedItem Data + */ + construct: function(licensedItemData) { + this.base(arguments); + + console.log("licensedItemData", licensedItemData); + + let thumbnail = ""; + let date = null; + let licensedResources = []; + if (licensedItemData["licensedResources"]) { + if (licensedItemData["licensedResources"].length) { + const firstItem = licensedItemData["licensedResources"][0]["source"]; + if (firstItem["thumbnail"]) { + thumbnail = firstItem["thumbnail"]; + } + if (firstItem["features"] && firstItem["features"]["date"]) { + date = firstItem["features"]["date"]; + } + } + licensedItemData["licensedResources"].forEach(licensedRsrc => licensedResources.push(licensedRsrc["source"])); + } + + this.set({ + licensedItemId: licensedItemData.licensedItemId, + categoryId: licensedItemData.categoryId, + categoryDisplay: licensedItemData.categoryDisplay, + categoryIcon: licensedItemData.categoryIcon, + pricingPlanId: licensedItemData.pricingPlanId, + key: licensedItemData.key, + version: licensedItemData.version, + thumbnail: thumbnail, + displayName: licensedItemData.displayName, + date: new Date(date), + licensedResources: licensedResources, + seats: licensedItemData.seats || null, + }); + }, + + properties: { + licensedItemId: { + check: "String", + nullable: false, + init: null, + event: "changeLicensedItemId", + }, + + categoryId: { + check: "String", + nullable: true, + init: null, + event: "changeCategoryId", + }, + + categoryDisplay: { + check: "String", + nullable: true, + init: null, + event: "changeCategoryDisplay", + }, + + categoryIcon: { + check: "String", + nullable: true, + init: null, + event: "changeCategoryIcon", + }, + + pricingPlanId: { + check: "Number", + nullable: false, + init: null, + event: "changePricingPlanId", + }, + + key: { + check: "String", + nullable: false, + init: null, + event: "changeKey", + }, + + version: { + check: "String", + nullable: false, + init: null, + event: "changeVersion", + }, + + thumbnail: { + check: "String", + nullable: true, + init: null, + event: "changeThumbnail", + }, + + displayName: { + check: "String", + nullable: false, + init: null, + event: "changeDisplayName", + }, + + date: { + check: "Date", + nullable: false, + init: null, + event: "changeDate", + }, + + licensedResources: { + check: "Array", + nullable: false, + init: [], + event: "changeLicensedResources", + }, + + seats: { + check: "Object", + nullable: true, + init: null, + event: "changeSeats", + }, + }, + + members: { + } +}); From 9b33174fb37e561eba10c87749f75c0d1bbe3238 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 16:08:35 +0100 Subject: [PATCH 10/20] addLicensedItemsToCache --- .../client/source/class/osparc/store/LicensedItems.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/store/LicensedItems.js b/services/static-webserver/client/source/class/osparc/store/LicensedItems.js index 16fd69b77f9..77627e9d333 100644 --- a/services/static-webserver/client/source/class/osparc/store/LicensedItems.js +++ b/services/static-webserver/client/source/class/osparc/store/LicensedItems.js @@ -23,6 +23,7 @@ qx.Class.define("osparc.store.LicensedItems", { this.base(arguments); this.__licensedItems = null; + this.__cachedLicensedItems = {}; }, statics: { @@ -94,6 +95,7 @@ qx.Class.define("osparc.store.LicensedItems", { members: { __licensedItems: null, + __cachedLicensedItems: null, getLicensedItems: function() { if (this.__licensedItems) { @@ -102,11 +104,17 @@ qx.Class.define("osparc.store.LicensedItems", { return osparc.data.Resources.getInstance().getAllPages("licensedItems") .then(licensedItems => { + licensedItems.forEach(licensedItemData => this.__addLicensedItemsToCache(licensedItemData)); this.__licensedItems = licensedItems; return this.__licensedItems; }); }, + __addLicensedItemsToCache: function(licensedItemData) { + const licensedItem = new osparc.data.model.LicensedItem(licensedItemData); + this.__cachedLicensedItems[licensedItem.getLicensedItemId()] = licensedItem; + }, + getPurchasedLicensedItems: function(walletId, urlParams, options = {}) { let purchasesParams = { url: { From 4c95452b3f4537c775e2c70cd8d36dada691e587 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 16:21:20 +0100 Subject: [PATCH 11/20] update market --- .../class/osparc/data/model/LicensedItem.js | 28 ++++++++++++++-- .../class/osparc/store/LicensedItems.js | 33 +++---------------- .../source/class/osparc/vipMarket/Market.js | 22 ++++++------- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js index 487b3ff39e7..b82af23a26c 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js +++ b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js @@ -46,7 +46,7 @@ qx.Class.define("osparc.data.model.LicensedItem", { licensedItemId: licensedItemData.licensedItemId, categoryId: licensedItemData.categoryId, categoryDisplay: licensedItemData.categoryDisplay, - categoryIcon: licensedItemData.categoryIcon, + categoryIcon: licensedItemData.categoryIcon || `osparc/market/${licensedItemData.categoryId}.svg`, pricingPlanId: licensedItemData.pricingPlanId, key: licensedItemData.key, version: licensedItemData.version, @@ -137,13 +137,37 @@ qx.Class.define("osparc.data.model.LicensedItem", { }, seats: { - check: "Object", + check: "Array", nullable: true, init: null, event: "changeSeats", }, }, + statics: { + addSeatsFromPurchases: function(licensedItems, purchases) { + // reset seats + Object.values(licensedItems).forEach(licensedItem => licensedItem.setSeats([])); + // populate seats + purchases.forEach(purchase => { + const { + key, + version, + } = purchase; + Object.values(licensedItems).forEach(licensedItem => { + if (licensedItem.getKey() === key && licensedItem.getVersion() <= version) { + licensedItem.getSeats().push({ + licensedItemId: purchase["licensedItemId"], + licensedItemPurchaseId: purchase["licensedItemPurchaseId"], + numOfSeats: purchase["numOfSeats"], + expireAt: new Date(purchase["expireAt"]), + }); + } + }); + }) + }, + }, + members: { } }); diff --git a/services/static-webserver/client/source/class/osparc/store/LicensedItems.js b/services/static-webserver/client/source/class/osparc/store/LicensedItems.js index 77627e9d333..75f9296fd96 100644 --- a/services/static-webserver/client/source/class/osparc/store/LicensedItems.js +++ b/services/static-webserver/client/source/class/osparc/store/LicensedItems.js @@ -27,28 +27,6 @@ qx.Class.define("osparc.store.LicensedItems", { }, statics: { - populateSeatsFromPurchases: function(licensedItems, purchases) { - // reset seats - licensedItems.forEach(licensedItem => licensedItem["seats"] = []); - // populate seats - purchases.forEach(purchase => { - const { - key, - version, - } = purchase; - licensedItems.forEach(licensedItem => { - if (licensedItem["key"] === key && licensedItem["version"] <= version) { - licensedItem["seats"].push({ - licensedItemId: purchase["licensedItemId"], - licensedItemPurchaseId: purchase["licensedItemPurchaseId"], - numOfSeats: purchase["numOfSeats"], - expireAt: new Date(purchase["expireAt"]), - }); - } - }); - }) - }, - getLowerLicensedItems: function(licensedItems, key, version) { const lowerLicensedItems = []; licensedItems.forEach(licensedItem => { @@ -98,15 +76,14 @@ qx.Class.define("osparc.store.LicensedItems", { __cachedLicensedItems: null, getLicensedItems: function() { - if (this.__licensedItems) { - return new Promise(resolve => resolve(this.__licensedItems)); + if (this.__cachedLicensedItems.length) { + return new Promise(resolve => resolve(this.__cachedLicensedItems)); } return osparc.data.Resources.getInstance().getAllPages("licensedItems") - .then(licensedItems => { - licensedItems.forEach(licensedItemData => this.__addLicensedItemsToCache(licensedItemData)); - this.__licensedItems = licensedItems; - return this.__licensedItems; + .then(licensedItemsData => { + licensedItemsData.forEach(licensedItemData => this.__addLicensedItemsToCache(licensedItemData)); + return this.__cachedLicensedItems; }); }, diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js index 4a85fece443..043c8bd7c03 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js @@ -68,7 +68,7 @@ qx.Class.define("osparc.vipMarket.Market", { .then(values => { const licensedItems = values[0]; const purchasedItems = values[1]; - osparc.store.LicensedItems.populateSeatsFromPurchases(licensedItems, purchasedItems); + osparc.data.model.LicensedItem.addSeatsFromPurchases(licensedItems, purchasedItems); const categories = []; const availableCategory = { categoryId: "availableModels", @@ -78,21 +78,21 @@ qx.Class.define("osparc.vipMarket.Market", { }; categories.push(availableCategory); let openCategory = null; - licensedItems.forEach(licensedItem => { - if (licensedItem["seats"].length) { + Object.values(licensedItems).forEach(licensedItem => { + if (licensedItem.getSeats().length) { availableCategory["items"].push(licensedItem); if (!this.__reqOpenCategory) { openCategory = availableCategory["categoryId"]; } } - if (licensedItem && licensedItem["categoryId"]) { - const categoryId = licensedItem["categoryId"]; + if (licensedItem && licensedItem.getCategoryId()) { + const categoryId = licensedItem.getCategoryId(); let category = categories.find(cat => cat["categoryId"] === categoryId); if (!category) { category = { categoryId, - label: licensedItem["categoryDisplay"] || "Category", - icon: licensedItem["categoryIcon"] || `osparc/market/${categoryId}.svg`, + label: licensedItem.getCategoryDisplay() || "Category", + icon: licensedItem.getCategoryIcon(), items: [], }; if (!openCategory) { @@ -122,7 +122,7 @@ qx.Class.define("osparc.vipMarket.Market", { .then(async licensedItems => { this.__freeItems = []; for (const licensedItem of licensedItems) { - const pricingUnits = await osparc.store.Pricing.getInstance().fetchPricingUnits(licensedItem["pricingPlanId"]); + const pricingUnits = await osparc.store.Pricing.getInstance().fetchPricingUnits(licensedItem.getPricingPlanId()); if (pricingUnits.length === 1 && pricingUnits[0].getCost() === 0) { this.__freeItems.push(licensedItem); } @@ -164,10 +164,10 @@ qx.Class.define("osparc.vipMarket.Market", { .then(values => { const licensedItems = values[0]; const purchasedItems = values[1]; - osparc.store.LicensedItems.populateSeatsFromPurchases(licensedItems, purchasedItems); + osparc.data.model.LicensedItem.addSeatsFromPurchases(licensedItems, purchasedItems); let items = []; - licensedItems.forEach(licensedItem => { - if (licensedItem["seats"].length) { + Object.values(licensedItems).forEach(licensedItem => { + if (licensedItem.getSeats().length) { items.push(licensedItem); } }); From 3c91a3e3ef3c5a5f467ad0c05e463a192bd36231 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 16:26:39 +0100 Subject: [PATCH 12/20] VipMarket --- .../source/class/osparc/vipMarket/Market.js | 3 +- .../class/osparc/vipMarket/VipMarket.js | 36 ++++++------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js index 043c8bd7c03..32df2d05c51 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js @@ -121,7 +121,8 @@ qx.Class.define("osparc.vipMarket.Market", { licensedItemsStore.getLicensedItems() .then(async licensedItems => { this.__freeItems = []; - for (const licensedItem of licensedItems) { + const licensedItemsArr = Object.values(licensedItems); + for (const licensedItem of licensedItemsArr) { const pricingUnits = await osparc.store.Pricing.getInstance().fetchPricingUnits(licensedItem.getPricingPlanId()); if (pricingUnits.length === 1 && pricingUnits[0].getCost() === 0) { this.__freeItems.push(licensedItem); diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js index 82ef0d68ff6..ce98bdefe2e 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js @@ -174,7 +174,7 @@ qx.Class.define("osparc.vipMarket.VipMarket", { const selection = e.getData(); if (selection.length) { const licensedItemId = selection[0].getLicensedItemId(); - const licensedItemBundle = this.__anatomicalBundles.find(anatomicalBundle => anatomicalBundle["licensedItemId"] === licensedItemId); + const licensedItemBundle = this.__anatomicalBundles.find(anatomicalBundle => anatomicalBundle.getLicensedItemId() === licensedItemId); if (licensedItemBundle) { anatomicModelDetails.setAnatomicalModelsData(licensedItemBundle); return; @@ -191,21 +191,7 @@ qx.Class.define("osparc.vipMarket.VipMarket", { return; } - this.__anatomicalBundles = []; - licensedBundles.forEach(licensedBundle => { - licensedBundle["thumbnail"] = ""; - licensedBundle["date"] = null; - if (licensedBundle["licensedResources"] && licensedBundle["licensedResources"].length) { - const firstItem = licensedBundle["licensedResources"][0]["source"]; - if (firstItem["thumbnail"]) { - licensedBundle["thumbnail"] = firstItem["thumbnail"]; - } - if (firstItem["features"] && firstItem["features"]["date"]) { - licensedBundle["date"] = new Date(firstItem["features"]["date"]); - } - } - this.__anatomicalBundles.push(licensedBundle); - }); + this.__anatomicalBundles = licensedBundles; this.__populateModels(); @@ -238,8 +224,8 @@ qx.Class.define("osparc.vipMarket.VipMarket", { const sortModel = sortBy => { models.sort((a, b) => { // first criteria - const nASeats = osparc.store.LicensedItems.seatsToNSeats(a["seats"]); - const nBSeats = osparc.store.LicensedItems.seatsToNSeats(b["seats"]); + const nASeats = osparc.store.LicensedItems.seatsToNSeats(a.getSeats()); + const nBSeats = osparc.store.LicensedItems.seatsToNSeats(b.getSeats()); if (nBSeats !== nASeats) { // nSeats first return nBSeats - nASeats; @@ -249,20 +235,20 @@ qx.Class.define("osparc.vipMarket.VipMarket", { if (sortBy["sort"] === "name") { if (sortBy["order"] === "down") { // A -> Z - return a["displayName"].localeCompare(b["displayName"]); + return a.getDisplayName().localeCompare(b.getDisplayName()); } - return b["displayName"].localeCompare(a["displayName"]); + return b.getDisplayName().localeCompare(a.getDisplayName()); } else if (sortBy["sort"] === "date") { if (sortBy["order"] === "down") { // Now -> Yesterday - return b["date"] - a["date"]; + return b.getDate() - a.getDate(); } - return a["date"] - b["date"]; + return a.getDate() - b.getDate(); } } // default criteria // A -> Z - return a["displayName"].localeCompare(b["displayName"]); + return a.getDisplayName().localeCompare(b.getDisplayName()); }); }; sortModel(); @@ -309,9 +295,9 @@ qx.Class.define("osparc.vipMarket.VipMarket", { msg += " rented until " + osparc.utils.Utils.formatDate(new Date(purchaseData["expireAt"])); osparc.FlashMessenger.getInstance().logAs(msg, "INFO"); - const found = this.__anatomicalBundles.find(model => model["licensedItemId"] === licensedItemId); + const found = this.__anatomicalBundles.find(model => model.getLicensedItemId() === licensedItemId); if (found) { - found["seats"].push({ + found.getSeats().push({ licensedItemId: purchaseData["licensedItemId"], licensedItemPurchaseId: purchaseData["licensedItemPurchaseId"], numOfSeats: purchaseData["numOfSeats"], From 2946b40420649cc2a28247a5af1959022654a7c0 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 16:40:53 +0100 Subject: [PATCH 13/20] AnatomicalModelDetails --- .../class/osparc/data/model/LicensedItem.js | 27 +++++++- .../class/osparc/store/LicensedItems.js | 21 ------ .../vipMarket/AnatomicalModelDetails.js | 68 +++++++++---------- 3 files changed, 57 insertions(+), 59 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js index b82af23a26c..54e509cf8c7 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js +++ b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js @@ -54,7 +54,7 @@ qx.Class.define("osparc.data.model.LicensedItem", { displayName: licensedItemData.displayName, date: new Date(date), licensedResources: licensedResources, - seats: licensedItemData.seats || null, + seats: licensedItemData.seats || [], }); }, @@ -138,8 +138,8 @@ qx.Class.define("osparc.data.model.LicensedItem", { seats: { check: "Array", - nullable: true, - init: null, + nullable: false, + init: [], event: "changeSeats", }, }, @@ -166,6 +166,27 @@ qx.Class.define("osparc.data.model.LicensedItem", { }); }) }, + + licensedResourceTitle: function(licensedResource) { + const name = licensedResource["features"]["name"] || osparc.data.model.LicensedItem.extractNameFromDescription(licensedResource); + const version = licensedResource["features"]["version"] || ""; + const functionality = licensedResource["features"]["functionality"] || "Static"; + return `${name} ${version}, ${functionality}`; + }, + + extractNameFromDescription: function(licensedResource) { + const description = licensedResource["description"] || ""; + const delimiter = " - "; + let typeAndName = description.split(delimiter); + if (typeAndName.length > 1) { + // drop the type + typeAndName.shift(); + // join the name + typeAndName = typeAndName.join(delimiter); + return typeAndName; + } + return ""; + }, }, members: { diff --git a/services/static-webserver/client/source/class/osparc/store/LicensedItems.js b/services/static-webserver/client/source/class/osparc/store/LicensedItems.js index 75f9296fd96..41e17ba41df 100644 --- a/services/static-webserver/client/source/class/osparc/store/LicensedItems.js +++ b/services/static-webserver/client/source/class/osparc/store/LicensedItems.js @@ -48,27 +48,6 @@ qx.Class.define("osparc.store.LicensedItems", { }); return nSeats; }, - - licensedResourceTitle: function(licensedResource) { - const name = licensedResource["source"]["features"]["name"] || osparc.store.LicensedItems.extractNameFromDescription(licensedResource); - const version = licensedResource["source"]["features"]["version"] || ""; - const functionality = licensedResource["source"]["features"]["functionality"] || "Static"; - return `${name} ${version}, ${functionality}`; - }, - - extractNameFromDescription: function(licensedResource) { - const description = licensedResource["source"]["description"] || ""; - const delimiter = " - "; - let typeAndName = description.split(delimiter); - if (typeAndName.length > 1) { - // drop the type - typeAndName.shift(); - // join the name - typeAndName = typeAndName.join(delimiter); - return typeAndName; - } - return ""; - }, }, members: { diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js b/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js index 4ab2391aa56..264b76ce850 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js @@ -70,8 +70,8 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { __populateLayout: function() { this._removeAll(); - const licensedItemBundleData = this.getAnatomicalModelsData(); - if (licensedItemBundleData && licensedItemBundleData["licensedResources"].length) { + const licensedItem = this.getAnatomicalModelsData(); + if (licensedItem && licensedItem.getLicensedResources().length) { this.__addModelsInfo(); this.__addSeatsSection(); this.__addPricing(); @@ -99,9 +99,9 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { this.__populateModelsInfo(); - const licensedItemBundleData = this.getAnatomicalModelsData(); - const modelsInfo = licensedItemBundleData["licensedResources"]; - if (modelsInfo.length > 1) { + const licensedItem = this.getAnatomicalModelsData(); + const licensedResources = licensedItem.getLicensedResources(); + if (licensedResources.length > 1) { const modelSelectionLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(4)); const titleLabel = new qx.ui.basic.Label(this.tr("This bundle contains:")); modelSelectionLayout.add(titleLabel); @@ -121,15 +121,15 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { }); } - modelsInfo.forEach((modelInfo, idx) => { + licensedResources.forEach((licensedResource, idx) => { const modelLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(4)).set({ allowGrowX: false, }); - const miniThumbnail = this.self().createThumbnail(modelInfo["source"]["thumbnail"], 32); + const miniThumbnail = this.self().createThumbnail(licensedResource["thumbnail"], 32); osparc.utils.Utils.addBorder(miniThumbnail); modelLayout.add(miniThumbnail); const title = new qx.ui.basic.Label().set({ - value: osparc.store.LicensedItems.licensedResourceTitle(modelInfo), + value: osparc.data.model.LicensedItem.licensedResourceTitle(licensedResource), alignY: "middle" }); modelLayout.add(title); @@ -148,17 +148,15 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { __populateModelsInfo: function() { this.__modelsInfoStack.removeAll(); - const licensedItemBundleData = this.getAnatomicalModelsData(); - const modelsInfo = licensedItemBundleData["licensedResources"]; - modelsInfo.forEach((modelInfo, index) => { + const licensedItem = this.getAnatomicalModelsData(); + const licensedResources = licensedItem.getLicensedResources(); + licensedResources.forEach((licensedResource, index) => { const modelInfoLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(4)); - const anatomicalModel = modelInfo["source"]; - const topGrid = new qx.ui.layout.Grid(8, 6); topGrid.setColumnFlex(0, 1); const headerLayout = new qx.ui.container.Composite(topGrid); - let description = anatomicalModel["description"] || ""; + let description = licensedResource["description"] || ""; description = description.replace(/SPEAG/g, " "); // remove SPEAG substring const delimiter = " - "; let titleAndSubtitle = description.split(delimiter); @@ -190,13 +188,13 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { row: 1, }); } - if (anatomicalModel["thumbnail"]) { + if (licensedResource["thumbnail"]) { const manufacturerData = {}; - if (anatomicalModel["thumbnail"].includes("itis.swiss")) { + if (licensedResource["thumbnail"].includes("itis.swiss")) { manufacturerData["label"] = "IT'IS Foundation"; manufacturerData["link"] = "https://itis.swiss/virtual-population/"; manufacturerData["icon"] = "https://media.licdn.com/dms/image/v2/C4D0BAQE_FGa66IyvrQ/company-logo_200_200/company-logo_200_200/0/1631341490431?e=2147483647&v=beta&t=7f_IK-ArGjPrz-1xuWolAT4S2NdaVH-e_qa8hsKRaAc"; - } else if (anatomicalModel["thumbnail"].includes("speag.swiss")) { + } else if (licensedResource["thumbnail"].includes("speag.swiss")) { manufacturerData["label"] = "Speag"; manufacturerData["link"] = "https://speag.swiss/products/em-phantoms/overview-2/"; manufacturerData["icon"] = "https://media.licdn.com/dms/image/v2/D4E0BAQG2CYG28KAKbA/company-logo_200_200/company-logo_200_200/0/1700045977122/schmid__partner_engineering_ag_logo?e=2147483647&v=beta&t=6CZb1jjg5TnnzQWkrZBS9R3ebRKesdflg-_xYi4dwD8"; @@ -228,10 +226,10 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { const middleLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(16)); - const thumbnail = this.self().createThumbnail(anatomicalModel["thumbnail"], 256); + const thumbnail = this.self().createThumbnail(licensedResource["thumbnail"], 256); middleLayout.add(thumbnail); - const features = anatomicalModel["features"]; + const features = licensedResource["features"]; const featuresGrid = new qx.ui.layout.Grid(8, 8); const featuresLayout = new qx.ui.container.Composite(featuresGrid); let idx = 0; @@ -280,7 +278,7 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { } }); - if (anatomicalModel["doi"]) { + if (licensedResource["doi"]) { const doiTitle = new qx.ui.basic.Label().set({ value: "DOI", font: "text-14", @@ -307,14 +305,14 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { } return doiLabel; }; - featuresLayout.add(doiToLink(anatomicalModel["doi"]), { + featuresLayout.add(doiToLink(licensedResource["doi"]), { column: 1, row: idx, }); idx++; } - if (licensedItemBundleData["termsOfUseUrl"] || anatomicalModel["termsOfUseUrl"]) { // remove the first one when this info goes down to the model + if (licensedItem["termsOfUseUrl"] || licensedResource["termsOfUseUrl"]) { // remove the first one when this info goes down to the model const tAndC = new qx.ui.basic.Label().set({ font: "text-14", value: this.tr("Terms and Conditions"), @@ -322,7 +320,7 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { anonymous: false, cursor: "pointer", }); - tAndC.addListener("tap", () => this.__openLicense(licensedItemBundleData["termsOfUseUrl"] || anatomicalModel["termsOfUseUrl"])); + tAndC.addListener("tap", () => this.__openLicense(licensedItem["termsOfUseUrl"] || licensedResource["termsOfUseUrl"])); featuresLayout.add(tAndC, { column: 1, row: idx, @@ -334,7 +332,7 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { modelInfoLayout.add(middleLayout); - const importSection = this.__createImportSection(licensedItemBundleData, index); + const importSection = this.__createImportSection(licensedItem, index); modelInfoLayout.add(importSection); this.__modelsInfoStack.add(modelInfoLayout); @@ -372,15 +370,15 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { }); importButton.addListener("execute", () => { this.fireDataEvent("modelImportRequested", { - modelId: anatomicalModelsData["licensedResources"][selectedIdx]["source"]["id"], - categoryId: anatomicalModelsData["categoryId"], + modelId: anatomicalModelsData.getLicensedResources()[selectedIdx]["id"], + categoryId: anatomicalModelsData.getCategoryId(), }); }, this); - osparc.store.Pricing.getInstance().fetchPricingUnits(anatomicalModelsData["pricingPlanId"]) + osparc.store.Pricing.getInstance().fetchPricingUnits(anatomicalModelsData.getPricingPlanId()) .then(pricingUnits => { if ( - anatomicalModelsData["seats"].length || + anatomicalModelsData.getSeats().length || (pricingUnits.length === 1 && pricingUnits[0].getCost() === 0) ) { importSection.add(importButton); @@ -404,8 +402,8 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { const pricingUnitsLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ alignX: "center" })); - const licensedItemData = this.getAnatomicalModelsData(); - osparc.store.Pricing.getInstance().fetchPricingUnits(licensedItemData["pricingPlanId"]) + const licensedItem = this.getAnatomicalModelsData(); + osparc.store.Pricing.getInstance().fetchPricingUnits(licensedItem.getPricingPlanId()) .then(pricingUnits => { if (pricingUnits.length === 1 && pricingUnits[0].getCost() === 0) { const availableForImporting = new qx.ui.basic.Label().set({ @@ -428,8 +426,8 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { }); pUnit.addListener("rentPricingUnit", () => { this.fireDataEvent("modelPurchaseRequested", { - licensedItemId: licensedItemData["licensedItemId"], - pricingPlanId: licensedItemData["pricingPlanId"], + licensedItemId: licensedItem.getLicensedItemId(), + pricingPlanId: licensedItem.getPricingPlanId(), pricingUnitId: pricingUnit.getPricingUnitId(), }); }, this); @@ -445,15 +443,15 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { }, __addSeatsSection: function() { - const licensedItemData = this.getAnatomicalModelsData(); - if (licensedItemData["seats"].length === 0) { + const licensedItem = this.getAnatomicalModelsData(); + if (licensedItem.getSeats().length === 0) { return; } const seatsSection = new qx.ui.container.Composite(new qx.ui.layout.VBox(5).set({ alignX: "left", })); - licensedItemData["seats"].forEach(purchase => { + licensedItem.getSeats().forEach(purchase => { const nSeats = purchase["numOfSeats"]; const seatsText = "seat" + (nSeats > 1 ? "s" : ""); const entry = new qx.ui.basic.Label().set({ From 112fcebf4610d11467bb2bcfb45d38d81ed55274 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 16:42:53 +0100 Subject: [PATCH 14/20] rename files --- .../{AnatomicalModelDetails.js => LicensedItemDetails.js} | 2 +- .../{AnatomicalModelListItem.js => LicensedItemListItem.js} | 2 +- .../client/source/class/osparc/vipMarket/VipMarket.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename services/static-webserver/client/source/class/osparc/vipMarket/{AnatomicalModelDetails.js => LicensedItemDetails.js} (99%) rename services/static-webserver/client/source/class/osparc/vipMarket/{AnatomicalModelListItem.js => LicensedItemListItem.js} (98%) diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js b/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js similarity index 99% rename from services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js rename to services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js index 264b76ce850..7bbe777c333 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js @@ -15,7 +15,7 @@ ************************************************************************ */ -qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", { +qx.Class.define("osparc.vipMarket.LicensedItemDetails", { extend: qx.ui.core.Widget, construct: function() { diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js b/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemListItem.js similarity index 98% rename from services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js rename to services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemListItem.js index 164bbed280d..fd6681c4d0d 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemListItem.js @@ -15,7 +15,7 @@ ************************************************************************ */ -qx.Class.define("osparc.vipMarket.AnatomicalModelListItem", { +qx.Class.define("osparc.vipMarket.LicensedItemListItem", { extend: qx.ui.core.Widget, implement : [qx.ui.form.IModel, osparc.filter.IFilterable], include : [qx.ui.form.MModelProperty, osparc.filter.MFilterable], diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js index ce98bdefe2e..e521c8f96b6 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js @@ -122,7 +122,7 @@ qx.Class.define("osparc.vipMarket.VipMarket", { }); break; case "models-details": { - control = new osparc.vipMarket.AnatomicalModelDetails().set({ + control = new osparc.vipMarket.LicensedItemDetails().set({ padding: 5, }); const scrollView = new qx.ui.container.Scroll(); @@ -145,7 +145,7 @@ qx.Class.define("osparc.vipMarket.VipMarket", { const anatomicalModelsModel = this.__anatomicalBundlesModel = new qx.data.Array(); const membersCtrl = new qx.data.controller.List(anatomicalModelsModel, modelsUIList, "displayName"); membersCtrl.setDelegate({ - createItem: () => new osparc.vipMarket.AnatomicalModelListItem(), + createItem: () => new osparc.vipMarket.LicensedItemListItem(), bindItem: (ctrl, item, id) => { ctrl.bindProperty("key", "key", null, item, id); ctrl.bindProperty("version", "version", null, item, id); From 2df75bccf7b7c49baf9db143b896f88dfb280e73 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 16:47:10 +0100 Subject: [PATCH 15/20] tables --- .../class/osparc/desktop/credits/CheckoutsTableModel.js | 4 ++-- .../class/osparc/desktop/credits/PurchasesTableModel.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/CheckoutsTableModel.js b/services/static-webserver/client/source/class/osparc/desktop/credits/CheckoutsTableModel.js index 8aba01876d9..8e25d5740b7 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/CheckoutsTableModel.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/CheckoutsTableModel.js @@ -131,7 +131,7 @@ qx.Class.define("osparc.desktop.credits.CheckoutsTableModel", { const checkoutsCols = osparc.desktop.credits.CheckoutsTable.COLS; checkoutsItems.forEach(checkoutsItem => { const licensedItemId = checkoutsItem["licensedItemId"]; - const licensedItem = licensedItems.find(licItem => licItem["licensedItemId"] === licensedItemId); + const licensedItem = licensedItems[licensedItemId]; let start = ""; let duration = ""; if (checkoutsItem["startedAt"]) { @@ -143,7 +143,7 @@ qx.Class.define("osparc.desktop.credits.CheckoutsTableModel", { data.push({ [checkoutsCols.CHECKOUT_ID.id]: checkoutsItem["licensedItemCheckoutId"], [checkoutsCols.ITEM_ID.id]: licensedItemId, - [checkoutsCols.ITEM_LABEL.id]: licensedItem ? licensedItem["displayName"] : "unknown model", + [checkoutsCols.ITEM_LABEL.id]: licensedItem ? licensedItem.getDisplayName() : "unknown model", [checkoutsCols.START.id]: start, [checkoutsCols.DURATION.id]: duration, [checkoutsCols.SEATS.id]: checkoutsItem["numOfSeats"], diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/PurchasesTableModel.js b/services/static-webserver/client/source/class/osparc/desktop/credits/PurchasesTableModel.js index 0792fb03180..97502433ff2 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/PurchasesTableModel.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/PurchasesTableModel.js @@ -130,11 +130,11 @@ qx.Class.define("osparc.desktop.credits.PurchasesTableModel", { const purchasesCols = osparc.desktop.credits.PurchasesTable.COLS; purchasesItems.forEach(purchasesItem => { const licensedItemId = purchasesItem["licensedItemId"]; - const licensedItem = licensedItems.find(licItem => licItem["licensedItemId"] === licensedItemId); + const licensedItem = licensedItems[licensedItemId]; data.push({ [purchasesCols.PURCHASE_ID.id]: purchasesItem["licensedItemPurchaseId"], [purchasesCols.ITEM_ID.id]: licensedItemId, - [purchasesCols.ITEM_LABEL.id]: licensedItem ? licensedItem["displayName"] : "unknown model", + [purchasesCols.ITEM_LABEL.id]: licensedItem ? licensedItem.getDisplayName() : "unknown model", [purchasesCols.START.id]: osparc.utils.Utils.formatDateAndTime(new Date(purchasesItem["startAt"])), [purchasesCols.END.id]: osparc.utils.Utils.formatDateAndTime(new Date(purchasesItem["expireAt"])), [purchasesCols.SEATS.id]: purchasesItem["numOfSeats"], From 34221c43906d1d15a312f767321e96f599d3b5a6 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 16:57:05 +0100 Subject: [PATCH 16/20] minor --- .../client/source/class/osparc/data/model/LicensedItem.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js index 54e509cf8c7..d498083ce0b 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js +++ b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js @@ -24,8 +24,7 @@ qx.Class.define("osparc.data.model.LicensedItem", { construct: function(licensedItemData) { this.base(arguments); - console.log("licensedItemData", licensedItemData); - + let categoryIcon = licensedItemData.categoryIcon || `osparc/market/${licensedItemData.categoryId}.svg`; let thumbnail = ""; let date = null; let licensedResources = []; @@ -46,7 +45,7 @@ qx.Class.define("osparc.data.model.LicensedItem", { licensedItemId: licensedItemData.licensedItemId, categoryId: licensedItemData.categoryId, categoryDisplay: licensedItemData.categoryDisplay, - categoryIcon: licensedItemData.categoryIcon || `osparc/market/${licensedItemData.categoryId}.svg`, + categoryIcon: categoryIcon, pricingPlanId: licensedItemData.pricingPlanId, key: licensedItemData.key, version: licensedItemData.version, From e85ab50f22e6999a76896fdf972e984b922dfd1c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 17:04:15 +0100 Subject: [PATCH 17/20] LicensedItemResource --- .../osparc/data/model/LicensedItemResource.js | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 services/static-webserver/client/source/class/osparc/data/model/LicensedItemResource.js diff --git a/services/static-webserver/client/source/class/osparc/data/model/LicensedItemResource.js b/services/static-webserver/client/source/class/osparc/data/model/LicensedItemResource.js new file mode 100644 index 00000000000..078fba8470c --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/data/model/LicensedItemResource.js @@ -0,0 +1,187 @@ +/* ************************************************************************ + + 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.data.model.LicensedItemResource", { + extend: qx.core.Object, + + /** + * @param licensedItemResourceData {Object} Object containing the serialized LicensedItem Data + */ + construct: function(licensedItemResourceData) { + this.base(arguments); + + let description = licensedItemResourceData["description"] || ""; + let title = ""; + let subtitle = null; + description = description.replace(/SPEAG/g, " "); // remove SPEAG substring + const delimiter = " - "; + let titleAndSubtitle = description.split(delimiter); + if (titleAndSubtitle.length > 0) { + title = titleAndSubtitle[0]; + titleAndSubtitle.shift(); + } + if (titleAndSubtitle.length > 0) { + subtitle = titleAndSubtitle.join(delimiter); + } + + const manufacturerData = {}; + if (licensedItemResourceData["thumbnail"]) { + if (licensedItemResourceData["thumbnail"].includes("itis.swiss")) { + manufacturerData["label"] = "IT'IS Foundation"; + manufacturerData["link"] = "https://itis.swiss/virtual-population/"; + manufacturerData["icon"] = "https://media.licdn.com/dms/image/v2/C4D0BAQE_FGa66IyvrQ/company-logo_200_200/company-logo_200_200/0/1631341490431?e=2147483647&v=beta&t=7f_IK-ArGjPrz-1xuWolAT4S2NdaVH-e_qa8hsKRaAc"; + } else if (licensedItemResourceData["thumbnail"].includes("speag.swiss")) { + manufacturerData["label"] = "Speag"; + manufacturerData["link"] = "https://speag.swiss/products/em-phantoms/overview-2/"; + manufacturerData["icon"] = "https://media.licdn.com/dms/image/v2/D4E0BAQG2CYG28KAKbA/company-logo_200_200/company-logo_200_200/0/1700045977122/schmid__partner_engineering_ag_logo?e=2147483647&v=beta&t=6CZb1jjg5TnnzQWkrZBS9R3ebRKesdflg-_xYi4dwD8"; + } + } + + this.set({ + description: description, + title: title, + subtitle: subtitle, + thumbnail: licensedItemResourceData.thumbnail || "", + features: licensedItemResourceData.features || {}, + doi: licensedItemResourceData.doi || null, + termsOfUseUrl: licensedItemResourceData.termsOfUseUrl || null, + manufacturerLabel: manufacturerData.label || null, + manufacturerLink: manufacturerData.link || null, + manufacturerIcon: manufacturerData.icon || null, + }); + }, + + properties: { + description: { + check: "String", + nullable: false, + init: null, + event: "changeDescription", + }, + + title: { + check: "String", + nullable: false, + init: null, + event: "changeTitle", + }, + + subtitle: { + check: "String", + nullable: true, + init: null, + event: "changeSubtitle", + }, + + thumbnail: { + check: "String", + nullable: false, + init: null, + event: "changeThumbnail", + }, + + features: { + check: "Object", + nullable: false, + init: null, + event: "changeFeatures", + }, + + doi: { + check: "String", + nullable: true, + init: null, + event: "changeDoi", + }, + + termsOfUseUrl: { + check: "String", + nullable: true, + init: null, + event: "changeTermsOfUseUrl", + }, + + manufacturerLabel: { + check: "String", + nullable: true, + init: null, + event: "changeManufacturerLabel", + }, + + manufacturerLink: { + check: "String", + nullable: true, + init: null, + event: "changeManufacturerLink", + }, + + manufacturerIcon: { + check: "String", + nullable: true, + init: null, + event: "changeManufacturerIcon", + }, + }, + + statics: { + addSeatsFromPurchases: function(licensedItemResources, purchases) { + // reset seats + Object.values(licensedItemResources).forEach(licensedItemResource => licensedItemResource.setSeats([])); + // populate seats + purchases.forEach(purchase => { + const { + key, + version, + } = purchase; + Object.values(licensedItemResources).forEach(licensedItemResource => { + if (licensedItemResource.getKey() === key && licensedItemResource.getVersion() <= version) { + licensedItemResource.getSeats().push({ + licensedItemResourceId: purchase["licensedItemResourceId"], + licensedItemResourcePurchaseId: purchase["licensedItemResourcePurchaseId"], + numOfSeats: purchase["numOfSeats"], + expireAt: new Date(purchase["expireAt"]), + }); + } + }); + }) + }, + + licensedResourceTitle: function(licensedResource) { + const name = licensedResource["features"]["name"] || osparc.data.model.LicensedItem.extractNameFromDescription(licensedResource); + const version = licensedResource["features"]["version"] || ""; + const functionality = licensedResource["features"]["functionality"] || "Static"; + return `${name} ${version}, ${functionality}`; + }, + + extractNameFromDescription: function(licensedResource) { + const description = licensedResource["description"] || ""; + const delimiter = " - "; + let typeAndName = description.split(delimiter); + if (typeAndName.length > 1) { + // drop the type + typeAndName.shift(); + // join the name + typeAndName = typeAndName.join(delimiter); + return typeAndName; + } + return ""; + }, + }, + + members: { + } +}); From 738053cabfcb93ee64ef378749ece8c72ed467b2 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 17:18:55 +0100 Subject: [PATCH 18/20] LicensedItemResource --- .../class/osparc/data/model/LicensedItem.js | 31 ++----- .../osparc/data/model/LicensedItemResource.js | 44 +--------- .../osparc/vipMarket/LicensedItemDetails.js | 84 ++++++++----------- 3 files changed, 46 insertions(+), 113 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js index d498083ce0b..a8c576b6f49 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js +++ b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js @@ -38,7 +38,15 @@ qx.Class.define("osparc.data.model.LicensedItem", { date = firstItem["features"]["date"]; } } - licensedItemData["licensedResources"].forEach(licensedRsrc => licensedResources.push(licensedRsrc["source"])); + licensedItemData["licensedResources"].forEach(licensedRsrc => { + const licensedItemResource = new osparc.data.model.LicensedItemResource(licensedRsrc["source"]); + if (licensedItemData["termsOfUseUrl"]) { + licensedItemResource.set({ + termsOfUseUrl: licensedItemData["termsOfUseUrl"], + }) + } + licensedResources.push(licensedItemResource); + }); } this.set({ @@ -165,27 +173,6 @@ qx.Class.define("osparc.data.model.LicensedItem", { }); }) }, - - licensedResourceTitle: function(licensedResource) { - const name = licensedResource["features"]["name"] || osparc.data.model.LicensedItem.extractNameFromDescription(licensedResource); - const version = licensedResource["features"]["version"] || ""; - const functionality = licensedResource["features"]["functionality"] || "Static"; - return `${name} ${version}, ${functionality}`; - }, - - extractNameFromDescription: function(licensedResource) { - const description = licensedResource["description"] || ""; - const delimiter = " - "; - let typeAndName = description.split(delimiter); - if (typeAndName.length > 1) { - // drop the type - typeAndName.shift(); - // join the name - typeAndName = typeAndName.join(delimiter); - return typeAndName; - } - return ""; - }, }, members: { diff --git a/services/static-webserver/client/source/class/osparc/data/model/LicensedItemResource.js b/services/static-webserver/client/source/class/osparc/data/model/LicensedItemResource.js index 078fba8470c..7a345ba0a0a 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/LicensedItemResource.js +++ b/services/static-webserver/client/source/class/osparc/data/model/LicensedItemResource.js @@ -138,48 +138,12 @@ qx.Class.define("osparc.data.model.LicensedItemResource", { }, statics: { - addSeatsFromPurchases: function(licensedItemResources, purchases) { - // reset seats - Object.values(licensedItemResources).forEach(licensedItemResource => licensedItemResource.setSeats([])); - // populate seats - purchases.forEach(purchase => { - const { - key, - version, - } = purchase; - Object.values(licensedItemResources).forEach(licensedItemResource => { - if (licensedItemResource.getKey() === key && licensedItemResource.getVersion() <= version) { - licensedItemResource.getSeats().push({ - licensedItemResourceId: purchase["licensedItemResourceId"], - licensedItemResourcePurchaseId: purchase["licensedItemResourcePurchaseId"], - numOfSeats: purchase["numOfSeats"], - expireAt: new Date(purchase["expireAt"]), - }); - } - }); - }) - }, - - licensedResourceTitle: function(licensedResource) { - const name = licensedResource["features"]["name"] || osparc.data.model.LicensedItem.extractNameFromDescription(licensedResource); - const version = licensedResource["features"]["version"] || ""; - const functionality = licensedResource["features"]["functionality"] || "Static"; + longName: function(licensedResource) { + const name = licensedResource.getFeatures()["name"] || licensedResource.getSubtitle(); + const version = licensedResource.getFeatures()["version"] || ""; + const functionality = licensedResource.getFeatures()["functionality"] || "Static"; return `${name} ${version}, ${functionality}`; }, - - extractNameFromDescription: function(licensedResource) { - const description = licensedResource["description"] || ""; - const delimiter = " - "; - let typeAndName = description.split(delimiter); - if (typeAndName.length > 1) { - // drop the type - typeAndName.shift(); - // join the name - typeAndName = typeAndName.join(delimiter); - return typeAndName; - } - return ""; - }, }, members: { diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js b/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js index 7bbe777c333..503762a089d 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js @@ -125,11 +125,11 @@ qx.Class.define("osparc.vipMarket.LicensedItemDetails", { const modelLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(4)).set({ allowGrowX: false, }); - const miniThumbnail = this.self().createThumbnail(licensedResource["thumbnail"], 32); + const miniThumbnail = this.self().createThumbnail(licensedResource.getThumbnail(), 32); osparc.utils.Utils.addBorder(miniThumbnail); modelLayout.add(miniThumbnail); const title = new qx.ui.basic.Label().set({ - value: osparc.data.model.LicensedItem.licensedResourceTitle(licensedResource), + value: osparc.data.model.LicensedItemResource.longName(licensedResource), alignY: "middle" }); modelLayout.add(title); @@ -156,13 +156,9 @@ qx.Class.define("osparc.vipMarket.LicensedItemDetails", { const topGrid = new qx.ui.layout.Grid(8, 6); topGrid.setColumnFlex(0, 1); const headerLayout = new qx.ui.container.Composite(topGrid); - let description = licensedResource["description"] || ""; - description = description.replace(/SPEAG/g, " "); // remove SPEAG substring - const delimiter = " - "; - let titleAndSubtitle = description.split(delimiter); - if (titleAndSubtitle.length > 0) { + if (licensedResource.getTitle()) { const titleLabel = new qx.ui.basic.Label().set({ - value: titleAndSubtitle[0], + value: licensedResource.getTitle(), font: "text-16", alignY: "middle", allowGrowX: true, @@ -172,12 +168,10 @@ qx.Class.define("osparc.vipMarket.LicensedItemDetails", { column: 0, row: 0, }); - titleAndSubtitle.shift(); } - if (titleAndSubtitle.length > 0) { - titleAndSubtitle = titleAndSubtitle.join(delimiter); + if (licensedResource.getSubtitle()) { const subtitleLabel = new qx.ui.basic.Label().set({ - value: titleAndSubtitle, + value: licensedResource.getSubtitle(), font: "text-16", alignY: "middle", allowGrowX: true, @@ -188,48 +182,36 @@ qx.Class.define("osparc.vipMarket.LicensedItemDetails", { row: 1, }); } - if (licensedResource["thumbnail"]) { - const manufacturerData = {}; - if (licensedResource["thumbnail"].includes("itis.swiss")) { - manufacturerData["label"] = "IT'IS Foundation"; - manufacturerData["link"] = "https://itis.swiss/virtual-population/"; - manufacturerData["icon"] = "https://media.licdn.com/dms/image/v2/C4D0BAQE_FGa66IyvrQ/company-logo_200_200/company-logo_200_200/0/1631341490431?e=2147483647&v=beta&t=7f_IK-ArGjPrz-1xuWolAT4S2NdaVH-e_qa8hsKRaAc"; - } else if (licensedResource["thumbnail"].includes("speag.swiss")) { - manufacturerData["label"] = "Speag"; - manufacturerData["link"] = "https://speag.swiss/products/em-phantoms/overview-2/"; - manufacturerData["icon"] = "https://media.licdn.com/dms/image/v2/D4E0BAQG2CYG28KAKbA/company-logo_200_200/company-logo_200_200/0/1700045977122/schmid__partner_engineering_ag_logo?e=2147483647&v=beta&t=6CZb1jjg5TnnzQWkrZBS9R3ebRKesdflg-_xYi4dwD8"; - } - if (Object.keys(manufacturerData).length) { - const manufacturerLink = new qx.ui.basic.Atom().set({ - label: manufacturerData["label"], - icon: manufacturerData["icon"], - font: "text-16", - gap: 10, - iconPosition: "right", - cursor: "pointer", - }); - manufacturerLink.getChildControl("icon").set({ - maxWidth: 32, - maxHeight: 32, - scale: true, - decorator: "rounded", - }); - manufacturerLink.addListener("tap", () => window.open(manufacturerData["link"])); - headerLayout.add(manufacturerLink, { - column: 1, - row: 0, - rowSpan: 2, - }); - } + if (licensedResource.getManufacturerLabel()) { + const manufacturerLink = new qx.ui.basic.Atom().set({ + label: licensedResource.getManufacturerLabel(), + icon: licensedResource.getManufacturerIcon(), + font: "text-16", + gap: 10, + iconPosition: "right", + cursor: "pointer", + }); + manufacturerLink.getChildControl("icon").set({ + maxWidth: 32, + maxHeight: 32, + scale: true, + decorator: "rounded", + }); + manufacturerLink.addListener("tap", () => window.open(licensedResource.getManufacturerLink())); + headerLayout.add(manufacturerLink, { + column: 1, + row: 0, + rowSpan: 2, + }); } modelInfoLayout.add(headerLayout); const middleLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(16)); - const thumbnail = this.self().createThumbnail(licensedResource["thumbnail"], 256); + const thumbnail = this.self().createThumbnail(licensedResource.getThumbnail(), 256); middleLayout.add(thumbnail); - const features = licensedResource["features"]; + const features = licensedResource.getFeatures(); const featuresGrid = new qx.ui.layout.Grid(8, 8); const featuresLayout = new qx.ui.container.Composite(featuresGrid); let idx = 0; @@ -278,7 +260,7 @@ qx.Class.define("osparc.vipMarket.LicensedItemDetails", { } }); - if (licensedResource["doi"]) { + if (licensedResource.getDoi()) { const doiTitle = new qx.ui.basic.Label().set({ value: "DOI", font: "text-14", @@ -305,14 +287,14 @@ qx.Class.define("osparc.vipMarket.LicensedItemDetails", { } return doiLabel; }; - featuresLayout.add(doiToLink(licensedResource["doi"]), { + featuresLayout.add(doiToLink(licensedResource.getDoi()), { column: 1, row: idx, }); idx++; } - if (licensedItem["termsOfUseUrl"] || licensedResource["termsOfUseUrl"]) { // remove the first one when this info goes down to the model + if (licensedResource.getTermsOfUseUrl()) { // remove the first one when this info goes down to the model const tAndC = new qx.ui.basic.Label().set({ font: "text-14", value: this.tr("Terms and Conditions"), @@ -320,7 +302,7 @@ qx.Class.define("osparc.vipMarket.LicensedItemDetails", { anonymous: false, cursor: "pointer", }); - tAndC.addListener("tap", () => this.__openLicense(licensedItem["termsOfUseUrl"] || licensedResource["termsOfUseUrl"])); + tAndC.addListener("tap", () => this.__openLicense(licensedResource.getTermsOfUseUrl())); featuresLayout.add(tAndC, { column: 1, row: idx, From 1f2b6589b6ebc8c9bd8a0dff3378673256c42d8a Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 17:26:31 +0100 Subject: [PATCH 19/20] LicensedItemDetails to the left --- .../source/class/osparc/vipMarket/LicensedItemDetails.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js b/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js index 503762a089d..f242f1bfb6b 100644 --- a/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js +++ b/services/static-webserver/client/source/class/osparc/vipMarket/LicensedItemDetails.js @@ -24,6 +24,10 @@ qx.Class.define("osparc.vipMarket.LicensedItemDetails", { const layout = new qx.ui.layout.VBox(15); this._setLayout(layout); + this.set({ + allowGrowX: false, + }); + this.__populateLayout(); }, From 57db3160c87d1c50cc020869b0e85c6a137e961d Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 21 Feb 2025 17:38:20 +0100 Subject: [PATCH 20/20] default icon --- .../client/source/class/osparc/data/model/LicensedItem.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js index a8c576b6f49..b184523a35d 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js +++ b/services/static-webserver/client/source/class/osparc/data/model/LicensedItem.js @@ -24,7 +24,6 @@ qx.Class.define("osparc.data.model.LicensedItem", { construct: function(licensedItemData) { this.base(arguments); - let categoryIcon = licensedItemData.categoryIcon || `osparc/market/${licensedItemData.categoryId}.svg`; let thumbnail = ""; let date = null; let licensedResources = []; @@ -48,6 +47,12 @@ qx.Class.define("osparc.data.model.LicensedItem", { licensedResources.push(licensedItemResource); }); } + let categoryIcon = "@FontAwesome5Solid/shopping-bag/20"; + if (licensedItemData.categoryIcon) { + categoryIcon = licensedItemData.categoryIcon; + } else if (qx.util.ResourceManager.getInstance().has(`osparc/market/${licensedItemData.categoryId}.svg`)) { + categoryIcon = `osparc/market/${licensedItemData.categoryId}.svg`; + } this.set({ licensedItemId: licensedItemData.licensedItemId,