web/satellite/vuetify-poc: add functionality to add credit cards
This implements functionality to add cards to users' accounts, to the vuetify POC. Issue: https://github.com/storj/storj/issues/6097 Change-Id: Ie56e85e74fd44de6839e6a985877843b730a3f5f
This commit is contained in:
parent
df60380793
commit
0fd7f2958f
@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="dns-prefetch" href="https://js.stripe.com">
|
||||
<title>Storj DCS</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<v-card title="Add Card" variant="flat" :border="true" rounded="xlg">
|
||||
<v-card-text>
|
||||
<v-btn v-if="!isCardInputShown" variant="outlined" color="default" size="small" class="mr-2" @click="isCardInputShown = true">+ Add New Card</v-btn>
|
||||
<template v-else>
|
||||
<StripeCardInput
|
||||
ref="stripeCardInput"
|
||||
:on-stripe-response-callback="addCardToDB"
|
||||
/>
|
||||
</template>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions v-if="isCardInputShown">
|
||||
<v-btn
|
||||
variant="outlined" color="primary" size="small" class="mr-2"
|
||||
:disabled="isLoading"
|
||||
:loading="isLoading"
|
||||
@click="onSaveCardClick"
|
||||
>
|
||||
Add Card
|
||||
</v-btn>
|
||||
<v-btn
|
||||
variant="outlined" color="default" size="small" class="mr-2"
|
||||
:disabled="isLoading"
|
||||
:loading="isLoading"
|
||||
@click="isCardInputShown = false"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VBtn, VCard, VCardText, VCardActions } from 'vuetify/components';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useLoading } from '@/composables/useLoading';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import StripeCardInput from '@/components/account/billing/paymentMethods/StripeCardInput.vue';
|
||||
|
||||
interface StripeForm {
|
||||
onSubmit(): Promise<void>;
|
||||
}
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
const notify = useNotify();
|
||||
const billingStore = useBillingStore();
|
||||
const { isLoading } = useLoading();
|
||||
|
||||
const stripeCardInput = ref<typeof StripeCardInput & StripeForm | null>(null);
|
||||
|
||||
const isCardInputShown = ref(false);
|
||||
|
||||
/**
|
||||
* Provides card information to Stripe.
|
||||
*/
|
||||
async function onSaveCardClick(): Promise<void> {
|
||||
if (isLoading.value || !stripeCardInput.value) return;
|
||||
|
||||
try {
|
||||
await stripeCardInput.value.onSubmit();
|
||||
} catch (error) {
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.BILLING_PAYMENT_METHODS_TAB);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds card after Stripe confirmation.
|
||||
*
|
||||
* @param token from Stripe
|
||||
*/
|
||||
async function addCardToDB(token: string): Promise<void> {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await billingStore.addCreditCard(token);
|
||||
notify.success('Card successfully added');
|
||||
isCardInputShown.value = false;
|
||||
isLoading.value = false;
|
||||
|
||||
// We fetch User one more time to update their Paid Tier status.
|
||||
usersStore.getUser().catch();
|
||||
|
||||
billingStore.getCreditCards().catch();
|
||||
} catch (error) {
|
||||
isLoading.value = false;
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.BILLING_PAYMENT_METHODS_TAB);
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<v-card title="Credit Card" variant="flat" :border="true" rounded="xlg">
|
||||
<v-card-text>
|
||||
<v-chip rounded color="default" variant="tonal" class="font-weight-bold mr-2 text-capitalize">{{ card.brand }}</v-chip>
|
||||
<v-chip v-if="card.isDefault" rounded color="primary" variant="tonal" class="font-weight-bold">Default</v-chip>
|
||||
<v-divider class="my-4" />
|
||||
<p>Card Number</p>
|
||||
<v-chip rounded color="default" variant="text" class="pl-0 font-weight-bold mt-2">**** **** **** {{ card.last4 }}</v-chip>
|
||||
<v-divider class="my-4" />
|
||||
<p>Exp. Date</p>
|
||||
<v-chip rounded color="default" variant="text" class="pl-0 font-weight-bold mt-2">
|
||||
{{ card.expMonth }}/{{ card.expYear }}
|
||||
</v-chip>
|
||||
<v-divider class="my-4" />
|
||||
<v-btn variant="outlined" color="default" size="small" class="mr-2">Remove</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VBtn, VCard, VCardText, VChip, VDivider } from 'vuetify/components';
|
||||
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { CreditCard } from '@/types/payments';
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
|
||||
const props = defineProps<{
|
||||
card: CreditCard,
|
||||
}>();
|
||||
</script>
|
@ -128,31 +128,12 @@
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="4">
|
||||
<v-card title="Credit Card" variant="flat" :border="true" rounded="xlg">
|
||||
<v-card-text>
|
||||
<v-chip rounded color="default" variant="tonal" class="font-weight-bold mr-2">Visa</v-chip>
|
||||
<!-- <v-chip rounded color="primary" variant="tonal" class="font-weight-bold mr-2">Default</v-chip> -->
|
||||
<v-divider class="my-4" />
|
||||
<p>Card Number</p>
|
||||
<v-chip rounded color="default" variant="text" class="pl-0 font-weight-bold mt-2">**** **** **** 2759</v-chip>
|
||||
<v-divider class="my-4" />
|
||||
<p>Exp. Date</p>
|
||||
<v-chip rounded color="default" variant="text" class="pl-0 font-weight-bold mt-2">12/27</v-chip>
|
||||
<v-divider class="my-4" />
|
||||
<v-btn variant="outlined" color="default" size="small" class="mr-2">Edit</v-btn>
|
||||
<v-btn variant="outlined" color="default" size="small" class="mr-2">Remove</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-col v-for="(card, i) in creditCards" :key="i" cols="12" sm="4">
|
||||
<CreditCardComponent :card="card" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="4">
|
||||
<v-card title="Add Card" variant="flat" :border="true" rounded="xlg">
|
||||
<v-card-text>
|
||||
<!-- <v-chip rounded color="info" variant="tonal" class="font-weight-bold mr-2">Visa</v-chip> -->
|
||||
<v-btn variant="outlined" color="default" size="small" class="mr-2">+ Add New Card</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<AddCreditCardComponent />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
@ -244,7 +225,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import {
|
||||
VContainer,
|
||||
VCard,
|
||||
@ -264,10 +245,25 @@ import {
|
||||
} from 'vuetify/components';
|
||||
import { VDataTable } from 'vuetify/labs/components';
|
||||
|
||||
import { useLoading } from '@/composables/useLoading';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { CreditCard } from '@/types/payments';
|
||||
|
||||
import CreditCardComponent from '@poc/components/CreditCardComponent.vue';
|
||||
import AddCreditCardComponent from '@poc/components/AddCreditCardComponent.vue';
|
||||
|
||||
const tab = ref<string>('Overview');
|
||||
const search = ref<string>('');
|
||||
const selected = ref([]);
|
||||
|
||||
const billingStore = useBillingStore();
|
||||
|
||||
const { isLoading, withLoading } = useLoading();
|
||||
|
||||
const creditCards = computed((): CreditCard[] => {
|
||||
return billingStore.state.creditCards;
|
||||
});
|
||||
|
||||
const sortBy = [{ key: 'date', order: 'asc' }];
|
||||
const headers = [
|
||||
{ title: 'Date', key: 'date' },
|
||||
@ -301,4 +297,10 @@ function getColor(status: string): string {
|
||||
if (status === 'Pending') return 'warning';
|
||||
return 'error';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
withLoading(async () => {
|
||||
await billingStore.getCreditCards();
|
||||
});
|
||||
});
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user