web/satellite: low STORJ balance notification
Added a new banner to the dashboard of all projects. This banner is displayed when a user meets the following conditions: they have no credit cards on file, they have a payment history with tokens, and their estimated charges are higher than their current balance. Issue: https://github.com/storj/storj/issues/6234 Change-Id: I1f90ae81032d459111b111d23ce2e1d8119e649d
This commit is contained in:
parent
bc517cae2f
commit
ecc527ad3b
@ -144,11 +144,14 @@ function routeToCoupons(): void {
|
||||
|
||||
/**
|
||||
* Mounted lifecycle hook after initial render.
|
||||
* Fetches account balance.
|
||||
* Fetches account balance and credit cards.
|
||||
*/
|
||||
onMounted(async (): Promise<void> => {
|
||||
try {
|
||||
await billingStore.getBalance();
|
||||
await Promise.all([
|
||||
billingStore.getBalance(),
|
||||
billingStore.getCreditCards(),
|
||||
]);
|
||||
} catch (error) {
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.BILLING_AREA);
|
||||
}
|
||||
|
@ -201,18 +201,8 @@ export const useBillingStore = defineStore('billing', () => {
|
||||
state.wallet = new Wallet();
|
||||
}
|
||||
|
||||
const canUserCreateFirstProject = computed((): boolean => {
|
||||
return state.balance.sum > 0 || state.creditCards.length > 0;
|
||||
});
|
||||
|
||||
const isBalancePositive = computed((): boolean => {
|
||||
return state.balance.sum > 0;
|
||||
});
|
||||
|
||||
return {
|
||||
state,
|
||||
canUserCreateFirstProject,
|
||||
isBalancePositive,
|
||||
getBalance,
|
||||
getWallet,
|
||||
claimWallet,
|
||||
|
@ -158,6 +158,10 @@ export class AccountBalance {
|
||||
return parseFloat(this._coins);
|
||||
}
|
||||
|
||||
public get credits(): number {
|
||||
return parseFloat(this._credits);
|
||||
}
|
||||
|
||||
public get formattedCredits(): string {
|
||||
return formatPrice(decimalShift(this._credits, 2));
|
||||
}
|
||||
@ -167,7 +171,7 @@ export class AccountBalance {
|
||||
}
|
||||
|
||||
public get sum(): number {
|
||||
return this.freeCredits + this.coins;
|
||||
return this.credits + this.coins;
|
||||
}
|
||||
|
||||
public hasCredits(): boolean {
|
||||
|
@ -161,13 +161,6 @@ onMounted(async () => {
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
|
||||
}
|
||||
|
||||
try {
|
||||
await billingStore.getCreditCards();
|
||||
} catch (error) {
|
||||
error.message = `Unable to get credit cards. ${error.message}`;
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
|
||||
}
|
||||
|
||||
try {
|
||||
await projectsStore.getUserInvitations();
|
||||
} catch (error) {
|
||||
|
@ -30,11 +30,20 @@
|
||||
:dashboard-ref="parentRef"
|
||||
:on-link-click="redirectToBillingPage"
|
||||
/>
|
||||
|
||||
<v-banner
|
||||
v-if="isLowBalance && parentRef"
|
||||
class="all-dashboard-banners__low-balance"
|
||||
message="Your STORJ Token balance is low. Deposit more STORJ tokens or add a credit card to avoid interruptions in service."
|
||||
link-text="Go to billing"
|
||||
severity="warning"
|
||||
:dashboard-ref="parentRef"
|
||||
:on-link-click="redirectToBillingOverview"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
@ -42,12 +51,14 @@ import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { MODALS } from '@/utils/constants/appStatePopUps';
|
||||
import { useAppStore } from '@/store/modules/appStore';
|
||||
import { RouteConfig } from '@/types/router';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
|
||||
import VBanner from '@/components/common/VBanner.vue';
|
||||
import UpgradeNotification from '@/components/notifications/UpgradeNotification.vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const billingStore = useBillingStore();
|
||||
const usersStore = useUsersStore();
|
||||
const appStore = useAppStore();
|
||||
|
||||
@ -69,6 +80,15 @@ const isAccountWarned = computed((): boolean => {
|
||||
return usersStore.state.user.freezeStatus.warned;
|
||||
});
|
||||
|
||||
/**
|
||||
* Indicates if low STORJ token balance banner is shown.
|
||||
*/
|
||||
const isLowBalance = computed((): boolean => {
|
||||
return !billingStore.state.creditCards.length &&
|
||||
billingStore.state.nativePaymentsHistory.length > 0 &&
|
||||
billingStore.state.balance.sum < billingStore.state.projectCharges.getPrice();
|
||||
});
|
||||
|
||||
/* whether the paid tier banner should be shown */
|
||||
const isPaidTierBannerShown = computed((): boolean => {
|
||||
return !usersStore.state.user.paidTier
|
||||
@ -97,6 +117,13 @@ function togglePMModal(): void {
|
||||
async function redirectToBillingPage(): Promise<void> {
|
||||
await router.push(RouteConfig.AccountSettings.with(RouteConfig.Billing2.with(RouteConfig.BillingPaymentMethods2)).path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to Billing Page Overview tab.
|
||||
*/
|
||||
async function redirectToBillingOverview(): Promise<void> {
|
||||
await router.push(RouteConfig.AccountSettings.with(RouteConfig.Billing2.with(RouteConfig.BillingOverview2)).path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -104,9 +131,9 @@ async function redirectToBillingPage(): Promise<void> {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&__upgrade,
|
||||
&__project-limit,
|
||||
&__freeze,
|
||||
&__warning {
|
||||
&__warning,
|
||||
&__low-balance {
|
||||
margin: 20px 0 0;
|
||||
box-shadow: 0 0 20px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
@ -82,11 +82,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { Project, ProjectInvitation } from '@/types/projects';
|
||||
import { RouteConfig } from '@/types/router';
|
||||
import {
|
||||
AnalyticsErrorEventSource,
|
||||
AnalyticsEvent,
|
||||
} from '@/utils/constants/analyticsEventNames';
|
||||
import { User } from '@/types/users';
|
||||
@ -97,6 +98,8 @@ import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
import { useResize } from '@/composables/resize';
|
||||
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
|
||||
import EmptyProjectItem from '@/views/all-dashboard/components/EmptyProjectItem.vue';
|
||||
import ProjectItem from '@/views/all-dashboard/components/ProjectItem.vue';
|
||||
@ -110,12 +113,14 @@ import RocketIcon from '@/../static/images/common/rocket.svg';
|
||||
import CardsIcon from '@/../static/images/common/cardsIcon.svg';
|
||||
import TableIcon from '@/../static/images/common/tableIcon.svg';
|
||||
|
||||
const billingStore = useBillingStore();
|
||||
const appStore = useAppStore();
|
||||
const configStore = useConfigStore();
|
||||
const usersStore = useUsersStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const analyticsStore = useAnalyticsStore();
|
||||
|
||||
const notify = useNotify();
|
||||
const { isMobile } = useResize();
|
||||
|
||||
const content = ref<HTMLElement | null>(null);
|
||||
@ -169,6 +174,23 @@ function onCreateProjectClicked(): void {
|
||||
appStore.updateActiveModal(MODALS.createProjectPrompt);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!configStore.state.config.nativeTokenPaymentsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
billingStore.getBalance(),
|
||||
billingStore.getProjectUsageAndChargesCurrentRollup(),
|
||||
billingStore.getCreditCards(),
|
||||
billingStore.getNativePaymentsHistory(),
|
||||
]);
|
||||
} catch (error) {
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.ALL_PROJECT_DASHBOARD);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
@ -437,13 +437,6 @@ onMounted(async () => {
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
await billingStore.getCreditCards();
|
||||
} catch (error) {
|
||||
error.message = `Unable to get credit cards. ${error.message}`;
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
await projectsStore.getUserInvitations();
|
||||
} catch (error) {
|
||||
|
@ -112,13 +112,6 @@ onBeforeMount(async () => {
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
await billingStore.getCreditCards();
|
||||
} catch (error) {
|
||||
error.message = `Unable to get credit cards. ${error.message}`;
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
|
||||
await selectProject(route.params.projectId as string);
|
||||
|
||||
if (!agStore.state.accessGrantsWebWorker) await agStore.startWorker();
|
||||
|
Loading…
Reference in New Issue
Block a user