Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [Frontend] Feature: Pay study's debt #7061

Merged
merged 62 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
5a7887f
renaming
odeimaiz Jan 20, 2025
29f9703
[skip ci] refactor
odeimaiz Jan 20, 2025
2afb8e3
[skip ci] minor
odeimaiz Jan 20, 2025
9ab7842
minor
odeimaiz Jan 20, 2025
d898819
minor
odeimaiz Jan 20, 2025
9a4de1b
minor
odeimaiz Jan 20, 2025
b1a9626
[skip ci] inDebt Study Property
odeimaiz Jan 20, 2025
923f7cc
mock debt response
odeimaiz Jan 20, 2025
74ba663
inDebt number
odeimaiz Jan 20, 2025
b6567ef
[skip ci] setStudyDebt
odeimaiz Jan 20, 2025
1a917c9
wiring debt
odeimaiz Jan 20, 2025
e540fc2
Merge branch 'master' into feature/pay-project-debt
odeimaiz Jan 21, 2025
9b16d0e
minor
odeimaiz Jan 21, 2025
b423326
[skip ci] block card IN_DEBT
odeimaiz Jan 21, 2025
3d61709
Merge branch 'master' into feature/pay-project-debt
odeimaiz Jan 21, 2025
2a0820b
undo mock
odeimaiz Jan 21, 2025
f3d643e
connect backend
odeimaiz Jan 21, 2025
0d093b9
[skip ci] minor
odeimaiz Jan 21, 2025
fc68b50
[skip ci] extract debt from message
odeimaiz Jan 21, 2025
dc7be95
Tier Settings -> Billing Settings
odeimaiz Jan 21, 2025
92a5bda
[skip ci] isInDebt
odeimaiz Jan 21, 2025
a206246
[skip ci] naming
odeimaiz Jan 21, 2025
b338892
not needed
odeimaiz Jan 21, 2025
7bec6d4
payDebt resource
odeimaiz Jan 21, 2025
ff6b094
[skip ci] minor
odeimaiz Jan 21, 2025
72874ce
[skip ci] ups
odeimaiz Jan 21, 2025
c98c8fd
refactor
odeimaiz Jan 21, 2025
934b933
more refactor
odeimaiz Jan 21, 2025
5c8c5a3
minor
odeimaiz Jan 21, 2025
3079815
minor
odeimaiz Jan 21, 2025
e366063
Show pay debt button
odeimaiz Jan 21, 2025
f2e2caa
minor
odeimaiz Jan 21, 2025
d1fc5f3
Merge branch 'master' into feature/pay-project-debt
odeimaiz Jan 22, 2025
c2cf5e3
Merge branch 'feature/pay-project-debt' of github.com:odeimaiz/osparc…
odeimaiz Jan 22, 2025
8aca7c1
Merge branch 'master' into feature/pay-project-debt
odeimaiz Jan 22, 2025
db56deb
[skip ci] minor
odeimaiz Jan 22, 2025
71e908e
[skip ci] explanations
odeimaiz Jan 22, 2025
3de8d1f
minor
odeimaiz Jan 22, 2025
d1e5c92
openBuyCredits
odeimaiz Jan 22, 2025
d38e786
[skip ci] buy credits
odeimaiz Jan 22, 2025
9308ead
minor
odeimaiz Jan 22, 2025
a33d235
[skip ci] transfer working
odeimaiz Jan 22, 2025
427693a
wording
odeimaiz Jan 22, 2025
f8c8a2d
[skip ci] transfer wording
odeimaiz Jan 22, 2025
4f0d121
[skip ci] typos
odeimaiz Jan 22, 2025
24a05f9
[skip ci] callback after transfer
odeimaiz Jan 22, 2025
333335e
minor
odeimaiz Jan 22, 2025
9b42618
creditsUpdated
odeimaiz Jan 22, 2025
31eff78
callbacks
odeimaiz Jan 22, 2025
9cee03e
fixes
odeimaiz Jan 22, 2025
239ffe7
minor
odeimaiz Jan 22, 2025
4508cfd
workflow
odeimaiz Jan 22, 2025
85654e5
minor
odeimaiz Jan 22, 2025
dea2047
one less item
odeimaiz Jan 22, 2025
4524765
minors
odeimaiz Jan 22, 2025
45882b7
refactoring
odeimaiz Jan 22, 2025
282d397
simplify
odeimaiz Jan 22, 2025
24126c7
minors
odeimaiz Jan 22, 2025
c7cbc82
Merge branch 'master' into feature/pay-project-debt
odeimaiz Jan 22, 2025
a091df7
minor
odeimaiz Jan 22, 2025
a0d6c26
wording
odeimaiz Jan 22, 2025
2c474bb
Merge branch 'master' into feature/pay-project-debt
odeimaiz Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ qx.Class.define("osparc.dashboard.CardBase", {

trashedAt: {
check: "Date",
apply: "_applyTrasehdAt",
apply: "_applyTrashedAt",
nullable: true
},

Expand Down Expand Up @@ -385,15 +385,15 @@ qx.Class.define("osparc.dashboard.CardBase", {
apply: "__applyState"
},

projectState: {
check: ["NOT_STARTED", "STARTED", "SUCCESS", "FAILED", "UNKNOWN"],
nullable: false,
init: "UNKNOWN",
apply: "_applyProjectState"
debt: {
check: "Number",
nullable: true,
init: 0,
apply: "__applyDebt"
},

blocked: {
check: [true, "UNKNOWN_SERVICES", "IN_USE", false],
check: [true, "UNKNOWN_SERVICES", "IN_USE", "IN_DEBT", false],
init: false,
nullable: false,
apply: "__applyBlocked"
Expand Down Expand Up @@ -547,7 +547,7 @@ qx.Class.define("osparc.dashboard.CardBase", {
throw new Error("Abstract method called!");
},

_applyTrasehdAt: function(value, old) {
_applyTrashedAt: function(value, old) {
throw new Error("Abstract method called!");
},

Expand Down Expand Up @@ -633,7 +633,7 @@ qx.Class.define("osparc.dashboard.CardBase", {
const unaccessibleServices = osparc.study.Utils.getInaccessibleServices(workbench)
if (unaccessibleServices.length) {
this.setBlocked("UNKNOWN_SERVICES");
const image = "@FontAwesome5Solid/ban/";
let image = "@FontAwesome5Solid/ban/";
let toolTipText = this.tr("Service info missing");
unaccessibleServices.forEach(unSrv => {
toolTipText += "<br>" + unSrv.key + ":" + unSrv.version;
Expand Down Expand Up @@ -681,65 +681,75 @@ qx.Class.define("osparc.dashboard.CardBase", {
},

__applyState: function(state) {
const locked = ("locked" in state) ? state["locked"]["value"] : false;
this.setBlocked(locked ? "IN_USE" : false);
if (locked) {
this.__showBlockedCardFromStatus(state["locked"]);
let lockInUse = false;
if ("locked" in state && "value" in state["locked"]) {
lockInUse = state["locked"]["value"];
}
this.setBlocked(lockInUse ? "IN_USE" : false);
if (lockInUse) {
this.__showBlockedCardFromStatus("IN_USE", state["locked"]);
}

const projectState = ("state" in state) ? state["state"]["value"] : undefined;
if (projectState) {
this._applyProjectState(state["state"]);
const pipelineState = ("state" in state) ? state["state"]["value"] : undefined;
if (pipelineState) {
this.__applyPipelineState(state["state"]["value"]);
}
},

_applyProjectState: function(projectStatus) {
const status = projectStatus["value"];
let icon;
let toolTip;
let border;
switch (status) {
__applyDebt: function(debt) {
this.setBlocked(debt ? "IN_DEBT" : false);
if (debt) {
this.__showBlockedCardFromStatus("IN_DEBT", debt);
}
},

// pipelineState: ["NOT_STARTED", "STARTED", "SUCCESS", "ABORTED", "FAILED", "UNKNOWN"]
__applyPipelineState: function(pipelineState) {
let iconSource;
let toolTipText;
let borderColor;
switch (pipelineState) {
case "STARTED":
icon = "@FontAwesome5Solid/spinner/10";
toolTip = this.tr("Running");
border = "info";
iconSource = "@FontAwesome5Solid/spinner/10";
toolTipText = this.tr("Running");
borderColor = "info";
break;
case "SUCCESS":
icon = "@FontAwesome5Solid/check/10";
toolTip = this.tr("Ran successfully");
border = "success";
iconSource = "@FontAwesome5Solid/check/10";
toolTipText = this.tr("Ran successfully");
borderColor = "success";
break;
case "ABORTED":
icon = "@FontAwesome5Solid/exclamation/10";
toolTip = this.tr("Run aborted");
border = "warning";
iconSource = "@FontAwesome5Solid/exclamation/10";
toolTipText = this.tr("Run aborted");
borderColor = "warning";
break;
case "FAILED":
icon = "@FontAwesome5Solid/exclamation/10";
toolTip = this.tr("Ran with error");
border = "error";
iconSource = "@FontAwesome5Solid/exclamation/10";
toolTipText = this.tr("Ran with error");
borderColor = "error";
break;
case "UNKNOWN":
case "NOT_STARTED":
default:
icon = null;
toolTip = null;
border = null;
iconSource = null;
toolTipText = null;
borderColor = null;
break;
}
this.__applyProjectLabel(icon, toolTip, border);
},

__applyProjectLabel: function(icn, toolTipText, bdr) {
const border = new qx.ui.decoration.Decorator().set({
radius: 10,
width: 1,
style: "solid",
color: bdr,
backgroundColor: bdr ? bdr + "-bg" : null
color: borderColor,
backgroundColor: borderColor ? borderColor + "-bg" : null
});

const projectStatusLabel = this.getChildControl("project-status");
projectStatusLabel.set({
decorator: border,
textColor: bdr,
textColor: borderColor,
alignX: "center",
alignY: "middle",
height: 17,
Expand All @@ -748,14 +758,25 @@ qx.Class.define("osparc.dashboard.CardBase", {
});

projectStatusLabel.set({
visibility: icn && toolTipText && bdr ? "visible" : "excluded",
source: icn,
toolTipIcon: icn,
visibility: iconSource && toolTipText && borderColor ? "visible" : "excluded",
source: iconSource,
toolTipIcon: iconSource,
toolTipText
});
},

__showBlockedCardFromStatus: function(lockedStatus) {
__showBlockedCardFromStatus: function(reason, moreInfo) {
switch (reason) {
case "IN_USE":
this.__blockedInUse(moreInfo);
break;
case "IN_DEBT":
this.__blockedInDebt(moreInfo);
break;
}
},

__blockedInUse: function(lockedStatus) {
const status = lockedStatus["status"];
const owner = lockedStatus["owner"];
let toolTip = osparc.utils.Utils.firstsUp(owner["first_name"] || this.tr("A user"), owner["last_name"] || ""); // it will be replaced by "userName"
Expand Down Expand Up @@ -788,14 +809,23 @@ qx.Class.define("osparc.dashboard.CardBase", {
this.__showBlockedCard(image, toolTip);
},

__blockedInDebt: function() {
const studyAlias = osparc.product.Utils.getStudyAlias({firstUpperCase: true});
const toolTip = studyAlias + " " + this.tr("Embargoed<br>Credits Required");
const image = "@FontAwesome5Solid/lock/";
this.__showBlockedCard(image, toolTip);
},

__showBlockedCard: function(lockImageSrc, toolTipText) {
this.getChildControl("lock-status").set({
opacity: 1.0,
visibility: "visible"
});

const lockImage = this.getChildControl("lock-status").getChildControl("image");
lockImageSrc += this.classname.includes("Grid") ? "32" : "22";
lockImage.setSource(lockImageSrc);

if (toolTipText) {
this.set({
toolTipText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", {
},

// overridden
_applyTrasehdAt: function(value) {
_applyTrashedAt: function(value) {
if (value && value.getTime() !== new Date(0).getTime()) {
if (this.isResourceType("study") || this.isResourceType("template")) {
const dateBy = this.getChildControl("date-by");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ qx.Class.define("osparc.dashboard.ListButtonItem", {
},

// overridden
_applyTrasehdAt: function(value) {
_applyTrashedAt: function(value) {
if (value && value.getTime() !== new Date(0).getTime()) {
if (this.isResourceType("study") || this.isResourceType("template")) {
const dateBy = this.getChildControl("date-by");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
height: this.HEIGHT,
});
return win;
},

createToolbar: function() {
const toolbar = new qx.ui.container.Composite(new qx.ui.layout.HBox(20).set({
alignX: "right",
alignY: "top"
})).set({
maxHeight: 40
});
return toolbar;
}
},

Expand All @@ -90,32 +100,36 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
__billingSettings: null,
__classifiersPage: null,
__qualityPage: null,
__openButton: null,

__createToolbar: function() {
const toolbar = new qx.ui.container.Composite(new qx.ui.layout.HBox(20).set({
alignX: "right",
alignY: "top"
})).set({
maxHeight: 40
});
return toolbar;
},

__addOpenButton: function(page) {
const resourceData = this.__resourceData;

const toolbar = this.__createToolbar();
const toolbar = this.self().createToolbar();
page.addToHeader(toolbar);

if (this.__resourceData["resourceType"] === "study") {
const payDebtButton = new qx.ui.form.Button(this.tr("Credits required"));
page.payDebtButton = payDebtButton;
osparc.dashboard.resources.pages.BasePage.decorateHeaderButton(payDebtButton);
payDebtButton.addListener("execute", () => this.openBillingSettings());
if (this.__resourceData["resourceType"] === "study") {
const studyData = this.__resourceData;
payDebtButton.set({
visibility: osparc.study.Utils.isInDebt(studyData) ? "visible" : "excluded"
});
}
toolbar.add(payDebtButton);
}

if (osparc.utils.Resources.isService(resourceData)) {
const serviceVersionSelector = this.__createServiceVersionSelector();
toolbar.add(serviceVersionSelector);
}

const openButton = this.__openButton = new osparc.ui.form.FetchButton(this.tr("Open")).set({
const openButton = new osparc.ui.form.FetchButton(this.tr("Open")).set({
enabled: true
});
page.openButton = openButton;
osparc.dashboard.resources.pages.BasePage.decorateHeaderButton(openButton);
osparc.utils.Utils.setIdToWidget(openButton, "openResource");
const store = osparc.store.Store.getInstance();
Expand All @@ -125,8 +139,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
this.bind("showOpenButton", openButton, "visibility", {
converter: show => (store.getCurrentStudy() === null && show) ? "visible" : "excluded"
});

openButton.addListener("execute", () => this.__openTapped());
openButton.addListener("execute", () => this.__openTapped(openButton));

if (this.__resourceData["resourceType"] === "study") {
const studyData = this.__resourceData;
Expand All @@ -137,21 +150,21 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
toolbar.add(openButton);
},

__openTapped: function() {
__openTapped: function(openButton) {
if (this.__resourceData["resourceType"] !== "study") {
// Template or Service, nothing to pre-check
this.__openResource();
return;
}
this.__openButton.setFetching(true);
openButton.setFetching(true);
const params = {
url: {
"studyId": this.__resourceData["uuid"]
}
};
osparc.data.Resources.getOne("studies", params)
.then(updatedStudyData => {
this.__openButton.setFetching(false);
openButton.setFetching(false);
const updatableServices = osparc.metadata.ServicesInStudyUpdate.updatableNodeIds(updatedStudyData.workbench);
if (updatableServices.length && osparc.data.model.Study.canIWrite(updatedStudyData["accessRights"])) {
this.__confirmUpdate();
Expand All @@ -162,7 +175,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
.catch(err => {
console.error(err);
osparc.FlashMessenger.logAs(err.message, "ERROR");
this.__openButton.setFetching(false);
openButton.setFetching(false);
});
},

Expand Down Expand Up @@ -365,19 +378,27 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
const resourceData = this.__resourceData;
if (osparc.utils.Resources.isStudy(resourceData)) {
const id = "Billing";
const title = this.tr("Tier Settings");
const title = this.tr("Billing Settings");
const iconSrc = "@FontAwesome5Solid/cogs/22";
const page = this.__billingSettings = new osparc.dashboard.resources.pages.BasePage(title, iconSrc, id);
this.__addOpenButton(page);

if (this.__resourceData["resourceType"] === "study") {
const studyData = this.__resourceData;
const canBeOpened = osparc.study.Utils.canShowBillingOptions(studyData);
if (resourceData["resourceType"] === "study") {
const canBeOpened = osparc.study.Utils.canShowBillingOptions(resourceData);
page.setEnabled(canBeOpened);
}

const lazyLoadContent = () => {
const billingSettings = new osparc.study.BillingSettings(resourceData);
billingSettings.addListener("debtPayed", () => {
if (resourceData["resourceType"] === "study") {
page.payDebtButton.set({
visibility: osparc.study.Utils.isInDebt(resourceData) ? "visible" : "excluded"
});
const canBeOpened = osparc.study.Utils.canBeOpened(resourceData);
page.openButton.setEnabled(canBeOpened);
}
})
const billingScroll = new qx.ui.container.Scroll(billingSettings);
page.addToContent(billingScroll);
}
Expand Down Expand Up @@ -751,7 +772,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {

const publishTemplateButton = saveAsTemplate.getPublishTemplateButton();
osparc.dashboard.resources.pages.BasePage.decorateHeaderButton(publishTemplateButton);
const toolbar = this.__createToolbar();
const toolbar = this.self().createToolbar();
toolbar.add(publishTemplateButton);
page.addToHeader(toolbar);
page.addToContent(saveAsTemplate);
Expand Down
Loading
Loading