satellite/{web/payments}: show token balance on billing overview

This change shows STORJ token balance on the billing overview page instead of the Stripe balance it shows currently.
It changes the text on the "Available balance" card to reflect the new balance being displayed. Finally, it adds shortcuts to navigate straight to token history or add tokens modal when call to action on "Balance card"

Issue: https://github.com/storj/storj/issues/5204

Change-Id: Ic88e43c602e4949b6c6be4c7644c04f3c7d38585
This commit is contained in:
Wilfred Asomani 2022-11-30 16:24:09 +00:00 committed by Storj Robot
parent 907c911f57
commit bf106131b0
6 changed files with 76 additions and 31 deletions

View File

@ -3,9 +3,13 @@
package payments
import (
"github.com/shopspring/decimal"
)
// Balance is an entity that holds free credits and coins balance of user.
// Earned by applying of promotional coupon and coins depositing, respectively.
type Balance struct {
FreeCredits int64 `json:"freeCredits"`
Coins int64 `json:"coins"`
FreeCredits int64 `json:"freeCredits"`
Coins decimal.Decimal `json:"coins"`
}

View File

@ -97,21 +97,14 @@ func (accounts *accounts) Setup(ctx context.Context, userID uuid.UUID, email str
func (accounts *accounts) Balance(ctx context.Context, userID uuid.UUID) (_ payments.Balance, err error) {
defer mon.Task()(&ctx, userID)(&err)
customerID, err := accounts.service.db.Customers().GetCustomerID(ctx, userID)
balance, err := accounts.service.billingDB.GetBalance(ctx, userID)
if err != nil {
return payments.Balance{}, Error.Wrap(err)
}
c, err := accounts.service.stripeClient.Customers().Get(customerID, nil)
if err != nil {
return payments.Balance{}, Error.Wrap(err)
}
accountBalance := payments.Balance{
Coins: -c.Balance,
}
return accountBalance, nil
return payments.Balance{
Coins: balance.AsDecimal(),
}, nil
}
// ProjectCharges returns how much money current user will be charged for each project.

View File

@ -35,13 +35,13 @@
</div>
<div class="total-cost__card">
<AvailableBalanceIcon class="total-cost__card__main-icon" />
<p class="total-cost__card__money-text">{{ balance.coins | centsToDollars }}</p>
<p class="total-cost__card__label-text">Available Balance</p>
<p class="total-cost__card__money-text">${{ balance.coins }}</p>
<p class="total-cost__card__label-text">STORJ Token Balance</p>
<p
class="total-cost__card__link-text"
@click="routeToPaymentMethods"
@click="balanceClicked"
>
View Payment Methods
{{ hasZeroCoins ? "Add Funds" : "See Balance" }}
</p>
</div>
</div>
@ -112,7 +112,6 @@ import CalendarIcon from '@/../static/images/account/billing/calendar-icon.svg';
},
})
export default class BillingArea extends Vue {
public availableBalance = 0;
public showChargesTooltip = false;
public isDataFetching = true;
public currentDate = '';
@ -152,6 +151,13 @@ export default class BillingArea extends Vue {
return this.$store.state.paymentsModule.balance;
}
/**
* Returns whether the user's STORJ balance is empty.
*/
public get hasZeroCoins(): boolean {
return this.balance.coins === 0;
}
/**
* projectUsageAndCharges is an array of all stored ProjectUsageAndCharges.
*/
@ -176,6 +182,13 @@ export default class BillingArea extends Vue {
this.$router.push(RouteConfig.Account.with(RouteConfig.Billing).with(RouteConfig.BillingPaymentMethods).path);
}
public balanceClicked(): void {
this.$router.push({
name: RouteConfig.Account.with(RouteConfig.Billing).with(RouteConfig.BillingPaymentMethods).name,
params: { action: this.hasZeroCoins ? 'add tokens' : 'token history' },
});
}
}
</script>

View File

@ -283,6 +283,12 @@ export default class PaymentMethods extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
public mounted(): void {
if (this.$route.params.action === 'token history') {
this.showTransactionsTable();
}
}
private get wallet(): Wallet {
return this.$store.state.paymentsModule.wallet;
}

View File

@ -123,6 +123,44 @@ export default class AddTokenCardNative extends Vue {
public isLoading = false;
async mounted(): Promise<void> {
await this.getWallet();
// check if user navigated here from Billing overview screen
if (this.$route.params.action !== 'add tokens') {
return;
}
// user clicked 'Add Funds' on Billing overview screen.
if (this.wallet.address) {
this.onAddTokensClick();
} else {
await this.claimWalletClick();
}
}
/**
* getWallet tries to get an existing wallet for this user. this will not claim a wallet.
*/
private async getWallet() {
if (this.wallet.address) {
return;
}
this.isLoading = true;
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_WALLET).catch(_ => {});
this.isLoading = false;
}
/**
* claimWallet claims a wallet for the current account.
*/
private async claimWallet(): Promise<void> {
if (!this.wallet.address)
await this.$store.dispatch(PAYMENTS_ACTIONS.CLAIM_WALLET);
}
/**
* Called when "Add STORJ Tokens" button is clicked.
*/
public async claimWalletClick(): Promise<void> {
this.isLoading = true;
try {
@ -135,18 +173,6 @@ export default class AddTokenCardNative extends Vue {
this.isLoading = false;
}
mounted(): void {
if (!this.wallet.address) {
// try to get an existing wallet for this user. this will not claim a wallet.
this.$store.dispatch(PAYMENTS_ACTIONS.GET_WALLET);
}
}
public async claimWallet(): Promise<void> {
if (!this.wallet.address)
await this.$store.dispatch(PAYMENTS_ACTIONS.CLAIM_WALLET);
}
/**
* Holds on add tokens button click logic.
* Triggers Add funds popup.

View File

@ -104,9 +104,12 @@ export interface PaymentsApi {
export class AccountBalance {
constructor(
public freeCredits: number = 0,
public coins: number = 0,
private _coins: string = '0',
) { }
public get coins(): number {
return parseFloat(this._coins);
}
public get sum(): number {
return this.freeCredits + this.coins;
}