Skip to content

Commit 4cf8825

Browse files
authored
✨🐛♻️ Billing center (#5272)
1 parent daef85c commit 4cf8825

File tree

30 files changed

+707
-671
lines changed

30 files changed

+707
-671
lines changed

api/specs/web-server/_resource_usage.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ async def list_resource_usage_services(
7373
)
7474

7575

76-
@router.post(
77-
"/services/-/resource-usages:export",
76+
@router.get(
77+
"/services/-/usage-report",
7878
status_code=status.HTTP_302_FOUND,
7979
responses={
8080
status.HTTP_302_FOUND: {

services/static-webserver/client/.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
"semi": "off",
4040
"comma-dangle": "off",
4141
"object-curly-spacing": "off",
42-
"no-implicit-coercion": "off"
42+
"no-implicit-coercion": "off",
43+
"arrow-body-style": "off"
4344
},
4445
"env": {
4546
"browser": true

services/static-webserver/client/source/class/osparc/Preferences.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ qx.Class.define("osparc.Preferences", {
122122
check: "Boolean",
123123
event: "changeAllowMetricsCollection",
124124
apply: "__patchPreference"
125+
},
126+
127+
billingCenterUsageColumnOrder: {
128+
nullable: true,
129+
check: "Array",
130+
event: "changeBillingCenterUsageColumnOrder",
131+
apply: "__patchPreference"
125132
}
126133
},
127134

services/static-webserver/client/source/class/osparc/data/Resources.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -263,16 +263,15 @@ qx.Class.define("osparc.data.Resources", {
263263
"resourceUsage": {
264264
useCache: false,
265265
endpoints: {
266-
getPage: {
266+
get: {
267267
method: "GET",
268-
url: statics.API + "/services/-/resource-usages?offset={offset}&limit={limit}"
269-
}
270-
}
271-
},
272-
"resourceUsagePerWallet": {
273-
useCache: false,
274-
endpoints: {
275-
getPage: {
268+
url: statics.API + "/services/-/resource-usages?offset={offset}&limit={limit}&filters={filters}&order_by={orderBy}"
269+
},
270+
getWithWallet: {
271+
method: "GET",
272+
url: statics.API + "/services/-/resource-usages?wallet_id={walletId}&offset={offset}&limit={limit}&filters={filters}&order_by={orderBy}"
273+
},
274+
getWithWallet2: {
276275
method: "GET",
277276
url: statics.API + "/services/-/resource-usages?wallet_id={walletId}&offset={offset}&limit={limit}"
278277
}
@@ -735,7 +734,7 @@ qx.Class.define("osparc.data.Resources", {
735734
endpoints: {
736735
get: {
737736
method: "GET",
738-
url: statics.API + "/wallets/-/payments"
737+
url: statics.API + "/wallets/-/payments?offset={offset}&limit={limit}"
739738
},
740739
startPayment: {
741740
method: "POST",

services/static-webserver/client/source/class/osparc/desktop/credits/AutoRecharge.js

Lines changed: 103 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@
1212
1313
Authors:
1414
* Odei Maiz (odeimaiz)
15+
* Ignacio Pascual
1516
1617
************************************************************************ */
1718

1819
qx.Class.define("osparc.desktop.credits.AutoRecharge", {
19-
extend: qx.ui.core.Widget,
20+
extend: qx.ui.container.Stack,
2021

2122
construct: function(walletId) {
2223
this.base(arguments);
2324

24-
this._setLayout(new qx.ui.layout.VBox(15));
25-
2625
const store = osparc.store.Store.getInstance();
2726
const wallet = store.getWallets().find(w => w.getWalletId() == walletId);
27+
2828
this.__buildLayout();
2929
this.setWallet(wallet);
3030
},
@@ -48,40 +48,38 @@ qx.Class.define("osparc.desktop.credits.AutoRecharge", {
4848
__topUpAmountField: null,
4949
__monthlyLimitField: null,
5050
__paymentMethodField: null,
51-
__topUpAmountHelper: null,
52-
53-
_createChildControlImpl: function(id) {
54-
let control;
55-
switch (id) {
56-
case "auto-recharge-description":
57-
control = new qx.ui.basic.Label().set({
58-
value: this.tr("Keep your balance running smoothly by automatically setting your credits to be recharged when it runs low."),
59-
font: "text-14",
60-
rich: true,
61-
wrap: true
62-
});
63-
this._add(control);
64-
break;
65-
case "auto-recharge-form":
66-
control = this.__getAutoRechargeForm();
67-
this._add(control);
68-
break;
69-
case "buttons-layout-2":
70-
control = new qx.ui.container.Composite(new qx.ui.layout.HBox(5));
71-
this._add(control);
72-
break;
73-
case "save-auto-recharge-button":
74-
control = this.__getSaveAutoRechargeButton();
75-
this.getChildControl("buttons-layout-2").add(control);
76-
break;
77-
}
78-
return control || this.base(arguments, id);
79-
},
8051

8152
__buildLayout: function() {
82-
this.getChildControl("auto-recharge-description");
83-
this.getChildControl("auto-recharge-form");
84-
this.getChildControl("save-auto-recharge-button");
53+
this.removeAll()
54+
55+
this.__mainContent = new qx.ui.container.Composite(new qx.ui.layout.VBox(15).set({
56+
alignX: "center"
57+
}))
58+
const title = new qx.ui.basic.Label("Auto-recharge").set({
59+
marginTop: 25,
60+
font: "title-18"
61+
});
62+
const subtitle = new qx.ui.basic.Label("Keep your balance running smoothly by automatically setting your credits to be recharged when it runs low.").set({
63+
rich: true,
64+
font: "text-14",
65+
textAlign: "center"
66+
});
67+
this.__mainContent.add(title);
68+
this.__mainContent.add(subtitle);
69+
this.__mainContent.add(this.__getAutoRechargeForm())
70+
this.__mainContent.add(this.__getButtons())
71+
this.add(this.__mainContent)
72+
73+
this.__fetchingView = new qx.ui.container.Composite(new qx.ui.layout.VBox().set({
74+
alignX: "center",
75+
alignY: "middle"
76+
}))
77+
const image = new qx.ui.basic.Image("@FontAwesome5Solid/circle-notch/26")
78+
image.getContentElement().addClass("rotate")
79+
this.__fetchingView.add(image)
80+
this.add(this.__fetchingView)
81+
82+
this.setSelection([this.__fetchingView])
8583
},
8684

8785
__applyWallet: function(wallet) {
@@ -100,6 +98,8 @@ qx.Class.define("osparc.desktop.credits.AutoRecharge", {
10098
const paymentMethodSB = this.__paymentMethodField;
10199
await osparc.desktop.credits.Utils.populatePaymentMethodSelector(wallet, paymentMethodSB);
102100

101+
this.setSelection([this.__fetchingView])
102+
103103
// populate the form
104104
const params = {
105105
url: {
@@ -114,7 +114,6 @@ qx.Class.define("osparc.desktop.credits.AutoRecharge", {
114114
__populateForm: function(arData) {
115115
this.__enabledField.setValue(arData.enabled);
116116
this.__topUpAmountField.setValue(arData["topUpAmountInUsd"]);
117-
this.__topUpAmountHelper.setValue(this.tr(`When your account reaches ${arData["minBalanceInUsd"]} credits, it gets recharged by this amount`));
118117
if (arData["monthlyLimitInUsd"]) {
119118
this.__monthlyLimitField.setValue(arData["monthlyLimitInUsd"] > 0 ? arData["monthlyLimitInUsd"] : 0);
120119
} else {
@@ -125,52 +124,57 @@ qx.Class.define("osparc.desktop.credits.AutoRecharge", {
125124
if (paymentMethodFound) {
126125
paymentMethodSB.setSelection([paymentMethodFound]);
127126
}
127+
this.setSelection([this.__mainContent])
128128
},
129129

130130
__getAutoRechargeForm: function() {
131-
const autoRechargeLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(15));
132-
131+
const autoRechargeLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10).set({
132+
alignX: "center"
133+
})).set({
134+
marginTop: 20
135+
});
133136

134-
const enabledLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));
135-
const enabledTitle = new qx.ui.basic.Label().set({
136-
value: this.tr("ENABLED"),
137-
font: "text-14"
137+
const enabledLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)).set({
138+
allowStretchX: false,
139+
width: 274
140+
});
141+
const enabledTitle = new qx.ui.basic.Label(this.tr("Enabled")).set({
142+
marginLeft: 2
143+
})
144+
const enabledCheckbox = this.__enabledField = new qx.ui.form.CheckBox().set({
145+
appearance: "appmotion-buy-credits-checkbox"
138146
});
139-
const enabledCheckbox = this.__enabledField = new qx.ui.form.CheckBox();
140147
enabledLayout.add(enabledTitle);
141148
enabledLayout.add(enabledCheckbox);
142149
autoRechargeLayout.add(enabledLayout);
143150

144-
145-
const topUpAmountLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));
151+
const topUpAmountLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)).set({
152+
allowStretchX: false,
153+
marginTop: 15
154+
});
146155
const topUpAmountTitleLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5));
147-
const topUpAmountTitle = new qx.ui.basic.Label().set({
148-
value: this.tr("RECHARGING AMOUNT (US$)"),
149-
font: "text-14"
156+
const topUpAmountTitle = new qx.ui.basic.Label(this.tr("Recharging amount (USD)")).set({
157+
marginLeft: 15
150158
});
151159
topUpAmountTitleLayout.add(topUpAmountTitle);
152-
const topUpAmountInfo = new osparc.ui.hint.InfoHint("Amount in US$ payed when auto-recharge condition is satisfied.");
160+
const topUpAmountInfo = new osparc.ui.hint.InfoHint("Amount in USD payed when auto-recharge condition is satisfied.");
153161
topUpAmountTitleLayout.add(topUpAmountInfo);
154162
topUpAmountLayout.add(topUpAmountTitleLayout);
155163
const topUpAmountField = this.__topUpAmountField = new qx.ui.form.Spinner().set({
156164
minimum: 10,
157165
maximum: 10000,
158-
maxWidth: 200
166+
width: 300,
167+
appearance: "appmotion-buy-credits-spinner"
159168
});
160169
topUpAmountLayout.add(topUpAmountField);
161-
const topUpAmountHelper = this.__topUpAmountHelper = new qx.ui.basic.Label().set({
162-
font: "text-12",
163-
rich: true,
164-
wrap: true
165-
});
166-
topUpAmountLayout.add(topUpAmountHelper);
167170
autoRechargeLayout.add(topUpAmountLayout);
168171

169-
const monthlyLimitLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));
172+
const monthlyLimitLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)).set({
173+
allowStretchX: false
174+
});
170175
const monthlyLimitTitleLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5));
171-
const monthlyLimitTitle = new qx.ui.basic.Label().set({
172-
value: this.tr("MONTHLY LIMIT (US$)"),
173-
font: "text-14"
176+
const monthlyLimitTitle = new qx.ui.basic.Label(this.tr("Monthly limit (USD)")).set({
177+
marginLeft: 15
174178
});
175179
monthlyLimitTitleLayout.add(monthlyLimitTitle);
176180
const monthlyLimitTitleInfo = new osparc.ui.hint.InfoHint(this.tr("Maximum amount in US$ charged within a natural month."));
@@ -179,38 +183,49 @@ qx.Class.define("osparc.desktop.credits.AutoRecharge", {
179183
const monthlyLimitField = this.__monthlyLimitField = new qx.ui.form.Spinner().set({
180184
minimum: 0,
181185
maximum: 100000,
182-
maxWidth: 200
186+
width: 300,
187+
appearance: "appmotion-buy-credits-spinner"
183188
});
184189
monthlyLimitLayout.add(monthlyLimitField);
185-
const monthlyLimitHelper = new qx.ui.basic.Label().set({
186-
value: this.tr("To disable spending limit, clear input field"),
190+
const monthlyLimitHelper = new qx.ui.basic.Label(this.tr("To disable spending limit, clear input field")).set({
187191
font: "text-12",
188-
rich: true,
189-
wrap: true
192+
marginLeft: 15,
193+
rich: true
190194
});
191195
monthlyLimitLayout.add(monthlyLimitHelper);
192196
autoRechargeLayout.add(monthlyLimitLayout);
193197

194-
const paymentMethodLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));
195-
const paymentMethodTitle = new qx.ui.basic.Label().set({
196-
value: this.tr("PAY WITH"),
197-
font: "text-14"
198+
const paymentMethodLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)).set({
199+
allowStretchX: false
200+
});
201+
const paymentMethodTitle = new qx.ui.basic.Label(this.tr("Pay with")).set({
202+
marginLeft: 15
198203
});
199204
paymentMethodLayout.add(paymentMethodTitle);
200205
const paymentMethodField = this.__paymentMethodField = new qx.ui.form.SelectBox().set({
201-
minWidth: 200,
202-
maxWidth: 200
206+
width: 300,
207+
appearance: "appmotion-buy-credits-select"
203208
});
204209
paymentMethodLayout.add(paymentMethodField);
205210
const addNewPaymentMethod = new qx.ui.basic.Label(this.tr("Add Payment Method")).set({
206-
padding: 0,
211+
marginLeft: 15,
207212
cursor: "pointer",
208213
font: "link-label-12"
209214
});
210215
addNewPaymentMethod.addListener("tap", () => this.fireEvent("addNewPaymentMethod"));
211216
paymentMethodLayout.add(addNewPaymentMethod);
212217
autoRechargeLayout.add(paymentMethodLayout);
213218

219+
enabledCheckbox.bind("value", monthlyLimitField, "enabled")
220+
enabledCheckbox.bind("value", paymentMethodField, "enabled")
221+
enabledCheckbox.bind("value", topUpAmountField, "enabled")
222+
enabledCheckbox.bind("value", monthlyLimitHelper, "visibility", {
223+
converter: value => value ? "visible" : "hidden"
224+
})
225+
enabledCheckbox.bind("value", addNewPaymentMethod, "visibility", {
226+
converter: value => value ? "visible" : "hidden"
227+
})
228+
214229
return autoRechargeLayout;
215230
},
216231

@@ -243,17 +258,25 @@ qx.Class.define("osparc.desktop.credits.AutoRecharge", {
243258
.finally(() => fetchButton.setFetching(false));
244259
},
245260

246-
__getSaveAutoRechargeButton: function() {
247-
const saveAutoRechargeBtn = new osparc.ui.form.FetchButton().set({
248-
label: this.tr("Save and close"),
249-
font: "text-14",
250-
appearance: "strong-button",
251-
maxWidth: 200,
252-
center: true
261+
__getButtons: function() {
262+
const btnContainer = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({
263+
alignX: "center"
264+
})).set({
265+
marginTop: 30,
266+
marginBottom: 15
267+
});
268+
const saveAutoRechargeBtn = new osparc.ui.form.FetchButton(this.tr("Save and close")).set({
269+
appearance: "appmotion-button-action"
253270
});
254271
const successfulMsg = this.tr("Changes on the Auto recharge were successfully saved");
255272
saveAutoRechargeBtn.addListener("execute", () => this.__updateAutoRecharge(this.__enabledField.getValue(), saveAutoRechargeBtn, successfulMsg));
256-
return saveAutoRechargeBtn;
273+
btnContainer.add(saveAutoRechargeBtn)
274+
const cancelBtn = new qx.ui.form.Button("Cancel").set({
275+
appearance: "appmotion-button"
276+
});
277+
cancelBtn.addListener("execute", () => this.fireEvent("close"))
278+
btnContainer.add(cancelBtn)
279+
return btnContainer;
257280
}
258281
}
259282
});

services/static-webserver/client/source/class/osparc/desktop/credits/BillingCenter.js

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -114,21 +114,6 @@ qx.Class.define("osparc.desktop.credits.BillingCenter", {
114114
return page;
115115
},
116116

117-
__getBuyCreditsPage: function() {
118-
const title = this.tr("Buy Credits");
119-
const iconSrc = "@FontAwesome5Solid/dollar-sign/22";
120-
const page = new osparc.desktop.preferences.pages.BasePage(title, iconSrc);
121-
page.showLabelOnTab();
122-
const buyCredits = this.__buyCredits = new osparc.desktop.credits.BuyCredits();
123-
buyCredits.set({
124-
margin: 10
125-
});
126-
buyCredits.addListener("addNewPaymentMethod", () => this.openPaymentMethods(true), this);
127-
buyCredits.addListener("transactionCompleted", () => this.openTransactions(true), this);
128-
page.add(buyCredits);
129-
return page;
130-
},
131-
132117
__getTransactionsPage: function() {
133118
const title = this.tr("Transactions");
134119
const iconSrc = "@FontAwesome5Solid/exchange-alt/22";
@@ -138,7 +123,7 @@ qx.Class.define("osparc.desktop.credits.BillingCenter", {
138123
transactions.set({
139124
margin: 10
140125
});
141-
page.add(transactions);
126+
page.add(transactions, { flex: 1 });
142127
return page;
143128
},
144129

@@ -151,7 +136,7 @@ qx.Class.define("osparc.desktop.credits.BillingCenter", {
151136
usage.set({
152137
margin: 10
153138
});
154-
page.add(usage);
139+
page.add(usage, { flex: 1 });
155140
return page;
156141
},
157142

0 commit comments

Comments
 (0)