web/satellite/vuetify-poc: enable/disable billing features depending on config value
Added client side logic to disable billing features depending on config value. Issue: https://github.com/storj/storj-private/issues/464 Change-Id: I80ead8c91a39a387a1651efc700ca2d2341b6e0f
This commit is contained in:
parent
5c49ba1d85
commit
b0a52f4b51
@ -32,7 +32,7 @@
|
||||
/>
|
||||
|
||||
<v-banner
|
||||
v-if="isLowBalance && parentRef"
|
||||
v-if="isLowBalance && parentRef && billingEnabled"
|
||||
class="all-dashboard-banners__low-balance"
|
||||
message="Your STORJ Token balance is low. Deposit more STORJ tokens or add a credit card to avoid interruptions in service."
|
||||
link-text="Go to billing"
|
||||
|
@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<v-card-title class="font-weight-bold">
|
||||
{{ !isProjectLimitReached ? 'Create New Project' : 'Get More Projects' }}
|
||||
{{ isProjectLimitReached && billingEnabled ? 'Get More Projects' : 'Create New Project' }}
|
||||
</v-card-title>
|
||||
|
||||
<template #append>
|
||||
@ -36,7 +36,10 @@
|
||||
|
||||
<v-form v-model="formValid" class="pa-7" @submit.prevent>
|
||||
<v-row>
|
||||
<template v-if="!isProjectLimitReached">
|
||||
<v-col v-if="isProjectLimitReached && billingEnabled">
|
||||
Upgrade to Pro Account to create more projects and gain access to higher limits.
|
||||
</v-col>
|
||||
<template v-else>
|
||||
<v-col cols="12">
|
||||
Projects are where you and your team can upload and manage data, and view usage statistics and billing.
|
||||
</v-col>
|
||||
@ -76,9 +79,6 @@
|
||||
/>
|
||||
</v-col>
|
||||
</template>
|
||||
<v-col v-else>
|
||||
Upgrade to Pro Account to create more projects and gain access to higher limits.
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
|
||||
@ -97,10 +97,10 @@
|
||||
variant="flat"
|
||||
:loading="isLoading"
|
||||
block
|
||||
:append-icon="isProjectLimitReached ? 'mdi-arrow-right' : undefined"
|
||||
:append-icon="isProjectLimitReached && billingEnabled ? 'mdi-arrow-right' : undefined"
|
||||
@click="onPrimaryClick"
|
||||
>
|
||||
{{ !isProjectLimitReached ? 'Create Project' : 'Upgrade' }}
|
||||
{{ isProjectLimitReached && billingEnabled ? 'Upgrade' : 'Create Project' }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@ -146,6 +146,8 @@ import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
import { useAppStore } from '@poc/store/appStore';
|
||||
|
||||
import UpgradeAccountDialog from '@poc/components/dialogs/upgradeAccountFlow/UpgradeAccountDialog.vue';
|
||||
|
||||
@ -164,6 +166,9 @@ const model = computed<boolean>({
|
||||
|
||||
const projectsStore = useProjectsStore();
|
||||
const usersStore = useUsersStore();
|
||||
const configStore = useConfigStore();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const { isLoading, withLoading } = useLoading();
|
||||
const notify = useNotify();
|
||||
const router = useRouter();
|
||||
@ -184,11 +189,21 @@ const descriptionRules: ValidationRule<string>[] = [
|
||||
v => v.length <= MAX_DESCRIPTION_LENGTH || 'Description is too long',
|
||||
];
|
||||
|
||||
/**
|
||||
* Indicates if billing features are enabled.
|
||||
*/
|
||||
const billingEnabled = computed<boolean>(() => configStore.state.config.billingFeaturesEnabled);
|
||||
|
||||
function startUpgradeFlow(): void {
|
||||
model.value = false;
|
||||
appStore.toggleUpgradeFlow(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles primary button click.
|
||||
*/
|
||||
async function onPrimaryClick(): Promise<void> {
|
||||
if (isProjectLimitReached.value) {
|
||||
if (isProjectLimitReached.value && billingEnabled.value) {
|
||||
isUpgradeDialogShown.value = true;
|
||||
return;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
</template>
|
||||
</navigation-item>
|
||||
|
||||
<navigation-item title="Account Billing" to="billing">
|
||||
<navigation-item v-if="billingEnabled" title="Account Billing" to="billing">
|
||||
<template #prepend>
|
||||
<icon-card />
|
||||
</template>
|
||||
@ -54,6 +54,7 @@ import { useDisplay } from 'vuetify';
|
||||
|
||||
import { useAppStore } from '@poc/store/appStore';
|
||||
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
|
||||
import IconCard from '@poc/components/icons/IconCard.vue';
|
||||
import IconSettings from '@poc/components/icons/IconSettings.vue';
|
||||
@ -62,6 +63,7 @@ import NavigationItem from '@poc/layouts/default/NavigationItem.vue';
|
||||
|
||||
const analyticsStore = useAnalyticsStore();
|
||||
const appStore = useAppStore();
|
||||
const configStore = useConfigStore();
|
||||
|
||||
const { mdAndDown } = useDisplay();
|
||||
|
||||
@ -70,6 +72,11 @@ const model = computed<boolean>({
|
||||
set: value => appStore.toggleNavigationDrawer(value),
|
||||
});
|
||||
|
||||
/**
|
||||
* Indicates if billing features are enabled.
|
||||
*/
|
||||
const billingEnabled = computed<boolean>(() => configStore.state.config.billingFeaturesEnabled);
|
||||
|
||||
/**
|
||||
* Returns the path to the most recent non-account-related page.
|
||||
*/
|
||||
|
@ -87,7 +87,7 @@
|
||||
|
||||
<!-- My Account Menu -->
|
||||
<v-list class="px-2 rounded-lg">
|
||||
<v-list-item class="py-2 rounded-lg">
|
||||
<v-list-item v-if="billingEnabled" class="py-2 rounded-lg">
|
||||
<!-- <template #prepend>
|
||||
<icon-team size="18"></icon-team>
|
||||
</template> -->
|
||||
@ -104,16 +104,18 @@
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item v-if="!isPaidTier" link class="my-1 rounded-lg" @click="toggleUpgradeFlow">
|
||||
<template #prepend>
|
||||
<icon-upgrade size="18" />
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2 ml-3">
|
||||
Upgrade to Pro
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<template v-if="billingEnabled">
|
||||
<v-list-item v-if="!isPaidTier" link class="my-1 rounded-lg" @click="toggleUpgradeFlow">
|
||||
<template #prepend>
|
||||
<icon-upgrade size="18" />
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2 ml-3">
|
||||
Upgrade to Pro
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider class="my-2" />
|
||||
<v-divider class="my-2" />
|
||||
</template>
|
||||
|
||||
<v-list-item class="py-2 rounded-lg">
|
||||
<template #prepend>
|
||||
@ -127,7 +129,7 @@
|
||||
|
||||
<v-divider class="my-2" />
|
||||
|
||||
<v-list-item link class="my-1 rounded-lg" router-link to="/account/billing" @click="closeSideNav">
|
||||
<v-list-item v-if="billingEnabled" link class="my-1 rounded-lg" router-link to="/account/billing" @click="closeSideNav">
|
||||
<template #prepend>
|
||||
<icon-card size="18" />
|
||||
</template>
|
||||
@ -231,6 +233,11 @@ const props = withDefaults(defineProps<{
|
||||
showNavDrawerButton: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* Indicates if billing features are enabled.
|
||||
*/
|
||||
const billingEnabled = computed<boolean>(() => configStore.state.config.billingFeaturesEnabled);
|
||||
|
||||
/**
|
||||
* Returns the name of the current satellite.
|
||||
*/
|
||||
|
@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount, ref, watch } from 'vue';
|
||||
import { computed, onBeforeMount, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { VApp, VProgressCircular } from 'vuetify/components';
|
||||
|
||||
@ -36,6 +36,7 @@ import { useAnalyticsStore } from '@/store/modules/analyticsStore';
|
||||
import { useAccessGrantsStore } from '@/store/modules/accessGrantsStore';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
|
||||
import SessionWrapper from '@poc/components/utils/SessionWrapper.vue';
|
||||
import UpgradeAccountDialog from '@poc/components/dialogs/upgradeAccountFlow/UpgradeAccountDialog.vue';
|
||||
@ -51,9 +52,15 @@ const abTestingStore = useABTestingStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const appStore = useAppStore();
|
||||
const agStore = useAccessGrantsStore();
|
||||
const configStore = useConfigStore();
|
||||
|
||||
const isLoading = ref<boolean>(true);
|
||||
|
||||
/**
|
||||
* Indicates if billing features are enabled.
|
||||
*/
|
||||
const billingEnabled = computed<boolean>(() => configStore.state.config.billingFeaturesEnabled);
|
||||
|
||||
/**
|
||||
* Selects the project with the given URL ID, redirecting to the
|
||||
* all projects dashboard if no such project exists.
|
||||
@ -121,11 +128,13 @@ onBeforeMount(async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await billingStore.setupAccount();
|
||||
} catch (error) {
|
||||
error.message = `Unable to setup account. ${error.message}`;
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
if (billingEnabled.value) {
|
||||
try {
|
||||
await billingStore.setupAccount();
|
||||
} catch (error) {
|
||||
error.message = `Unable to setup account. ${error.message}`;
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.OVERALL_APP_WRAPPER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
await selectProject(route.params.id as string);
|
||||
|
@ -104,6 +104,16 @@ export const router = createRouter({
|
||||
routes,
|
||||
});
|
||||
|
||||
router.beforeEach((to, _, next) => {
|
||||
const configStore = useConfigStore();
|
||||
if (!configStore.state.config.billingFeaturesEnabled && to.name === RouteName.Billing) {
|
||||
next({ name: RouteName.AccountSettings });
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
export function startTitleWatcher(): void {
|
||||
const projectsStore = useProjectsStore();
|
||||
const configStore = useConfigStore();
|
||||
|
@ -32,14 +32,14 @@
|
||||
link="https://docs.storj.io/dcs/pricing#per-segment-fee"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col v-if="!isPaidTier" cols="auto">
|
||||
<v-col v-if="!isPaidTier && billingEnabled" cols="auto">
|
||||
<v-btn @click="appStore.toggleUpgradeFlow(true)">
|
||||
Upgrade
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row class="d-flex align-center justify-center mt-2">
|
||||
<v-row class="d-flex align-center mt-2">
|
||||
<v-col cols="12" sm="6" md="4" lg="2">
|
||||
<CardStatsComponent icon="file" title="Files" subtitle="Project files" :data="limits.objectCount.toLocaleString()" to="buckets" />
|
||||
</v-col>
|
||||
@ -55,7 +55,7 @@
|
||||
<v-col cols="12" sm="6" md="4" lg="2">
|
||||
<CardStatsComponent icon="team" title="Team" subtitle="Project members" :data="teamSize.toLocaleString()" to="team" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" lg="2">
|
||||
<v-col v-if="billingEnabled" cols="12" sm="6" md="4" lg="2">
|
||||
<CardStatsComponent icon="card" title="Billing" :subtitle="`${paidTierString} account`" :data="paidTierString" to="/account/billing" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
@ -135,13 +135,13 @@
|
||||
:used="`${limits.segmentUsed.toLocaleString()} Used`"
|
||||
:limit="`Limit: ${limits.segmentLimit.toLocaleString()}`"
|
||||
:available="`${availableSegment.toLocaleString()} Available`"
|
||||
:cta="isPaidTier ? 'Learn more' : 'Need more?'"
|
||||
:cta="!isPaidTier && billingEnabled ? 'Need more?' : 'Learn more'"
|
||||
@cta-click="onSegmentsCTAClicked"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<UsageProgressComponent
|
||||
v-if="billingStore.state.coupon"
|
||||
v-if="billingStore.state.coupon && billingEnabled"
|
||||
icon="check"
|
||||
:title="isFreeTierCoupon ? 'Free Usage' : 'Coupon'"
|
||||
:progress="couponProgress"
|
||||
@ -184,6 +184,9 @@ import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { useAppStore } from '@poc/store/appStore';
|
||||
import { LocalData } from '@/utils/localData';
|
||||
import { ProjectMembersPage } from '@/types/projectMembers';
|
||||
import { AccessGrantsPage } from '@/types/accessGrants';
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
|
||||
import PageTitleComponent from '@poc/components/PageTitleComponent.vue';
|
||||
import PageSubtitleComponent from '@poc/components/PageSubtitleComponent.vue';
|
||||
@ -205,6 +208,7 @@ const pmStore = useProjectMembersStore();
|
||||
const agStore = useAccessGrantsStore();
|
||||
const billingStore = useBillingStore();
|
||||
const bucketsStore = useBucketsStore();
|
||||
const configStore = useConfigStore();
|
||||
|
||||
const notify = useNotify();
|
||||
const router = useRouter();
|
||||
@ -218,6 +222,11 @@ const limitToChange = ref<LimitToChange>(LimitToChange.Storage);
|
||||
const isCreateBucketDialogShown = ref<boolean>(false);
|
||||
const isSetPassphraseDialogShown = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* Indicates if billing features are enabled.
|
||||
*/
|
||||
const billingEnabled = computed<boolean>(() => configStore.state.config.billingFeaturesEnabled);
|
||||
|
||||
/**
|
||||
* Returns percent of coupon used.
|
||||
*/
|
||||
@ -267,25 +276,6 @@ const isPaidTier = computed((): boolean => {
|
||||
return usersStore.state.user.paidTier;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns formatted amount.
|
||||
*/
|
||||
function usedLimitFormatted(value: number): string {
|
||||
return formattedValue(new Size(value, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats value to needed form and returns it.
|
||||
*/
|
||||
function formattedValue(value: Size): string {
|
||||
switch (value.label) {
|
||||
case Dimensions.Bytes:
|
||||
return '0';
|
||||
default:
|
||||
return `${value.formattedBytes.replace(/\.0+$/, '')}${value.label}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user account tier string.
|
||||
*/
|
||||
@ -409,6 +399,25 @@ const promptForPassphrase = computed((): boolean => {
|
||||
return bucketsStore.state.promptForPassphrase;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns formatted amount.
|
||||
*/
|
||||
function usedLimitFormatted(value: number): string {
|
||||
return formattedValue(new Size(value, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats value to needed form and returns it.
|
||||
*/
|
||||
function formattedValue(value: Size): string {
|
||||
switch (value.label) {
|
||||
case Dimensions.Bytes:
|
||||
return '0';
|
||||
default:
|
||||
return `${value.formattedBytes.replace(/\.0+$/, '')}${value.label}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used container size recalculation for charts resizing.
|
||||
*/
|
||||
@ -429,7 +438,7 @@ function getDimension(dataStamps: DataStamp[]): Dimensions {
|
||||
* or the edit limit dialog.
|
||||
*/
|
||||
function onNeedMoreClicked(source: LimitToChange): void {
|
||||
if (!isPaidTier.value) {
|
||||
if (!isPaidTier.value && billingEnabled.value) {
|
||||
appStore.toggleUpgradeFlow(true);
|
||||
return;
|
||||
}
|
||||
@ -442,7 +451,7 @@ function onNeedMoreClicked(source: LimitToChange): void {
|
||||
* Conditionally opens the upgrade dialog or docs link.
|
||||
*/
|
||||
function onSegmentsCTAClicked(): void {
|
||||
if (!isPaidTier.value) {
|
||||
if (!isPaidTier.value && billingEnabled.value) {
|
||||
appStore.toggleUpgradeFlow(true);
|
||||
return;
|
||||
}
|
||||
@ -477,16 +486,24 @@ onMounted(async (): Promise<void> => {
|
||||
const past = new Date();
|
||||
past.setDate(past.getDate() - 30);
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
projectsStore.getDailyProjectData({ since: past, before: now }),
|
||||
projectsStore.getProjectLimits(projectID),
|
||||
let promises: Promise<void | ProjectMembersPage | AccessGrantsPage>[] = [
|
||||
projectsStore.getDailyProjectData({ since: past, before: now }),
|
||||
projectsStore.getProjectLimits(projectID),
|
||||
pmStore.getProjectMembers(FIRST_PAGE, projectID),
|
||||
agStore.getAccessGrants(FIRST_PAGE, projectID),
|
||||
bucketsStore.getBuckets(FIRST_PAGE, projectID),
|
||||
];
|
||||
|
||||
if (billingEnabled.value) {
|
||||
promises = [
|
||||
...promises,
|
||||
billingStore.getProjectUsageAndChargesCurrentRollup(),
|
||||
billingStore.getCoupon(),
|
||||
pmStore.getProjectMembers(FIRST_PAGE, projectID),
|
||||
agStore.getAccessGrants(FIRST_PAGE, projectID),
|
||||
bucketsStore.getBuckets(FIRST_PAGE, projectID),
|
||||
]);
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
} catch (error) {
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.PROJECT_DASHBOARD_PAGE);
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-list-item v-if="!isPaidTier">
|
||||
<v-list-item v-if="!isPaidTier && billingEnabled">
|
||||
<v-list-item-title>Free Account</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ storageLimitFormatted }} Storage / {{ bandwidthLimitFormatted }} Bandwidth.
|
||||
@ -160,6 +160,11 @@ const configStore = useConfigStore();
|
||||
|
||||
const notify = useNotify();
|
||||
|
||||
/**
|
||||
* Indicates if billing features are enabled.
|
||||
*/
|
||||
const billingEnabled = computed<boolean>(() => configStore.state.config.billingFeaturesEnabled);
|
||||
|
||||
/**
|
||||
* Returns selected project from the store.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user