diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js b/services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js
index 5eaf26b69472..1be932d331ae 100644
--- a/services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js
+++ b/services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js
@@ -36,7 +36,8 @@ qx.Class.define("osparc.desktop.credits.BillingCenter", {
if (osparc.product.Utils.showS4LStore()) {
this.__addPurchasesPage();
- this.__addCheckoutsPage();
+ // For now, do not add checkouts page
+ // this.__addCheckoutsPage();
}
},
@@ -102,7 +103,7 @@ qx.Class.define("osparc.desktop.credits.BillingCenter", {
__addPurchasesPage: function() {
const title = this.tr("Purchases");
- const iconSrc = "@FontAwesome5Solid/list/22";
+ const iconSrc = "@FontAwesome5Solid/shopping-bag/22";
const purchases = new osparc.desktop.credits.Purchases();
const page = this.addTab(title, iconSrc, purchases);
return page;
diff --git a/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/BasePage.js b/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/BasePage.js
index a32ec216d700..449546236ca3 100644
--- a/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/BasePage.js
+++ b/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/BasePage.js
@@ -30,7 +30,17 @@ qx.Class.define("osparc.desktop.preferences.pages.BasePage", {
paddingLeft: 15
});
- this.__showLabelOnTab(title)
+ this.__showLabelOnTab(title);
+
+ const tabButton = this.getChildControl("button");
+ if (tabButton.getIcon() && tabButton.getIcon().includes(".svg")) {
+ tabButton.getChildControl("icon").set({
+ minWidth: 24,
+ minHeight: 24,
+ scale: true,
+ });
+ osparc.ui.basic.SVGImage.setColorToImage(tabButton.getChildControl("icon"), "text");
+ }
},
members: {
diff --git a/services/static-webserver/client/source/class/osparc/study/PricingUnitLicense.js b/services/static-webserver/client/source/class/osparc/study/PricingUnitLicense.js
index 47335fef9249..925fb0f5e949 100644
--- a/services/static-webserver/client/source/class/osparc/study/PricingUnitLicense.js
+++ b/services/static-webserver/client/source/class/osparc/study/PricingUnitLicense.js
@@ -85,8 +85,11 @@ qx.Class.define("osparc.study.PricingUnitLicense", {
},
__rentUnit: function() {
+ const nCredits = this.getUnitData().getCost();
const expirationDate = osparc.study.PricingUnitLicense.getExpirationDate();
- const msg = this.getUnitData().getName() + this.tr(" will be available until ") + osparc.utils.Utils.formatDate(expirationDate);
+ let msg = this.getUnitData().getName() + this.tr(" will be available until ") + osparc.utils.Utils.formatDate(expirationDate);
+ msg += "
";
+ msg += `The rental will cost ${nCredits} credits`;
const confirmationWin = new osparc.ui.window.Confirmation(msg).set({
caption: this.tr("Rent"),
confirmText: this.tr("Rent"),
diff --git a/services/static-webserver/client/source/class/osparc/ui/basic/SVGImage.js b/services/static-webserver/client/source/class/osparc/ui/basic/SVGImage.js
index 83ef2e946468..878413af5e9d 100644
--- a/services/static-webserver/client/source/class/osparc/ui/basic/SVGImage.js
+++ b/services/static-webserver/client/source/class/osparc/ui/basic/SVGImage.js
@@ -127,6 +127,22 @@ qx.Class.define("osparc.ui.basic.SVGImage", {
const brightnessValue = l3 => l3;
const contrastValue = l4 => l4 > 50 ? 50 : l4;
return `invert(${invertValue(l)}%) sepia(${sepiaValue(s)}%) saturate(${saturateValue(s)}%) hue-rotate(${h}deg) brightness(${brightnessValue(l)}%) contrast(${contrastValue(l)}%)`;
+ },
+
+ setColorToImage: function(image, keywordOrRgb) {
+ if (keywordOrRgb === null) {
+ keywordOrRgb = "text";
+ }
+ let filterValue = this.self().keywordToCSSFilter(keywordOrRgb);
+ if (filterValue === null) {
+ const hexColor = qx.theme.manager.Color.getInstance().resolve(keywordOrRgb);
+ const rgbColor = qx.util.ColorUtil.hexStringToRgb(hexColor);
+ filterValue = this.self().rgbToCSSFilter(rgbColor);
+ }
+ const myStyle = {
+ "filter": filterValue
+ };
+ image.getContentElement().setStyles(myStyle);
}
},
@@ -160,19 +176,7 @@ qx.Class.define("osparc.ui.basic.SVGImage", {
* @param keywordOrRgb {string} predefined keyword or rgb in the following format "0,255,0"
*/
__applyImageColor: function(keywordOrRgb) {
- if (keywordOrRgb === null) {
- keywordOrRgb = "text";
- }
- let filterValue = this.self().keywordToCSSFilter(keywordOrRgb);
- if (filterValue === null) {
- const hexColor = qx.theme.manager.Color.getInstance().resolve(keywordOrRgb);
- const rgbColor = qx.util.ColorUtil.hexStringToRgb(hexColor);
- filterValue = this.self().rgbToCSSFilter(rgbColor);
- }
- const myStyle = {
- "filter": filterValue
- };
- this.getChildControl("image").getContentElement().setStyles(myStyle);
- }
+ this.self().setColorToImage(this.getChildControl("image"), keywordOrRgb);
+ },
}
});
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 23adae7c5fca..dc21d9af607a 100644
--- a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js
+++ b/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelDetails.js
@@ -21,7 +21,7 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
construct: function() {
this.base(arguments);
- const layout = new qx.ui.layout.VBox(15);
+ const layout = new qx.ui.layout.VBox(10);
this._setLayout(layout);
this.__populateLayout();
@@ -53,13 +53,10 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
this._removeAll();
const anatomicalModelsData = this.getAnatomicalModelsData();
- if (anatomicalModelsData && anatomicalModelsData["licensedResourceData"]) {
- const modelInfo = this.__createModelInfo(anatomicalModelsData["licensedResourceData"]["source"]);
- const pricingUnits = this.__createPricingUnits(anatomicalModelsData);
- const importButton = this.__createImportSection(anatomicalModelsData);
- this._add(modelInfo);
- this._add(pricingUnits);
- this._add(importButton);
+ if (anatomicalModelsData && anatomicalModelsData["licensedResources"].length) {
+ this.__addModelsInfo();
+ this.__addPricingUnits();
+ this.__addSeatsSection();
} else {
const selectModelLabel = new qx.ui.basic.Label().set({
value: this.tr("Select a model for more details"),
@@ -73,10 +70,45 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
}
},
- __createModelInfo: function(anatomicalModelsDataSource) {
- const cardLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(16));
+ __addModelsInfo: function() {
+ const modelLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(16));
- const description = anatomicalModelsDataSource["description"] || "";
+ const anatomicalModelsData = this.getAnatomicalModelsData();
+ const modelsInfo = anatomicalModelsData["licensedResources"];
+ if (modelsInfo.length > 1) {
+ const sBox = new qx.ui.form.SelectBox().set({
+ minWidth: 200,
+ allowGrowX: false,
+ });
+ modelsInfo.forEach(modelInfo => {
+ const sbItem = new qx.ui.form.ListItem(modelInfo["source"]["features"]["name"]);
+ sbItem.modelId = modelInfo["source"]["id"];
+ sBox.add(sbItem);
+ });
+ this._add(sBox);
+ sBox.addListener("changeSelection", e => {
+ const selection = e.getData();
+ if (selection.length) {
+ const idxFound = modelsInfo.findIndex(mdlInfo => mdlInfo["source"]["id"] === selection[0].modelId)
+ this.__populateModelInfo(modelLayout, anatomicalModelsData, idxFound);
+ }
+ }, this);
+ this.__populateModelInfo(modelLayout, anatomicalModelsData, 0);
+ } else {
+ this.__populateModelInfo(modelLayout, anatomicalModelsData, 0);
+ }
+
+ this._add(modelLayout);
+ },
+
+ __populateModelInfo: function(modelLayout, anatomicalModelsData, selectedIdx = 0) {
+ modelLayout.removeAll();
+
+ const anatomicalModel = anatomicalModelsData["licensedResources"][selectedIdx]["source"];
+ const topGrid = new qx.ui.layout.Grid(8, 8);
+ topGrid.setColumnFlex(0, 1);
+ const topLayout = new qx.ui.container.Composite(topGrid);
+ const description = anatomicalModel["description"] || "";
const delimiter = " - ";
let titleAndSubtitle = description.split(delimiter);
if (titleAndSubtitle.length > 0) {
@@ -87,7 +119,10 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
allowGrowX: true,
allowGrowY: true,
});
- cardLayout.add(titleLabel);
+ topLayout.add(titleLabel, {
+ column: 0,
+ row: 0,
+ });
titleAndSubtitle.shift();
}
if (titleAndSubtitle.length > 0) {
@@ -99,13 +134,49 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
allowGrowX: true,
allowGrowY: true,
});
- cardLayout.add(subtitleLabel);
+ topLayout.add(subtitleLabel, {
+ column: 0,
+ row: 1,
+ });
}
+ if (anatomicalModel["thumbnail"]) {
+ const manufacturerData = {};
+ if (anatomicalModel["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")) {
+ 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";
+ }
+ 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"]));
+ topLayout.add(manufacturerLink, {
+ column: 1,
+ row: 0,
+ rowSpan: 2,
+ });
+ }
+ modelLayout.add(topLayout);
const middleLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(16));
const thumbnail = new qx.ui.basic.Image().set({
- source: anatomicalModelsDataSource["thumbnail"],
+ source: anatomicalModel["thumbnail"],
alignY: "middle",
scale: true,
allowGrowX: true,
@@ -117,7 +188,7 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
});
middleLayout.add(thumbnail);
- const features = anatomicalModelsDataSource["features"];
+ const features = anatomicalModel["features"];
const featuresGrid = new qx.ui.layout.Grid(8, 8);
const featuresLayout = new qx.ui.container.Composite(featuresGrid);
let idx = 0;
@@ -157,45 +228,75 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
}
});
- const doiTitle = new qx.ui.basic.Label().set({
- value: "DOI",
- font: "text-14",
- alignX: "right",
- marginTop: 16,
- });
- featuresLayout.add(doiTitle, {
- column: 0,
- row: idx,
- });
-
- const doiToLink = doi => {
- const doiLabel = new osparc.ui.basic.LinkLabel("-").set({
+ if (anatomicalModel["doi"]) {
+ const doiTitle = new qx.ui.basic.Label().set({
+ value: "DOI",
font: "text-14",
- alignX: "left",
+ alignX: "right",
marginTop: 16,
});
- if (doi) {
- doiLabel.set({
- value: doi,
- url: "https://doi.org/" + doi,
- font: "link-label-14",
+ featuresLayout.add(doiTitle, {
+ column: 0,
+ row: idx,
+ });
+
+ const doiToLink = doi => {
+ const doiLabel = new osparc.ui.basic.LinkLabel("-").set({
+ font: "text-14",
+ alignX: "left",
+ marginTop: 16,
});
- }
- return doiLabel;
- };
- featuresLayout.add(doiToLink(anatomicalModelsDataSource["doi"]), {
- column: 1,
- row: idx,
- });
+ if (doi) {
+ doiLabel.set({
+ value: doi,
+ url: "https://doi.org/" + doi,
+ font: "link-label-14",
+ });
+ }
+ return doiLabel;
+ };
+ featuresLayout.add(doiToLink(anatomicalModel["doi"]), {
+ column: 1,
+ row: idx,
+ });
+ }
middleLayout.add(featuresLayout);
- cardLayout.add(middleLayout);
+ modelLayout.add(middleLayout);
- return cardLayout;
+ const importButton = this.__createImportSection(anatomicalModelsData, selectedIdx);
+ modelLayout.add(importButton);
+ },
+
+ __createImportSection: function(anatomicalModelsData, selectedIdx) {
+ const importSection = new qx.ui.container.Composite(new qx.ui.layout.VBox(5).set({
+ alignX: "center"
+ }));
+
+ const importButton = new qx.ui.form.Button().set({
+ label: this.tr("Import"),
+ appearance: "strong-button",
+ center: true,
+ maxWidth: 200,
+ alignX: "center",
+ });
+ this.bind("openBy", importButton, "visibility", {
+ converter: openBy => openBy ? "visible" : "excluded"
+ });
+ importButton.addListener("execute", () => {
+ this.fireDataEvent("modelImportRequested", {
+ modelId: anatomicalModelsData["licensedResources"][selectedIdx]["source"]["id"]
+ });
+ }, this);
+ if (anatomicalModelsData["purchases"].length) {
+ importSection.add(importButton);
+ }
+ return importSection;
},
- __createPricingUnits: function(anatomicalModelsData) {
+ __addPricingUnits: function() {
+ const anatomicalModelsData = this.getAnatomicalModelsData();
const pricingUnitsLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({
alignX: "center"
}));
@@ -211,7 +312,6 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
});
pUnit.addListener("rentPricingUnit", () => {
this.fireDataEvent("modelPurchaseRequested", {
- modelId: anatomicalModelsData["licensedResourceData"]["source"]["id"],
licensedItemId: anatomicalModelsData["licensedItemId"],
pricingPlanId: anatomicalModelsData["pricingPlanId"],
pricingUnitId: pricingUnit.getPricingUnitId(),
@@ -222,12 +322,13 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
})
.catch(err => console.error(err));
- return pricingUnitsLayout;
+ this._add(pricingUnitsLayout);
},
- __createImportSection: function(anatomicalModelsData) {
- const importSection = new qx.ui.container.Composite(new qx.ui.layout.VBox(5).set({
- alignX: "center"
+ __addSeatsSection: function() {
+ const anatomicalModelsData = this.getAnatomicalModelsData();
+ const seatsSection = new qx.ui.container.Composite(new qx.ui.layout.VBox(5).set({
+ alignX: "center",
}));
anatomicalModelsData["purchases"].forEach(purchase => {
@@ -236,28 +337,10 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelDetails", {
value: `${purchase["numberOfSeats"]} ${seatsText} available until ${osparc.utils.Utils.formatDate(purchase["expiresAt"])}`,
font: "text-14",
});
- importSection.add(entry);
+ seatsSection.add(entry);
});
- const importButton = new qx.ui.form.Button().set({
- label: this.tr("Import"),
- appearance: "strong-button",
- center: true,
- maxWidth: 200,
- alignX: "center",
- });
- this.bind("openBy", importButton, "visibility", {
- converter: openBy => openBy ? "visible" : "excluded"
- });
- importButton.addListener("execute", () => {
- this.fireDataEvent("modelImportRequested", {
- modelId: anatomicalModelsData["licensedResourceData"]["source"]["id"]
- });
- }, this);
- if (anatomicalModelsData["purchases"].length) {
- importSection.add(importButton);
- }
- return importSection;
+ this._add(seatsSection);
},
}
});
diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js b/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js
index 83568616d0f0..fb87e1e89aa7 100644
--- a/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js
+++ b/services/static-webserver/client/source/class/osparc/vipMarket/AnatomicalModelListItem.js
@@ -54,11 +54,25 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelListItem", {
init: "selectable"
},
- modelId: {
- check: "Number",
+ licenseKey: {
+ check: "String",
init: null,
nullable: false,
- event: "changeModelId",
+ event: "changeLicenseKey",
+ },
+
+ licenseVersion: {
+ check: "String",
+ init: null,
+ nullable: false,
+ event: "changeLicenseVersion",
+ },
+
+ licensedItemId: {
+ check: "String",
+ init: null,
+ nullable: false,
+ event: "changeLicensedItemId",
},
thumbnail: {
@@ -84,13 +98,6 @@ qx.Class.define("osparc.vipMarket.AnatomicalModelListItem", {
event: "changeDate",
},
- licensedItemId: {
- check: "String",
- init: null,
- nullable: false,
- event: "changeLicensedItemId",
- },
-
pricingPlanId: {
check: "Number",
init: null,
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 6e76681978dc..a772378afd60 100644
--- a/services/static-webserver/client/source/class/osparc/vipMarket/Market.js
+++ b/services/static-webserver/client/source/class/osparc/vipMarket/Market.js
@@ -41,18 +41,35 @@ qx.Class.define("osparc.vipMarket.Market", {
])
.then(values => {
const licensedItems = values[0];
+ const purchasedItems = values[1];
const categories = [];
+ const purchasedCategory = {
+ categoryId: "purchasedModels",
+ label: this.tr("Rented"),
+ icon: "osparc/market/RentedModels.svg",
+ items: [],
+ };
+ categories.push(purchasedCategory);
licensedItems.forEach(licensedItem => {
- if (licensedItem["licensedResourceData"] && licensedItem["licensedResourceData"]["categoryId"]) {
- const categoryId = licensedItem["licensedResourceData"]["categoryId"];
+ if (purchasedItems.find(purchasedItem => purchasedItem["licensedItemId"] === licensedItem["licensedItemId"])) {
+ purchasedCategory["items"].push(licensedItem);
+ if (!openCategory) {
+ openCategory = purchasedCategory["categoryId"];
+ }
+ }
+ if (licensedItem && licensedItem["categoryId"]) {
+ const categoryId = licensedItem["categoryId"];
let category = categories.find(cat => cat["categoryId"] === categoryId);
if (!category) {
category = {
categoryId,
- label: licensedItem["licensedResourceData"]["categoryDisplay"] || "Category",
- icon: licensedItem["licensedResourceData"]["categoryIcon"] || "@FontAwesome5Solid/users/20",
+ label: licensedItem["categoryDisplay"] || "Category",
+ icon: licensedItem["categoryIcon"] || `osparc/market/${categoryId}.svg`,
items: [],
};
+ if (!openCategory) {
+ openCategory = categoryId;
+ }
categories.push(category);
}
category["items"].push(licensedItem);
@@ -70,7 +87,7 @@ qx.Class.define("osparc.vipMarket.Market", {
},
events: {
- "importMessageSent": "qx.event.type.Data",
+ "importMessageSent": "qx.event.type.Event",
},
properties: {
@@ -83,18 +100,50 @@ qx.Class.define("osparc.vipMarket.Market", {
},
members: {
+ __purchasedCategoryButton: null,
+ __purchasedCategoryMarket: null,
+
__buildViPMarketPage: function(marketTabInfo, licensedItems = []) {
const vipMarketView = new osparc.vipMarket.VipMarket(licensedItems);
vipMarketView.set({
category: marketTabInfo["categoryId"],
});
this.bind("openBy", vipMarketView, "openBy");
+ vipMarketView.addListener("modelPurchased", () => this.__repopulatePurchasedCategory());
vipMarketView.addListener("importMessageSent", () => this.fireEvent("importMessageSent"));
const page = this.addTab(marketTabInfo["label"], marketTabInfo["icon"], vipMarketView);
page.category = marketTabInfo["categoryId"];
+ if (page.category === "purchasedModels") {
+ this.__purchasedCategoryMarket = vipMarketView;
+ this.__purchasedCategoryButton = page.getChildControl("button");
+ this.__purchasedCategoryButton.setVisibility(licensedItems.length ? "visible" : "excluded");
+ }
return page;
},
+ __repopulatePurchasedCategory: function() {
+ const store = osparc.store.Store.getInstance();
+ const contextWallet = store.getContextWallet();
+ const walletId = contextWallet.getWalletId();
+ const licensedItemsStore = osparc.store.LicensedItems.getInstance();
+ Promise.all([
+ licensedItemsStore.getLicensedItems(),
+ licensedItemsStore.getPurchasedLicensedItems(walletId),
+ ])
+ .then(values => {
+ const licensedItems = values[0];
+ const purchasedItems = values[1];
+ let items = [];
+ licensedItems.forEach(licensedItem => {
+ if (purchasedItems.find(purchasedItem => purchasedItem["licensedItemId"] === licensedItem["licensedItemId"])) {
+ items.push(licensedItem);
+ }
+ });
+ this.__purchasedCategoryButton.setVisibility(items.length ? "visible" : "excluded");
+ this.__purchasedCategoryMarket.setLicensedItems(items);
+ });
+ },
+
__openCategory: function(category) {
const viewFound = this.getChildControl("tabs-view").getChildren().find(view => view.category === category);
if (viewFound) {
diff --git a/services/static-webserver/client/source/class/osparc/vipMarket/MarketWindow.js b/services/static-webserver/client/source/class/osparc/vipMarket/MarketWindow.js
index c238f5618b8e..035d7221ad4d 100644
--- a/services/static-webserver/client/source/class/osparc/vipMarket/MarketWindow.js
+++ b/services/static-webserver/client/source/class/osparc/vipMarket/MarketWindow.js
@@ -23,7 +23,7 @@ qx.Class.define("osparc.vipMarket.MarketWindow", {
osparc.utils.Utils.setIdToWidget(this, "storeWindow");
- const width = 1035;
+ const width = 1050;
const height = 700;
this.set({
width,
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 4fa9bbac8d39..a518b3f2455f 100644
--- a/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js
+++ b/services/static-webserver/client/source/class/osparc/vipMarket/VipMarket.js
@@ -18,20 +18,21 @@
qx.Class.define("osparc.vipMarket.VipMarket", {
extend: qx.ui.core.Widget,
- construct: function(anatomicalModels) {
+ construct: function(licensedItems) {
this.base(arguments);
this._setLayout(new qx.ui.layout.HBox(10));
this.__buildLayout();
- if (anatomicalModels) {
- this.setLicensedItems(anatomicalModels);
+ if (licensedItems) {
+ this.setLicensedItems(licensedItems);
}
},
events: {
- "importMessageSent": "qx.event.type.Data"
+ "modelPurchased": "qx.event.type.Event",
+ "importMessageSent": "qx.event.type.Event",
},
properties: {
@@ -50,8 +51,8 @@ qx.Class.define("osparc.vipMarket.VipMarket", {
},
members: {
- __anatomicalModels: null,
- __anatomicalModelsModel: null,
+ __anatomicalBundles: null,
+ __anatomicalBundlesModel: null,
_createChildControlImpl: function(id) {
let control;
@@ -127,12 +128,13 @@ qx.Class.define("osparc.vipMarket.VipMarket", {
this.getChildControl("filter-text");
const modelsUIList = this.getChildControl("models-list");
- const anatomicalModelsModel = this.__anatomicalModelsModel = new qx.data.Array();
+ 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(),
bindItem: (ctrl, item, id) => {
- ctrl.bindProperty("modelId", "modelId", null, item, id);
+ ctrl.bindProperty("licenseKey", "licenseKey", null, item, id);
+ ctrl.bindProperty("licenseVersion", "licenseVersion", null, item, id);
ctrl.bindProperty("thumbnail", "thumbnail", null, item, id);
ctrl.bindProperty("displayName", "displayName", null, item, id);
ctrl.bindProperty("date", "date", null, item, id);
@@ -150,17 +152,17 @@ qx.Class.define("osparc.vipMarket.VipMarket", {
thumbnail: "@FontAwesome5Solid/spinner/32",
name: this.tr("Loading"),
};
- this.__anatomicalModelsModel.append(qx.data.marshal.Json.createModel(loadingModel));
+ this.__anatomicalBundlesModel.append(qx.data.marshal.Json.createModel(loadingModel));
const anatomicModelDetails = this.getChildControl("models-details");
modelsUIList.addListener("changeSelection", e => {
const selection = e.getData();
if (selection.length) {
- const modelId = selection[0].getModelId();
- const modelFound = this.__anatomicalModels.find(anatomicalModel => anatomicalModel["modelId"] === modelId);
- if (modelFound) {
- anatomicModelDetails.setAnatomicalModelsData(modelFound);
+ const licensedItemId = selection[0].getLicensedItemId();
+ const bundleFound = this.__anatomicalBundles.find(anatomicalBundle => anatomicalBundle["licensedItemId"] === licensedItemId);
+ if (bundleFound) {
+ anatomicModelDetails.setAnatomicalModelsData(bundleFound);
return;
}
}
@@ -168,90 +170,59 @@ qx.Class.define("osparc.vipMarket.VipMarket", {
}, this);
},
- setLicensedItems: function(licensedItems) {
+ setLicensedItems: function(licensedBundles) {
const store = osparc.store.Store.getInstance();
const contextWallet = store.getContextWallet();
if (!contextWallet) {
return;
}
- const licensedItemsStore = osparc.store.LicensedItems.getInstance();
const walletId = contextWallet.getWalletId();
+ const licensedItemsStore = osparc.store.LicensedItems.getInstance();
licensedItemsStore.getPurchasedLicensedItems(walletId)
.then(purchasesItems => {
- this.__anatomicalModels = [];
- licensedItems.forEach(licensedItem => {
- const anatomicalModel = osparc.utils.Utils.deepCloneObject(licensedItem);
- const anatomicalModelDataSource = anatomicalModel["licensedResourceData"]["source"];
- anatomicalModel["modelId"] = anatomicalModelDataSource["id"];
- anatomicalModel["thumbnail"] = "";
- anatomicalModel["date"] = null;
- if (anatomicalModelDataSource["thumbnail"]) {
- anatomicalModel["thumbnail"] = anatomicalModelDataSource["thumbnail"];
- }
- if (anatomicalModelDataSource["features"] && anatomicalModelDataSource["features"]["date"]) {
- anatomicalModel["date"] = new Date(anatomicalModelDataSource["features"]["date"]);
+ this.__anatomicalBundles = [];
+ licensedBundles.forEach(licensedBundle => {
+ const anatomicalBundle = osparc.utils.Utils.deepCloneObject(licensedBundle);
+ anatomicalBundle["licenseKey"] = anatomicalBundle["key"];
+ anatomicalBundle["licenseVersion"] = anatomicalBundle["version"];
+ anatomicalBundle["thumbnail"] = "";
+ anatomicalBundle["date"] = null;
+ if (anatomicalBundle["licensedResources"] && anatomicalBundle["licensedResources"].length) {
+ const firstItem = anatomicalBundle["licensedResources"][0]["source"];
+ if (firstItem["thumbnail"]) {
+ anatomicalBundle["thumbnail"] = firstItem["thumbnail"];
+ }
+ if (firstItem["features"] && firstItem["features"]["date"]) {
+ anatomicalBundle["date"] = new Date(firstItem["features"]["date"]);
+ }
}
// attach license data
- anatomicalModel["licensedItemId"] = licensedItem["licensedItemId"];
- anatomicalModel["pricingPlanId"] = licensedItem["pricingPlanId"];
- // attach leased data
- anatomicalModel["purchases"] = []; // default
- const purchasesItemsFound = purchasesItems.filter(purchasesItem => purchasesItem["licensedItemId"] === licensedItem["licensedItemId"]);
+ anatomicalBundle["pricingPlanId"] = licensedBundle["pricingPlanId"];
+ // attach purchases data
+ anatomicalBundle["purchases"] = []; // default
+ const purchasesItemsFound = purchasesItems.filter(purchasesItem => purchasesItem["licensedItemId"] === licensedBundle["licensedItemId"]);
if (purchasesItemsFound.length) {
purchasesItemsFound.forEach(purchasesItemFound => {
- anatomicalModel["purchases"].push({
+ anatomicalBundle["purchases"].push({
expiresAt: new Date(purchasesItemFound["expireAt"]),
numberOfSeats: purchasesItemFound["numOfSeats"],
})
});
}
- this.__anatomicalModels.push(anatomicalModel);
+ this.__anatomicalBundles.push(anatomicalBundle);
});
this.__populateModels();
const anatomicModelDetails = this.getChildControl("models-details");
anatomicModelDetails.addListener("modelPurchaseRequested", e => {
- if (!contextWallet) {
- return;
- }
const {
- modelId,
licensedItemId,
pricingPlanId,
pricingUnitId,
} = e.getData();
- let numberOfSeats = null;
- const pricingUnit = osparc.store.Pricing.getInstance().getPricingUnit(pricingPlanId, pricingUnitId);
- if (pricingUnit) {
- const split = pricingUnit.getName().split(" ");
- numberOfSeats = parseInt(split[0]);
- }
- licensedItemsStore.purchaseLicensedItem(licensedItemId, walletId, pricingPlanId, pricingUnitId, numberOfSeats)
- .then(() => {
- const expirationDate = osparc.study.PricingUnitLicense.getExpirationDate();
- const purchaseData = {
- expiresAt: expirationDate, // get this info from the response
- numberOfSeats, // get this info from the response
- };
-
- let msg = numberOfSeats;
- msg += " seat" + (purchaseData["numberOfSeats"] > 1 ? "s" : "");
- msg += " rented until " + osparc.utils.Utils.formatDate(purchaseData["expiresAt"]);
- osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
-
- const found = this.__anatomicalModels.find(model => model["modelId"] === modelId);
- if (found) {
- found["purchases"].push(purchaseData);
- this.__populateModels(modelId);
- anatomicModelDetails.setAnatomicalModelsData(found);
- }
- })
- .catch(err => {
- const msg = err.message || this.tr("Cannot purchase model");
- osparc.FlashMessenger.getInstance().logAs(msg, "ERROR");
- });
+ this.__modelPurchaseRequested(licensedItemId, pricingPlanId, pricingUnitId);
}, this);
anatomicModelDetails.addListener("modelImportRequested", e => {
@@ -263,10 +234,10 @@ qx.Class.define("osparc.vipMarket.VipMarket", {
});
},
- __populateModels: function(selectModelId) {
- const models = this.__anatomicalModels;
+ __populateModels: function(selectLicensedItemId) {
+ const models = this.__anatomicalBundles;
- this.__anatomicalModelsModel.removeAll();
+ this.__anatomicalBundlesModel.removeAll();
const sortModel = sortBy => {
models.sort((a, b) => {
// first criteria
@@ -298,20 +269,20 @@ qx.Class.define("osparc.vipMarket.VipMarket", {
});
};
sortModel();
- models.forEach(model => this.__anatomicalModelsModel.append(qx.data.marshal.Json.createModel(model)));
+ models.forEach(model => this.__anatomicalBundlesModel.append(qx.data.marshal.Json.createModel(model)));
this.getChildControl("sort-button").addListener("sortBy", e => {
- this.__anatomicalModelsModel.removeAll();
+ this.__anatomicalBundlesModel.removeAll();
const sortBy = e.getData();
sortModel(sortBy);
- models.forEach(model => this.__anatomicalModelsModel.append(qx.data.marshal.Json.createModel(model)));
+ models.forEach(model => this.__anatomicalBundlesModel.append(qx.data.marshal.Json.createModel(model)));
}, this);
// select model after timeout, there is something that changes the selection to empty after populating the list
setTimeout(() => {
const modelsUIList = this.getChildControl("models-list");
- if (selectModelId) {
- const entryFound = modelsUIList.getSelectables().find(entry => "getModelId" in entry && entry.getModelId() === selectModelId);
+ if (selectLicensedItemId) {
+ const entryFound = modelsUIList.getSelectables().find(entry => "getLicensedItemId" in entry && entry.getLicensedItemId() === selectLicensedItemId);
modelsUIList.setSelection([entryFound]);
} else if (modelsUIList.getSelectables().length) {
// select first
@@ -320,6 +291,48 @@ qx.Class.define("osparc.vipMarket.VipMarket", {
}, 100);
},
+ __modelPurchaseRequested: function(licensedItemId, pricingPlanId, pricingUnitId) {
+ const store = osparc.store.Store.getInstance();
+ const contextWallet = store.getContextWallet();
+ if (!contextWallet) {
+ return;
+ }
+ const walletId = contextWallet.getWalletId();
+ let numberOfSeats = null;
+ const pricingUnit = osparc.store.Pricing.getInstance().getPricingUnit(pricingPlanId, pricingUnitId);
+ if (pricingUnit) {
+ const split = pricingUnit.getName().split(" ");
+ numberOfSeats = parseInt(split[0]);
+ }
+ const licensedItemsStore = osparc.store.LicensedItems.getInstance();
+ licensedItemsStore.purchaseLicensedItem(licensedItemId, walletId, pricingPlanId, pricingUnitId, numberOfSeats)
+ .then(() => {
+ const expirationDate = osparc.study.PricingUnitLicense.getExpirationDate();
+ const purchaseData = {
+ expiresAt: expirationDate, // get this info from the response
+ numberOfSeats, // get this info from the response
+ };
+
+ let msg = numberOfSeats;
+ msg += " seat" + (purchaseData["numberOfSeats"] > 1 ? "s" : "");
+ msg += " rented until " + osparc.utils.Utils.formatDate(purchaseData["expiresAt"]);
+ osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
+
+ const found = this.__anatomicalBundles.find(model => model["licensedItemId"] === licensedItemId);
+ if (found) {
+ found["purchases"].push(purchaseData);
+ this.__populateModels(licensedItemId);
+ const anatomicModelDetails = this.getChildControl("models-details");
+ anatomicModelDetails.setAnatomicalModelsData(found);
+ }
+ this.fireEvent("modelPurchased");
+ })
+ .catch(err => {
+ const msg = err.message || this.tr("Cannot purchase model");
+ osparc.FlashMessenger.getInstance().logAs(msg, "ERROR");
+ });
+ },
+
__sendImportModelMessage: function(modelId) {
const store = osparc.store.Store.getInstance();
const currentStudy = store.getCurrentStudy();
diff --git a/services/static-webserver/client/source/resource/osparc/market/AnimalWholeBody.svg b/services/static-webserver/client/source/resource/osparc/market/AnimalWholeBody.svg
new file mode 100644
index 000000000000..cbfbc598fc17
--- /dev/null
+++ b/services/static-webserver/client/source/resource/osparc/market/AnimalWholeBody.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/services/static-webserver/client/source/resource/osparc/market/ComputationalPhantom.svg b/services/static-webserver/client/source/resource/osparc/market/ComputationalPhantom.svg
new file mode 100644
index 000000000000..806af72544a2
--- /dev/null
+++ b/services/static-webserver/client/source/resource/osparc/market/ComputationalPhantom.svg
@@ -0,0 +1,48 @@
+
+
diff --git a/services/static-webserver/client/source/resource/osparc/market/HumanBodyRegion.svg b/services/static-webserver/client/source/resource/osparc/market/HumanBodyRegion.svg
new file mode 100644
index 000000000000..f3fd1e478485
--- /dev/null
+++ b/services/static-webserver/client/source/resource/osparc/market/HumanBodyRegion.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/services/static-webserver/client/source/resource/osparc/market/HumanWholeBody.svg b/services/static-webserver/client/source/resource/osparc/market/HumanWholeBody.svg
new file mode 100644
index 000000000000..2321ac476704
--- /dev/null
+++ b/services/static-webserver/client/source/resource/osparc/market/HumanWholeBody.svg
@@ -0,0 +1,51 @@
+
+
diff --git a/services/static-webserver/client/source/resource/osparc/market/RentedModels.svg b/services/static-webserver/client/source/resource/osparc/market/RentedModels.svg
new file mode 100644
index 000000000000..12fa9d1d5c17
--- /dev/null
+++ b/services/static-webserver/client/source/resource/osparc/market/RentedModels.svg
@@ -0,0 +1,53 @@
+
+