Skip to content

Commit

Permalink
web/satellite: free trial UI updates
Browse files Browse the repository at this point in the history
Rename Free Account to Free Trial.
Remove included free storage and download in upgrade modal.
Move the Storage and Download price to the description in upgrade modal.
Rename disabled button (Current) in the Free Account plan to show the days remaining in the free trial.
After trial_expiration has passed, but before "trial expiration freeze" has occurred, continue to display the "your trial is expiring soon" banner in the UI.
Update signup page to have "Start your free trial" as the call to action instead of "Create Account".

Issue:
storj/storj-private#643

Change-Id: I792270ad3d296ca3fd1c2231a2c48d4033fa6c1c
  • Loading branch information
VitaliiShpital authored and Storj Robot committed Mar 21, 2024
1 parent cb733d6 commit 03a9d0f
Show file tree
Hide file tree
Showing 11 changed files with 38 additions and 34 deletions.
Expand Up @@ -7,7 +7,7 @@ export class SignupPageObjects {
static INPUT_PASSWORD_XPATH = `//input[@id='Password']`;
static INPUT_RETYPE_PASSWORD_XPATH = `//input[@id='Retype Password']`;
static TOS_CHECKMARK_XPATH = `//input[@id='Terms checkbox']`;
static CREATE_ACCOUNT_BUTTON_XPATH = `//button[span[text()=' Create your account ']]`;
static CREATE_ACCOUNT_BUTTON_XPATH = `//button[span[text()=' Start your free trial ']]`;
static HEADER_TEXT_XPATH = `//h1[.='Start using Storj today.']`;
static SUBHEADER_TEXT_XPATH = `//p[contains(text(),'Whether migrating your data or just testing out')]`;

Expand Down
Expand Up @@ -65,7 +65,7 @@ const plans = ref<PricingPlanInfo[]>([
),
]);
/*
/**
* Loads pricing plan config. Assumes that user is already eligible for a plan prior to component being mounted.
*/
onBeforeMount(async () => {
Expand All @@ -74,7 +74,7 @@ onBeforeMount(async () => {
...plans.value,
new PricingPlanInfo(
PricingPlanType.FREE,
'Free Account',
'Free Trial',
'Limited',
'Free usage up to 25GB storage and 25GB egress bandwidth per month.',
null,
Expand Down
Expand Up @@ -4,13 +4,13 @@
<template>
<v-row>
<v-col v-if="!smAndDown" cols="6">
<h4 class="font-weight-bold my-2">Free Account</h4>
<h4 class="font-weight-bold my-2">Free Trial</h4>
<v-btn
block
disabled
color="default"
>
Current
{{ freeTrialButtonLabel }}
</v-btn>
<v-card class="my-4">
<InfoBullet title="Projects" :info="freeProjects" />
Expand Down Expand Up @@ -68,6 +68,7 @@ import { mdiArrowRight } from '@mdi/js';
import { useUsersStore } from '@/store/modules/usersStore';
import { useNotify } from '@/utils/hooks';
import { useTrialCheck } from '@/composables/useTrialCheck';
import { User } from '@/types/users';
import { Size } from '@/utils/bytesSize';
Expand All @@ -76,23 +77,33 @@ import InfoBullet from '@/components/dialogs/upgradeAccountFlow/InfoBullet.vue';
const usersStore = useUsersStore();
const notify = useNotify();
const { smAndDown } = useDisplay();
const { isExpired, expirationInfo } = useTrialCheck();
const props = defineProps<{
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 storagePrice = ref<string>('Storage');
const storagePriceInfo = ref<string>('$0.004 GB / month');
const segmentInfo = ref<string>('$0.0000088 segment per month');
const projectsInfo = ref<string>('3 projects + more on request');
const downloadPrice = ref<string>('Download $0.007 GB');
const downloadInfo = ref<string>('25 GB free every month');
const downloadPrice = ref<string>('Download');
const downloadInfo = ref<string>('$0.007 GB');
const downloadMoreInfo = ref<string>('');
/**
* Returns free trial button label based on expiration status.
*/
const freeTrialButtonLabel = computed<string>(() => {
if (isExpired.value) return 'Trial Expired';
return `${expirationInfo.value.days} day${expirationInfo.value.days !== 1 ? 's' : ''} remaining`;
});
/**
* Returns user entity from store.
*/
Expand Down Expand Up @@ -124,8 +135,8 @@ onBeforeMount(async () => {
const partner = usersStore.state.user.partner;
const config = (await import('@/configs/upgradeConfig.json')).default;
if (partner && config[partner]) {
if (config[partner].storagePrice) {
storagePrice.value = config[partner].storagePrice;
if (config[partner].storagePriceInfo) {
storagePriceInfo.value = config[partner].storagePriceInfo;
}
if (config[partner].downloadInfo) {
Expand Down
Expand Up @@ -48,13 +48,11 @@ import { VBtn, VCol, VIcon, VRow, VAlert } from 'vuetify/components';
import { mdiCreditCard, mdiPlusCircle } from '@mdi/js';
import { useUsersStore } from '@/store/modules/usersStore';
import { useNotify } from '@/utils/hooks';
import { User } from '@/types/users';
const usersStore = useUsersStore();
const notify = useNotify();
const props = defineProps<{
defineProps<{
loading: boolean;
}>();
Expand Down
11 changes: 5 additions & 6 deletions web/satellite/src/composables/useTrialCheck.ts
Expand Up @@ -6,28 +6,26 @@ import { computed } from 'vue';
import { useUsersStore } from '@/store/modules/usersStore';
import { useAppStore } from '@/store/modules/appStore';
import { useConfigStore } from '@/store/modules/configStore';
import { User } from '@/types/users';
import { ExpirationInfo, User } from '@/types/users';

export function useTrialCheck() {
const userStore = useUsersStore();
const appStore = useAppStore();
const configStore = useConfigStore();

const user = computed<User>(() => userStore.state.user);
const expirationInfo = computed<ExpirationInfo>(() => user.value.getExpirationInfo(configStore.state.config.daysBeforeTrialEndNotification));

const isTrialExpirationBanner = computed<boolean>(() => {
if (user.value.paidTier) return false;

const expirationInfo = user.value.getExpirationInfo(configStore.state.config.daysBeforeTrialEndNotification);

return user.value.freezeStatus.trialExpiredFrozen || expirationInfo.isCloseToExpiredTrial;
return user.value.freezeStatus.trialExpiredFrozen || expirationInfo.value.isCloseToExpiredTrial;
});

const isExpired = computed<boolean>(() => user.value.freezeStatus.trialExpiredFrozen);

function withTrialCheck(callback: () => void | Promise<void>): void {
const user = userStore.state.user;
if (!user.paidTier && user.freezeStatus.trialExpiredFrozen) {
if (!user.value.paidTier && user.value.freezeStatus.trialExpiredFrozen) {
appStore.toggleExpirationDialog(true);
return;
}
Expand All @@ -38,6 +36,7 @@ export function useTrialCheck() {
return {
isTrialExpirationBanner,
isExpired,
expirationInfo,
withTrialCheck,
};
}
2 changes: 1 addition & 1 deletion web/satellite/src/layouts/default/AppBar.vue
Expand Up @@ -98,7 +98,7 @@
size="small"
rounded
>
{{ isPaidTier ? 'Pro Account' : 'Free Account' }}
{{ isPaidTier ? 'Pro Account' : 'Free Trial' }}
</v-chip>
</v-list-item-title>
</v-list-item>
Expand Down
7 changes: 3 additions & 4 deletions web/satellite/src/layouts/default/AuthBar.vue
Expand Up @@ -83,20 +83,19 @@
</template>

<script setup lang="ts">
import { useTheme } from 'vuetify';
import { onBeforeMount, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { VAppBar, VAppBarTitle, VBtn, VBtnToggle, VIcon, VImg, VMenu, VTooltip } from 'vuetify/components';
import { useTheme } from 'vuetify';
import { VAppBar, VBtn, VBtnToggle, VIcon, VTooltip } from 'vuetify/components';
import { mdiWeatherNight, mdiWeatherSunny } from '@mdi/js';
import { PartnerConfig } from '@/types/partners';
import { ROUTES } from '@/router';
const route = useRoute();
const theme = useTheme();
const activeTheme = ref(0);
const menu = ref(false);
const partnerConfig = ref<PartnerConfig | null>(null);
function toggleTheme(newTheme: string): void {
Expand Down
2 changes: 1 addition & 1 deletion web/satellite/src/types/users.ts
Expand Up @@ -139,7 +139,7 @@ export class User {
const daysBeforeNotifyInMilliseconds = daysBeforeNotify * millisecondsInDay;

return {
isCloseToExpiredTrial: diff > 0 && diff < daysBeforeNotifyInMilliseconds,
isCloseToExpiredTrial: diff < daysBeforeNotifyInMilliseconds,
days: Math.round(Math.abs(diff) / millisecondsInDay),
};
}
Expand Down
2 changes: 1 addition & 1 deletion web/satellite/src/views/AccountSettings.vue
Expand Up @@ -56,7 +56,7 @@
size="small"
rounded
>
{{ isPaidTier ? 'Pro Account' : 'Free Account' }}
{{ isPaidTier ? 'Pro Account' : 'Free Trial' }}
</v-chip>
<v-divider class="my-4" />
<v-btn v-if="isPaidTier" variant="outlined" color="default" size="small" :to="ROUTES.Billing.path">
Expand Down
2 changes: 1 addition & 1 deletion web/satellite/src/views/ProjectSettings.vue
Expand Up @@ -63,7 +63,7 @@

<v-row v-if="!isPaidTier && billingEnabled">
<v-col cols="12">
<v-card title="Free Account" variant="outlined" :border="true" rounded="xlg">
<v-card title="Free Trial" variant="outlined" border rounded="xlg">
<v-card-subtitle>
{{ storageLimitFormatted }} Storage / {{ bandwidthLimitFormatted }} Bandwidth. <br>
Need more? Upgrade to Pro Account.
Expand Down
7 changes: 2 additions & 5 deletions web/satellite/src/views/Signup.vue
Expand Up @@ -176,7 +176,7 @@
size="large"
block
>
Create your account
Start your free trial
</v-btn>
</v-form>
</v-card-text>
Expand Down Expand Up @@ -292,8 +292,6 @@ import { useRoute, useRouter } from 'vue-router';
import { mdiCheckBold } from '@mdi/js';
import { useConfigStore } from '@/store/modules/configStore';
import { useAppStore } from '@/store/modules/appStore';
import { useUsersStore } from '@/store/modules/usersStore';
import { EmailRule, RequiredRule, ValidationRule } from '@/types/common';
import { MultiCaptchaConfig } from '@/types/config.gen';
import { PartnerConfig } from '@/types/partners';
Expand All @@ -311,8 +309,7 @@ const auth = new AuthHttpApi();
const analyticsStore = useAnalyticsStore();
const configStore = useConfigStore();
const appStore = useAppStore();
const usersStore = useUsersStore();
const router = useRouter();
const notify = useNotify();
const route = useRoute();
Expand Down

0 comments on commit 03a9d0f

Please sign in to comment.