web/satellite/vuetify-poc: add upgrade account dialog
This change adds the account upgrade dialog with the first information step. It allows a user to toggle on this dialog from the account dropdown or the dashboard. Issue: https://github.com/storj/storj/issues/6288 https://github.com/storj/storj/issues/6292 Change-Id: Ide87612994c999759150c8aa85ead3866e9df1f5
This commit is contained in:
parent
c14e4b1eb4
commit
1e3da9f276
@ -14,7 +14,7 @@
|
||||
</v-col>
|
||||
<v-col>
|
||||
<h4 class="text-right">{{ available }}</h4>
|
||||
<p class="text-right text-medium-emphasis"><small>{{ cta }}</small></p>
|
||||
<p class="text-cursor-pointer text-right text-medium-emphasis" @click="emit('ctaClick')"><small>{{ cta }}</small></p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-item>
|
||||
@ -32,4 +32,8 @@ const props = defineProps<{
|
||||
available: string;
|
||||
cta: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
ctaClick: [];
|
||||
}>();
|
||||
</script>
|
||||
|
@ -48,7 +48,7 @@
|
||||
</v-form>
|
||||
</v-card-item>
|
||||
<v-card-item class="px-8 py-0">
|
||||
<a class="text-decoration-underline" style="cursor: pointer;" @click="toggleRecoveryCodeState">
|
||||
<a class="text-decoration-underline text-cursor-pointer" @click="toggleRecoveryCodeState">
|
||||
{{ useRecoveryCode ? "or use 2FA code" : "or use a recovery code" }}
|
||||
</a>
|
||||
</v-card-item>
|
||||
|
@ -49,7 +49,7 @@
|
||||
</v-form>
|
||||
</v-card-item>
|
||||
<v-card-item class="px-8 py-0">
|
||||
<a class="text-decoration-underline" style="cursor: pointer;" @click="toggleRecoveryCodeState">
|
||||
<a class="text-decoration-underline text-cursor-pointer" @click="toggleRecoveryCodeState">
|
||||
{{ useRecoveryCode ? "or use 2FA code" : "or use a recovery code" }}
|
||||
</a>
|
||||
</v-card-item>
|
||||
|
@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<v-row class="pa-0 flex-nowrap">
|
||||
<v-col class="pa-2" cols="1">
|
||||
<img v-if="!isPro" src="@/../static/images/modals/upgradeFlow/greyCheckmark.svg" alt="checkmark">
|
||||
<img v-else src="@/../static/images/modals/upgradeFlow/greenCheckmark.svg" alt="checkmark">
|
||||
</v-col>
|
||||
<v-col class="pa-2" cols="11">
|
||||
<p class="font-weight-bold">
|
||||
{{ title }}
|
||||
<v-tooltip v-if="$slots.moreInfo" max-width="200px" location="top" activator="parent">
|
||||
<slot name="moreInfo" />
|
||||
</v-tooltip>
|
||||
</p>
|
||||
<p>{{ info }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VCol, VRow, VTooltip } from 'vuetify/components';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
isPro?: boolean;
|
||||
title: string;
|
||||
info: string;
|
||||
}>(), {
|
||||
isPro: false,
|
||||
title: '',
|
||||
info: '',
|
||||
});
|
||||
</script>
|
@ -0,0 +1,181 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<v-dialog
|
||||
v-model="model"
|
||||
width="auto"
|
||||
scrollable
|
||||
min-width="460px"
|
||||
:max-width="step === UpgradeAccountStep.Info || step === UpgradeAccountStep.PricingPlanSelection ? '700px' : '460px'"
|
||||
transition="fade-transition"
|
||||
:persistent="loading"
|
||||
>
|
||||
<v-card ref="content" rounded="xlg">
|
||||
<v-card-item class="pl-7 py-4">
|
||||
<template v-if="step === UpgradeAccountStep.Success" #prepend>
|
||||
<img class="d-block" src="@/../static/images/modals/upgradeFlow/success.svg" alt="success">
|
||||
</template>
|
||||
<v-card-title class="font-weight-bold">{{ stepTitles[step] }}</v-card-title>
|
||||
<template #append>
|
||||
<v-btn
|
||||
icon="$close"
|
||||
variant="text"
|
||||
size="small"
|
||||
color="default"
|
||||
@click="model = false"
|
||||
/>
|
||||
</template>
|
||||
</v-card-item>
|
||||
|
||||
<v-divider class="mx-8" />
|
||||
|
||||
<v-card-item class="px-8 py-4">
|
||||
<v-window v-model="step">
|
||||
<v-window-item :value="UpgradeAccountStep.Info">
|
||||
<UpgradeInfoStep
|
||||
:loading="loading"
|
||||
@upgrade="setSecondStep"
|
||||
/>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-card-item>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { VBtn, VCard, VCardItem, VCardTitle, VDialog, VDivider, VWindow, VWindowItem } from 'vuetify/components';
|
||||
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
import { useAppStore } from '@poc/store/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 { User } from '@/types/users';
|
||||
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
|
||||
import { PricingPlanInfo } from '@/types/common';
|
||||
|
||||
import UpgradeInfoStep from '@poc/components/dialogs/upgradeAccountFlow/UpgradeInfoStep.vue';
|
||||
|
||||
enum UpgradeAccountStep {
|
||||
Info = 'infoStep',
|
||||
Options = 'optionsStep',
|
||||
AddCC = 'addCCStep',
|
||||
AddTokens = 'addTokensStep',
|
||||
Success = 'successStep',
|
||||
PricingPlanSelection = 'pricingPlanSelectionStep',
|
||||
PricingPlan = 'pricingPlanStep',
|
||||
}
|
||||
|
||||
const analyticsStore = useAnalyticsStore();
|
||||
const configStore = useConfigStore();
|
||||
const appStore = useAppStore();
|
||||
const usersStore = useUsersStore();
|
||||
const billingStore = useBillingStore();
|
||||
const notify = useNotify();
|
||||
const payments: PaymentsHttpApi = new PaymentsHttpApi();
|
||||
|
||||
const step = ref<UpgradeAccountStep>(UpgradeAccountStep.Info);
|
||||
const loading = ref<boolean>(false);
|
||||
const plan = ref<PricingPlanInfo | null>(null);
|
||||
const content = ref<HTMLElement | null>(null);
|
||||
|
||||
const model = computed<boolean>({
|
||||
get: () => appStore.state.isUpgradeFlowDialogShown,
|
||||
set: value => appStore.toggleUpgradeFlow(value),
|
||||
});
|
||||
|
||||
const stepTitles = computed(() => {
|
||||
return {
|
||||
[UpgradeAccountStep.Info]: 'Your account',
|
||||
[UpgradeAccountStep.Options]: 'Upgrade to Pro',
|
||||
[UpgradeAccountStep.AddCC]: 'Add Credit Card',
|
||||
[UpgradeAccountStep.AddTokens]: 'Add tokens',
|
||||
[UpgradeAccountStep.Success]: 'Success',
|
||||
[UpgradeAccountStep.PricingPlanSelection]: 'Upgrade',
|
||||
[UpgradeAccountStep.PricingPlan]: plan.value?.title || '',
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Claims wallet and sets add token step.
|
||||
*/
|
||||
async function onAddTokens(): Promise<void> {
|
||||
if (loading.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
await billingStore.claimWallet();
|
||||
|
||||
analyticsStore.eventTriggered(AnalyticsEvent.ADD_FUNDS_CLICKED);
|
||||
|
||||
setStep(UpgradeAccountStep.AddTokens);
|
||||
} catch (error) {
|
||||
notify.notifyError(error, AnalyticsErrorEventSource.UPGRADE_ACCOUNT_MODAL);
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets specific flow step.
|
||||
*/
|
||||
function setStep(s: UpgradeAccountStep) {
|
||||
step.value = s;
|
||||
}
|
||||
|
||||
function onSelectPricingPlan(p: PricingPlanInfo) {
|
||||
plan.value = p;
|
||||
setStep(UpgradeAccountStep.PricingPlan);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.notifyError(error, null);
|
||||
setStep(UpgradeAccountStep.Options);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
if (!pkgAvailable) {
|
||||
setStep(UpgradeAccountStep.Options);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
setStep(UpgradeAccountStep.PricingPlanSelection);
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
watch(content, (value) => {
|
||||
if (!value) {
|
||||
setStep(UpgradeAccountStep.Info);
|
||||
plan.value = null;
|
||||
}
|
||||
});
|
||||
</script>
|
@ -0,0 +1,139 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col v-if="!smAndDown" cols="6">
|
||||
<h4 class="font-weight-bold mb-2">Free</h4>
|
||||
<v-btn
|
||||
block
|
||||
disabled
|
||||
color="grey"
|
||||
>
|
||||
Current
|
||||
</v-btn>
|
||||
<div class="border-sm rounded-lg pa-4 mt-3 mb-3">
|
||||
<InfoBullet title="Projects" :info="freeProjects" />
|
||||
<InfoBullet title="Storage" :info="`${freeUsageValue(user.projectStorageLimit)} limit`" />
|
||||
<InfoBullet title="Egress" :info="`${freeUsageValue(user.projectBandwidthLimit)} limit`" />
|
||||
<InfoBullet title="Segments" :info="`${user.projectSegmentLimit.toLocaleString()} segments limit`" />
|
||||
<InfoBullet title="Link Sharing" info="Link sharing with Storj domain" />
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col :cols="smAndDown ? 12 : '6'">
|
||||
<h4 class="font-weight-bold mb-2">Pro Account</h4>
|
||||
<v-btn
|
||||
class="mb-1"
|
||||
block
|
||||
color="success"
|
||||
:loading="loading"
|
||||
@click="emit('upgrade')"
|
||||
>
|
||||
Upgrade to Pro
|
||||
</v-btn>
|
||||
<div class="border-sm rounded-lg pa-4 mt-3 mb-3">
|
||||
<InfoBullet is-pro title="Projects" :info="projectsInfo" />
|
||||
<InfoBullet is-pro :title="storagePrice" :info="storagePriceInfo" />
|
||||
<InfoBullet is-pro :title="downloadPrice" :info="downloadInfo">
|
||||
<template v-if="downloadMoreInfo" #moreInfo>
|
||||
<p>{{ downloadMoreInfo }}</p>
|
||||
</template>
|
||||
</InfoBullet>
|
||||
<InfoBullet is-pro title="Segments" :info="segmentInfo">
|
||||
<template #moreInfo>
|
||||
<a
|
||||
class="text-surface"
|
||||
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 is-pro title="Secure Custom Domains (HTTPS)" info="Link sharing with your domain" />
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeMount, ref } from 'vue';
|
||||
import { VBtn, VCol, VRow } from 'vuetify/components';
|
||||
import { useDisplay } from 'vuetify';
|
||||
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { User } from '@/types/users';
|
||||
import { Size } from '@/utils/bytesSize';
|
||||
|
||||
import InfoBullet from '@poc/components/dialogs/upgradeAccountFlow/InfoBullet.vue';
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
const notify = useNotify();
|
||||
const { smAndDown } = useDisplay();
|
||||
|
||||
const props = defineProps<{
|
||||
loading: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
upgrade: [];
|
||||
}>();
|
||||
|
||||
const storagePrice = ref<string>('Storage $0.004 GB / month');
|
||||
const storagePriceInfo = ref<string>('25 GB free included');
|
||||
const segmentInfo = ref<string>('$0.0000088 segment per month');
|
||||
const projectsInfo = ref<string>('3 projects + more on request');
|
||||
const downloadPrice = ref<string>('Egress $0.007 GB');
|
||||
const downloadInfo = ref<string>('25 GB free every month');
|
||||
const downloadMoreInfo = ref<string>('');
|
||||
|
||||
/**
|
||||
* Returns user entity from store.
|
||||
*/
|
||||
const user = computed((): User => {
|
||||
return usersStore.state.user;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns formatted free projects count.
|
||||
*/
|
||||
const freeProjects = computed((): string => {
|
||||
return `${user.value.projectLimit} project${user.value.projectLimit > 1 ? 's' : ''}`;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns formatted free usage value.
|
||||
*/
|
||||
function freeUsageValue(value: number): string {
|
||||
const size = new Size(value);
|
||||
return `${size.formattedBytes} ${size.label}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook before initial render.
|
||||
* If applicable, loads additional clarifying text based on user partner.
|
||||
*/
|
||||
onBeforeMount(async () => {
|
||||
try {
|
||||
const partner = usersStore.state.user.partner;
|
||||
const config = (await import('@poc/components/dialogs/upgradeAccountFlow/upgradeConfig.json')).default;
|
||||
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>
|
@ -0,0 +1 @@
|
||||
{}
|
@ -7,6 +7,8 @@
|
||||
<default-bar show-nav-drawer-button />
|
||||
<account-nav />
|
||||
<default-view />
|
||||
|
||||
<UpgradeAccountDialog />
|
||||
</session-wrapper>
|
||||
</v-app>
|
||||
</template>
|
||||
@ -25,6 +27,7 @@ import { useNotify } from '@/utils/hooks';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import SessionWrapper from '@poc/components/utils/SessionWrapper.vue';
|
||||
import UpgradeAccountDialog from '@poc/components/dialogs/upgradeAccountFlow/UpgradeAccountDialog.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const usersStore = useUsersStore();
|
||||
|
@ -6,6 +6,8 @@
|
||||
<session-wrapper>
|
||||
<default-bar />
|
||||
<default-view />
|
||||
|
||||
<UpgradeAccountDialog />
|
||||
</session-wrapper>
|
||||
</v-app>
|
||||
</template>
|
||||
@ -22,6 +24,7 @@ import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
|
||||
import SessionWrapper from '@poc/components/utils/SessionWrapper.vue';
|
||||
import UpgradeAccountDialog from '@poc/components/dialogs/upgradeAccountFlow/UpgradeAccountDialog.vue';
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
const notify = useNotify();
|
||||
|
@ -106,7 +106,7 @@
|
||||
|
||||
<v-divider class="my-2" />
|
||||
|
||||
<v-list-item link class="my-1 rounded-lg" @click="closeSideNav">
|
||||
<v-list-item v-if="!isPaidTier" link class="my-1 rounded-lg" @click="toggleUpgradeFlow">
|
||||
<template #prepend>
|
||||
<img src="@poc/assets/icon-upgrade.svg" alt="Upgrade">
|
||||
</template>
|
||||
@ -245,6 +245,11 @@ function closeSideNav(): void {
|
||||
if (mdAndDown.value) appStore.toggleNavigationDrawer(false);
|
||||
}
|
||||
|
||||
function toggleUpgradeFlow(): void {
|
||||
closeSideNav();
|
||||
appStore.toggleUpgradeFlow(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs out user and navigates to login page.
|
||||
*/
|
||||
|
@ -10,6 +10,8 @@
|
||||
<default-bar show-nav-drawer-button />
|
||||
<ProjectNav />
|
||||
<default-view />
|
||||
|
||||
<UpgradeAccountDialog />
|
||||
</session-wrapper>
|
||||
</v-app>
|
||||
</template>
|
||||
@ -36,6 +38,7 @@ import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
|
||||
import SessionWrapper from '@poc/components/utils/SessionWrapper.vue';
|
||||
import UpgradeAccountDialog from '@poc/components/dialogs/upgradeAccountFlow/UpgradeAccountDialog.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
@ -6,6 +6,7 @@ import { reactive } from 'vue';
|
||||
|
||||
class AppState {
|
||||
public isNavigationDrawerShown = true;
|
||||
public isUpgradeFlowDialogShown = false;
|
||||
public pathBeforeAccountPage: string | null = null;
|
||||
}
|
||||
|
||||
@ -16,18 +17,24 @@ export const useAppStore = defineStore('vuetifyApp', () => {
|
||||
state.isNavigationDrawerShown = isShown ?? !state.isNavigationDrawerShown;
|
||||
}
|
||||
|
||||
function toggleUpgradeFlow(isShown?: boolean): void {
|
||||
state.isUpgradeFlowDialogShown = isShown ?? !state.isUpgradeFlowDialogShown;
|
||||
}
|
||||
|
||||
function setPathBeforeAccountPage(path: string) {
|
||||
state.pathBeforeAccountPage = path;
|
||||
}
|
||||
|
||||
function clear(): void {
|
||||
state.isNavigationDrawerShown = true;
|
||||
state.isUpgradeFlowDialogShown = false;
|
||||
state.pathBeforeAccountPage = null;
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
toggleNavigationDrawer,
|
||||
toggleUpgradeFlow,
|
||||
setPathBeforeAccountPage,
|
||||
clear,
|
||||
};
|
||||
|
@ -318,4 +318,9 @@ table {
|
||||
// Positions
|
||||
.pos-relative {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
// text styles
|
||||
.text-cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
@ -3,11 +3,20 @@
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<PageTitleComponent title="Project Overview" />
|
||||
<PageSubtitleComponent
|
||||
:subtitle="`Your ${limits.objectCount.toLocaleString()} files are stored in ${limits.segmentCount.toLocaleString()} segments around the world.`"
|
||||
link="https://docs.storj.io/dcs/pricing#per-segment-fee"
|
||||
/>
|
||||
<v-row align="center" justify="space-between">
|
||||
<v-col cols="12" md="auto">
|
||||
<PageTitleComponent title="Project Overview" />
|
||||
<PageSubtitleComponent
|
||||
:subtitle="`Your ${limits.objectCount.toLocaleString()} files are stored in ${limits.segmentCount.toLocaleString()} segments around the world.`"
|
||||
link="https://docs.storj.io/dcs/pricing#per-segment-fee"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col v-if="!isPaidTier" cols="auto">
|
||||
<v-btn @click="appStore.toggleUpgradeFlow(true)">
|
||||
Upgrade plan
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row class="d-flex align-center justify-center mt-2">
|
||||
<v-col cols="12" md="6">
|
||||
@ -59,10 +68,10 @@
|
||||
|
||||
<v-row class="d-flex align-center justify-center">
|
||||
<v-col cols="12" md="6">
|
||||
<UsageProgressComponent title="Storage" :progress="storageUsedPercent" :used="`${usedLimitFormatted(limits.storageUsed)} Used`" :limit="`Limit: ${usedLimitFormatted(limits.storageLimit)}`" :available="`${usedLimitFormatted(availableStorage)} Available`" cta="Need more?" />
|
||||
<UsageProgressComponent title="Storage" :progress="storageUsedPercent" :used="`${usedLimitFormatted(limits.storageUsed)} Used`" :limit="`Limit: ${usedLimitFormatted(limits.storageLimit)}`" :available="`${usedLimitFormatted(availableStorage)} Available`" cta="Need more?" @cta-click="onNeedMoreClicked(LimitToChange.Storage)" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<UsageProgressComponent title="Download" :progress="egressUsedPercent" :used="`${usedLimitFormatted(limits.bandwidthUsed)} Used`" :limit="`Limit: ${usedLimitFormatted(limits.bandwidthLimit)}`" :available="`${usedLimitFormatted(availableEgress)} Available`" cta="Need more?" />
|
||||
<UsageProgressComponent title="Download" :progress="egressUsedPercent" :used="`${usedLimitFormatted(limits.bandwidthUsed)} Used`" :limit="`Limit: ${usedLimitFormatted(limits.bandwidthLimit)}`" :available="`${usedLimitFormatted(availableEgress)} Available`" cta="Need more?" @cta-click="onNeedMoreClicked(LimitToChange.Bandwidth)" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<UsageProgressComponent title="Segments" :progress="segmentUsedPercent" :used="`${limits.segmentUsed} Used`" :limit="`Limit: ${limits.segmentLimit}`" :available="`${availableSegment} Available`" cta="Learn more" />
|
||||
@ -76,11 +85,13 @@
|
||||
<buckets-data-table />
|
||||
</v-col>
|
||||
</v-container>
|
||||
|
||||
<edit-project-limit-dialog v-model="isEditLimitDialogShown" :limit-type="limitToChange" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { VContainer, VRow, VCol, VCard, VCardTitle } from 'vuetify/components';
|
||||
import { VBtn, VCard, VCardTitle, VCol, VContainer, VRow } from 'vuetify/components';
|
||||
import { ComponentPublicInstance } from '@vue/runtime-core';
|
||||
|
||||
import { useUsersStore } from '@/store/modules/usersStore';
|
||||
@ -89,11 +100,12 @@ import { useProjectMembersStore } from '@/store/modules/projectMembersStore';
|
||||
import { useAccessGrantsStore } from '@/store/modules/accessGrantsStore';
|
||||
import { useBillingStore } from '@/store/modules/billingStore';
|
||||
import { useBucketsStore } from '@/store/modules/bucketsStore';
|
||||
import { DataStamp, Project, ProjectLimits } from '@/types/projects';
|
||||
import { DataStamp, LimitToChange, Project, ProjectLimits } from '@/types/projects';
|
||||
import { Dimensions, Size } from '@/utils/bytesSize';
|
||||
import { ChartUtils } from '@/utils/chart';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { useAppStore } from '@poc/store/appStore';
|
||||
|
||||
import PageTitleComponent from '@poc/components/PageTitleComponent.vue';
|
||||
import PageSubtitleComponent from '@poc/components/PageSubtitleComponent.vue';
|
||||
@ -102,7 +114,9 @@ import UsageProgressComponent from '@poc/components/UsageProgressComponent.vue';
|
||||
import BandwidthChart from '@/components/project/dashboard/BandwidthChart.vue';
|
||||
import StorageChart from '@/components/project/dashboard/StorageChart.vue';
|
||||
import BucketsDataTable from '@poc/components/BucketsDataTable.vue';
|
||||
import EditProjectLimitDialog from '@poc/components/dialogs/EditProjectLimitDialog.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const usersStore = useUsersStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const pmStore = useProjectMembersStore();
|
||||
@ -114,6 +128,8 @@ const notify = useNotify();
|
||||
|
||||
const chartWidth = ref<number>(0);
|
||||
const chartContainer = ref<ComponentPublicInstance>();
|
||||
const isEditLimitDialogShown = ref<boolean>(false);
|
||||
const limitToChange = ref<LimitToChange>(LimitToChange.Storage);
|
||||
|
||||
/**
|
||||
* Returns percent of coupon used.
|
||||
@ -144,6 +160,13 @@ const couponRemainingPercent = computed((): number => {
|
||||
return 100 - couponProgress.value;
|
||||
});
|
||||
|
||||
/**
|
||||
* Whether the user is in paid tier.
|
||||
*/
|
||||
const isPaidTier = computed((): boolean => {
|
||||
return usersStore.state.user.paidTier;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns formatted amount.
|
||||
*/
|
||||
@ -294,6 +317,20 @@ function getDimension(dataStamps: DataStamp[]): Dimensions {
|
||||
return new Size(maxValue).label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally opens the upgrade dialog
|
||||
* or the edit limit dialog.
|
||||
*/
|
||||
function onNeedMoreClicked(source: LimitToChange): void {
|
||||
if (!usersStore.state.user.paidTier) {
|
||||
appStore.toggleUpgradeFlow(true);
|
||||
return;
|
||||
}
|
||||
|
||||
limitToChange.value = source;
|
||||
isEditLimitDialogShown.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
* Fetches project limits.
|
||||
|
Loading…
Reference in New Issue
Block a user