Skip to content

Commit

Permalink
web/satellite/vuetify-poc: add STORJ token from billing
Browse files Browse the repository at this point in the history
This change allows to add STORJ tokens from the billing page. It reuses
the token step of the account upgrade dialog.

Issue: #6494

Change-Id: Ic521b8ab5113124634b8489f0fb92ab680d9f3df
  • Loading branch information
wilfred-asomanii authored and Storj Robot committed Dec 18, 2023
1 parent ad2d897 commit 6925f87
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 18 deletions.
2 changes: 1 addition & 1 deletion web/satellite/src/store/modules/billingStore.ts
Expand Up @@ -16,7 +16,7 @@ import {
PaymentStatus,
PaymentWithConfirmations,
ProjectCharges,
ProjectUsagePriceModel,
ProjectUsagePriceModel, TokenAmount,
Wallet,
} from '@/types/payments';
import { PaymentsHttpApi } from '@/api/payments';
Expand Down
Expand Up @@ -21,27 +21,35 @@
<p>Total Balance</p>
<v-chip rounded color="green" variant="outlined" class="font-weight-bold mt-2">{{ balance || '------' }}</v-chip>
<v-divider class="my-4" />
<v-btn v-if="wallet.address" variant="flat" color="success" size="small" :loading="isLoading" class="mr-2">+ Add STORJ Tokens</v-btn>
<v-btn v-if="wallet.address" variant="flat" color="success" size="small" :loading="isLoading" class="mr-2" @click="onAddTokens">+ Add STORJ Tokens</v-btn>
<v-btn v-else variant="flat" color="success" size="small" :loading="isLoading" @click="claimWalletClick">Create New Wallet</v-btn>
<v-btn v-if="wallet.address" variant="outlined" color="default" size="small" :loading="isLoading" @click="emit('historyClicked')">View Transactions</v-btn>
</v-card-text>
</v-card>

<AddTokensDialog v-model="isAddTokenDialogOpen" />
</template>

<script setup lang="ts">
import { VBtn, VCard, VCardText, VChip, VDivider, VTooltip } from 'vuetify/components';
import { computed, onMounted } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { Wallet } from '@/types/payments';
import { useLoading } from '@/composables/useLoading';
import { useBillingStore } from '@/store/modules/billingStore';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { useNotify } from '@/utils/hooks';
import { useAppStore } from '@poc/store/appStore';
import AddTokensDialog from '@poc/components/dialogs/AddTokensDialog.vue';
const appStore = useAppStore();
const billingStore = useBillingStore();
const notify = useNotify();
const { isLoading, withLoading } = useLoading();
const isAddTokenDialogOpen = ref(false);
const emit = defineEmits(['historyClicked']);
/**
Expand Down Expand Up @@ -119,6 +127,29 @@ function claimWalletClick(): void {
});
}
/**
* Open the add tokens step of the upgrade modal
* Conditionally claim a wallet before that.
*/
function onAddTokens(): void {
withLoading(async () => {
if (!wallet.value.address) {
// not possible from this component
// but this function is exported and used Billing.vue
try {
await billingStore.claimWallet();
} catch (error) {
notify.notifyError(error, AnalyticsErrorEventSource.BILLING_STORJ_TOKEN_CONTAINER);
return;
}
}
isAddTokenDialogOpen.value = true;
});
}
defineExpose({ onAddTokens });
onMounted(() => {
getWallet();
});
Expand Down
@@ -0,0 +1,88 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.

<template>
<v-dialog
v-model="model"
scrollable
width="720px"
transition="fade-transition"
:persistent="loading"
>
<v-card ref="content" rounded="xlg">
<v-card-item class="pa-5 pl-7">
<v-card-title class="font-weight-bold"> Add Tokens </v-card-title>
<template #append>
<v-btn
icon="$close"
variant="text"
size="small"
color="default"
@click="model = false"
/>
</template>
</v-card-item>

<v-divider />

<v-card-item class="py-4">
<v-window v-model="step">
<v-window-item :value="AddTokensDialogStep.AddTokens">
<AddTokensStep
is-root
@success="() => setStep(AddTokensDialogStep.Success)"
/>
</v-window-item>

<v-window-item :value="AddTokensDialogStep.Success">
<SuccessStep @continue="model = false" />
</v-window-item>
</v-window>
</v-card-item>
</v-card>
</v-dialog>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { VBtn, VCard, VCardItem, VCardTitle, VDialog, VDivider, VWindow, VWindowItem } from 'vuetify/components';
import AddTokensStep from '@poc/components/dialogs/upgradeAccountFlow/AddTokensStep.vue';
import SuccessStep from '@poc/components/dialogs/upgradeAccountFlow/SuccessStep.vue';
enum AddTokensDialogStep {
AddTokens,
Success,
}
const step = ref(AddTokensDialogStep.AddTokens);
const loading = ref<boolean>(false);
const content = ref<HTMLElement | null>(null);
const props = defineProps<{
modelValue: boolean,
}>();
const emit = defineEmits<{
'update:modelValue': [value: boolean];
}>();
const model = computed<boolean>({
get: () => props.modelValue,
set: value => emit('update:modelValue', value),
});
/**
* Sets specific flow step.
*/
function setStep(s: AddTokensDialogStep) {
step.value = s;
}
watch(content, (value) => {
if (!value) {
setStep(AddTokensDialogStep.AddTokens);
return;
}
});
</script>
Expand Up @@ -70,7 +70,7 @@
/>

