web/satellite: enable customers to purchase packages after onboarding
This change allows users who are eligible to purchase a package plan to do so, even if they missed the opportunity during onboarding. Now, if the user is eligible, an opportunity to select a package for purchase is presented as part of the upgrade modal. Issue: https://github.com/storj/storj/issues/5932 Change-Id: I45575274839701bf7b80815330a4ae86a1d32093
This commit is contained in:
parent
bb620e746b
commit
62e3b2cfe6
@ -6,7 +6,7 @@
|
||||
<template #content>
|
||||
<div v-if="!isSuccess" class="content">
|
||||
<div class="content__top">
|
||||
<h1 class="content__top__title">Activate your account</h1>
|
||||
<h1 class="content__top__title">Activate your plan</h1>
|
||||
<div class="content__top__icon">
|
||||
<CheckIcon />
|
||||
</div>
|
||||
@ -57,7 +57,7 @@
|
||||
<CircleCheck />
|
||||
</div>
|
||||
<h1 class="content-success__title">Success</h1>
|
||||
<p class="content-success__subtitle">Your account has been successfully activated.</p>
|
||||
<p class="content-success__subtitle">Your plan has been successfully activated.</p>
|
||||
<div class="content-success__info">
|
||||
<ThinCheck class="content-success__info__icon" />
|
||||
<p class="content-success__info__title">
|
||||
@ -135,10 +135,14 @@ const isFree = computed((): boolean => {
|
||||
});
|
||||
|
||||
/**
|
||||
* Closes the modal and advances to the next step in the onboarding tour.
|
||||
* Closes the modal. If the user has not completed the onboarding tour, advance to the next step.
|
||||
*/
|
||||
function onClose(): void {
|
||||
appStore.removeActiveModal();
|
||||
// do not reroute if the user has already completed onboarding
|
||||
if (usersStore.state.settings.onboardingEnd) {
|
||||
return;
|
||||
}
|
||||
if (isSuccess.value) {
|
||||
if (configStore.state.config.allProjectsDashboard) {
|
||||
router.push(RouteConfig.AllProjectsDashboard.path);
|
||||
|
@ -0,0 +1,108 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<UpgradeAccountWrapper title="Upgrade">
|
||||
<template #content>
|
||||
<div class="pricing-area">
|
||||
<VLoader v-if="isLoading" class="pricing-area__loader" width="90px" height="90px" />
|
||||
<template v-else>
|
||||
<div class="pricing-area__plans">
|
||||
<PricingPlanContainer
|
||||
v-for="(plan, index) in plans"
|
||||
:key="index"
|
||||
:plan="plan"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</UpgradeAccountWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { PricingPlanInfo, PricingPlanType } from '@/types/common';
|
||||
import { User } from '@/types/users';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
|
||||
import UpgradeAccountWrapper from '@/components/modals/upgradeAccountFlow/UpgradeAccountWrapper.vue';
|
||||
import PricingPlanContainer from '@/components/account/billing/pricingPlans/PricingPlanContainer.vue';
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
const router = useRouter();
|
||||
const notify = useNotify();
|
||||
|
||||
const isLoading = ref<boolean>(true);
|
||||
|
||||
const plans = ref<PricingPlanInfo[]>([
|
||||
new PricingPlanInfo(
|
||||
PricingPlanType.PRO,
|
||||
'Pro Account',
|
||||
'25 GB Free',
|
||||
'Only pay for what you need. $4/TB stored per month* $7/TB for egress bandwidth.',
|
||||
'*Additional per-segment fee of $0.0000088 applies.',
|
||||
null,
|
||||
null,
|
||||
'Add a credit card to activate your Pro Account.<br><br>Get 25GB free storage and egress. Only pay for what you use beyond that.',
|
||||
'No charge today',
|
||||
'25GB Free',
|
||||
),
|
||||
]);
|
||||
|
||||
/*
|
||||
* Loads pricing plan config. Assumes that user is already eligible for a plan prior to component being mounted.
|
||||
*/
|
||||
onBeforeMount(async () => {
|
||||
const user: User = usersStore.state.user;
|
||||
|
||||
let config;
|
||||
try {
|
||||
config = require('@/components/account/billing/pricingPlans/pricingPlanConfig.json');
|
||||
} catch {
|
||||
notify.error('No pricing plan configuration file.', null);
|
||||
return;
|
||||
}
|
||||
|
||||
const plan = config[user.partner] as PricingPlanInfo;
|
||||
if (!plan) {
|
||||
notify.error(`No pricing plan configuration for partner '${user.partner}'.`, null);
|
||||
return;
|
||||
}
|
||||
plan.type = PricingPlanType.PARTNER;
|
||||
plans.value.unshift(plan);
|
||||
|
||||
isLoading.value = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pricing-area {
|
||||
|
||||
&__loader {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__plans {
|
||||
margin-top: 41px;
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (width <= 963px) {
|
||||
|
||||
.pricing-area__plans {
|
||||
max-width: 444px;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -6,7 +6,8 @@
|
||||
<template #content>
|
||||
<UpgradeInfoStep
|
||||
v-if="step === UpgradeAccountStep.Info"
|
||||
:on-upgrade="() => setStep(UpgradeAccountStep.Options)"
|
||||
:on-upgrade="setSecondStep"
|
||||
:loading="loading"
|
||||
/>
|
||||
<UpgradeOptionsStep
|
||||
v-if="step === UpgradeAccountStep.Options"
|
||||
@ -23,6 +24,9 @@
|
||||
v-if="step === UpgradeAccountStep.Success"
|
||||
:on-continue="closeModal"
|
||||
/>
|
||||
<PricingPlanStep
|
||||
v-if="step === UpgradeAccountStep.PricingPlan"
|
||||
/>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
@ -30,9 +34,12 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
import { useAppStore } from '@/store/modules/appStore';
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { PaymentsHttpApi } from '@/api/payments';
|
||||
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
|
||||
import { AnalyticsHttpApi } from '@/api/analytics';
|
||||
|
||||
@ -42,6 +49,7 @@ import UpgradeOptionsStep from '@/components/modals/upgradeAccountFlow/UpgradeOp
|
||||
import AddCreditCardStep from '@/components/modals/upgradeAccountFlow/AddCreditCardStep.vue';
|
||||
import SuccessStep from '@/components/modals/upgradeAccountFlow/SuccessStep.vue';
|
||||
import AddTokensStep from '@/components/modals/upgradeAccountFlow/AddTokensStep.vue';
|
||||
import PricingPlanStep from '@/components/modals/upgradeAccountFlow/PricingPlanStep.vue';
|
||||
|
||||
enum UpgradeAccountStep {
|
||||
Info = 'infoStep',
|
||||
@ -49,11 +57,15 @@ enum UpgradeAccountStep {
|
||||
AddCC = 'addCCStep',
|
||||
AddTokens = 'addTokensStep',
|
||||
Success = 'successStep',
|
||||
PricingPlan = 'pricingPlanStep',
|
||||
}
|
||||
|
||||
const configStore = useConfigStore();
|
||||
const appStore = useAppStore();
|
||||
const usersStore = useUsersStore();
|
||||
const billingStore = useBillingStore();
|
||||
const notify = useNotify();
|
||||
const payments: PaymentsHttpApi = new PaymentsHttpApi();
|
||||
|
||||
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
|
||||
|
||||
@ -88,6 +100,44 @@ function setStep(s: UpgradeAccountStep) {
|
||||
step.value = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets second step in the flow (after user clicks to upgrade).
|
||||
* Most users will go to the Options step, but if a user is eligible for a
|
||||
* pricing plan (and pricing plans are enabled), they will be sent to the PricingPlan step.
|
||||
*/
|
||||
async function setSecondStep() {
|
||||
if (loading.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const user: User = usersStore.state.user;
|
||||
const pricingPkgsEnabled = configStore.state.config.pricingPackagesEnabled;
|
||||
if (!pricingPkgsEnabled || !user.partner) {
|
||||
setStep(UpgradeAccountStep.Options);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let pkgAvailable = false;
|
||||
try {
|
||||
pkgAvailable = await payments.pricingPackageAvailable();
|
||||
} catch (error) {
|
||||
notify.error(error.message, null);
|
||||
setStep(UpgradeAccountStep.Options);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
if (!pkgAvailable) {
|
||||
setStep(UpgradeAccountStep.Options);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
setStep(UpgradeAccountStep.PricingPlan);
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes upgrade account modal.
|
||||
*/
|
||||
|
@ -34,6 +34,7 @@
|
||||
height="48px"
|
||||
:is-green="true"
|
||||
:on-press="onUpgrade"
|
||||
:is-disabled="loading"
|
||||
/>
|
||||
<div class="info-step__column__bullets">
|
||||
<InfoBullet class="info-step__column__bullets__item" is-pro title="Projects" info="3 projects + more on request" />
|
||||
@ -77,6 +78,7 @@ const usersStore = useUsersStore();
|
||||
const notify = useNotify();
|
||||
|
||||
const props = defineProps<{
|
||||
loading: boolean;
|
||||
onUpgrade: () => void;
|
||||
}>();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user