web/satellite: new upgrade account flow
Replaced old modal with new upgrade account flow. Issue: https://github.com/storj/storj/issues/5753 Change-Id: I7ab53324fd3983f46a209052a2f2d478ca005f0d
This commit is contained in:
parent
b8983640d6
commit
816c3d31ac
@ -354,7 +354,7 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const response = await this.client.post(path, null);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Can not claim new wallet');
|
||||
throw new Error('Can not claim wallet');
|
||||
}
|
||||
|
||||
const wallet = await response.json();
|
||||
|
@ -194,6 +194,7 @@ import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { useAppStore } from '@/store/modules/appStore';
|
||||
import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
import { MODALS } from '@/utils/constants/appStatePopUps';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
@ -414,6 +415,12 @@ async function onConfirmAddStripe(): Promise<void> {
|
||||
|
||||
function addPaymentMethodHandler(): void {
|
||||
analytics.eventTriggered(AnalyticsEvent.ADD_NEW_PAYMENT_METHOD_CLICKED);
|
||||
|
||||
if (!usersStore.state.user.paidTier) {
|
||||
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||
return;
|
||||
}
|
||||
|
||||
isAddingPayment.value = true;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { computed, VueConstructor } from 'vue';
|
||||
|
||||
import AddCircleIcon from '@/../static/images/common/addCircle.svg';
|
||||
@ -170,6 +169,11 @@ function handleClick(): void {
|
||||
.label {
|
||||
color: #354049 !important;
|
||||
}
|
||||
|
||||
:deep(path),
|
||||
:deep(rect) {
|
||||
fill: #354049 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.solid-red {
|
||||
@ -196,7 +200,7 @@ function handleClick(): void {
|
||||
|
||||
:deep(path),
|
||||
:deep(rect) {
|
||||
fill: #354049;
|
||||
fill: #354049 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,12 +236,12 @@ function handleClick(): void {
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background-color: #dadde5 !important;
|
||||
border-color: #dadde5 !important;
|
||||
background-color: var(--c-grey-5) !important;
|
||||
border-color: var(--c-grey-5) !important;
|
||||
pointer-events: none !important;
|
||||
|
||||
.label {
|
||||
color: #acb0bc !important;
|
||||
color: var(--c-white) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,6 +267,11 @@ function handleClick(): void {
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
|
||||
:deep(path),
|
||||
:deep(rect) {
|
||||
fill: var(--c-white);
|
||||
}
|
||||
|
||||
.trash-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
@ -1,694 +0,0 @@
|
||||
// Copyright (C) 2022 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<VModal :on-close="closeModal">
|
||||
<template #content>
|
||||
<div v-if="isAddModal" class="add-modal">
|
||||
<div class="add-modal__top">
|
||||
<h1 class="add-modal__top__title" aria-roledescription="modal-title">Upgrade to Pro Account</h1>
|
||||
<div class="add-modal__top__header">
|
||||
<p class="add-modal__top__header__sub-title">Add Payment Method</p>
|
||||
<div class="add-modal__top__header__choices">
|
||||
<p class="add-modal__top__header__choices__var" :class="{active: !isAddCard}" @click.stop="setIsAddToken">
|
||||
STORJ Token
|
||||
</p>
|
||||
<p class="add-modal__top__header__choices__var" :class="{active: isAddCard}" @click.stop="setIsAddCard">
|
||||
Card
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isAddCard" class="add-modal__card">
|
||||
<StripeCardInput
|
||||
ref="stripeCardInput"
|
||||
class="add-modal__card__stripe"
|
||||
:on-stripe-response-callback="addCardToDB"
|
||||
/>
|
||||
<VButton
|
||||
width="100%"
|
||||
height="48px"
|
||||
border-radius="32px"
|
||||
label="Add Credit Card"
|
||||
:on-press="onAddCardClick"
|
||||
/>
|
||||
<p class="add-modal__card__info">Pay as you go, no contract required.</p>
|
||||
</div>
|
||||
<div v-else class="add-modal__tokens">
|
||||
<p class="add-modal__tokens__banner">
|
||||
Deposit STORJ Token to your account and receive a 10% bonus, or $10 for every $100.
|
||||
</p>
|
||||
<p class="add-modal__tokens__support-info">To deposit STORJ token and request higher limits, please contact <a target="_blank" rel="noopener noreferrer" href="https://supportdcs.storj.io/hc/en-us/requests/new?ticket_form_id=360000683212">Support</a></p>
|
||||
</div>
|
||||
<div class="add-modal__bullets">
|
||||
<div class="add-modal__bullets__left">
|
||||
<h2 class="add-modal__bullets__left__title">Pro Account Limits:</h2>
|
||||
<div class="add-modal__bullets__left__item">
|
||||
<CheckMarkIcon />
|
||||
<p class="add-modal__bullets__left__item__label">3 projects</p>
|
||||
</div>
|
||||
<div class="add-modal__bullets__left__item">
|
||||
<CheckMarkIcon />
|
||||
<p class="add-modal__bullets__left__item__label">100 buckets per project</p>
|
||||
</div>
|
||||
<div class="add-modal__bullets__left__item">
|
||||
<CheckMarkIcon />
|
||||
<p class="add-modal__bullets__left__item__label">Up to 25 TB storage per project</p>
|
||||
</div>
|
||||
<div class="add-modal__bullets__left__item">
|
||||
<CheckMarkIcon />
|
||||
<p class="add-modal__bullets__left__item__label">Up to 100 TB egress bandwidth per project per month</p>
|
||||
</div>
|
||||
<div class="add-modal__bullets__left__item">
|
||||
<CheckMarkIcon />
|
||||
<p class="add-modal__bullets__left__item__label">100 request per second rate limit</p>
|
||||
</div>
|
||||
</div>
|
||||
<VLoader v-if="isPriceFetching" class="add-modal__bullets__right-loader" width="90px" height="90px" />
|
||||
<div v-else class="add-modal__bullets__right">
|
||||
<h2 class="add-modal__bullets__right__title">Storage price:</h2>
|
||||
<div class="add-modal__bullets__right__item">
|
||||
<p class="add-modal__bullets__right__item__price">{{ storagePrice }}</p>
|
||||
<p class="add-modal__bullets__right__item__label">TB / month</p>
|
||||
</div>
|
||||
<h2 class="add-modal__bullets__right__title">Bandwidth price:</h2>
|
||||
<div class="add-modal__bullets__right__item">
|
||||
<p class="add-modal__bullets__right__item__price">{{ bandwidthPrice }}</p>
|
||||
<p class="add-modal__bullets__right__item__label">TB</p>
|
||||
</div>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<p v-if="extraBandwidthPriceInfo" class="add-modal__bullets__right__item__label__special" v-html="extraBandwidthPriceInfo" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="add-modal__security">
|
||||
<LockImage />
|
||||
<p class="add-modal__security__info">
|
||||
Your information is secured with 128-bit SSL & AES-256 encryption.
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="isLoading" class="add-modal__blur">
|
||||
<VLoader
|
||||
class="add-modal__blur__loader"
|
||||
width="30px"
|
||||
height="30px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="success-modal">
|
||||
<BigCheckMarkIcon />
|
||||
<h2 class="success-modal__title">Congratulations!</h2>
|
||||
<h2 class="success-modal__sub-title">You've just upgraded to a Pro Account.</h2>
|
||||
<p class="success-modal__info">
|
||||
Now you can have up to
|
||||
<b class="success-modal__info__bold">75TB</b>
|
||||
of total storage and
|
||||
<b>300TB</b>
|
||||
of egress bandwidth per month. If you need more
|
||||
than this, please
|
||||
<a
|
||||
class="success-modal__info__link"
|
||||
:href="limitsIncreaseRequestURL"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
contact us
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<VButton
|
||||
width="100%"
|
||||
height="48px"
|
||||
border-radius="32px"
|
||||
label="Done"
|
||||
:on-press="closeModal"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onBeforeMount, ref, reactive } from 'vue';
|
||||
|
||||
import { useNotify, useRouter } from '@/utils/hooks';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
import { MODALS } from '@/utils/constants/appStatePopUps';
|
||||
import { ProjectUsagePriceModel } from '@/types/payments';
|
||||
import { decimalShift, formatPrice, CENTS_MB_TO_DOLLARS_TB_SHIFT } from '@/utils/strings';
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { useAppStore } from '@/store/modules/appStore';
|
||||
import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
|
||||
import VModal from '@/components/common/VModal.vue';
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import StripeCardInput from '@/components/account/billing/paymentMethods/StripeCardInput.vue';
|
||||
|
||||
import BigCheckMarkIcon from '@/../static/images/common/greenRoundCheckmarkBig.svg';
|
||||
import CheckMarkIcon from '@/../static/images/common/greenRoundCheckmark.svg';
|
||||
import LockImage from '@/../static/images/account/billing/greyLock.svg';
|
||||
|
||||
interface StripeForm {
|
||||
onSubmit(): Promise<void>;
|
||||
}
|
||||
|
||||
const configStore = useConfigStore();
|
||||
const appStore = useAppStore();
|
||||
const billingStore = useBillingStore();
|
||||
const usersStore = useUsersStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const notify = useNotify();
|
||||
const nativeRouter = useRouter();
|
||||
const router = reactive(nativeRouter);
|
||||
|
||||
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
const isAddModal = ref<boolean>(true);
|
||||
const isAddCard = ref<boolean>(true);
|
||||
const isLoading = ref<boolean>(false);
|
||||
const isPriceFetching = ref<boolean>(true);
|
||||
|
||||
const stripeCardInput = ref<typeof StripeCardInput & StripeForm | null>(null);
|
||||
|
||||
const extraBandwidthPriceInfo = ref<string>('');
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Fetches project usage price model.
|
||||
*/
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await billingStore.getProjectUsagePriceModel();
|
||||
isPriceFetching.value = false;
|
||||
} catch (error) {
|
||||
notify.error(error.message, AnalyticsErrorEventSource.UPGRADE_ACCOUNT_MODAL);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides card information to Stripe.
|
||||
*/
|
||||
async function onAddCardClick(): Promise<void> {
|
||||
if (isLoading.value || !stripeCardInput.value) return;
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
await stripeCardInput.value.onSubmit();
|
||||
} catch (error) {
|
||||
notify.error(error.message, AnalyticsErrorEventSource.UPGRADE_ACCOUNT_MODAL);
|
||||
}
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds card after Stripe confirmation.
|
||||
*
|
||||
* @param token from Stripe
|
||||
*/
|
||||
async function addCardToDB(token: string): Promise<void> {
|
||||
try {
|
||||
await billingStore.addCreditCard(token);
|
||||
notify.success('Card successfully added');
|
||||
// We fetch User one more time to update their Paid Tier status.
|
||||
await usersStore.getUser();
|
||||
|
||||
if (router.currentRoute.name === RouteConfig.ProjectDashboard.name) {
|
||||
await projectsStore.getProjectLimits(projectsStore.state.selectedProject.id);
|
||||
}
|
||||
|
||||
await analytics.eventTriggered(AnalyticsEvent.MODAL_ADD_CARD);
|
||||
|
||||
} catch (error) {
|
||||
notify.error(error.message, AnalyticsErrorEventSource.UPGRADE_ACCOUNT_MODAL);
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
isAddModal.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes add payment method modal.
|
||||
*/
|
||||
function closeModal(): void {
|
||||
appStore.updateActiveModal(MODALS.addPaymentMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets modal state to add STORJ tokens.
|
||||
*/
|
||||
function setIsAddToken(): void {
|
||||
isAddCard.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets modal state to add credit card.
|
||||
*/
|
||||
function setIsAddCard(): void {
|
||||
isAddCard.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns project limits increase request url from config.
|
||||
*/
|
||||
const limitsIncreaseRequestURL = computed((): string => {
|
||||
return configStore.state.config.projectLimitsIncreaseRequestURL;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns project usage price model from store.
|
||||
*/
|
||||
const priceModel = computed((): ProjectUsagePriceModel => {
|
||||
return billingStore.state.usagePriceModel;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the storage price formatted as dollars per terabyte.
|
||||
*/
|
||||
const storagePrice = computed((): string => {
|
||||
const storage = priceModel.value.storageMBMonthCents.toString();
|
||||
return formatPrice(decimalShift(storage, CENTS_MB_TO_DOLLARS_TB_SHIFT));
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the bandwidth (egress) price formatted as dollars per terabyte.
|
||||
*/
|
||||
const bandwidthPrice = computed((): string => {
|
||||
const egress = priceModel.value.egressMBCents.toString();
|
||||
return formatPrice(decimalShift(egress, CENTS_MB_TO_DOLLARS_TB_SHIFT));
|
||||
});
|
||||
|
||||
/**
|
||||
* Lifecycle hook before initial render.
|
||||
* If applicable, loads additional clarifying text based on user partner.
|
||||
*/
|
||||
onBeforeMount(() => {
|
||||
try {
|
||||
const partner = usersStore.state.user.partner;
|
||||
const config = require('@/components/account/billing/billingConfig.json');
|
||||
if (partner !== '' && config[partner] && config[partner].extraBandwidthPriceInfo) {
|
||||
extraBandwidthPriceInfo.value = config[partner].extraBandwidthPriceInfo;
|
||||
}
|
||||
} catch (e) {
|
||||
notify.error('No configuration file for page.', null);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.add-modal {
|
||||
width: 760px;
|
||||
padding-top: 50px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
|
||||
@media screen and (max-width: 850px) {
|
||||
width: unset;
|
||||
}
|
||||
|
||||
&__top {
|
||||
padding: 0 50px;
|
||||
|
||||
@media screen and (max-width: 850px) {
|
||||
padding: 0 36px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 36px;
|
||||
line-height: 44px;
|
||||
color: #1b2533;
|
||||
margin-bottom: 40px;
|
||||
text-align: left;
|
||||
|
||||
@media screen and (max-width: 420px) {
|
||||
max-width: 248px;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30px;
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: unset;
|
||||
}
|
||||
|
||||
&__sub-title {
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&__choices {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 20px;
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
margin-top: 23px;
|
||||
column-gap: 50px;
|
||||
}
|
||||
|
||||
&__var {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: var(--c-blue-3);
|
||||
padding: 0 10px 5px;
|
||||
cursor: pointer;
|
||||
border-bottom: 3px solid #fff;
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
padding: 0 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__card {
|
||||
padding: 0 50px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media screen and (max-width: 850px) {
|
||||
padding: 0 36px;
|
||||
width: 642px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
width: unset;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
&__stripe {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin-top: 20px;
|
||||
font-size: 12px;
|
||||
line-height: 19px;
|
||||
text-align: center;
|
||||
color: #a8a8a8;
|
||||
}
|
||||
}
|
||||
|
||||
&__tokens {
|
||||
padding: 0 50px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
@media screen and (max-width: 850px) {
|
||||
padding: 0 36px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
&__banner {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #384761;
|
||||
padding: 20px 35px;
|
||||
background: #edf4fe;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 25px;
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
padding: 16px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__selection {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
&__checkout-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 25px;
|
||||
|
||||
&__link {
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
color: #2683ff;
|
||||
}
|
||||
}
|
||||
|
||||
&__note {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #14142a;
|
||||
margin: 25px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #14142a;
|
||||
text-align: left;
|
||||
|
||||
&__link {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
text-decoration: underline !important;
|
||||
text-underline-position: under;
|
||||
|
||||
&:visited {
|
||||
color: #14142a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__support-info {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #000;
|
||||
|
||||
a {
|
||||
color: var(--c-blue-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__bullets {
|
||||
background: #f0f0f0;
|
||||
padding: 35px 50px 90px;
|
||||
border-radius: 0 0 32px 32px;
|
||||
display: flex;
|
||||
|
||||
@media screen and (max-width: 850px) {
|
||||
padding: 35px 36px 90px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
padding: 35px 24px 90px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__left {
|
||||
width: 50%;
|
||||
border-right: 1px solid #ccc;
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
width: 100%;
|
||||
border-right: unset;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 26px;
|
||||
color: #000;
|
||||
margin-bottom: 5px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
|
||||
svg {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
&__label {
|
||||
margin-left: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.4735px;
|
||||
color: #000;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__right-loader {
|
||||
width: 50%;
|
||||
align-items: center;
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
width: 100%;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__right {
|
||||
padding-left: 50px;
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
padding-left: unset;
|
||||
margin-top: 35px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 26px;
|
||||
color: #000;
|
||||
margin-bottom: 5px;
|
||||
text-align: left;
|
||||
|
||||
&:last-of-type {
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
letter-spacing: 0.4735px;
|
||||
|
||||
&__price {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 42px;
|
||||
color: var(--c-blue-3);
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
color: #a9a9a9;
|
||||
margin: 5px 0 0 5px;
|
||||
|
||||
&__special {
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__security {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
border-radius: 0 0 32px 32px;
|
||||
padding: 15px 36px;
|
||||
|
||||
@media screen and (max-width: 570px) {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
svg {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
color: #3f3f3f;
|
||||
margin-left: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
&__blur {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-radius: 32px;
|
||||
z-index: 1;
|
||||
background-color: rgb(245 246 250 / 50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.success-modal {
|
||||
width: 480px;
|
||||
padding: 50px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
|
||||
&__title,
|
||||
&__sub-title {
|
||||
font-size: 36px;
|
||||
line-height: 54px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&__sub-title {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin: 35px 0 48px;
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
color: #000;
|
||||
|
||||
&__bold {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
}
|
||||
|
||||
&__link {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
text-decoration: underline !important;
|
||||
text-underline-position: under;
|
||||
|
||||
&:visited {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
border-color: var(--c-blue-3);
|
||||
}
|
||||
</style>
|
@ -128,7 +128,7 @@ onMounted(async (): Promise<void> => {
|
||||
try {
|
||||
await QRCode.toCanvas(canvas.value, wallet.value.address);
|
||||
} catch (error) {
|
||||
await notify.error(error.message, AnalyticsErrorEventSource.ADD_TOKEN_FUNDS_MODAL);
|
||||
notify.error(error.message, AnalyticsErrorEventSource.ADD_TOKEN_FUNDS_MODAL);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -45,7 +45,7 @@ const appStore = useAppStore();
|
||||
*/
|
||||
function onClick(): void {
|
||||
appStore.updateActiveModal(MODALS.createProjectPrompt);
|
||||
appStore.updateActiveModal(MODALS.addPaymentMethod);
|
||||
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,137 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<UpgradeAccountWrapper title="Add Credit Card">
|
||||
<template #content>
|
||||
<p class="card-info">
|
||||
By saving your card information, you allow Storj to charge your card for future payments in accordance with
|
||||
the terms.
|
||||
</p>
|
||||
<StripeCardInput
|
||||
ref="stripeCardInput"
|
||||
:on-stripe-response-callback="addCardToDB"
|
||||
/>
|
||||
<VButton
|
||||
class="button"
|
||||
label="Save card"
|
||||
icon="lock"
|
||||
width="100%"
|
||||
height="48px"
|
||||
border-radius="10px"
|
||||
font-size="14px"
|
||||
:is-green="true"
|
||||
:on-press="onSaveCardClick"
|
||||
:is-disabled="loading"
|
||||
/>
|
||||
<p class="security-info">Your information is secured with 128-bit SSL & AES-256 encryption.</p>
|
||||
</template>
|
||||
</UpgradeAccountWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
import { RouteConfig } from '@/router';
|
||||
import { useNotify, useRouter } from '@/utils/hooks';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import UpgradeAccountWrapper from '@/components/modals/upgradeAccountFlow/UpgradeAccountWrapper.vue';
|
||||
import StripeCardInput from '@/components/account/billing/paymentMethods/StripeCardInput.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
interface StripeForm {
|
||||
onSubmit(): Promise<void>;
|
||||
}
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
const billingStore = useBillingStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const notify = useNotify();
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
setSuccess: () => void;
|
||||
}>();
|
||||
|
||||
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
const stripeCardInput = ref<typeof StripeCardInput & StripeForm | null>(null);
|
||||
|
||||
/**
|
||||
* Provides card information to Stripe.
|
||||
*/
|
||||
async function onSaveCardClick(): Promise<void> {
|
||||
if (loading.value || !stripeCardInput.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
await stripeCardInput.value.onSubmit();
|
||||
} catch (error) {
|
||||
notify.error(error.message, AnalyticsErrorEventSource.UPGRADE_ACCOUNT_MODAL);
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds card after Stripe confirmation.
|
||||
*
|
||||
* @param token from Stripe
|
||||
*/
|
||||
async function addCardToDB(token: string): Promise<void> {
|
||||
try {
|
||||
await billingStore.addCreditCard(token);
|
||||
notify.success('Card successfully added');
|
||||
// We fetch User one more time to update their Paid Tier status.
|
||||
await usersStore.getUser();
|
||||
|
||||
if (router.currentRoute.name === RouteConfig.ProjectDashboard.name) {
|
||||
await projectsStore.getProjectLimits(projectsStore.state.selectedProject.id);
|
||||
}
|
||||
|
||||
if (router.currentRoute.path.includes(RouteConfig.Billing.path) || router.currentRoute.path.includes(RouteConfig.Billing2.path)) {
|
||||
await billingStore.getCreditCards();
|
||||
}
|
||||
|
||||
analytics.eventTriggered(AnalyticsEvent.MODAL_ADD_CARD);
|
||||
|
||||
loading.value = false;
|
||||
props.setSuccess();
|
||||
} catch (error) {
|
||||
await notify.error(error.message, AnalyticsErrorEventSource.UPGRADE_ACCOUNT_MODAL);
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card-info {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-blue-6);
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
text-align: left;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.button {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.security-info {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
color: var(--c-black);
|
||||
}
|
||||
</style>
|
@ -0,0 +1,244 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<UpgradeAccountWrapper title="Add STORJ Tokens">
|
||||
<template #content>
|
||||
<div class="add-tokens">
|
||||
<p class="add-tokens__info">Send more than $10 in STORJ Tokens to the following deposit address.</p>
|
||||
<canvas ref="canvas" />
|
||||
<div class="add-tokens__label">
|
||||
<h2 class="add-tokens__label__text">Deposit Address</h2>
|
||||
<VInfo class="add-tokens__label__info">
|
||||
<template #icon>
|
||||
<InfoIcon />
|
||||
</template>
|
||||
<template #message>
|
||||
<p class="add-tokens__label__info__msg">
|
||||
This is a Storj deposit address generated just for you.
|
||||
<a
|
||||
class="add-tokens__label__info__msg__link"
|
||||
href=""
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
</p>
|
||||
</template>
|
||||
</VInfo>
|
||||
</div>
|
||||
<div class="add-tokens__address">
|
||||
<p class="add-tokens__address__value">{{ wallet.address }}</p>
|
||||
<VButton
|
||||
class="add-tokens__address__copy-button"
|
||||
label="Copy"
|
||||
width="84px"
|
||||
height="32px"
|
||||
font-size="12px"
|
||||
border-radius="8px"
|
||||
icon="copy"
|
||||
:on-press="onCopyAddressClick"
|
||||
/>
|
||||
</div>
|
||||
<div class="add-tokens__divider" />
|
||||
<div class="add-tokens__send-info">
|
||||
<h2 class="add-tokens__send-info__title">Send only STORJ Tokens to this deposit address.</h2>
|
||||
<p class="add-tokens__send-info__message">
|
||||
Sending anything else may result in the loss of your deposit.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UpgradeAccountWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { Wallet } from '@/types/payments';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import UpgradeAccountWrapper from '@/components/modals/upgradeAccountFlow/UpgradeAccountWrapper.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VInfo from '@/components/common/VInfo.vue';
|
||||
|
||||
import InfoIcon from '@/../static/images/payments/infoIcon.svg';
|
||||
|
||||
const billingStore = useBillingStore();
|
||||
const notify = useNotify();
|
||||
|
||||
const canvas = ref<HTMLCanvasElement>();
|
||||
|
||||
/**
|
||||
* Returns wallet from store.
|
||||
*/
|
||||
const wallet = computed((): Wallet => {
|
||||
return billingStore.state.wallet as Wallet;
|
||||
});
|
||||
|
||||
/**
|
||||
* Copies address to user's clipboard.
|
||||
*/
|
||||
function onCopyAddressClick(): void {
|
||||
navigator.clipboard.writeText(wallet.value.address);
|
||||
notify.success('Address copied to your clipboard');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounted lifecycle hook after initial render.
|
||||
* Renders QR code.
|
||||
*/
|
||||
onMounted(async (): Promise<void> => {
|
||||
if (!canvas.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await QRCode.toCanvas(canvas.value, wallet.value.address, { width: 124 });
|
||||
} catch (error) {
|
||||
notify.error(error.message, AnalyticsErrorEventSource.UPGRADE_ACCOUNT_MODAL);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.add-tokens {
|
||||
max-width: 482px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 470px) {
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-blue-6);
|
||||
margin-bottom: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: flex-start;
|
||||
margin-top: 16px;
|
||||
|
||||
&__text {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-grey-6);
|
||||
margin-right: 9px;
|
||||
font-family: 'font_medium', sans-serif;
|
||||
}
|
||||
|
||||
&__info {
|
||||
cursor: pointer;
|
||||
max-height: 16px;
|
||||
|
||||
&__msg {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
color: var(--c-white);
|
||||
|
||||
&__link {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: var(--c-white);
|
||||
text-decoration: underline !important;
|
||||
|
||||
&:visited {
|
||||
color: var(--c-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__address {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border: 1px solid var(--c-grey-4);
|
||||
border-radius: 8px;
|
||||
padding: 10px 15px;
|
||||
margin: 8px 0 16px;
|
||||
width: 100%;
|
||||
|
||||
&__value {
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
color: var(--c-black);
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__copy-button {
|
||||
margin-left: 10px;
|
||||
min-width: 84px;
|
||||
}
|
||||
}
|
||||
|
||||
&__divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
margin-top: 16px;
|
||||
background-color: var(--c-grey-2);
|
||||
}
|
||||
|
||||
&__send-info {
|
||||
margin-top: 16px;
|
||||
padding: 16px;
|
||||
background: var(--c-yellow-1);
|
||||
border: 1px solid var(--c-yellow-2);
|
||||
box-shadow: 0 7px 20px rgb(0 0 0 / 15%);
|
||||
border-radius: 10px;
|
||||
|
||||
&__title,
|
||||
&__message {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-black);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.info__box) {
|
||||
width: 214px;
|
||||
left: calc(50% - 107px);
|
||||
top: calc(100% - 80px);
|
||||
cursor: default;
|
||||
filter: none;
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
|
||||
:deep(.info__box__message) {
|
||||
background: var(--c-grey-6);
|
||||
border-radius: 4px;
|
||||
padding: 10px 8px;
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
|
||||
:deep(.info__box__arrow) {
|
||||
background: var(--c-grey-6);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,113 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="bullet">
|
||||
<GreyCheckmark v-if="!isPro" class="bullet__icon" />
|
||||
<GreenCheckmark v-else class="bullet__icon" />
|
||||
<div class="bullet__column">
|
||||
<div class="bullet__column__header">
|
||||
<h3 class="bullet__column__header__title">{{ title }}</h3>
|
||||
<VInfo v-if="slots.moreInfo">
|
||||
<template #icon>
|
||||
<InfoIcon class="bullet__column__header__icon" />
|
||||
</template>
|
||||
<template #message>
|
||||
<slot name="moreInfo" />
|
||||
</template>
|
||||
</VInfo>
|
||||
</div>
|
||||
<p class="bullet__column__info">{{ info }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSlots } from 'vue';
|
||||
|
||||
import VInfo from '@/components/common/VInfo.vue';
|
||||
|
||||
import GreyCheckmark from '@/../static/images/modals/upgradeFlow/greyCheckmark.svg';
|
||||
import GreenCheckmark from '@/../static/images/modals/upgradeFlow/greenCheckmark.svg';
|
||||
import InfoIcon from '@/../static/images/modals/upgradeFlow/info.svg';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
isPro?: boolean;
|
||||
title: string;
|
||||
info: string;
|
||||
}>(), {
|
||||
isPro: false,
|
||||
title: '',
|
||||
info: '',
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bullet {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
|
||||
&__icon {
|
||||
min-width: 16px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
&__column {
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-black);
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-black);
|
||||
white-space: nowrap;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
cursor: pointer;
|
||||
max-height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.info) {
|
||||
max-height: 14px;
|
||||
}
|
||||
|
||||
:deep(.info__box) {
|
||||
top: calc(100% + 1px);
|
||||
cursor: default;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
:deep(.info__box__message) {
|
||||
width: 245px;
|
||||
background: var(--c-grey-6);
|
||||
border-radius: 4px;
|
||||
padding: 10px 8px;
|
||||
}
|
||||
|
||||
:deep(.info__box__arrow) {
|
||||
background: var(--c-grey-6);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-bottom: -3px;
|
||||
border-radius: 0;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<UpgradeAccountWrapper title="Success" :icon="SuccessIcon">
|
||||
<template #content>
|
||||
<p class="success-info">Your Pro Account has been successfully activated.</p>
|
||||
<VButton
|
||||
label="Continue ->"
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
:is-green="true"
|
||||
:on-press="onContinue"
|
||||
/>
|
||||
</template>
|
||||
</UpgradeAccountWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import UpgradeAccountWrapper from '@/components/modals/upgradeAccountFlow/UpgradeAccountWrapper.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import SuccessIcon from '@/../static/images/modals/upgradeFlow/success.svg';
|
||||
|
||||
const props = defineProps<{
|
||||
onContinue: () => void;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.success-info {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-blue-6);
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,97 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<VModal :on-close="closeModal">
|
||||
<template #content>
|
||||
<UpgradeInfoStep
|
||||
v-if="step === UpgradeAccountStep.Info"
|
||||
:on-upgrade="() => setStep(UpgradeAccountStep.Options)"
|
||||
/>
|
||||
<UpgradeOptionsStep
|
||||
v-if="step === UpgradeAccountStep.Options"
|
||||
:on-add-card="() => setStep(UpgradeAccountStep.AddCC)"
|
||||
:on-add-tokens="onAddTokens"
|
||||
:loading="loading"
|
||||
/>
|
||||
<AddCreditCardStep
|
||||
v-if="step === UpgradeAccountStep.AddCC"
|
||||
:set-success="() => setStep(UpgradeAccountStep.Success)"
|
||||
/>
|
||||
<AddTokensStep v-if="step === UpgradeAccountStep.AddTokens" />
|
||||
<SuccessStep
|
||||
v-if="step === UpgradeAccountStep.Success"
|
||||
:on-continue="closeModal"
|
||||
/>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useAppStore } from '@/store/modules/appStore';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
import VModal from '@/components/common/VModal.vue';
|
||||
import UpgradeInfoStep from '@/components/modals/upgradeAccountFlow/UpgradeInfoStep.vue';
|
||||
import UpgradeOptionsStep from '@/components/modals/upgradeAccountFlow/UpgradeOptionsStep.vue';
|
||||
import AddCreditCardStep from '@/components/modals/upgradeAccountFlow/AddCreditCardStep.vue';
|
||||
import SuccessStep from '@/components/modals/upgradeAccountFlow/SuccessStep.vue';
|
||||
import AddTokensStep from '@/components/modals/upgradeAccountFlow/AddTokensStep.vue';
|
||||
|
||||
enum UpgradeAccountStep {
|
||||
Info = 'infoStep',
|
||||
Options = 'optionsStep',
|
||||
AddCC = 'addCCStep',
|
||||
AddTokens = 'addTokensStep',
|
||||
Success = 'successStep',
|
||||
}
|
||||
|
||||
const appStore = useAppStore();
|
||||
const billingStore = useBillingStore();
|
||||
const notify = useNotify();
|
||||
|
||||
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
const step = ref<UpgradeAccountStep>(UpgradeAccountStep.Info);
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* Claims wallet and sets add token step.
|
||||
*/
|
||||
async function onAddTokens(): Promise<void> {
|
||||
if (loading.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
await billingStore.claimWallet();
|
||||
|
||||
analytics.eventTriggered(AnalyticsEvent.ADD_FUNDS_CLICKED);
|
||||
|
||||
setStep(UpgradeAccountStep.AddTokens);
|
||||
} catch (error) {
|
||||
notify.error(error.message, AnalyticsErrorEventSource.UPGRADE_ACCOUNT_MODAL);
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specific flow step.
|
||||
*/
|
||||
function setStep(s: UpgradeAccountStep) {
|
||||
step.value = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes upgrade account modal.
|
||||
*/
|
||||
function closeModal(): void {
|
||||
appStore.removeActiveModal();
|
||||
}
|
||||
</script>
|
@ -0,0 +1,56 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="upgrade-wrapper">
|
||||
<div class="upgrade-wrapper__header">
|
||||
<component :is="icon" v-if="icon" class="upgrade-wrapper__header__icon " />
|
||||
<h1 class="upgrade-wrapper__header__title">{{ title }}</h1>
|
||||
</div>
|
||||
<slot name="content" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Vue, { VueConstructor } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
icon?: VueConstructor<Vue>;
|
||||
title: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.upgrade-wrapper {
|
||||
padding: 32px;
|
||||
font-family: 'font_bold', sans-serif;
|
||||
|
||||
@media screen and (max-width: 350px) {
|
||||
padding: 32px 16px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
|
||||
@media screen and (max-width: 690px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 24px;
|
||||
line-height: 31px;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--c-black);
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,169 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<UpgradeAccountWrapper title="Your account">
|
||||
<template #content>
|
||||
<div class="info-step">
|
||||
<div class="info-step__column">
|
||||
<h2 class="info-step__column__title">Free</h2>
|
||||
<VButton
|
||||
label="Current"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
width="280px"
|
||||
height="48px"
|
||||
:is-disabled="true"
|
||||
:on-press="() => {}"
|
||||
/>
|
||||
<div class="info-step__column__bullets">
|
||||
<InfoBullet class="info-step__column__bullets__item" title="Projects" info="1 project" />
|
||||
<InfoBullet class="info-step__column__bullets__item" title="Storage" info="25 GB limit" />
|
||||
<InfoBullet class="info-step__column__bullets__item" title="Download" info="10,000 segments limit" />
|
||||
<InfoBullet class="info-step__column__bullets__item" title="Segments" info="1 project" />
|
||||
<InfoBullet class="info-step__column__bullets__item" title="Link Sharing" info="Link sharing with Storj domain" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-step__column">
|
||||
<h2 class="info-step__column__title">Pro Account</h2>
|
||||
<VButton
|
||||
label="Upgrade to Pro"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
width="280px"
|
||||
height="48px"
|
||||
:is-green="true"
|
||||
:on-press="onUpgrade"
|
||||
/>
|
||||
<div class="info-step__column__bullets">
|
||||
<InfoBullet class="info-step__column__bullets__item" is-pro title="Projects" info="3 projects + more on request" />
|
||||
<InfoBullet class="info-step__column__bullets__item" is-pro :title="storagePrice" info="25 GB free included" />
|
||||
<InfoBullet class="info-step__column__bullets__item" is-pro title="Download $0.007 GB" :info="downloadInfo">
|
||||
<template v-if="downloadMoreInfo" #moreInfo>
|
||||
<p class="info-step__column__bullets__message">{{ downloadMoreInfo }}</p>
|
||||
</template>
|
||||
</InfoBullet>
|
||||
<InfoBullet class="info-step__column__bullets__item" is-pro title="Segments" info="$0.0000088 segment per month">
|
||||
<template #moreInfo>
|
||||
<a
|
||||
class="info-step__column__bullets__link"
|
||||
href="https://docs.storj.io/dcs/billing-payment-and-accounts-1/pricing/billing-and-payment"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn more about segments
|
||||
</a>
|
||||
</template>
|
||||
</InfoBullet>
|
||||
<InfoBullet class="info-step__column__bullets__item" is-pro title="Secure Custom Domains (HTTPS)" info="Link sharing with your domain" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UpgradeAccountWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount, ref } from 'vue';
|
||||
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
|
||||
import UpgradeAccountWrapper from '@/components/modals/upgradeAccountFlow/UpgradeAccountWrapper.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import InfoBullet from '@/components/modals/upgradeAccountFlow/InfoBullet.vue';
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
const notify = useNotify();
|
||||
|
||||
const props = defineProps<{
|
||||
onUpgrade: () => void;
|
||||
}>();
|
||||
|
||||
const storagePrice = ref<string>('Storage $0.004 GB / month');
|
||||
const downloadInfo = ref<string>('25 GB free every month');
|
||||
const downloadMoreInfo = ref<string>('');
|
||||
|
||||
/**
|
||||
* Lifecycle hook before initial render.
|
||||
* If applicable, loads additional clarifying text based on user partner.
|
||||
*/
|
||||
onBeforeMount(() => {
|
||||
try {
|
||||
const partner = usersStore.state.user.partner;
|
||||
const config = require('@/components/modals/upgradeAccountFlow/upgradeConfig.json');
|
||||
if (partner && config[partner]) {
|
||||
if (config[partner].storagePrice) {
|
||||
storagePrice.value = config[partner].storagePrice;
|
||||
}
|
||||
|
||||
if (config[partner].downloadInfo) {
|
||||
downloadInfo.value = config[partner].downloadInfo;
|
||||
}
|
||||
|
||||
if (config[partner].downloadMoreInfo) {
|
||||
downloadMoreInfo.value = config[partner].downloadMoreInfo;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
notify.error('No configuration file for page.', null);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.info-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
|
||||
&__column {
|
||||
|
||||
&:first-of-type {
|
||||
@media screen and (max-width: 690px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 31px;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--c-black);
|
||||
text-align: left;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&__bullets {
|
||||
padding: 16px 0 16px 16px;
|
||||
border: 1px solid var(--c-grey-2);
|
||||
border-radius: 8px;
|
||||
margin-top: 16px;
|
||||
width: 280px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&__item:not(:first-of-type) {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: var(--c-white);
|
||||
}
|
||||
|
||||
&__link {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
text-decoration: underline !important;
|
||||
text-underline-position: under;
|
||||
color: var(--c-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<UpgradeAccountWrapper title="Upgrade to Pro">
|
||||
<template #content>
|
||||
<p class="options-info">
|
||||
Add a credit card to activate your Pro Account, or deposit more than $10 in STORJ tokens to upgrade
|
||||
and get 10% bonus on your STORJ tokens deposit.
|
||||
</p>
|
||||
<div class="options-buttons">
|
||||
<VButton
|
||||
label="Add Credit Card"
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
icon="credit-card"
|
||||
:is-green="true"
|
||||
:on-press="onAddCard"
|
||||
:is-disabled="loading"
|
||||
/>
|
||||
<VButton
|
||||
label="Add STORJ Tokens"
|
||||
width="100%"
|
||||
height="48px"
|
||||
font-size="14px"
|
||||
border-radius="10px"
|
||||
icon="addcircle"
|
||||
:on-press="onAddTokens"
|
||||
:is-disabled="loading"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UpgradeAccountWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
|
||||
import UpgradeAccountWrapper from '@/components/modals/upgradeAccountFlow/UpgradeAccountWrapper.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
const notify = useNotify();
|
||||
|
||||
const props = defineProps<{
|
||||
loading: boolean;
|
||||
onAddCard: () => void;
|
||||
onAddTokens: () => Promise<void>;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.options-info {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-blue-6);
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--c-grey-2);
|
||||
margin-bottom: 16px;
|
||||
max-width: 400px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.options-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
|
||||
@media screen and (max-width: 520px) {
|
||||
flex-direction: column;
|
||||
column-gap: unset;
|
||||
row-gap: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1 @@
|
||||
{}
|
@ -31,6 +31,10 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!user.paidTier" tabindex="0" class="account-area__dropdown__item" @click="onUpgrade" @keyup.enter="onUpgrade">
|
||||
<UpgradeIcon />
|
||||
<p class="account-area__dropdown__item__label">Upgrade</p>
|
||||
</div>
|
||||
<div tabindex="0" class="account-area__dropdown__item" @click="navigateToBilling" @keyup.enter="navigateToBilling">
|
||||
<BillingIcon />
|
||||
<p class="account-area__dropdown__item__label">Billing</p>
|
||||
@ -55,7 +59,7 @@ import { RouteConfig } from '@/router';
|
||||
import { AuthHttpApi } from '@/api/auth';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
import { APP_STATE_DROPDOWNS } from '@/utils/constants/appStatePopUps';
|
||||
import { APP_STATE_DROPDOWNS, MODALS } from '@/utils/constants/appStatePopUps';
|
||||
import { useNotify, useRouter } from '@/utils/hooks';
|
||||
import { useABTestingStore } from '@/store/modules/abTestingStore';
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
@ -75,6 +79,7 @@ import SatelliteIcon from '@/../static/images/navigation/satellite.svg';
|
||||
import AccountIcon from '@/../static/images/navigation/account.svg';
|
||||
import ArrowImage from '@/../static/images/navigation/arrowExpandRight.svg';
|
||||
import SettingsIcon from '@/../static/images/navigation/settings.svg';
|
||||
import UpgradeIcon from '@/../static/images/navigation/upgrade.svg';
|
||||
import LogoutIcon from '@/../static/images/navigation/logout.svg';
|
||||
import TierBadgeFree from '@/../static/images/navigation/tierBadgeFree.svg';
|
||||
import TierBadgePro from '@/../static/images/navigation/tierBadgePro.svg';
|
||||
@ -129,6 +134,15 @@ const user = computed((): User => {
|
||||
return usersStore.state.user;
|
||||
});
|
||||
|
||||
/**
|
||||
* Starts upgrade account flow.
|
||||
*/
|
||||
function onUpgrade(): void {
|
||||
closeDropdown();
|
||||
|
||||
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates user to billing page.
|
||||
*/
|
||||
@ -190,9 +204,10 @@ function toggleDropdown(): void {
|
||||
const DROPDOWN_HEIGHT = 224; // pixels
|
||||
const SIXTEEN_PIXELS = 16;
|
||||
const TWENTY_PIXELS = 20;
|
||||
const SEVENTY_PIXELS = 70;
|
||||
const accountContainer = accountArea.value.getBoundingClientRect();
|
||||
|
||||
dropdownYPos.value = accountContainer.bottom - DROPDOWN_HEIGHT - SIXTEEN_PIXELS;
|
||||
dropdownYPos.value = accountContainer.bottom - DROPDOWN_HEIGHT - (usersStore.state.user.paidTier ? SIXTEEN_PIXELS : SEVENTY_PIXELS);
|
||||
dropdownXPos.value = accountContainer.right - TWENTY_PIXELS;
|
||||
|
||||
appStore.toggleActiveDropdown(APP_STATE_DROPDOWNS.ACCOUNT);
|
||||
|
@ -139,6 +139,10 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!user.paidTier" tabindex="0" class="account-area__dropdown__item" @click="onUpgrade" @keyup.enter="onUpgrade">
|
||||
<UpgradeIcon />
|
||||
<p class="account-area__dropdown__item__label">Upgrade</p>
|
||||
</div>
|
||||
<div class="account-area__dropdown__item" @click="navigateToBilling">
|
||||
<BillingIcon />
|
||||
<p class="account-area__dropdown__item__label">Billing</p>
|
||||
@ -194,6 +198,7 @@ import AccountIcon from '@/../static/images/navigation/account.svg';
|
||||
import ArrowIcon from '@/../static/images/navigation/arrowExpandRight.svg';
|
||||
import BillingIcon from '@/../static/images/navigation/billing.svg';
|
||||
import BucketsIcon from '@/../static/images/navigation/buckets.svg';
|
||||
import UpgradeIcon from '@/../static/images/navigation/upgrade.svg';
|
||||
import CheckmarkIcon from '@/../static/images/navigation/checkmark.svg';
|
||||
import CreateProjectIcon from '@/../static/images/navigation/createProject.svg';
|
||||
import InfoIcon from '@/../static/images/navigation/info.svg';
|
||||
@ -439,6 +444,14 @@ function onCreateLinkClick(): void {
|
||||
isProjectDropdownShown.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts upgrade account flow.
|
||||
*/
|
||||
function onUpgrade(): void {
|
||||
isOpened.value = false;
|
||||
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates user to billing page.
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@
|
||||
<p class="overview-area__subtitle">Get started using the web browser, or the command line.</p>
|
||||
<div class="overview-area__routes">
|
||||
<OverviewContainer
|
||||
is-web="true"
|
||||
:is-web="true"
|
||||
title="Start with web browser"
|
||||
info="Start uploading files in the browser and instantly see how your data gets distributed over the Storj network around the world."
|
||||
button-label="Continue in web ->"
|
||||
|
@ -47,10 +47,10 @@ import WebIcon from '@/../static/images/onboardingTour/web.svg';
|
||||
import CLIIcon from '@/../static/images/onboardingTour/cli.svg';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
isWeb: boolean;
|
||||
isWeb?: boolean;
|
||||
title: string;
|
||||
info: string;
|
||||
isDisabled: boolean;
|
||||
isDisabled?: boolean;
|
||||
buttonLabel: string;
|
||||
onClick: () => void;
|
||||
}>(), {
|
||||
|
@ -326,7 +326,7 @@ function recalculateChartWidth(): void {
|
||||
* Holds on upgrade button click logic.
|
||||
*/
|
||||
function onUpgradeClick(): void {
|
||||
appStore.updateActiveModal(MODALS.addPaymentMethod);
|
||||
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,6 @@ import AddTeamMemberModal from '@/components/modals/AddTeamMemberModal.vue';
|
||||
import EditProfileModal from '@/components/modals/EditProfileModal.vue';
|
||||
import ChangePasswordModal from '@/components/modals/ChangePasswordModal.vue';
|
||||
import CreateProjectModal from '@/components/modals/CreateProjectModal.vue';
|
||||
import AddPaymentMethodModal from '@/components/modals/AddPaymentMethodModal.vue';
|
||||
import OpenBucketModal from '@/components/modals/OpenBucketModal.vue';
|
||||
import MFARecoveryCodesModal from '@/components/modals/MFARecoveryCodesModal.vue';
|
||||
import EnableMFAModal from '@/components/modals/EnableMFAModal.vue';
|
||||
@ -30,6 +29,7 @@ import EnterPassphraseModal from '@/components/modals/EnterPassphraseModal.vue';
|
||||
import PricingPlanModal from '@/components/modals/PricingPlanModal.vue';
|
||||
import NewCreateProjectModal from '@/components/modals/NewCreateProjectModal.vue';
|
||||
import EditSessionTimeoutModal from '@/components/modals/EditSessionTimeoutModal.vue';
|
||||
import UpgradeAccountModal from '@/components/modals/upgradeAccountFlow/UpgradeAccountModal.vue';
|
||||
|
||||
export const APP_STATE_DROPDOWNS = {
|
||||
ACCOUNT: 'isAccountDropdownShown',
|
||||
@ -53,7 +53,6 @@ enum Modals {
|
||||
EDIT_PROFILE = 'editProfile',
|
||||
CHANGE_PASSWORD = 'changePassword',
|
||||
CREATE_PROJECT = 'createProject',
|
||||
ADD_PAYMENT_METHOD = 'addPaymentMethod',
|
||||
OPEN_BUCKET = 'openBucket',
|
||||
MFA_RECOVERY = 'mfaRecovery',
|
||||
ENABLE_MFA = 'enableMFA',
|
||||
@ -75,6 +74,7 @@ enum Modals {
|
||||
PRICING_PLAN = 'pricingPlan',
|
||||
NEW_CREATE_PROJECT = 'newCreateProject',
|
||||
EDIT_SESSION_TIMEOUT = 'editSessionTimeout',
|
||||
UPGRADE_ACCOUNT = 'upgradeAccount',
|
||||
}
|
||||
|
||||
// modals could be of VueConstructor type or Object (for composition api components).
|
||||
@ -83,7 +83,6 @@ export const MODALS: Record<Modals, unknown> = {
|
||||
[Modals.EDIT_PROFILE]: EditProfileModal,
|
||||
[Modals.CHANGE_PASSWORD]: ChangePasswordModal,
|
||||
[Modals.CREATE_PROJECT]: CreateProjectModal,
|
||||
[Modals.ADD_PAYMENT_METHOD]: AddPaymentMethodModal,
|
||||
[Modals.OPEN_BUCKET]: OpenBucketModal,
|
||||
[Modals.MFA_RECOVERY]: MFARecoveryCodesModal,
|
||||
[Modals.ENABLE_MFA]: EnableMFAModal,
|
||||
@ -105,4 +104,5 @@ export const MODALS: Record<Modals, unknown> = {
|
||||
[Modals.PRICING_PLAN]: PricingPlanModal,
|
||||
[Modals.NEW_CREATE_PROJECT]: NewCreateProjectModal,
|
||||
[Modals.EDIT_SESSION_TIMEOUT]: EditSessionTimeoutModal,
|
||||
[Modals.UPGRADE_ACCOUNT]: UpgradeAccountModal,
|
||||
};
|
||||
|
@ -657,7 +657,10 @@ async function generateNewMFARecoveryCodes(): Promise<void> {
|
||||
function togglePMModal(): void {
|
||||
isHundredLimitModalShown.value = false;
|
||||
isEightyLimitModalShown.value = false;
|
||||
appStore.updateActiveModal(MODALS.addPaymentMethod);
|
||||
|
||||
if (!usersStore.state.user.paidTier) {
|
||||
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -458,7 +458,10 @@ function closeInactivityModal(): void {
|
||||
function togglePMModal(): void {
|
||||
isHundredLimitModalShown.value = false;
|
||||
isEightyLimitModalShown.value = false;
|
||||
appStore.updateActiveModal(MODALS.addPaymentMethod);
|
||||
|
||||
if (!usersStore.state.user.paidTier) {
|
||||
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" rx="8" fill="#00AC26"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.8271 5.30044C12.1397 5.6127 12.14 6.11923 11.8277 6.43181L7.02717 11.2371C6.87717 11.3873 6.67365 11.4717 6.4614 11.4717C6.24916 11.4718 6.0456 11.3875 5.89552 11.2374L3.70111 9.04302C3.38869 8.7306 3.38869 8.22407 3.70111 7.91165C4.01353 7.59923 4.52006 7.59923 4.83248 7.91165L6.46092 9.54009L10.6958 5.301C11.008 4.98843 11.5146 4.98817 11.8271 5.30044Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 590 B |
@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" rx="8" fill="#929FB1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.8271 5.30044C12.1397 5.6127 12.14 6.11923 11.8277 6.43181L7.02717 11.2371C6.87717 11.3873 6.67365 11.4717 6.4614 11.4717C6.24916 11.4718 6.0456 11.3875 5.89552 11.2374L3.70111 9.04302C3.38869 8.7306 3.38869 8.22407 3.70111 7.91165C4.01353 7.59923 4.52006 7.59923 4.83248 7.91165L6.46092 9.54009L10.6958 5.301C11.008 4.98843 11.5146 4.98817 11.8271 5.30044Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 590 B |
3
web/satellite/static/images/modals/upgradeFlow/info.svg
Normal file
3
web/satellite/static/images/modals/upgradeFlow/info.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.0002 13.3002C3.5208 13.3002 0.700195 10.4796 0.700195 7.0002C0.700195 3.5208 3.5208 0.700195 7.0002 0.700195C10.4796 0.700195 13.3002 3.5208 13.3002 7.0002C13.3002 10.4796 10.4796 13.3002 7.0002 13.3002ZM7.0002 12.1452C9.8417 12.1452 12.1452 9.8417 12.1452 7.0002C12.1452 4.15869 9.8417 1.8552 7.0002 1.8552C4.15869 1.8552 1.8552 4.15869 1.8552 7.0002C1.8552 9.8417 4.15869 12.1452 7.0002 12.1452ZM6.4752 9.15238V7.3005C6.4752 6.98155 6.73375 6.723 7.0527 6.723C7.36386 6.723 7.61755 6.96909 7.62974 7.27727L7.6302 7.3005V9.11395C7.6302 9.43613 7.3747 9.70027 7.0527 9.71098C6.74419 9.72125 6.48577 9.47947 6.4755 9.17096C6.4753 9.16477 6.4752 9.15857 6.4752 9.15238ZM6.4752 4.84738V4.7805C6.4752 4.46155 6.73375 4.203 7.0527 4.203C7.36386 4.203 7.61755 4.44909 7.62974 4.75727L7.6302 4.7805V4.80895C7.6302 5.13113 7.3747 5.39527 7.0527 5.40598C6.74419 5.41625 6.48577 5.17447 6.4755 4.86596C6.4753 4.85977 6.4752 4.85357 6.4752 4.84738Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,5 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4423 0H23.3463C28.8835 0 31.0805 0.613723 33.2353 1.76617C35.3902 2.91861 37.0814 4.60978 38.2338 6.76466L38.3214 6.93056C39.4029 9.00672 39.9846 11.2 40 16.4423V23.3463C40 28.8835 39.3863 31.0805 38.2338 33.2353C37.0814 35.3902 35.3902 37.0814 33.2353 38.2338L33.0694 38.3214C30.9933 39.4029 28.8 39.9846 23.5577 40H16.6537C11.1165 40 8.91954 39.3863 6.76466 38.2338C4.60977 37.0814 2.91861 35.3902 1.76617 33.2353L1.67858 33.0695C0.597074 30.9933 0.0154219 28.8 0 23.5577V16.6537C0 11.1165 0.613723 8.91954 1.76617 6.76466C2.91861 4.60978 4.60977 2.91861 6.76466 1.76617L6.93055 1.67858C9.00672 0.597074 11.2 0.0154219 16.4423 0Z" fill="#00AC26"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.63197 17.0363C10.6491 16.0192 12.2981 16.0192 13.3153 17.0363L19.735 23.456C20.7521 24.4732 20.7521 26.1222 19.735 27.1393C18.7179 28.1565 17.0688 28.1565 16.0517 27.1393L9.63197 20.7196C8.61486 19.7025 8.61486 18.0534 9.63197 17.0363Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.8043 12.6328C31.8215 13.6499 31.8215 15.299 30.8043 16.3161L20 27.1204C18.9829 28.1376 17.3338 28.1376 16.3167 27.1204C15.2996 26.1033 15.2996 24.4543 16.3167 23.4371L27.121 12.6328C28.1382 11.6157 29.7872 11.6157 30.8043 12.6328Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
3
web/satellite/static/images/navigation/upgrade.svg
Normal file
3
web/satellite/static/images/navigation/upgrade.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.9999 0.900391C13.4734 0.900391 17.0999 4.52688 17.0999 9.00039C17.0999 13.4739 13.4734 17.1004 8.9999 17.1004C4.5264 17.1004 0.899902 13.4739 0.899902 9.00039C0.899902 4.52688 4.5264 0.900391 8.9999 0.900391ZM8.9999 2.38539C5.34654 2.38539 2.3849 5.34703 2.3849 9.00039C2.3849 12.6538 5.34654 15.6154 8.9999 15.6154C12.6533 15.6154 15.6149 12.6538 15.6149 9.00039C15.6149 5.34703 12.6533 2.38539 8.9999 2.38539ZM9.4607 4.84557L9.48224 4.86627L12.346 7.73005C12.636 8.02002 12.636 8.49014 12.346 8.78011C12.0631 9.063 11.6088 9.0699 11.3175 8.80081L11.296 8.78011L9.6749 7.15899V12.8479C9.6749 13.258 9.34247 13.5904 8.9324 13.5904C8.53233 13.5904 8.20616 13.274 8.19049 12.8778L8.1899 12.8479V7.2084L6.61846 8.78011C6.33556 9.063 5.88119 9.0699 5.58994 8.80081L5.5684 8.78011C5.28551 8.49722 5.27861 8.04284 5.5477 7.75159L5.5684 7.73005L8.43218 4.86627C8.71508 4.58338 9.16945 4.57648 9.4607 4.84557Z" fill="#56606D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
Loading…
Reference in New Issue
Block a user