<v-btn
v-if="viewState !== ViewState.Success"
v-if="viewState !== ViewState.Success && !isRoot"
class="mt-3"
block
variant="outlined"
Expand Down Expand Up @@ -110,8 +110,14 @@ const canvas = ref<HTMLCanvasElement>();
const intervalID = ref<NodeJS.Timer>();
const viewState = ref<ViewState>(ViewState.Default);
const props = defineProps<{
// whether this step is the first step in a flow
isRoot?: boolean;
}>();
const emit = defineEmits<{
back: [];
success: [];
}>();
/**
Expand Down Expand Up @@ -167,8 +173,20 @@ watch(() => pendingPayments.value, async () => {
}
clearInterval(intervalID.value);
billingStore.clearPendingPayments();
if (usersStore.state.user.paidTier) {
// in case this step was entered in to directly from
// the billing/payment method tab when the user is
// already in paid tier.
return;
}
// fetch User to update their Paid Tier status.
await usersStore.getUser();
// arbitrary delay to allow for user to read success banner.
await new Promise(resolve => setTimeout(resolve, 2000));
emit('success');
}, { deep: true });
/**
Expand Down
Expand Up @@ -6,7 +6,7 @@
v-model="model"
scrollable
min-width="460px"
:max-width="step === UpgradeAccountStep.Info || step === UpgradeAccountStep.PricingPlanSelection ? '720px' : '460px'"
:max-width="maxWidth"
transition="fade-transition"
:persistent="loading"
:scrim="scrim"
Expand Down Expand Up @@ -57,6 +57,7 @@
<v-window-item :value="UpgradeAccountStep.AddTokens">
<AddTokensStep
@back="() => setStep(UpgradeAccountStep.Options)"
@success="() => setStep(UpgradeAccountStep.Success)"
/>
</v-window-item>

Expand Down Expand Up @@ -151,6 +152,17 @@ const stepTitles = computed(() => {
};
});
const maxWidth = computed(() => {
switch (step.value) {
case UpgradeAccountStep.Info:
case UpgradeAccountStep.PricingPlanSelection:
case UpgradeAccountStep.AddTokens:
return '720px';
default:
return '460px';
}
});
/**
* Claims wallet and sets add token step.
*/
Expand Down Expand Up @@ -225,6 +237,7 @@ async function setSecondStep() {
watch(content, (value) => {
if (!value) {
setStep(UpgradeAccountStep.Info);
return;
}
});
</script>
29 changes: 16 additions & 13 deletions web/satellite/vuetify-poc/src/views/Billing.vue
Expand Up @@ -5,8 +5,8 @@
<v-container>
<low-token-balance-banner
v-if="isLowBalance"
:cta-label="tab !== 1 ? 'Deposit' : ''"
@click="tab = 1"
:cta-label="tab !== TABS['payment-methods'] ? 'Deposit' : ''"
@click="onAddTokensClicked"
/>

<v-row>
Expand Down Expand Up @@ -57,7 +57,7 @@
{{ centsToDollars(priceSummary) }}
</v-chip>
<v-divider class="my-4" />
<v-btn variant="outlined" color="default" size="small" class="mr-2" @click="tab = 3">View Billing History</v-btn>
<v-btn variant="outlined" color="default" size="small" class="mr-2" @click="tab = TABS['billing-history']">View Billing History</v-btn>
</v-card-text>
</v-card>
</v-col>
Expand All @@ -72,7 +72,7 @@
{{ formattedTokenBalance }}
</v-chip>
<v-divider class="my-4" />
<v-btn variant="outlined" color="default" size="small" class="mr-2" prepend-icon="mdi-plus" @click="tab = 1">
<v-btn variant="outlined" color="default" size="small" class="mr-2" prepend-icon="mdi-plus" @click="onAddTokensClicked">
Add STORJ Tokens
</v-btn>
</v-card-text>
Expand Down Expand Up @@ -174,7 +174,7 @@
<v-window-item>
<v-row>
<v-col cols="12" md="4" sm="6">
<StorjTokenCardComponent @historyClicked="tab = 2" />
<StorjTokenCardComponent ref="tokenCardComponent" @historyClicked="tab = TABS.transactions" />
</v-col>

<v-col v-for="(card, i) in creditCards" :key="i" cols="12" md="4" sm="6">
Expand Down Expand Up @@ -250,6 +250,10 @@ enum TABS {
'billing-history',
}
interface IStorjTokenCardComponent {
onAddTokens(): Promise<void>;
}
const billingStore = useBillingStore();
const projectsStore = useProjectsStore();
const configStore = useConfigStore();
Expand All @@ -263,6 +267,8 @@ const isLowBalance = useLowTokenBalance();
const isRollupLoading = ref(true);
const isAddCouponDialogShown = ref<boolean>(false);
const tokenCardComponent = ref<IStorjTokenCardComponent>();
const creditCards = computed((): CreditCard[] => {
return billingStore.state.creditCards;
});
Expand Down Expand Up @@ -310,7 +316,7 @@ const tab = computed({
return TABS[tabStr] ?? 0;
},
set: (value: number) => {
router.push({ query: { tab: TABS[value] ?? TABS.overview } });
router.push({ query: { tab: TABS[value] ?? TABS[tab.value] } });
},
});
Expand Down Expand Up @@ -356,13 +362,10 @@ function downloadReport(): void {
notify.success('Usage report download started successfully.');
}
/**
* set the tab on route change
*/
watch(route, () => {
const tabStr = route.query['tab'];
tab.value = TABS[tabStr as keyof typeof TABS] ?? 0;
}, { immediate: true });
function onAddTokensClicked(): void {
tab.value = TABS['payment-methods'];
tokenCardComponent.value?.onAddTokens();
}
onBeforeMount(() => {
if (!configStore.state.config.billingFeaturesEnabled) {
Expand Down

0 comments on commit 6925f87

Please sign in to comment.