mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-19 16:32:15 +00:00
added prettier
This commit is contained in:
@@ -1,93 +1,91 @@
|
||||
<template>
|
||||
<tr class="border-b dark:border-opacity-5 border-light border-dashed whitespace-nowrap">
|
||||
<td class="py-5 md:pr-1 pr-3">
|
||||
<span class="text-sm font-bold">
|
||||
{{ row.data.attributes.note }}
|
||||
</span>
|
||||
</td>
|
||||
<td v-if="user" class="md:px-1 px-3 whitespace-nowrap">
|
||||
<div v-if="row.data.relationships.user" class="flex items-center">
|
||||
<MemberAvatar
|
||||
:is-border="false"
|
||||
:size="36"
|
||||
:member="row.data.relationships.user"
|
||||
/>
|
||||
<div class="ml-3 pr-10">
|
||||
<b class="text-sm font-bold block max-w-1 overflow-hidden text-ellipsis whitespace-nowrap" style="max-width: 155px;">
|
||||
{{ row.data.relationships.user.data.attributes.name }}
|
||||
</b>
|
||||
<span class="block text-xs dark:text-gray-500 text-gray-600">
|
||||
{{ row.data.relationships.user.data.attributes.email }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="! row.data.relationships.user" class="text-xs text-gray-500 font-bold">
|
||||
{{ $t('User was deleted') }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<ColorLabel class="capitalize" :color="$getTransactionStatusColor(row.data.attributes.status)">
|
||||
{{ row.data.attributes.status }}
|
||||
</ColorLabel>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<span class="text-sm font-bold" :class="$getTransactionTypeTextColor(row.data.attributes.type)">
|
||||
{{ $getTransactionMark(row.data.attributes.type) + row.data.attributes.price }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<span class="text-sm font-bold">
|
||||
{{ row.data.attributes.created_at }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<div class="w-28">
|
||||
<img class="inline-block max-h-5" :src="$getPaymentLogo(row.data.attributes.driver)" :alt="row.data.attributes.driver">
|
||||
</div>
|
||||
</td>
|
||||
<td class="md:pl-1 pl-3 text-right">
|
||||
<div class="inline-block">
|
||||
<a :href="$getInvoiceLink(row.data.id)" target="_blank" class="inline-block cursor-pointer flex items-center justify-center w-8 h-8 rounded-md hover:bg-purple-100 dark:bg-2x-dark-foreground bg-light-background transition-colors">
|
||||
<FileTextIcon size="15" class="opacity-75" />
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="whitespace-nowrap border-b border-dashed border-light dark:border-opacity-5">
|
||||
<td class="py-5 pr-3 md:pr-1">
|
||||
<span class="text-sm font-bold">
|
||||
{{ row.data.attributes.note }}
|
||||
</span>
|
||||
</td>
|
||||
<td v-if="user" class="whitespace-nowrap px-3 md:px-1">
|
||||
<div v-if="row.data.relationships.user" class="flex items-center">
|
||||
<MemberAvatar :is-border="false" :size="36" :member="row.data.relationships.user" />
|
||||
<div class="ml-3 pr-10">
|
||||
<b class="max-w-1 block overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold" style="max-width: 155px">
|
||||
{{ row.data.relationships.user.data.attributes.name }}
|
||||
</b>
|
||||
<span class="block text-xs text-gray-600 dark:text-gray-500">
|
||||
{{ row.data.relationships.user.data.attributes.email }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="!row.data.relationships.user" class="text-xs font-bold text-gray-500">
|
||||
{{ $t('User was deleted') }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<ColorLabel class="capitalize" :color="$getTransactionStatusColor(row.data.attributes.status)">
|
||||
{{ row.data.attributes.status }}
|
||||
</ColorLabel>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<span class="text-sm font-bold" :class="$getTransactionTypeTextColor(row.data.attributes.type)">
|
||||
{{ $getTransactionMark(row.data.attributes.type) + row.data.attributes.price }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<span class="text-sm font-bold">
|
||||
{{ row.data.attributes.created_at }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<div class="w-28">
|
||||
<img class="inline-block max-h-5" :src="$getPaymentLogo(row.data.attributes.driver)" :alt="row.data.attributes.driver" />
|
||||
</div>
|
||||
</td>
|
||||
<td class="pl-3 text-right md:pl-1">
|
||||
<div class="inline-block">
|
||||
<a
|
||||
:href="$getInvoiceLink(row.data.id)"
|
||||
target="_blank"
|
||||
class="inline-block flex h-8 w-8 cursor-pointer items-center justify-center rounded-md bg-light-background transition-colors hover:bg-purple-100 dark:bg-2x-dark-foreground"
|
||||
>
|
||||
<FileTextIcon size="15" class="opacity-75" />
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<script>
|
||||
import MemberAvatar from "../FilesView/MemberAvatar";
|
||||
import MeteredTransactionDetailRow from "./MeteredTransactionDetailRow";
|
||||
import ColorLabel from "../Others/ColorLabel";
|
||||
import {EyeIcon, FileTextIcon} from 'vue-feather-icons'
|
||||
import MemberAvatar from '../FilesView/MemberAvatar'
|
||||
import MeteredTransactionDetailRow from './MeteredTransactionDetailRow'
|
||||
import ColorLabel from '../Others/ColorLabel'
|
||||
import { EyeIcon, FileTextIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'FixedTransactionRow',
|
||||
components: {
|
||||
MeteredTransactionDetailRow,
|
||||
MemberAvatar,
|
||||
FileTextIcon,
|
||||
ColorLabel,
|
||||
EyeIcon,
|
||||
},
|
||||
props: {
|
||||
row: {},
|
||||
user: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showedTransactionDetailById: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showTransactionDetail(id) {
|
||||
if (this.showedTransactionDetailById === id)
|
||||
this.showedTransactionDetailById = undefined
|
||||
else
|
||||
this.showedTransactionDetailById = id
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
export default {
|
||||
name: 'FixedTransactionRow',
|
||||
components: {
|
||||
MeteredTransactionDetailRow,
|
||||
MemberAvatar,
|
||||
FileTextIcon,
|
||||
ColorLabel,
|
||||
EyeIcon,
|
||||
},
|
||||
props: {
|
||||
row: {},
|
||||
user: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showedTransactionDetailById: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showTransactionDetail(id) {
|
||||
if (this.showedTransactionDetailById === id) this.showedTransactionDetailById = undefined
|
||||
else this.showedTransactionDetailById = id
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
<template>
|
||||
<tr>
|
||||
<td colspan="10" class="rounded-lg overflow-hidden py-2">
|
||||
<div class="flex items-center justify-between py-2 border-b dark:border-opacity-5 border-light border-dashed" v-for="(usage, i) in row.data.attributes.metadata" :key="i">
|
||||
<div class="w-2/4 leading-none">
|
||||
<b class="text-sm font-bold leading-none">
|
||||
{{ $t(usage.feature) }}
|
||||
</b>
|
||||
<small class="text-xs text-gray-500 pt-2 leading-none sm:block hidden">
|
||||
{{ $t(`feature_usage_desc_${usage.feature}`) }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="text-left w-1/4">
|
||||
<span class="text-sm font-bold text-gray-560">
|
||||
{{ usage.usage }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-right w-1/4">
|
||||
<span class="text-sm font-bold text-theme">
|
||||
{{ usage.cost }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="10" class="overflow-hidden rounded-lg py-2">
|
||||
<div
|
||||
class="flex items-center justify-between border-b border-dashed border-light py-2 dark:border-opacity-5"
|
||||
v-for="(usage, i) in row.data.attributes.metadata"
|
||||
:key="i"
|
||||
>
|
||||
<div class="w-2/4 leading-none">
|
||||
<b class="text-sm font-bold leading-none">
|
||||
{{ $t(usage.feature) }}
|
||||
</b>
|
||||
<small class="hidden pt-2 text-xs leading-none text-gray-500 sm:block">
|
||||
{{ $t(`feature_usage_desc_${usage.feature}`) }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="w-1/4 text-left">
|
||||
<span class="text-gray-560 text-sm font-bold">
|
||||
{{ usage.usage }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="w-1/4 text-right">
|
||||
<span class="text-theme text-sm font-bold">
|
||||
{{ usage.cost }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'MeteredTransactionDetailRow',
|
||||
props: [
|
||||
'row'
|
||||
]
|
||||
name: 'MeteredTransactionDetailRow',
|
||||
props: ['row'],
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,104 +1,103 @@
|
||||
<template>
|
||||
<tr class="border-b dark:border-opacity-5 border-light border-dashed whitespace-nowrap">
|
||||
<td class="py-5 md:pr-1 pr-3">
|
||||
<span class="text-sm font-bold">
|
||||
{{ row.data.attributes.note }}
|
||||
</span>
|
||||
</td>
|
||||
<td v-if="user" class="md:px-1 px-3 whitespace-nowrap">
|
||||
<div v-if="row.data.relationships.user" class="flex items-center">
|
||||
<MemberAvatar
|
||||
:is-border="false"
|
||||
:size="36"
|
||||
:member="row.data.relationships.user"
|
||||
/>
|
||||
<div class="ml-3 pr-10">
|
||||
<b class="text-sm font-bold block max-w-1 overflow-hidden text-ellipsis whitespace-nowrap" style="max-width: 155px;">
|
||||
{{ row.data.relationships.user.data.attributes.name }}
|
||||
</b>
|
||||
<span class="block text-xs dark:text-gray-500 text-gray-600">
|
||||
{{ row.data.relationships.user.data.attributes.email }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="! row.data.relationships.user" class="text-xs text-gray-500 font-bold">
|
||||
{{ $t('User was deleted') }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<ColorLabel class="capitalize" :color="$getTransactionStatusColor(row.data.attributes.status)">
|
||||
{{ row.data.attributes.status }}
|
||||
</ColorLabel>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<span class="text-sm font-bold capitalize">
|
||||
{{ $t(row.data.attributes.type) }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<span class="text-sm font-bold" :class="$getTransactionTypeTextColor(row.data.attributes.type)">
|
||||
{{ $getTransactionMark(row.data.attributes.type) + row.data.attributes.price }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<span class="text-sm font-bold">
|
||||
{{ row.data.attributes.created_at }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="md:px-1 px-3">
|
||||
<div class="w-28">
|
||||
<img class="inline-block max-h-5" :src="$getPaymentLogo(row.data.attributes.driver)" :alt="row.data.attributes.driver">
|
||||
</div>
|
||||
</td>
|
||||
<td class="md:pl-1 pl-3 text-right">
|
||||
<div v-if="row.data.attributes.metadata" class="flex space-x-2 w-full justify-end">
|
||||
<div @click="$emit('showDetail', row.data.id)" class="cursor-pointer flex items-center justify-center w-8 h-8 rounded-md hover:bg-green-100 dark:bg-2x-dark-foreground bg-light-background transition-colors">
|
||||
<EyeIcon size="15" class="opacity-75" />
|
||||
</div>
|
||||
<a :href="$getInvoiceLink(row.data.id)" target="_blank" class="cursor-pointer flex items-center justify-center w-8 h-8 rounded-md hover:bg-purple-100 dark:bg-2x-dark-foreground bg-light-background transition-colors">
|
||||
<FileTextIcon size="15" class="opacity-75" />
|
||||
</a>
|
||||
</div>
|
||||
<div v-else>
|
||||
-
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="whitespace-nowrap border-b border-dashed border-light dark:border-opacity-5">
|
||||
<td class="py-5 pr-3 md:pr-1">
|
||||
<span class="text-sm font-bold">
|
||||
{{ row.data.attributes.note }}
|
||||
</span>
|
||||
</td>
|
||||
<td v-if="user" class="whitespace-nowrap px-3 md:px-1">
|
||||
<div v-if="row.data.relationships.user" class="flex items-center">
|
||||
<MemberAvatar :is-border="false" :size="36" :member="row.data.relationships.user" />
|
||||
<div class="ml-3 pr-10">
|
||||
<b class="max-w-1 block overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold" style="max-width: 155px">
|
||||
{{ row.data.relationships.user.data.attributes.name }}
|
||||
</b>
|
||||
<span class="block text-xs text-gray-600 dark:text-gray-500">
|
||||
{{ row.data.relationships.user.data.attributes.email }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="!row.data.relationships.user" class="text-xs font-bold text-gray-500">
|
||||
{{ $t('User was deleted') }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<ColorLabel class="capitalize" :color="$getTransactionStatusColor(row.data.attributes.status)">
|
||||
{{ row.data.attributes.status }}
|
||||
</ColorLabel>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<span class="text-sm font-bold capitalize">
|
||||
{{ $t(row.data.attributes.type) }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<span class="text-sm font-bold" :class="$getTransactionTypeTextColor(row.data.attributes.type)">
|
||||
{{ $getTransactionMark(row.data.attributes.type) + row.data.attributes.price }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<span class="text-sm font-bold">
|
||||
{{ row.data.attributes.created_at }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3 md:px-1">
|
||||
<div class="w-28">
|
||||
<img class="inline-block max-h-5" :src="$getPaymentLogo(row.data.attributes.driver)" :alt="row.data.attributes.driver" />
|
||||
</div>
|
||||
</td>
|
||||
<td class="pl-3 text-right md:pl-1">
|
||||
<div v-if="row.data.attributes.metadata" class="flex w-full justify-end space-x-2">
|
||||
<div
|
||||
@click="$emit('showDetail', row.data.id)"
|
||||
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-md bg-light-background transition-colors hover:bg-green-100 dark:bg-2x-dark-foreground"
|
||||
>
|
||||
<EyeIcon size="15" class="opacity-75" />
|
||||
</div>
|
||||
<a
|
||||
:href="$getInvoiceLink(row.data.id)"
|
||||
target="_blank"
|
||||
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-md bg-light-background transition-colors hover:bg-purple-100 dark:bg-2x-dark-foreground"
|
||||
>
|
||||
<FileTextIcon size="15" class="opacity-75" />
|
||||
</a>
|
||||
</div>
|
||||
<div v-else>-</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<script>
|
||||
import MemberAvatar from "../FilesView/MemberAvatar";
|
||||
import MeteredTransactionDetailRow from "./MeteredTransactionDetailRow";
|
||||
import ColorLabel from "../Others/ColorLabel";
|
||||
import {EyeIcon, FileTextIcon} from 'vue-feather-icons'
|
||||
import MemberAvatar from '../FilesView/MemberAvatar'
|
||||
import MeteredTransactionDetailRow from './MeteredTransactionDetailRow'
|
||||
import ColorLabel from '../Others/ColorLabel'
|
||||
import { EyeIcon, FileTextIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'MeteredTransactionRow',
|
||||
components: {
|
||||
MeteredTransactionDetailRow,
|
||||
MemberAvatar,
|
||||
FileTextIcon,
|
||||
ColorLabel,
|
||||
EyeIcon,
|
||||
},
|
||||
props: {
|
||||
row: {},
|
||||
user: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showedTransactionDetailById: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showTransactionDetail(id) {
|
||||
if (this.showedTransactionDetailById === id)
|
||||
this.showedTransactionDetailById = undefined
|
||||
else
|
||||
this.showedTransactionDetailById = id
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
export default {
|
||||
name: 'MeteredTransactionRow',
|
||||
components: {
|
||||
MeteredTransactionDetailRow,
|
||||
MemberAvatar,
|
||||
FileTextIcon,
|
||||
ColorLabel,
|
||||
EyeIcon,
|
||||
},
|
||||
props: {
|
||||
row: {},
|
||||
user: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showedTransactionDetailById: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showTransactionDetail(id) {
|
||||
if (this.showedTransactionDetailById === id) this.showedTransactionDetailById = undefined
|
||||
else this.showedTransactionDetailById = id
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,59 +1,57 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between py-3 md:px-4 px-2 rounded-lg dark:bg-2x-dark-foreground bg-light-background">
|
||||
<div class="flex items-center">
|
||||
<img :src="`/assets/gateways/${card.data.attributes.brand}.svg`" alt="" class="h-5 mr-3 rounded">
|
||||
<b class="text-sm font-bold leading-none capitalize whitespace-nowrap">
|
||||
{{ card.data.attributes.brand }} •••• {{ card.data.attributes.last4 }}
|
||||
</b>
|
||||
</div>
|
||||
<b class="text-sm font-bold leading-none">
|
||||
{{ $t('Expires') }} {{ card.data.attributes.expiration }}
|
||||
</b>
|
||||
<Trash2Icon @click="deleteCreditCard(card.data.id)" size="15" class="cursor-pointer" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between rounded-lg bg-light-background py-3 px-2 dark:bg-2x-dark-foreground md:px-4">
|
||||
<div class="flex items-center">
|
||||
<img :src="`/assets/gateways/${card.data.attributes.brand}.svg`" alt="" class="mr-3 h-5 rounded" />
|
||||
<b class="whitespace-nowrap text-sm font-bold capitalize leading-none">
|
||||
{{ card.data.attributes.brand }} ••••
|
||||
{{ card.data.attributes.last4 }}
|
||||
</b>
|
||||
</div>
|
||||
<b class="text-sm font-bold leading-none"> {{ $t('Expires') }} {{ card.data.attributes.expiration }} </b>
|
||||
<Trash2Icon @click="deleteCreditCard(card.data.id)" size="15" class="cursor-pointer" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {Trash2Icon,} from "vue-feather-icons";
|
||||
import {events} from "../../bus";
|
||||
import axios from "axios";
|
||||
import { Trash2Icon } from 'vue-feather-icons'
|
||||
import { events } from '../../bus'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'PaymentCard',
|
||||
components: {
|
||||
Trash2Icon,
|
||||
},
|
||||
props: [
|
||||
'card',
|
||||
],
|
||||
methods: {
|
||||
deleteCreditCard(id) {
|
||||
events.$emit('confirm:open', {
|
||||
title: this.$t('Are you sure you want to delete your credit card?'),
|
||||
message: this.$t('We will no longer settle your payments automatically and you will have to fund your account for the next payments.'),
|
||||
action: {
|
||||
id: id,
|
||||
operation: 'delete-credit-card',
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('action:confirmed', data => {
|
||||
if (data.operation === 'delete-credit-card')
|
||||
axios.delete(`/api/stripe/credit-cards/${data.id}`)
|
||||
.then(() => {
|
||||
this.$store.dispatch('getAppData')
|
||||
export default {
|
||||
name: 'PaymentCard',
|
||||
components: {
|
||||
Trash2Icon,
|
||||
},
|
||||
props: ['card'],
|
||||
methods: {
|
||||
deleteCreditCard(id) {
|
||||
events.$emit('confirm:open', {
|
||||
title: this.$t('Are you sure you want to delete your credit card?'),
|
||||
message: this.$t('We will no longer settle your payments automatically and you will have to fund your account for the next payments.'),
|
||||
action: {
|
||||
id: id,
|
||||
operation: 'delete-credit-card',
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('action:confirmed', (data) => {
|
||||
if (data.operation === 'delete-credit-card')
|
||||
axios
|
||||
.delete(`/api/stripe/credit-cards/${data.id}`)
|
||||
.then(() => {
|
||||
this.$store.dispatch('getAppData')
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your credit card was deleted.'),
|
||||
})
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
events.$off('action:confirmed')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your credit card was deleted.'),
|
||||
})
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
events.$off('action:confirmed')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
<template>
|
||||
<div
|
||||
class="py-3 px-4 cursor-pointer rounded-lg block select-none"
|
||||
:class="{'dark:bg-2x-dark-foreground bg-light-background': isSelected}"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-1.5">
|
||||
<CheckBox :is-clicked="isSelected" />
|
||||
<b class="pl-4 text-lg text-left flex-1">
|
||||
{{ plan.data.attributes.name }}
|
||||
</b>
|
||||
<span class="ml-9 inline-block py-1 px-2 text-theme font-extrabold text-sm rounded-xl bg-theme-100 whitespace-nowrap">
|
||||
{{ plan.data.attributes.price }} / {{ $t(`interval.${plan.data.attributes.interval}`) }}
|
||||
</span>
|
||||
</div>
|
||||
<ul class="ml-9 mb-3">
|
||||
<li class="flex items-center mb-1.5" v-for="(value, key, i) in plan.data.attributes.features" :key="i">
|
||||
<CheckIcon size="12" class="svg-stroke-theme" />
|
||||
<small class="pl-1.5 text-xs text-gray-600 font-bold" v-if="value !== -1">
|
||||
{{ $t(key, {value: value}) }}
|
||||
</small>
|
||||
<small class="pl-1.5 text-xs text-gray-600 font-bold" v-if="value === -1">
|
||||
{{ $t(`${key}.unlimited`) }}
|
||||
</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="block cursor-pointer select-none rounded-lg py-3 px-4"
|
||||
:class="{
|
||||
'bg-light-background dark:bg-2x-dark-foreground': isSelected,
|
||||
}"
|
||||
>
|
||||
<div class="mb-1.5 flex items-center justify-between">
|
||||
<CheckBox :is-clicked="isSelected" />
|
||||
<b class="flex-1 pl-4 text-left text-lg">
|
||||
{{ plan.data.attributes.name }}
|
||||
</b>
|
||||
<span class="text-theme bg-theme-100 ml-9 inline-block whitespace-nowrap rounded-xl py-1 px-2 text-sm font-extrabold">
|
||||
{{ plan.data.attributes.price }} /
|
||||
{{ $t(`interval.${plan.data.attributes.interval}`) }}
|
||||
</span>
|
||||
</div>
|
||||
<ul class="ml-9 mb-3">
|
||||
<li class="mb-1.5 flex items-center" v-for="(value, key, i) in plan.data.attributes.features" :key="i">
|
||||
<CheckIcon size="12" class="svg-stroke-theme" />
|
||||
<small class="pl-1.5 text-xs font-bold text-gray-600" v-if="value !== -1">
|
||||
{{ $t(key, { value: value }) }}
|
||||
</small>
|
||||
<small class="pl-1.5 text-xs font-bold text-gray-600" v-if="value === -1">
|
||||
{{ $t(`${key}.unlimited`) }}
|
||||
</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {CheckIcon} from 'vue-feather-icons'
|
||||
import CheckBox from "../FilesView/CheckBox"
|
||||
import { CheckIcon } from 'vue-feather-icons'
|
||||
import CheckBox from '../FilesView/CheckBox'
|
||||
|
||||
export default {
|
||||
name: 'PlanDetail',
|
||||
components: {
|
||||
CheckIcon,
|
||||
CheckBox,
|
||||
},
|
||||
props: [
|
||||
'isSelected',
|
||||
'plan',
|
||||
],
|
||||
name: 'PlanDetail',
|
||||
components: {
|
||||
CheckIcon,
|
||||
CheckBox,
|
||||
},
|
||||
props: ['isSelected', 'plan'],
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,290 +1,257 @@
|
||||
<template>
|
||||
<PopupWrapper name="select-plan-subscription">
|
||||
<PopupHeader :title="$t('Upgrade Your Account')" icon="credit-card" />
|
||||
|
||||
<PopupHeader :title="$t('Upgrade Your Account')" icon="credit-card" />
|
||||
<!--Payment Options-->
|
||||
<div v-if="isPaymentOptionPage">
|
||||
<PopupContent>
|
||||
<!--Stripe implementation-->
|
||||
<PaymentMethod v-if="config.isStripe" driver="stripe" :description="$t('Pay by your credit card or Apple Pay')">
|
||||
<div v-if="stripe.isGettingCheckoutLink" class="translate-y-3 scale-50 transform">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span @click="payByStripe" :class="{ 'opacity-0': stripe.isGettingCheckoutLink }" class="text-theme cursor-pointer text-sm font-bold">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</PaymentMethod>
|
||||
|
||||
<!--Payment Options-->
|
||||
<div v-if="isPaymentOptionPage">
|
||||
<PopupContent>
|
||||
<!--PayPal implementation-->
|
||||
<div
|
||||
v-if="config.isPayPal"
|
||||
:class="{
|
||||
'mb-2 rounded-xl bg-light-background px-4 dark:bg-2x-dark-foreground': paypal.isMethodsLoaded,
|
||||
}"
|
||||
>
|
||||
<PaymentMethod @click.native="pickedPaymentMethod('paypal')" driver="paypal" :description="$t('Available PayPal Credit, Debit or Credit Card.')">
|
||||
<div v-if="paypal.isMethodLoading" class="translate-y-3 scale-50 transform">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span v-if="!paypal.isMethodsLoaded" :class="{ 'opacity-0': paypal.isMethodLoading }" class="text-theme cursor-pointer text-sm font-bold">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</PaymentMethod>
|
||||
|
||||
<!--Stripe implementation-->
|
||||
<PaymentMethod
|
||||
v-if="config.isStripe"
|
||||
driver="stripe"
|
||||
:description="$t('Pay by your credit card or Apple Pay')"
|
||||
>
|
||||
<div v-if="stripe.isGettingCheckoutLink" class="transform scale-50 translate-y-3">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span @click="payByStripe" :class="{'opacity-0': stripe.isGettingCheckoutLink}" class="text-sm text-theme font-bold cursor-pointer">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</PaymentMethod>
|
||||
<!--PayPal Buttons-->
|
||||
<div id="paypal-button-container"></div>
|
||||
</div>
|
||||
|
||||
<!--PayPal implementation-->
|
||||
<div
|
||||
v-if="config.isPayPal"
|
||||
:class="{'dark:bg-2x-dark-foreground bg-light-background rounded-xl px-4 mb-2': paypal.isMethodsLoaded}"
|
||||
>
|
||||
<PaymentMethod
|
||||
@click.native="pickedPaymentMethod('paypal')"
|
||||
driver="paypal"
|
||||
:description="$t('Available PayPal Credit, Debit or Credit Card.')"
|
||||
>
|
||||
<div v-if="paypal.isMethodLoading" class="transform scale-50 translate-y-3">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span v-if="! paypal.isMethodsLoaded" :class="{'opacity-0': paypal.isMethodLoading}" class="text-sm text-theme font-bold cursor-pointer">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</PaymentMethod>
|
||||
<!--Paystack implementation-->
|
||||
<PaymentMethod v-if="config.isPaystack" driver="paystack" :description="$t('Available Bank Account, USSD, Mobile Money, Apple Pay')">
|
||||
<paystack
|
||||
v-if="user && config"
|
||||
:channels="['bank', 'ussd', 'qr', 'mobile_money', 'bank_transfer']"
|
||||
class="font-bold"
|
||||
currency="ZAR"
|
||||
:plan="selectedPlan.data.meta.driver_plan_id.paystack"
|
||||
:amount="selectedPlan.data.attributes.amount * 100"
|
||||
:email="user.data.attributes.email"
|
||||
:paystackkey="config.paystack_public_key"
|
||||
:reference="$generatePaystackReference()"
|
||||
:callback="paymentSuccessful"
|
||||
:close="paystackClosed"
|
||||
>
|
||||
<span class="text-theme cursor-pointer text-sm font-bold">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</paystack>
|
||||
</PaymentMethod>
|
||||
</PopupContent>
|
||||
|
||||
<!--PayPal Buttons-->
|
||||
<div id="paypal-button-container"></div>
|
||||
</div>
|
||||
<PopupActions>
|
||||
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">
|
||||
{{ $t('Cancel Payment') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
|
||||
<!--Paystack implementation-->
|
||||
<PaymentMethod
|
||||
v-if="config.isPaystack"
|
||||
driver="paystack"
|
||||
:description="$t('Available Bank Account, USSD, Mobile Money, Apple Pay')"
|
||||
>
|
||||
<paystack
|
||||
v-if="user && config"
|
||||
:channels="['bank', 'ussd', 'qr', 'mobile_money', 'bank_transfer']"
|
||||
class="font-bold"
|
||||
currency="ZAR"
|
||||
:plan="selectedPlan.data.meta.driver_plan_id.paystack"
|
||||
:amount="selectedPlan.data.attributes.amount * 100"
|
||||
:email="user.data.attributes.email"
|
||||
:paystackkey="config.paystack_public_key"
|
||||
:reference="$generatePaystackReference()"
|
||||
:callback="paymentSuccessful"
|
||||
:close="paystackClosed"
|
||||
>
|
||||
<span class="text-sm text-theme font-bold cursor-pointer">
|
||||
{{ $t('Select') }}
|
||||
</span>
|
||||
</paystack>
|
||||
</PaymentMethod>
|
||||
</PopupContent>
|
||||
<!--Select Payment Plans-->
|
||||
<div v-if="!isPaymentOptionPage">
|
||||
<PopupContent v-if="plans">
|
||||
<!--Toggle yearly billing-->
|
||||
<div v-if="hasYearlyPlans.length > 0" class="mb-2 px-5 text-right">
|
||||
<label :class="{ 'text-gray-400': !isSelectedYearlyPlans }" class="cursor-pointer text-xs font-bold">
|
||||
{{ $t('Billed Annually') }}
|
||||
</label>
|
||||
<div class="relative inline-block w-12 select-none align-middle">
|
||||
<SwitchInput class="scale-75 transform" v-model="isSelectedYearlyPlans" :state="isSelectedYearlyPlans" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="$closePopup()"
|
||||
button-style="secondary"
|
||||
>
|
||||
{{ $t('Cancel Payment') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
<!--List available plans-->
|
||||
<div>
|
||||
<PlanDetail
|
||||
v-for="(plan, i) in plans.data"
|
||||
:plan="plan"
|
||||
:key="plan.data.id"
|
||||
v-if="plan.data.attributes.interval === intervalPlanType && userSubscribedPlanId !== plan.data.id"
|
||||
:is-selected="selectedPlan && selectedPlan.data.id === plan.data.id"
|
||||
@click.native="selectPlan(plan)"
|
||||
/>
|
||||
</div>
|
||||
</PopupContent>
|
||||
|
||||
<!--Select Payment Plans-->
|
||||
<div v-if="! isPaymentOptionPage">
|
||||
<PopupContent v-if="plans">
|
||||
|
||||
<!--Toggle yearly billing-->
|
||||
<div v-if="hasYearlyPlans.length > 0" class="px-5 mb-2 text-right">
|
||||
<label :class="{'text-gray-400': !isSelectedYearlyPlans}" class="font-bold cursor-pointer text-xs">
|
||||
{{ $t('Billed Annually') }}
|
||||
</label>
|
||||
<div class="relative inline-block w-12 align-middle select-none">
|
||||
<SwitchInput class="transform scale-75" v-model="isSelectedYearlyPlans" :state="isSelectedYearlyPlans" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--List available plans-->
|
||||
<div>
|
||||
<PlanDetail
|
||||
v-for="(plan, i) in plans.data"
|
||||
:plan="plan"
|
||||
:key="plan.data.id"
|
||||
v-if="plan.data.attributes.interval === intervalPlanType && userSubscribedPlanId !== plan.data.id"
|
||||
:is-selected="selectedPlan && selectedPlan.data.id === plan.data.id"
|
||||
@click.native="selectPlan(plan)"
|
||||
/>
|
||||
</div>
|
||||
</PopupContent>
|
||||
|
||||
<!--Actions-->
|
||||
<PopupActions>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
@click.native="$closePopup()"
|
||||
button-style="secondary"
|
||||
>{{ $t('popup_move_item.cancel') }}
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
class="w-full"
|
||||
:button-style="buttonStyle"
|
||||
@click.native="isPaymentOptionPage = true"
|
||||
>{{ $t('Upgrade Account') }}
|
||||
</ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
<!--Actions-->
|
||||
<PopupActions>
|
||||
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">{{ $t('popup_move_item.cancel') }} </ButtonBase>
|
||||
<ButtonBase class="w-full" :button-style="buttonStyle" @click.native="isPaymentOptionPage = true">{{ $t('Upgrade Account') }} </ButtonBase>
|
||||
</PopupActions>
|
||||
</div>
|
||||
</PopupWrapper>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PaymentMethod from "../Others/PaymentMethod";
|
||||
import {loadScript} from "@paypal/paypal-js";
|
||||
import SwitchInput from "../Others/Forms/SwitchInput";
|
||||
import PopupWrapper from "../Others/Popup/PopupWrapper";
|
||||
import PopupActions from '../Others/Popup/PopupActions'
|
||||
import PopupContent from '../Others/Popup/PopupContent'
|
||||
import PopupHeader from '../Others/Popup/PopupHeader'
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import PlanDetail from "./PlanDetail";
|
||||
import paystack from 'vue-paystack';
|
||||
import {mapGetters} from "vuex";
|
||||
import {events} from "../../bus";
|
||||
import axios from "axios";
|
||||
import Spinner from "../FilesView/Spinner";
|
||||
import PaymentMethod from '../Others/PaymentMethod'
|
||||
import { loadScript } from '@paypal/paypal-js'
|
||||
import SwitchInput from '../Others/Forms/SwitchInput'
|
||||
import PopupWrapper from '../Others/Popup/PopupWrapper'
|
||||
import PopupActions from '../Others/Popup/PopupActions'
|
||||
import PopupContent from '../Others/Popup/PopupContent'
|
||||
import PopupHeader from '../Others/Popup/PopupHeader'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import PlanDetail from './PlanDetail'
|
||||
import paystack from 'vue-paystack'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { events } from '../../bus'
|
||||
import axios from 'axios'
|
||||
import Spinner from '../FilesView/Spinner'
|
||||
|
||||
export default {
|
||||
name: 'SelectPlanSubscriptionPopup',
|
||||
components: {
|
||||
Spinner,
|
||||
PaymentMethod,
|
||||
paystack,
|
||||
PlanDetail,
|
||||
SwitchInput,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
},
|
||||
watch: {
|
||||
isSelectedYearlyPlans() {
|
||||
this.selectedPlan = undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'config',
|
||||
'user',
|
||||
]),
|
||||
intervalPlanType() {
|
||||
return this.isSelectedYearlyPlans
|
||||
? 'year'
|
||||
: 'month'
|
||||
},
|
||||
buttonStyle() {
|
||||
return this.selectedPlan
|
||||
? 'theme'
|
||||
: 'secondary'
|
||||
},
|
||||
userSubscribedPlanId() {
|
||||
return (this.user && this.user.data.relationships.subscription) && this.user.data.relationships.subscription.data.relationships.plan.data.id
|
||||
},
|
||||
hasYearlyPlans() {
|
||||
return this.plans.data.filter(plan => plan.data.attributes.interval === 'year')
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stripe: {
|
||||
isGettingCheckoutLink: false,
|
||||
},
|
||||
paypal: {
|
||||
isMethodsLoaded: false,
|
||||
isMethodLoading: false
|
||||
},
|
||||
isPaymentOptionPage: false,
|
||||
isSelectedYearlyPlans: false,
|
||||
isLoading: false,
|
||||
selectedPlan: undefined,
|
||||
plans: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pickedPaymentMethod(driver) {
|
||||
if (driver === 'paystack') {
|
||||
this.$closePopup()
|
||||
}
|
||||
if (driver === 'paypal' && !this.paypal.isMethodsLoaded) {
|
||||
this.PayPalInitialization()
|
||||
}
|
||||
},
|
||||
async PayPalInitialization() {
|
||||
this.paypal.isMethodLoading = true
|
||||
export default {
|
||||
name: 'SelectPlanSubscriptionPopup',
|
||||
components: {
|
||||
Spinner,
|
||||
PaymentMethod,
|
||||
paystack,
|
||||
PlanDetail,
|
||||
SwitchInput,
|
||||
PopupWrapper,
|
||||
PopupActions,
|
||||
PopupContent,
|
||||
PopupHeader,
|
||||
ButtonBase,
|
||||
},
|
||||
watch: {
|
||||
isSelectedYearlyPlans() {
|
||||
this.selectedPlan = undefined
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['config', 'user']),
|
||||
intervalPlanType() {
|
||||
return this.isSelectedYearlyPlans ? 'year' : 'month'
|
||||
},
|
||||
buttonStyle() {
|
||||
return this.selectedPlan ? 'theme' : 'secondary'
|
||||
},
|
||||
userSubscribedPlanId() {
|
||||
return this.user && this.user.data.relationships.subscription && this.user.data.relationships.subscription.data.relationships.plan.data.id
|
||||
},
|
||||
hasYearlyPlans() {
|
||||
return this.plans.data.filter((plan) => plan.data.attributes.interval === 'year')
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stripe: {
|
||||
isGettingCheckoutLink: false,
|
||||
},
|
||||
paypal: {
|
||||
isMethodsLoaded: false,
|
||||
isMethodLoading: false,
|
||||
},
|
||||
isPaymentOptionPage: false,
|
||||
isSelectedYearlyPlans: false,
|
||||
isLoading: false,
|
||||
selectedPlan: undefined,
|
||||
plans: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pickedPaymentMethod(driver) {
|
||||
if (driver === 'paystack') {
|
||||
this.$closePopup()
|
||||
}
|
||||
if (driver === 'paypal' && !this.paypal.isMethodsLoaded) {
|
||||
this.PayPalInitialization()
|
||||
}
|
||||
},
|
||||
async PayPalInitialization() {
|
||||
this.paypal.isMethodLoading = true
|
||||
|
||||
let paypal;
|
||||
let paypal
|
||||
|
||||
try {
|
||||
paypal = await loadScript({
|
||||
'client-id': this.config.paypal_client_id,
|
||||
'vault': true,
|
||||
});
|
||||
} catch (error) {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('Failed to load the PayPal service'),
|
||||
})
|
||||
}
|
||||
try {
|
||||
paypal = await loadScript({
|
||||
'client-id': this.config.paypal_client_id,
|
||||
vault: true,
|
||||
})
|
||||
} catch (error) {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('Failed to load the PayPal service'),
|
||||
})
|
||||
}
|
||||
|
||||
const planId = this.selectedPlan.data.meta.driver_plan_id.paypal
|
||||
const userId = this.user.data.id
|
||||
const app = this
|
||||
const planId = this.selectedPlan.data.meta.driver_plan_id.paypal
|
||||
const userId = this.user.data.id
|
||||
const app = this
|
||||
|
||||
this.paypal.isMethodsLoaded = true
|
||||
this.paypal.isMethodLoading = false
|
||||
this.paypal.isMethodsLoaded = true
|
||||
this.paypal.isMethodLoading = false
|
||||
|
||||
// Initialize paypal buttons for single charge
|
||||
await paypal.Buttons({
|
||||
createSubscription: function (data, actions) {
|
||||
return actions.subscription.create({
|
||||
plan_id: planId,
|
||||
custom_id: userId
|
||||
});
|
||||
},
|
||||
onApprove: function (data, actions) {
|
||||
app.paymentSuccessful()
|
||||
}
|
||||
}).render('#paypal-button-container');
|
||||
},
|
||||
payByStripe() {
|
||||
this.stripe.isGettingCheckoutLink = true
|
||||
// Initialize paypal buttons for single charge
|
||||
await paypal
|
||||
.Buttons({
|
||||
createSubscription: function (data, actions) {
|
||||
return actions.subscription.create({
|
||||
plan_id: planId,
|
||||
custom_id: userId,
|
||||
})
|
||||
},
|
||||
onApprove: function (data, actions) {
|
||||
app.paymentSuccessful()
|
||||
},
|
||||
})
|
||||
.render('#paypal-button-container')
|
||||
},
|
||||
payByStripe() {
|
||||
this.stripe.isGettingCheckoutLink = true
|
||||
|
||||
axios.post('/api/stripe/checkout', {
|
||||
planCode: this.selectedPlan.data.meta.driver_plan_id.stripe
|
||||
})
|
||||
.then(response => {
|
||||
window.location = response.data.url
|
||||
})
|
||||
},
|
||||
selectPlan(plan) {
|
||||
this.selectedPlan = plan
|
||||
},
|
||||
paymentSuccessful() {
|
||||
this.$closePopup()
|
||||
axios
|
||||
.post('/api/stripe/checkout', {
|
||||
planCode: this.selectedPlan.data.meta.driver_plan_id.stripe,
|
||||
})
|
||||
.then((response) => {
|
||||
window.location = response.data.url
|
||||
})
|
||||
},
|
||||
selectPlan(plan) {
|
||||
this.selectedPlan = plan
|
||||
},
|
||||
paymentSuccessful() {
|
||||
this.$closePopup()
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your payment was successfully received.'),
|
||||
})
|
||||
},
|
||||
paystackClosed() {
|
||||
// ...
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Load available plans
|
||||
axios.get('/api/subscriptions/plans')
|
||||
.then(response => {
|
||||
this.plans = response.data
|
||||
})
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your payment was successfully received.'),
|
||||
})
|
||||
},
|
||||
paystackClosed() {
|
||||
// ...
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// Load available plans
|
||||
axios.get('/api/subscriptions/plans').then((response) => {
|
||||
this.plans = response.data
|
||||
})
|
||||
|
||||
// Reset states on popup close
|
||||
events.$on('popup:close', () => {
|
||||
this.isSelectedYearlyPlans = false
|
||||
this.isPaymentOptionPage = false
|
||||
this.selectedPlan = undefined
|
||||
this.paypal.isMethodsLoaded = false
|
||||
})
|
||||
}
|
||||
}
|
||||
// Reset states on popup close
|
||||
events.$on('popup:close', () => {
|
||||
this.isSelectedYearlyPlans = false
|
||||
this.isPaymentOptionPage = false
|
||||
this.selectedPlan = undefined
|
||||
this.paypal.isMethodsLoaded = false
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,74 +1,73 @@
|
||||
<template>
|
||||
<div v-if="! hasPaymentMethod" class="card shadow-card">
|
||||
<FormLabel icon="dollar">
|
||||
{{ $t('Balance') }}
|
||||
</FormLabel>
|
||||
<div v-if="!hasPaymentMethod" class="card shadow-card">
|
||||
<FormLabel icon="dollar">
|
||||
{{ $t('Balance') }}
|
||||
</FormLabel>
|
||||
|
||||
<b class="sm:text-3xl text-2xl font-extrabold -mt-3 block mb-0.5">
|
||||
{{ user.data.relationships.balance.data.attributes.formatted }}
|
||||
</b>
|
||||
<b class="-mt-3 mb-0.5 block text-2xl font-extrabold sm:text-3xl">
|
||||
{{ user.data.relationships.balance.data.attributes.formatted }}
|
||||
</b>
|
||||
|
||||
<!-- Make payment form -->
|
||||
<ValidationObserver ref="fundAccount" @submit.prevent="makePayment" v-slot="{ invalid }" tag="form" class="mt-6">
|
||||
<ValidationProvider tag="div" v-slot="{ errors }" mode="passive" name="Amount" :rules="`required|min_value:${user.data.meta.totalDebt.amount}`">
|
||||
<AppInputText :description="$t('The amount will be increased as soon as we register your charge from payment gateway.')" :error="errors[0]" :is-last="true">
|
||||
<div class="sm:flex sm:space-x-4 sm:space-y-0 space-y-4">
|
||||
<input v-model="chargeAmount"
|
||||
:placeholder="$t('Fund Your Account Balance...')"
|
||||
type="number"
|
||||
min="1"
|
||||
max="999999999"
|
||||
class="focus-border-theme input-dark"
|
||||
:class="{'border-red': errors[0]}"
|
||||
/>
|
||||
<ButtonBase type="submit" button-style="theme" class="sm:w-auto w-full">
|
||||
{{ $t('Make a Payment') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</ValidationObserver>
|
||||
</div>
|
||||
<!-- Make payment form -->
|
||||
<ValidationObserver ref="fundAccount" @submit.prevent="makePayment" v-slot="{ invalid }" tag="form" class="mt-6">
|
||||
<ValidationProvider tag="div" v-slot="{ errors }" mode="passive" name="Amount" :rules="`required|min_value:${user.data.meta.totalDebt.amount}`">
|
||||
<AppInputText :description="$t('The amount will be increased as soon as we register your charge from payment gateway.')" :error="errors[0]" :is-last="true">
|
||||
<div class="space-y-4 sm:flex sm:space-x-4 sm:space-y-0">
|
||||
<input
|
||||
v-model="chargeAmount"
|
||||
:placeholder="$t('Fund Your Account Balance...')"
|
||||
type="number"
|
||||
min="1"
|
||||
max="999999999"
|
||||
class="focus-border-theme input-dark"
|
||||
:class="{ 'border-red': errors[0] }"
|
||||
/>
|
||||
<ButtonBase type="submit" button-style="theme" class="w-full sm:w-auto">
|
||||
{{ $t('Make a Payment') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</ValidationObserver>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {ValidationObserver, ValidationProvider} from 'vee-validate/dist/vee-validate.full'
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import AppInputText from "../Admin/AppInputText"
|
||||
import {mapGetters} from "vuex";
|
||||
import { ValidationObserver, ValidationProvider } from 'vee-validate/dist/vee-validate.full'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import AppInputText from '../Admin/AppInputText'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'UserBalance',
|
||||
components: {
|
||||
ValidationObserver,
|
||||
ValidationProvider,
|
||||
AppInputText,
|
||||
ButtonBase,
|
||||
FormLabel
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
]),
|
||||
hasPaymentMethod() {
|
||||
return this.user.data.relationships.creditCards && this.user.data.relationships.creditCards.data.length > 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chargeAmount: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async makePayment() {
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.fundAccount.validate();
|
||||
name: 'UserBalance',
|
||||
components: {
|
||||
ValidationObserver,
|
||||
ValidationProvider,
|
||||
AppInputText,
|
||||
ButtonBase,
|
||||
FormLabel,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
hasPaymentMethod() {
|
||||
return this.user.data.relationships.creditCards && this.user.data.relationships.creditCards.data.length > 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chargeAmount: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async makePayment() {
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.fundAccount.validate()
|
||||
|
||||
if (!isValid) return;
|
||||
if (!isValid) return
|
||||
|
||||
// Show payment methods popup
|
||||
this.$store.dispatch('callSingleChargeProcess', this.chargeAmount)
|
||||
},
|
||||
}
|
||||
// Show payment methods popup
|
||||
this.$store.dispatch('callSingleChargeProcess', this.chargeAmount)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,191 +1,195 @@
|
||||
<template>
|
||||
<div class="card shadow-card">
|
||||
<FormLabel icon="bell">
|
||||
{{ $t('Billing Alert') }}
|
||||
</FormLabel>
|
||||
<div class="card shadow-card">
|
||||
<FormLabel icon="bell">
|
||||
{{ $t('Billing Alert') }}
|
||||
</FormLabel>
|
||||
|
||||
<div v-if="user.data.relationships.alert">
|
||||
<b class="text-3xl font-extrabold -mt-3 block mb-0.5 flex items-center">
|
||||
{{ user.data.relationships.alert.data.attributes.formatted }}
|
||||
<edit2-icon v-if="! showUpdateBillingAlertForm" @click="showUpdateBillingAlertForm = ! showUpdateBillingAlertForm" size="12" class="vue-feather cursor-pointer ml-2 transform -translate-y-0.5" />
|
||||
<trash2-icon v-if="showUpdateBillingAlertForm" @click="deleteBillingAlert" size="12" class="vue-feather cursor-pointer ml-2 transform -translate-y-0.5" />
|
||||
</b>
|
||||
<div v-if="user.data.relationships.alert">
|
||||
<b class="-mt-3 mb-0.5 block flex items-center text-3xl font-extrabold">
|
||||
{{ user.data.relationships.alert.data.attributes.formatted }}
|
||||
<edit2-icon
|
||||
v-if="!showUpdateBillingAlertForm"
|
||||
@click="showUpdateBillingAlertForm = !showUpdateBillingAlertForm"
|
||||
size="12"
|
||||
class="vue-feather ml-2 -translate-y-0.5 transform cursor-pointer"
|
||||
/>
|
||||
<trash2-icon v-if="showUpdateBillingAlertForm" @click="deleteBillingAlert" size="12" class="vue-feather ml-2 -translate-y-0.5 transform cursor-pointer" />
|
||||
</b>
|
||||
|
||||
<b class="block text-sm text-gray-400">
|
||||
{{ $t('Alert will be triggered after you reach the value above.') }}
|
||||
</b>
|
||||
</div>
|
||||
<b class="block text-sm text-gray-400">
|
||||
{{ $t('Alert will be triggered after you reach the value above.') }}
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<ValidationObserver v-if="showUpdateBillingAlertForm" ref="updatebillingAlertForm" @submit.prevent="updateBillingAlert" v-slot="{ invalid }" tag="form" class="mt-6">
|
||||
<ValidationProvider tag="div" v-slot="{ errors }" mode="passive" name="Billing Alert" rules="required">
|
||||
<AppInputText :description="$t('You will receive an email whenever your monthly balance reaches the specified amount above.')" :error="errors[0]" :is-last="true">
|
||||
<div class="sm:flex sm:space-x-4 sm:space-y-0 space-y-4">
|
||||
<input v-model="billingAlertAmount"
|
||||
:placeholder="$t('Alert Amount...')"
|
||||
type="number"
|
||||
min="1"
|
||||
max="999999999"
|
||||
class="focus-border-theme input-dark"
|
||||
:class="{'border-red': errors[0]}"
|
||||
/>
|
||||
<ButtonBase :loadint="isSendingBillingAlert" :disabled="isSendingBillingAlert" type="submit" button-style="theme" class="sm:w-auto w-full">
|
||||
{{ $t('Update Alert') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</ValidationObserver>
|
||||
<ValidationObserver v-if="showUpdateBillingAlertForm" ref="updatebillingAlertForm" @submit.prevent="updateBillingAlert" v-slot="{ invalid }" tag="form" class="mt-6">
|
||||
<ValidationProvider tag="div" v-slot="{ errors }" mode="passive" name="Billing Alert" rules="required">
|
||||
<AppInputText :description="$t('You will receive an email whenever your monthly balance reaches the specified amount above.')" :error="errors[0]" :is-last="true">
|
||||
<div class="space-y-4 sm:flex sm:space-x-4 sm:space-y-0">
|
||||
<input
|
||||
v-model="billingAlertAmount"
|
||||
:placeholder="$t('Alert Amount...')"
|
||||
type="number"
|
||||
min="1"
|
||||
max="999999999"
|
||||
class="focus-border-theme input-dark"
|
||||
:class="{ 'border-red': errors[0] }"
|
||||
/>
|
||||
<ButtonBase :loadint="isSendingBillingAlert" :disabled="isSendingBillingAlert" type="submit" button-style="theme" class="w-full sm:w-auto">
|
||||
{{ $t('Update Alert') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</ValidationObserver>
|
||||
|
||||
<ValidationObserver v-if="! user.data.relationships.alert" ref="billingAlertForm" @submit.prevent="setBillingAlert" v-slot="{ invalid }" tag="form" class="mt-6">
|
||||
<ValidationProvider tag="div" v-slot="{ errors }" mode="passive" name="Billing Alert" rules="required">
|
||||
<AppInputText :description="$t('You will receive an email whenever your monthly balance reaches the specified amount above.')" :error="errors[0]" :is-last="true">
|
||||
<div class="sm:flex sm:space-x-4 sm:space-y-0 space-y-4">
|
||||
<input v-model="billingAlertAmount"
|
||||
:placeholder="$t('Alert Amount...')"
|
||||
type="number"
|
||||
min="1"
|
||||
max="999999999"
|
||||
class="focus-border-theme input-dark"
|
||||
:class="{'border-red': errors[0]}"
|
||||
/>
|
||||
<ButtonBase :loadint="isSendingBillingAlert" :disabled="isSendingBillingAlert" type="submit" button-style="theme" class="sm:w-auto w-full">
|
||||
{{ $t('Set Alert') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</ValidationObserver>
|
||||
</div>
|
||||
<ValidationObserver v-if="!user.data.relationships.alert" ref="billingAlertForm" @submit.prevent="setBillingAlert" v-slot="{ invalid }" tag="form" class="mt-6">
|
||||
<ValidationProvider tag="div" v-slot="{ errors }" mode="passive" name="Billing Alert" rules="required">
|
||||
<AppInputText :description="$t('You will receive an email whenever your monthly balance reaches the specified amount above.')" :error="errors[0]" :is-last="true">
|
||||
<div class="space-y-4 sm:flex sm:space-x-4 sm:space-y-0">
|
||||
<input
|
||||
v-model="billingAlertAmount"
|
||||
:placeholder="$t('Alert Amount...')"
|
||||
type="number"
|
||||
min="1"
|
||||
max="999999999"
|
||||
class="focus-border-theme input-dark"
|
||||
:class="{ 'border-red': errors[0] }"
|
||||
/>
|
||||
<ButtonBase :loadint="isSendingBillingAlert" :disabled="isSendingBillingAlert" type="submit" button-style="theme" class="w-full sm:w-auto">
|
||||
{{ $t('Set Alert') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</AppInputText>
|
||||
</ValidationProvider>
|
||||
</ValidationObserver>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {ValidationObserver, ValidationProvider} from 'vee-validate/dist/vee-validate.full'
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import AppInputText from "../Admin/AppInputText"
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import { ValidationObserver, ValidationProvider } from 'vee-validate/dist/vee-validate.full'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import AppInputText from '../Admin/AppInputText'
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import { Edit2Icon, Trash2Icon } from 'vue-feather-icons'
|
||||
import {events} from "../../bus";
|
||||
import {mapGetters} from "vuex";
|
||||
import axios from "axios";
|
||||
import { events } from '../../bus'
|
||||
import { mapGetters } from 'vuex'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'UserBillingAlerts',
|
||||
components: {
|
||||
ValidationObserver,
|
||||
ValidationProvider,
|
||||
AppInputText,
|
||||
ButtonBase,
|
||||
Trash2Icon,
|
||||
Edit2Icon,
|
||||
FormLabel,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
]),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
billingAlertAmount: undefined,
|
||||
isSendingBillingAlert: false,
|
||||
showUpdateBillingAlertForm: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async updateBillingAlert() {
|
||||
name: 'UserBillingAlerts',
|
||||
components: {
|
||||
ValidationObserver,
|
||||
ValidationProvider,
|
||||
AppInputText,
|
||||
ButtonBase,
|
||||
Trash2Icon,
|
||||
Edit2Icon,
|
||||
FormLabel,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
billingAlertAmount: undefined,
|
||||
isSendingBillingAlert: false,
|
||||
showUpdateBillingAlertForm: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async updateBillingAlert() {
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.updatebillingAlertForm.validate()
|
||||
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.updatebillingAlertForm.validate();
|
||||
if (!isValid) return
|
||||
|
||||
if (!isValid) return;
|
||||
this.isSendingBillingAlert = true
|
||||
|
||||
this.isSendingBillingAlert = true
|
||||
axios
|
||||
.patch(`/api/subscriptions/billing-alerts/${this.user.data.relationships.alert.data.id}`, {
|
||||
amount: this.billingAlertAmount,
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.dispatch('getAppData')
|
||||
|
||||
axios
|
||||
.patch(`/api/subscriptions/billing-alerts/${this.user.data.relationships.alert.data.id}`, {
|
||||
amount: this.billingAlertAmount
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.dispatch('getAppData')
|
||||
this.showUpdateBillingAlertForm = false
|
||||
|
||||
this.showUpdateBillingAlertForm = false
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your billing alert was updated successfully'),
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.isSendingBillingAlert = false
|
||||
})
|
||||
},
|
||||
async setBillingAlert() {
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.billingAlertForm.validate()
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your billing alert was updated successfully'),
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.isSendingBillingAlert = false
|
||||
})
|
||||
},
|
||||
async setBillingAlert() {
|
||||
if (!isValid) return
|
||||
|
||||
// Validate fields
|
||||
const isValid = await this.$refs.billingAlertForm.validate();
|
||||
this.isSendingBillingAlert = true
|
||||
|
||||
if (!isValid) return;
|
||||
axios
|
||||
.post('/api/subscriptions/billing-alerts', {
|
||||
amount: this.billingAlertAmount,
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.dispatch('getAppData')
|
||||
|
||||
this.isSendingBillingAlert = true
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your billing alert was set successfully'),
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.isSendingBillingAlert = false
|
||||
})
|
||||
},
|
||||
deleteBillingAlert() {
|
||||
events.$emit('confirm:open', {
|
||||
title: this.$t('Are you sure you want to delete your alert?'),
|
||||
message: this.$t('You will no longer receive any notifications that your billing limit has been exceeded.'),
|
||||
action: {
|
||||
id: this.user.data.relationships.alert.data.id,
|
||||
operation: 'delete-billing-alert',
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('action:confirmed', (data) => {
|
||||
if (data.operation === 'delete-billing-alert')
|
||||
axios
|
||||
.delete(`/api/subscriptions/billing-alerts/${this.user.data.relationships.alert.data.id}`)
|
||||
.then(() => {
|
||||
this.$store.dispatch('getAppData')
|
||||
|
||||
axios
|
||||
.post('/api/subscriptions/billing-alerts', {
|
||||
amount: this.billingAlertAmount
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.dispatch('getAppData')
|
||||
this.showUpdateBillingAlertForm = false
|
||||
this.billingAlertAmount = undefined
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your billing alert was set successfully'),
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.isSendingBillingAlert = false
|
||||
})
|
||||
},
|
||||
deleteBillingAlert() {
|
||||
events.$emit('confirm:open', {
|
||||
title: this.$t('Are you sure you want to delete your alert?'),
|
||||
message: this.$t('You will no longer receive any notifications that your billing limit has been exceeded.'),
|
||||
action: {
|
||||
id: this.user.data.relationships.alert.data.id,
|
||||
operation: 'delete-billing-alert',
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('action:confirmed', data => {
|
||||
if (data.operation === 'delete-billing-alert')
|
||||
axios.delete(`/api/subscriptions/billing-alerts/${this.user.data.relationships.alert.data.id}`)
|
||||
.then(() => {
|
||||
this.$store.dispatch('getAppData')
|
||||
|
||||
this.showUpdateBillingAlertForm = false
|
||||
this.billingAlertAmount = undefined
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your billing alert was deleted.'),
|
||||
})
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
events.$off('action:confirmed')
|
||||
},
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your billing alert was deleted.'),
|
||||
})
|
||||
})
|
||||
.catch(() => this.$isSomethingWrong())
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
events.$off('action:confirmed')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,101 +1,103 @@
|
||||
<template>
|
||||
<div v-if="hasSubscription" class="card shadow-card">
|
||||
<FormLabel>
|
||||
{{ $t('Edit your Subscription') }}
|
||||
</FormLabel>
|
||||
<div v-if="hasSubscription" class="card shadow-card">
|
||||
<FormLabel>
|
||||
{{ $t('Edit your Subscription') }}
|
||||
</FormLabel>
|
||||
|
||||
<AppInputButton v-if="subscription.attributes.status !== 'cancelled'" :title="$t('Cancel Subscription')" :description="$t('You can cancel your subscription now. You\'ll continue to have access to the features you\'ve paid for until the end of your billing cycle.')">
|
||||
<ButtonBase @click.native="cancelSubscriptionConfirmation" :loading="isCancelling" class="sm:w-auto w-full" button-style="secondary">
|
||||
{{ $t('Cancel Now') }}
|
||||
</ButtonBase>
|
||||
</AppInputButton>
|
||||
<AppInputButton
|
||||
v-if="subscription.attributes.status !== 'cancelled'"
|
||||
:title="$t('Cancel Subscription')"
|
||||
:description="$t('You can cancel your subscription now. You\'ll continue to have access to the features you\'ve paid for until the end of your billing cycle.')"
|
||||
>
|
||||
<ButtonBase @click.native="cancelSubscriptionConfirmation" :loading="isCancelling" class="w-full sm:w-auto" button-style="secondary">
|
||||
{{ $t('Cancel Now') }}
|
||||
</ButtonBase>
|
||||
</AppInputButton>
|
||||
|
||||
<AppInputButton :title="$t('Upgrade or Downgrade Plan')" :description="$t('You can upgrade your plan at any time you want.')" :is-last="true">
|
||||
<ButtonBase @click.native="$openUpgradeOptions" class="sm:w-auto w-full" button-style="secondary">
|
||||
{{ $t('Change Plan') }}
|
||||
</ButtonBase>
|
||||
</AppInputButton>
|
||||
</div>
|
||||
<AppInputButton :title="$t('Upgrade or Downgrade Plan')" :description="$t('You can upgrade your plan at any time you want.')" :is-last="true">
|
||||
<ButtonBase @click.native="$openUpgradeOptions" class="w-full sm:w-auto" button-style="secondary">
|
||||
{{ $t('Change Plan') }}
|
||||
</ButtonBase>
|
||||
</AppInputButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppInputButton from "../Admin/AppInputButton";
|
||||
import AppInputText from "../Admin/AppInputText";
|
||||
import AppInputSwitch from "../Admin/AppInputSwitch"
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import {events} from "../../bus";
|
||||
import axios from "axios";
|
||||
import AppInputButton from '../Admin/AppInputButton'
|
||||
import AppInputText from '../Admin/AppInputText'
|
||||
import AppInputSwitch from '../Admin/AppInputSwitch'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import { events } from '../../bus'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'UserEditSubscription',
|
||||
components: {
|
||||
AppInputButton,
|
||||
AppInputSwitch,
|
||||
AppInputText,
|
||||
ButtonBase,
|
||||
FormLabel
|
||||
},
|
||||
computed: {
|
||||
subscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription.data
|
||||
},
|
||||
hasSubscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isCancelling: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancelSubscriptionConfirmation() {
|
||||
events.$emit('confirm:open', {
|
||||
title: this.$t('Are you sure you want to cancel subscription?'),
|
||||
message: this.$t("You'll continue to have access to the features you've paid for until the end of your billing cycle."),
|
||||
action: {
|
||||
operation: 'cancel-subscription',
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('action:confirmed', data => {
|
||||
if (data.operation === 'cancel-subscription') {
|
||||
export default {
|
||||
name: 'UserEditSubscription',
|
||||
components: {
|
||||
AppInputButton,
|
||||
AppInputSwitch,
|
||||
AppInputText,
|
||||
ButtonBase,
|
||||
FormLabel,
|
||||
},
|
||||
computed: {
|
||||
subscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription.data
|
||||
},
|
||||
hasSubscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isCancelling: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancelSubscriptionConfirmation() {
|
||||
events.$emit('confirm:open', {
|
||||
title: this.$t('Are you sure you want to cancel subscription?'),
|
||||
message: this.$t("You'll continue to have access to the features you've paid for until the end of your billing cycle."),
|
||||
action: {
|
||||
operation: 'cancel-subscription',
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
events.$on('action:confirmed', (data) => {
|
||||
if (data.operation === 'cancel-subscription') {
|
||||
// Start deleting spinner button
|
||||
this.isCancelling = true
|
||||
|
||||
// Start deleting spinner button
|
||||
this.isCancelling = true
|
||||
// Send post request
|
||||
axios
|
||||
.post('/api/subscriptions/cancel')
|
||||
.then(() => {
|
||||
// Update user data
|
||||
this.$store.dispatch('getAppData').then(() => {
|
||||
this.fetchSubscriptionDetail()
|
||||
})
|
||||
|
||||
// Send post request
|
||||
axios
|
||||
.post('/api/subscriptions/cancel')
|
||||
.then(() => {
|
||||
|
||||
// Update user data
|
||||
this.$store.dispatch('getAppData').then(() => {
|
||||
this.fetchSubscriptionDetail()
|
||||
})
|
||||
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('popup_subscription_cancel.title'),
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.isCancelling = false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
events.$off('action:confirmed')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('popup_subscription_cancel.title'),
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.isCancelling = false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
events.$off('action:confirmed')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
<template>
|
||||
<div v-if="! hasSubscription" class="card shadow-card">
|
||||
<FormLabel>
|
||||
{{ $t('Subscription') }}
|
||||
</FormLabel>
|
||||
<div v-if="!hasSubscription" class="card shadow-card">
|
||||
<FormLabel>
|
||||
{{ $t('Subscription') }}
|
||||
</FormLabel>
|
||||
|
||||
<b class="sm:text-3xl text-2xl font-extrabold -mt-3 block mb-0.5">
|
||||
{{ $t('Free Plan') }}
|
||||
</b>
|
||||
<b class="-mt-3 mb-0.5 block text-2xl font-extrabold sm:text-3xl">
|
||||
{{ $t('Free Plan') }}
|
||||
</b>
|
||||
|
||||
<b class="mb-3 block text-sm text-gray-400 mb-8">
|
||||
{{ $t('1GB Free storage space with 5 Team members') }}
|
||||
</b>
|
||||
<b class="mb-3 mb-8 block text-sm text-gray-400">
|
||||
{{ $t('1GB Free storage space with 5 Team members') }}
|
||||
</b>
|
||||
|
||||
<ButtonBase @click.native="$openUpgradeOptions" type="submit" button-style="theme" class="w-full mt-4">
|
||||
{{ $t('Upgrade Your Account') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
<ButtonBase @click.native="$openUpgradeOptions" type="submit" button-style="theme" class="mt-4 w-full">
|
||||
{{ $t('Upgrade Your Account') }}
|
||||
</ButtonBase>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import InfoBox from "../Others/Forms/InfoBox";
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import ButtonBase from "../FilesView/ButtonBase"
|
||||
import InfoBox from '../Others/Forms/InfoBox'
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
|
||||
export default {
|
||||
name: 'UserEmptySubscription',
|
||||
components: {
|
||||
ButtonBase,
|
||||
FormLabel,
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
hasSubscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
export default {
|
||||
name: 'UserEmptySubscription',
|
||||
components: {
|
||||
ButtonBase,
|
||||
FormLabel,
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
hasSubscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,62 +1,58 @@
|
||||
<template>
|
||||
<div v-if="user.data.relationships.failedPayments && user.data.relationships.failedPayments.data.length > 0" class="card shadow-card">
|
||||
<FormLabel icon="frown">
|
||||
{{ $t('Failed Payments') }}
|
||||
</FormLabel>
|
||||
<div v-if="user.data.relationships.failedPayments && user.data.relationships.failedPayments.data.length > 0" class="card shadow-card">
|
||||
<FormLabel icon="frown">
|
||||
{{ $t('Failed Payments') }}
|
||||
</FormLabel>
|
||||
|
||||
<b class="sm:text-3xl text-2xl font-extrabold -mt-3 block mb-0.5">
|
||||
-{{ user.data.meta.totalDebt.formatted }}
|
||||
</b>
|
||||
<b class="-mt-3 mb-0.5 block text-2xl font-extrabold sm:text-3xl"> -{{ user.data.meta.totalDebt.formatted }} </b>
|
||||
|
||||
<b class="mb-3 block text-sm text-gray-400 mb-5">
|
||||
{{ $t("We are unable to charge your usage. Please register new credit card or fund your account with sufficient amount and we'll give it another try!") }}
|
||||
</b>
|
||||
<b class="mb-3 mb-5 block text-sm text-gray-400">
|
||||
{{ $t("We are unable to charge your usage. Please register new credit card or fund your account with sufficient amount and we'll give it another try!") }}
|
||||
</b>
|
||||
|
||||
<!--Failed Payments-->
|
||||
<div
|
||||
v-for="payment in user.data.relationships.failedPayments.data"
|
||||
:key="payment.data.id"
|
||||
class="flex items-center justify-between py-2 border-b dark:border-opacity-5 border-light border-dashed"
|
||||
>
|
||||
<div class="w-2/4 leading-none">
|
||||
<b class="text-sm font-bold leading-none">
|
||||
{{ payment.data.attributes.note }}
|
||||
</b>
|
||||
</div>
|
||||
<div class="text-left w-1/4">
|
||||
<span class="text-sm font-bold text-gray-560 capitalize">
|
||||
{{ $t(payment.data.attributes.source) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-right w-1/4">
|
||||
<span class="text-sm font-bold">
|
||||
{{ payment.data.attributes.created_at }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-right w-1/4">
|
||||
<span class="text-sm font-bold text-red">
|
||||
{{ payment.data.attributes.amount }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Failed Payments-->
|
||||
<div
|
||||
v-for="payment in user.data.relationships.failedPayments.data"
|
||||
:key="payment.data.id"
|
||||
class="flex items-center justify-between border-b border-dashed border-light py-2 dark:border-opacity-5"
|
||||
>
|
||||
<div class="w-2/4 leading-none">
|
||||
<b class="text-sm font-bold leading-none">
|
||||
{{ payment.data.attributes.note }}
|
||||
</b>
|
||||
</div>
|
||||
<div class="w-1/4 text-left">
|
||||
<span class="text-gray-560 text-sm font-bold capitalize">
|
||||
{{ $t(payment.data.attributes.source) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="w-1/4 text-right">
|
||||
<span class="text-sm font-bold">
|
||||
{{ payment.data.attributes.created_at }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="w-1/4 text-right">
|
||||
<span class="text-red text-sm font-bold">
|
||||
{{ payment.data.attributes.amount }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import InfoBox from "../Others/Forms/InfoBox";
|
||||
import {mapGetters} from "vuex";
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import InfoBox from '../Others/Forms/InfoBox'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'UserFailedPayments',
|
||||
components: {
|
||||
FormLabel,
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
])
|
||||
},
|
||||
name: 'UserFailedPayments',
|
||||
components: {
|
||||
FormLabel,
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,94 +1,90 @@
|
||||
<template>
|
||||
<div v-if="hasSubscription" class="card shadow-card">
|
||||
<FormLabel>
|
||||
{{ $t('Subscription') }}
|
||||
</FormLabel>
|
||||
<div v-if="hasSubscription" class="card shadow-card">
|
||||
<FormLabel>
|
||||
{{ $t('Subscription') }}
|
||||
</FormLabel>
|
||||
|
||||
<b class="sm:text-3xl text-xl font-extrabold -mt-3 block mb-0.5">
|
||||
{{ status }}
|
||||
</b>
|
||||
<b class="-mt-3 mb-0.5 block text-xl font-extrabold sm:text-3xl">
|
||||
{{ status }}
|
||||
</b>
|
||||
|
||||
<b class="mb-3 block text-sm text-gray-400 mb-8">
|
||||
{{ subscription.data.relationships.plan.data.attributes.name }} / {{ price }}
|
||||
</b>
|
||||
<b class="mb-3 mb-8 block text-sm text-gray-400">
|
||||
{{ subscription.data.relationships.plan.data.attributes.name }} /
|
||||
{{ price }}
|
||||
</b>
|
||||
|
||||
<div v-for="(limit, i) in limitations" :key="i" :class="{'mb-6': (Object.keys(limitations).length - 1) !== i}">
|
||||
<b class="mb-3 block text-sm text-gray-400">
|
||||
{{ limit.message }}
|
||||
</b>
|
||||
<ProgressLine :data="limit.distribution" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(limit, i) in limitations" :key="i" :class="{ 'mb-6': Object.keys(limitations).length - 1 !== i }">
|
||||
<b class="mb-3 block text-sm text-gray-400">
|
||||
{{ limit.message }}
|
||||
</b>
|
||||
<ProgressLine :data="limit.distribution" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import ProgressLine from "../Admin/ProgressLine"
|
||||
import {mapGetters} from "vuex";
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import ProgressLine from '../Admin/ProgressLine'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'UserFixedSubscriptionDetail',
|
||||
components: {
|
||||
ProgressLine,
|
||||
FormLabel,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
]),
|
||||
subscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
hasSubscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
limitations() {
|
||||
let limitations = []
|
||||
export default {
|
||||
name: 'UserFixedSubscriptionDetail',
|
||||
components: {
|
||||
ProgressLine,
|
||||
FormLabel,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
subscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
hasSubscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
limitations() {
|
||||
let limitations = []
|
||||
|
||||
Object
|
||||
.entries(this.user.data.meta.limitations)
|
||||
.map(([key, item]) => {
|
||||
Object.entries(this.user.data.meta.limitations).map(([key, item]) => {
|
||||
let payload = {
|
||||
color: {
|
||||
max_storage_amount: 'warning',
|
||||
max_team_members: 'purple',
|
||||
},
|
||||
message: {
|
||||
max_storage_amount: `Total ${item.use} of ${item.total} Used`,
|
||||
max_team_members: `Total ${item.use} of ${item.total} Members`,
|
||||
},
|
||||
title: {
|
||||
max_storage_amount: `Storage`,
|
||||
max_team_members: `Team Members`,
|
||||
},
|
||||
}
|
||||
|
||||
let payload = {
|
||||
color: {
|
||||
'max_storage_amount': 'warning',
|
||||
'max_team_members': 'purple',
|
||||
},
|
||||
message: {
|
||||
'max_storage_amount': `Total ${item.use} of ${item.total} Used`,
|
||||
'max_team_members': `Total ${item.use} of ${item.total} Members`,
|
||||
},
|
||||
title: {
|
||||
'max_storage_amount': `Storage`,
|
||||
'max_team_members': `Team Members`,
|
||||
}
|
||||
}
|
||||
limitations.push({
|
||||
message: payload.message[key],
|
||||
distribution: [
|
||||
{
|
||||
progress: item.percentage,
|
||||
color: payload.color[key],
|
||||
title: payload.title[key],
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
limitations.push({
|
||||
message: payload.message[key],
|
||||
distribution: [
|
||||
{
|
||||
progress: item.percentage,
|
||||
color: payload.color[key],
|
||||
title: payload.title[key],
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
return limitations
|
||||
},
|
||||
status() {
|
||||
return {
|
||||
'active': `Active until ${this.subscription.data.attributes.renews_at}`,
|
||||
'cancelled': `Ends at ${this.subscription.data.attributes.ends_at}`,
|
||||
}[this.subscription.data.attributes.status]
|
||||
},
|
||||
price() {
|
||||
return {
|
||||
'month': `${this.subscription.data.relationships.plan.data.attributes.price} Per Month`,
|
||||
'year': `${this.subscription.data.relationships.plan.data.attributes.price} Per Year`,
|
||||
}[this.subscription.data.relationships.plan.data.attributes.interval]
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
return limitations
|
||||
},
|
||||
status() {
|
||||
return {
|
||||
active: `Active until ${this.subscription.data.attributes.renews_at}`,
|
||||
cancelled: `Ends at ${this.subscription.data.attributes.ends_at}`,
|
||||
}[this.subscription.data.attributes.status]
|
||||
},
|
||||
price() {
|
||||
return {
|
||||
month: `${this.subscription.data.relationships.plan.data.attributes.price} Per Month`,
|
||||
year: `${this.subscription.data.relationships.plan.data.attributes.price} Per Year`,
|
||||
}[this.subscription.data.relationships.plan.data.attributes.interval]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,186 +1,190 @@
|
||||
<template>
|
||||
<div v-if="canShowForMeteredBilling || canShowForFixedBilling" class="card shadow-card">
|
||||
<FormLabel icon="credit-card">
|
||||
{{ $t('Payment Method') }}
|
||||
</FormLabel>
|
||||
<div v-if="canShowForMeteredBilling || canShowForFixedBilling" class="card shadow-card">
|
||||
<FormLabel icon="credit-card">
|
||||
{{ $t('Payment Method') }}
|
||||
</FormLabel>
|
||||
|
||||
<!-- User has registered payment method -->
|
||||
<div v-if="hasPaymentMethod">
|
||||
<b v-if="config.subscriptionType === 'metered' && user.data.relationships.balance.data.attributes.balance > 0" class="mb-3 block text-sm mb-5">
|
||||
{{ $t('credit_to_auto_withdraw', {credit: user.data.relationships.balance.data.attributes.formatted}) }}
|
||||
</b>
|
||||
<!-- User has registered payment method -->
|
||||
<div v-if="hasPaymentMethod">
|
||||
<b v-if="config.subscriptionType === 'metered' && user.data.relationships.balance.data.attributes.balance > 0" class="mb-3 mb-5 block text-sm">
|
||||
{{
|
||||
$t('credit_to_auto_withdraw', {
|
||||
credit: user.data.relationships.balance.data.attributes.formatted,
|
||||
})
|
||||
}}
|
||||
</b>
|
||||
|
||||
<!-- Card -->
|
||||
<PaymentCard
|
||||
v-for="card in user.data.relationships.creditCards.data"
|
||||
:key="card.data.id"
|
||||
:card="card"
|
||||
/>
|
||||
<!-- Card -->
|
||||
<PaymentCard v-for="card in user.data.relationships.creditCards.data" :key="card.data.id" :card="card" />
|
||||
|
||||
<small class="text-xs text-gray-500 pt-3 leading-none sm:block hidden">
|
||||
{{ $t('We are settling your payment automatically via your saved credit card.') }}
|
||||
</small>
|
||||
</div>
|
||||
<small class="hidden pt-3 text-xs leading-none text-gray-500 sm:block">
|
||||
{{ $t('We are settling your payment automatically via your saved credit card.') }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- User doesn't have registered payment method -->
|
||||
<div v-if="! hasPaymentMethod">
|
||||
<!-- User doesn't have registered payment method -->
|
||||
<div v-if="!hasPaymentMethod">
|
||||
<!-- Show credit card form -->
|
||||
<ButtonBase
|
||||
@click.native="showStoreCreditCardForm"
|
||||
v-if="!isCreditCardForm"
|
||||
:loading="stripe.storingStripePaymentMethod"
|
||||
type="submit"
|
||||
button-style="theme"
|
||||
class="mt-4 w-full"
|
||||
>
|
||||
{{ $t('Add Payment Method') }}
|
||||
</ButtonBase>
|
||||
|
||||
<!-- Show credit card form -->
|
||||
<ButtonBase @click.native="showStoreCreditCardForm" v-if="! isCreditCardForm" :loading="stripe.storingStripePaymentMethod" type="submit" button-style="theme" class="w-full mt-4">
|
||||
{{ $t('Add Payment Method') }}
|
||||
</ButtonBase>
|
||||
|
||||
<!-- Store credit card form -->
|
||||
<form v-if="isCreditCardForm" @submit.prevent="storeStripePaymentMethod" id="payment-form" class="mt-6">
|
||||
<div v-if="stripe.isInitialization" class="h-10 relative mb-6">
|
||||
<Spinner />
|
||||
</div>
|
||||
<div id="payment-element">
|
||||
<!-- Elements will create form elements here -->
|
||||
</div>
|
||||
<ButtonBase :loading="stripe.storingStripePaymentMethod" type="submit" button-style="theme" class="w-full mt-4">
|
||||
{{ $t('Store My Credit Card') }}
|
||||
</ButtonBase>
|
||||
<div id="error-message">
|
||||
<!-- Display error message to your customers here -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Store credit card form -->
|
||||
<form v-if="isCreditCardForm" @submit.prevent="storeStripePaymentMethod" id="payment-form" class="mt-6">
|
||||
<div v-if="stripe.isInitialization" class="relative mb-6 h-10">
|
||||
<Spinner />
|
||||
</div>
|
||||
<div id="payment-element">
|
||||
<!-- Elements will create form elements here -->
|
||||
</div>
|
||||
<ButtonBase :loading="stripe.storingStripePaymentMethod" type="submit" button-style="theme" class="mt-4 w-full">
|
||||
{{ $t('Store My Credit Card') }}
|
||||
</ButtonBase>
|
||||
<div id="error-message">
|
||||
<!-- Display error message to your customers here -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import PaymentCard from "./PaymentCard"
|
||||
import Spinner from "../FilesView/Spinner"
|
||||
import {mapGetters} from "vuex";
|
||||
import {events} from "../../bus";
|
||||
import {loadStripe} from "@stripe/stripe-js";
|
||||
import axios from "axios";
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import PaymentCard from './PaymentCard'
|
||||
import Spinner from '../FilesView/Spinner'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { events } from '../../bus'
|
||||
import { loadStripe } from '@stripe/stripe-js'
|
||||
import axios from 'axios'
|
||||
|
||||
// Define stripe variables
|
||||
let stripe, elements = undefined
|
||||
// Define stripe variables
|
||||
let stripe,
|
||||
elements = undefined
|
||||
|
||||
export default {
|
||||
name: 'UserStoredPaymentMethods',
|
||||
components: {
|
||||
ButtonBase,
|
||||
FormLabel,
|
||||
PaymentCard,
|
||||
Spinner
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'isDarkMode',
|
||||
'config',
|
||||
'user',
|
||||
]),
|
||||
canShowForMeteredBilling() {
|
||||
return this.config.isStripe && this.config.subscriptionType === 'metered'
|
||||
},
|
||||
canShowForFixedBilling() {
|
||||
return this.config.isStripe
|
||||
&& this.config.subscriptionType === 'fixed'
|
||||
&& this.$store.getters.user.data.relationships.subscription
|
||||
&& this.$store.getters.user.data.relationships.subscription.data.attributes.driver === 'stripe'
|
||||
},
|
||||
hasPaymentMethod() {
|
||||
return this.user.data.relationships.creditCards && this.user.data.relationships.creditCards.data.length > 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
isCreditCardForm: false,
|
||||
stripe: {
|
||||
isInitialization: true,
|
||||
storingStripePaymentMethod: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async storeStripePaymentMethod() {
|
||||
export default {
|
||||
name: 'UserStoredPaymentMethods',
|
||||
components: {
|
||||
ButtonBase,
|
||||
FormLabel,
|
||||
PaymentCard,
|
||||
Spinner,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isDarkMode', 'config', 'user']),
|
||||
canShowForMeteredBilling() {
|
||||
return this.config.isStripe && this.config.subscriptionType === 'metered'
|
||||
},
|
||||
canShowForFixedBilling() {
|
||||
return (
|
||||
this.config.isStripe &&
|
||||
this.config.subscriptionType === 'fixed' &&
|
||||
this.$store.getters.user.data.relationships.subscription &&
|
||||
this.$store.getters.user.data.relationships.subscription.data.attributes.driver === 'stripe'
|
||||
)
|
||||
},
|
||||
hasPaymentMethod() {
|
||||
return this.user.data.relationships.creditCards && this.user.data.relationships.creditCards.data.length > 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
isCreditCardForm: false,
|
||||
stripe: {
|
||||
isInitialization: true,
|
||||
storingStripePaymentMethod: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async storeStripePaymentMethod() {
|
||||
this.stripe.storingStripePaymentMethod = true
|
||||
|
||||
this.stripe.storingStripePaymentMethod = true
|
||||
const { error } = await stripe.confirmSetup({
|
||||
//`Elements` instance that was used to create the Payment Element
|
||||
elements,
|
||||
redirect: 'if_required',
|
||||
confirmParams: {
|
||||
return_url: window.location.href,
|
||||
},
|
||||
})
|
||||
|
||||
const {error} = await stripe.confirmSetup({
|
||||
//`Elements` instance that was used to create the Payment Element
|
||||
elements,
|
||||
redirect: 'if_required',
|
||||
confirmParams: {
|
||||
return_url: window.location.href,
|
||||
}
|
||||
});
|
||||
if (error) {
|
||||
// This point will only be reached if there is an immediate error when
|
||||
// confirming the payment. Show error to your customer (e.g., payment
|
||||
// details incomplete)
|
||||
const messageContainer = document.querySelector('#error-message')
|
||||
messageContainer.textContent = error.message
|
||||
} else {
|
||||
// Your customer will be redirected to your `return_url`. For some payment
|
||||
// methods like iDEAL, your customer will be redirected to an intermediate
|
||||
// site first to authorize the payment, then redirected to the `return_url`.
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your credit card was stored successfully'),
|
||||
})
|
||||
|
||||
if (error) {
|
||||
// This point will only be reached if there is an immediate error when
|
||||
// confirming the payment. Show error to your customer (e.g., payment
|
||||
// details incomplete)
|
||||
const messageContainer = document.querySelector('#error-message');
|
||||
messageContainer.textContent = error.message;
|
||||
} else {
|
||||
// Your customer will be redirected to your `return_url`. For some payment
|
||||
// methods like iDEAL, your customer will be redirected to an intermediate
|
||||
// site first to authorize the payment, then redirected to the `return_url`.
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
message: this.$t('Your credit card was stored successfully'),
|
||||
})
|
||||
// TODO: L9 - load credit card after was stored in database
|
||||
}
|
||||
|
||||
// TODO: L9 - load credit card after was stored in database
|
||||
}
|
||||
this.stripe.storingStripePaymentMethod = false
|
||||
},
|
||||
async stripeInit() {
|
||||
// Init stripe js
|
||||
stripe = await loadStripe(this.config.stripe_public_key)
|
||||
|
||||
this.stripe.storingStripePaymentMethod = false
|
||||
},
|
||||
async stripeInit() {
|
||||
await axios
|
||||
.get('/api/stripe/setup-intent')
|
||||
.then((response) => {
|
||||
// Set up Stripe.js and Elements to use in checkout form, passing the client secret obtained in step 2
|
||||
elements = stripe.elements({
|
||||
clientSecret: response.data.client_secret,
|
||||
appearance: {
|
||||
theme: 'stripe',
|
||||
variables: {
|
||||
colorPrimary: this.config.app_color,
|
||||
fontFamily: 'Nunito',
|
||||
borderRadius: '8px',
|
||||
colorText: this.isDarkMode ? '#bec6cf' : '#1B2539',
|
||||
colorBackground: this.isDarkMode ? '#191b1e' : '#fff',
|
||||
fontWeightNormal: '700',
|
||||
fontSizeSm: '0.875rem',
|
||||
colorSuccessText: '#0ABB87',
|
||||
colorSuccess: '#0ABB87',
|
||||
colorWarning: '#fd397a',
|
||||
colorWarningText: '#fd397a',
|
||||
colorDangerText: '#fd397a',
|
||||
colorTextSecondary: '#6b7280',
|
||||
spacingGridRow: '20px',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Init stripe js
|
||||
stripe = await loadStripe(this.config.stripe_public_key);
|
||||
// Create and mount the Payment Element
|
||||
const paymentElement = elements.create('payment')
|
||||
|
||||
await axios.get('/api/stripe/setup-intent')
|
||||
.then(response => {
|
||||
// Set up Stripe.js and Elements to use in checkout form, passing the client secret obtained in step 2
|
||||
elements = stripe.elements({
|
||||
clientSecret: response.data.client_secret,
|
||||
appearance: {
|
||||
theme: 'stripe',
|
||||
variables: {
|
||||
colorPrimary: this.config.app_color,
|
||||
fontFamily: 'Nunito',
|
||||
borderRadius: '8px',
|
||||
colorText: this.isDarkMode ? '#bec6cf' : '#1B2539',
|
||||
colorBackground: this.isDarkMode ? '#191b1e' : '#fff',
|
||||
fontWeightNormal: '700',
|
||||
fontSizeSm: '0.875rem',
|
||||
colorSuccessText: '#0ABB87',
|
||||
colorSuccess: '#0ABB87',
|
||||
colorWarning: '#fd397a',
|
||||
colorWarningText: '#fd397a',
|
||||
colorDangerText: '#fd397a',
|
||||
colorTextSecondary: '#6b7280',
|
||||
spacingGridRow: '20px',
|
||||
}
|
||||
},
|
||||
});
|
||||
paymentElement.mount('#payment-element')
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
})
|
||||
|
||||
// Create and mount the Payment Element
|
||||
const paymentElement = elements.create('payment');
|
||||
|
||||
paymentElement.mount('#payment-element');
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
})
|
||||
|
||||
this.stripe.isInitialization = false
|
||||
},
|
||||
showStoreCreditCardForm() {
|
||||
this.isCreditCardForm = !this.isCreditCardForm
|
||||
this.stripeInit()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
this.stripe.isInitialization = false
|
||||
},
|
||||
showStoreCreditCardForm() {
|
||||
this.isCreditCardForm = !this.isCreditCardForm
|
||||
this.stripeInit()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,48 +1,43 @@
|
||||
<template>
|
||||
<div class="card shadow-card">
|
||||
<FormLabel icon="file-text">
|
||||
{{ $t('Transactions') }}
|
||||
</FormLabel>
|
||||
<div class="card shadow-card">
|
||||
<FormLabel icon="file-text">
|
||||
{{ $t('Transactions') }}
|
||||
</FormLabel>
|
||||
|
||||
<DatatableWrapper
|
||||
class="overflow-x-auto"
|
||||
api="/api/subscriptions/transactions"
|
||||
:paginator="true"
|
||||
:columns="columns"
|
||||
>
|
||||
<template slot-scope="{ row }">
|
||||
<FixedTransactionRow :row="row" />
|
||||
</template>
|
||||
<DatatableWrapper class="overflow-x-auto" api="/api/subscriptions/transactions" :paginator="true" :columns="columns">
|
||||
<template slot-scope="{ row }">
|
||||
<FixedTransactionRow :row="row" />
|
||||
</template>
|
||||
|
||||
<!--Empty page-->
|
||||
<template v-slot:empty-page>
|
||||
<InfoBox style="margin-bottom: 0">
|
||||
<p>{{ $t('user_invoices.empty') }}</p>
|
||||
</InfoBox>
|
||||
</template>
|
||||
</DatatableWrapper>
|
||||
</div>
|
||||
<!--Empty page-->
|
||||
<template v-slot:empty-page>
|
||||
<InfoBox style="margin-bottom: 0">
|
||||
<p>{{ $t('user_invoices.empty') }}</p>
|
||||
</InfoBox>
|
||||
</template>
|
||||
</DatatableWrapper>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import InfoBox from "../Others/Forms/InfoBox";
|
||||
import DatatableWrapper from "../Others/Tables/DatatableWrapper"
|
||||
import FixedTransactionRow from "./FixedTransactionRow"
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import ColorLabel from "../Others/ColorLabel"
|
||||
import InfoBox from '../Others/Forms/InfoBox'
|
||||
import DatatableWrapper from '../Others/Tables/DatatableWrapper'
|
||||
import FixedTransactionRow from './FixedTransactionRow'
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import ColorLabel from '../Others/ColorLabel'
|
||||
|
||||
export default {
|
||||
name: 'UserTransactionsForFixedBilling',
|
||||
components: {
|
||||
FixedTransactionRow,
|
||||
DatatableWrapper,
|
||||
ColorLabel,
|
||||
FormLabel,
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
columns() {
|
||||
return this.$store.getters.transactionColumns.filter(column => ! ['type', 'user_id'].includes(column.field))
|
||||
}
|
||||
},
|
||||
name: 'UserTransactionsForFixedBilling',
|
||||
components: {
|
||||
FixedTransactionRow,
|
||||
DatatableWrapper,
|
||||
ColorLabel,
|
||||
FormLabel,
|
||||
InfoBox,
|
||||
},
|
||||
computed: {
|
||||
columns() {
|
||||
return this.$store.getters.transactionColumns.filter((column) => !['type', 'user_id'].includes(column.field))
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,74 +1,65 @@
|
||||
<template>
|
||||
<div class="card shadow-card">
|
||||
<FormLabel icon="file-text">
|
||||
{{ $t('Transactions') }}
|
||||
</FormLabel>
|
||||
<div class="card shadow-card">
|
||||
<FormLabel icon="file-text">
|
||||
{{ $t('Transactions') }}
|
||||
</FormLabel>
|
||||
|
||||
<DatatableWrapper
|
||||
class="overflow-x-auto"
|
||||
api="/api/user/transactions"
|
||||
:paginator="true"
|
||||
:columns="columns"
|
||||
>
|
||||
<template slot-scope="{ row }">
|
||||
<!--Transaction rows-->
|
||||
<MeteredTransactionRow :row="row" @showDetail="showTransactionDetail" />
|
||||
<DatatableWrapper class="overflow-x-auto" api="/api/user/transactions" :paginator="true" :columns="columns">
|
||||
<template slot-scope="{ row }">
|
||||
<!--Transaction rows-->
|
||||
<MeteredTransactionRow :row="row" @showDetail="showTransactionDetail" />
|
||||
|
||||
<!--Transaction detail-->
|
||||
<MeteredTransactionDetailRow v-if="row.data.attributes.metadata && showedTransactionDetailById === row.data.id" :row="row" />
|
||||
</template>
|
||||
<!--Transaction detail-->
|
||||
<MeteredTransactionDetailRow v-if="row.data.attributes.metadata && showedTransactionDetailById === row.data.id" :row="row" />
|
||||
</template>
|
||||
|
||||
<!--Empty page-->
|
||||
<template v-slot:empty-page>
|
||||
<InfoBox style="margin-bottom: 0">
|
||||
<p>{{ $t('user_invoices.empty') }}</p>
|
||||
</InfoBox>
|
||||
</template>
|
||||
</DatatableWrapper>
|
||||
</div>
|
||||
<!--Empty page-->
|
||||
<template v-slot:empty-page>
|
||||
<InfoBox style="margin-bottom: 0">
|
||||
<p>{{ $t('user_invoices.empty') }}</p>
|
||||
</InfoBox>
|
||||
</template>
|
||||
</DatatableWrapper>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {EyeIcon, FileTextIcon} from 'vue-feather-icons'
|
||||
import ColorLabel from "../Others/ColorLabel"
|
||||
import DatatableWrapper from "../Others/Tables/DatatableWrapper"
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import InfoBox from "../Others/Forms/InfoBox";
|
||||
import {mapGetters} from "vuex";
|
||||
import MeteredTransactionDetailRow from "./MeteredTransactionDetailRow"
|
||||
import MeteredTransactionRow from "./MeteredTransactionRow"
|
||||
import { EyeIcon, FileTextIcon } from 'vue-feather-icons'
|
||||
import ColorLabel from '../Others/ColorLabel'
|
||||
import DatatableWrapper from '../Others/Tables/DatatableWrapper'
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import InfoBox from '../Others/Forms/InfoBox'
|
||||
import { mapGetters } from 'vuex'
|
||||
import MeteredTransactionDetailRow from './MeteredTransactionDetailRow'
|
||||
import MeteredTransactionRow from './MeteredTransactionRow'
|
||||
|
||||
export default {
|
||||
name: 'UserTransactionsForMeteredBilling',
|
||||
components: {
|
||||
MeteredTransactionDetailRow,
|
||||
MeteredTransactionRow,
|
||||
DatatableWrapper,
|
||||
ColorLabel,
|
||||
FormLabel,
|
||||
InfoBox,
|
||||
FileTextIcon,
|
||||
EyeIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
]),
|
||||
columns() {
|
||||
return this.$store.getters.transactionColumns.filter(column => column.field !== 'user_id')
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showedTransactionDetailById: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showTransactionDetail(id) {
|
||||
if (this.showedTransactionDetailById === id)
|
||||
this.showedTransactionDetailById = undefined
|
||||
else
|
||||
this.showedTransactionDetailById = id
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
export default {
|
||||
name: 'UserTransactionsForMeteredBilling',
|
||||
components: {
|
||||
MeteredTransactionDetailRow,
|
||||
MeteredTransactionRow,
|
||||
DatatableWrapper,
|
||||
ColorLabel,
|
||||
FormLabel,
|
||||
InfoBox,
|
||||
FileTextIcon,
|
||||
EyeIcon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
columns() {
|
||||
return this.$store.getters.transactionColumns.filter((column) => column.field !== 'user_id')
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showedTransactionDetailById: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showTransactionDetail(id) {
|
||||
if (this.showedTransactionDetailById === id) this.showedTransactionDetailById = undefined
|
||||
else this.showedTransactionDetailById = id
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,69 +1,67 @@
|
||||
<template>
|
||||
<div v-if="canShowForSubscription" class="card shadow-card">
|
||||
<FormLabel>
|
||||
{{ $t('Update Payments') }}
|
||||
</FormLabel>
|
||||
<div v-if="canShowForSubscription" class="card shadow-card">
|
||||
<FormLabel>
|
||||
{{ $t('Update Payments') }}
|
||||
</FormLabel>
|
||||
|
||||
<AppInputButton :title="$t('Update your Payment Method')" :description="$t('You will be redirected to your payment provider to edit your payment method.')" :is-last="true">
|
||||
<ButtonBase @click.native="updatePaymentMethod" :loading="isGeneratedUpdateLink" class="sm:w-auto w-full" button-style="theme">
|
||||
{{ $t('Update Payments') }}
|
||||
</ButtonBase>
|
||||
</AppInputButton>
|
||||
</div>
|
||||
<AppInputButton :title="$t('Update your Payment Method')" :description="$t('You will be redirected to your payment provider to edit your payment method.')" :is-last="true">
|
||||
<ButtonBase @click.native="updatePaymentMethod" :loading="isGeneratedUpdateLink" class="w-full sm:w-auto" button-style="theme">
|
||||
{{ $t('Update Payments') }}
|
||||
</ButtonBase>
|
||||
</AppInputButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppInputSwitch from "../Admin/AppInputSwitch"
|
||||
import ButtonBase from "../FilesView/ButtonBase";
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import axios from "axios";
|
||||
import {events} from "../../bus";
|
||||
import AppInputButton from "../Admin/AppInputButton";
|
||||
import AppInputSwitch from '../Admin/AppInputSwitch'
|
||||
import ButtonBase from '../FilesView/ButtonBase'
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import axios from 'axios'
|
||||
import { events } from '../../bus'
|
||||
import AppInputButton from '../Admin/AppInputButton'
|
||||
|
||||
export default {
|
||||
name: 'UserUpdatePaymentMethodsExternally',
|
||||
components: {
|
||||
AppInputButton,
|
||||
AppInputSwitch,
|
||||
ButtonBase,
|
||||
FormLabel
|
||||
},
|
||||
computed: {
|
||||
canShowForSubscription() {
|
||||
return this.hasSubscription
|
||||
&& ! this.subscription.attributes.is_cancelled
|
||||
&& ['paystack', 'paypal'].includes(this.subscription.attributes.driver)
|
||||
},
|
||||
subscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription.data
|
||||
},
|
||||
hasSubscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isGeneratedUpdateLink: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updatePaymentMethod() {
|
||||
export default {
|
||||
name: 'UserUpdatePaymentMethodsExternally',
|
||||
components: {
|
||||
AppInputButton,
|
||||
AppInputSwitch,
|
||||
ButtonBase,
|
||||
FormLabel,
|
||||
},
|
||||
computed: {
|
||||
canShowForSubscription() {
|
||||
return this.hasSubscription && !this.subscription.attributes.is_cancelled && ['paystack', 'paypal'].includes(this.subscription.attributes.driver)
|
||||
},
|
||||
subscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription.data
|
||||
},
|
||||
hasSubscription() {
|
||||
return this.$store.getters.user.data.relationships.subscription
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isGeneratedUpdateLink: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updatePaymentMethod() {
|
||||
this.isGeneratedUpdateLink = true
|
||||
|
||||
this.isGeneratedUpdateLink = true
|
||||
axios
|
||||
.post(`/api/subscriptions/edit/${this.subscription.id}`)
|
||||
.then((response) => {
|
||||
window.location = response.data.url
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
|
||||
axios.post(`/api/subscriptions/edit/${this.subscription.id}`)
|
||||
.then(response => {
|
||||
window.location = response.data.url
|
||||
})
|
||||
.catch(() => {
|
||||
events.$emit('toaster', {
|
||||
type: 'danger',
|
||||
message: this.$t('popup_error.title'),
|
||||
})
|
||||
|
||||
this.isGeneratedUpdateLink = false
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
this.isGeneratedUpdateLink = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,56 +1,59 @@
|
||||
<template>
|
||||
<div class="card shadow-card">
|
||||
<FormLabel icon="bar-chart">
|
||||
{{ $t('Usage Estimates') }}
|
||||
</FormLabel>
|
||||
<div class="card shadow-card">
|
||||
<FormLabel icon="bar-chart">
|
||||
{{ $t('Usage Estimates') }}
|
||||
</FormLabel>
|
||||
|
||||
<b class="sm:text-3xl text-2xl font-extrabold -mt-3 block mb-0.5">
|
||||
{{ user.data.meta.usages.costEstimate }}
|
||||
</b>
|
||||
<b class="-mt-3 mb-0.5 block text-2xl font-extrabold sm:text-3xl">
|
||||
{{ user.data.meta.usages.costEstimate }}
|
||||
</b>
|
||||
|
||||
<b class="mb-3 block text-sm text-gray-400 mb-5">
|
||||
{{ user.data.relationships.subscription.data.attributes.updated_at }} {{ $t('till now') }}
|
||||
</b>
|
||||
<b class="mb-3 mb-5 block text-sm text-gray-400">
|
||||
{{ user.data.relationships.subscription.data.attributes.updated_at }}
|
||||
{{ $t('till now') }}
|
||||
</b>
|
||||
|
||||
<div class="flex items-center justify-between py-2 border-b dark:border-opacity-5 border-light border-dashed" v-for="(usage, i) in user.data.meta.usages.featureEstimates" :key="i">
|
||||
<div class="w-2/4 leading-none">
|
||||
<b class="text-sm font-bold leading-none">
|
||||
{{ $t(usage.feature) }}
|
||||
</b>
|
||||
<small class="text-xs text-gray-500 pt-2 leading-none sm:block hidden">
|
||||
{{ $t(`feature_usage_desc_${usage.feature}`) }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="text-left w-1/4">
|
||||
<span class="text-sm font-bold text-gray-560">
|
||||
{{ usage.usage }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-right w-1/4">
|
||||
<span class="text-sm font-bold text-theme">
|
||||
{{ usage.cost }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center justify-between border-b border-dashed border-light py-2 dark:border-opacity-5"
|
||||
v-for="(usage, i) in user.data.meta.usages.featureEstimates"
|
||||
:key="i"
|
||||
>
|
||||
<div class="w-2/4 leading-none">
|
||||
<b class="text-sm font-bold leading-none">
|
||||
{{ $t(usage.feature) }}
|
||||
</b>
|
||||
<small class="hidden pt-2 text-xs leading-none text-gray-500 sm:block">
|
||||
{{ $t(`feature_usage_desc_${usage.feature}`) }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="w-1/4 text-left">
|
||||
<span class="text-gray-560 text-sm font-bold">
|
||||
{{ usage.usage }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="w-1/4 text-right">
|
||||
<span class="text-theme text-sm font-bold">
|
||||
{{ usage.cost }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<small class="mt-6 block font-bold">
|
||||
{{ $t('Records are updated on daily bases.') }}
|
||||
</small>
|
||||
</div>
|
||||
<small class="mt-6 block font-bold">
|
||||
{{ $t('Records are updated on daily bases.') }}
|
||||
</small>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import FormLabel from "../Others/Forms/FormLabel"
|
||||
import {mapGetters} from "vuex";
|
||||
import FormLabel from '../Others/Forms/FormLabel'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'UserUsageEstimates',
|
||||
components: {
|
||||
FormLabel
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
])
|
||||
},
|
||||
name: 'UserUsageEstimates',
|
||||
components: {
|
||||
FormLabel,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user