Skip to content

Commit

Permalink
web/satellite{/vuetify-poc}: show upgrade dialog when trying to invite
Browse files Browse the repository at this point in the history
This change displays a dialog prompting free tier users to upgrade when
the button to invite project members is clicked.

Also, the Create New Project dialog in the Vuetify UI now opens the
upgrade dialog when its Upgrade button is clicked.

Change-Id: I6e233bd15fd14a486a3e9008bbc6fba3e669d67e
  • Loading branch information
jewharton committed Oct 18, 2023
1 parent 4721d2b commit 24ae793
Show file tree
Hide file tree
Showing 13 changed files with 192 additions and 78 deletions.
41 changes: 34 additions & 7 deletions web/satellite/src/components/modals/AddTeamMemberModal.vue
Expand Up @@ -7,14 +7,22 @@
<div class="modal">
<div class="modal__header">
<TeamMembersIcon />
<h1 class="modal__header__title">Invite team member</h1>
<h1 class="modal__header__title">
{{ isPaidTier ? 'Invite team member' : 'Upgrade to Pro' }}
</h1>
</div>

<p class="modal__info">
Add a team member to contribute to this project.
<template v-if="isPaidTier">
Add a team member to contribute to this project.
</template>
<template v-else>
Upgrade now to unlock collaboration and bring your team together in this project.
</template>
</p>

<VInput
v-if="isPaidTier"
class="modal__input"
label="Email"
height="38px"
Expand All @@ -35,13 +43,17 @@
:on-press="closeModal"
/>
<VButton
label="Invite"
:label="isPaidTier ? 'Invite' : 'Upgrade'"
height="48px"
font-size="14px"
border-radius="10px"
:on-press="onInviteClick"
:on-press="onPrimaryClick"
:is-disabled="!!formError || isLoading"
/>
>
<template v-if="!isPaidTier" #icon-right>
<ArrowIcon />
</template>
</VButton>
</div>
</div>
</template>
Expand All @@ -60,12 +72,14 @@ import { useAppStore } from '@/store/modules/appStore';
import { useProjectsStore } from '@/store/modules/projectsStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { useLoading } from '@/composables/useLoading';
import { MODALS } from '@/utils/constants/appStatePopUps';
import VButton from '@/components/common/VButton.vue';
import VModal from '@/components/common/VModal.vue';
import VInput from '@/components/common/VInput.vue';
import TeamMembersIcon from '@/../static/images/team/teamMembers.svg';
import ArrowIcon from '@/../static/images/onboardingTour/arrowRight.svg';
const analyticsStore = useAnalyticsStore();
const appStore = useAppStore();
Expand All @@ -84,6 +98,7 @@ const email = ref<string>('');
* or a message describing the validation error.
*/
const formError = computed<string | boolean>(() => {
if (!isPaidTier.value) return false;
if (!email.value) return true;
if (email.value.toLocaleLowerCase() === usersStore.state.user.email.toLowerCase()) {
return `You can't add yourself to the project.`;
Expand All @@ -95,9 +110,21 @@ const formError = computed<string | boolean>(() => {
});
/**
* Tries to add the user with the input email to the current project.
* Returns user's paid tier status from store.
*/
async function onInviteClick(): Promise<void> {
const isPaidTier = computed<boolean>(() => {
return usersStore.state.user.paidTier;
});
/**
* Handles primary button click.
*/
async function onPrimaryClick(): Promise<void> {
if (!isPaidTier.value) {
appStore.updateActiveModal(MODALS.upgradeAccount);
return;
}
await withLoading(async () => {
try {
await pmStore.inviteMember(email.value, projectsStore.state.selectedProject.id);
Expand Down
Expand Up @@ -37,7 +37,7 @@
@update:itemsPerPage="onLimitChange"
>
<template #item="{ props: rowProps }">
<v-data-table-row class="pos-relative" v-bind="rowProps">
<v-data-table-row v-bind="rowProps">
<template #item.name="{ item }: ItemSlotProps">
<v-btn
class="rounded-lg w-100 px-1 justify-start font-weight-bold"
Expand Down
Expand Up @@ -3,61 +3,68 @@

<template>
<v-dialog
v-model="model"
:model-value="model && !isUpgradeDialogShown"
width="auto"
max-width="420px"
transition="fade-transition"
:persistent="isLoading"
:scrim="false"
@update:model-value="v => model = v"
>
<v-card rounded="xlg">
<v-sheet>
<v-card-item class="pl-7 py-4">
<template #prepend>
<v-card-title class="font-weight-bold">
Add Member
</v-card-title>
</template>

<template #append>
<v-btn
icon="$close"
variant="text"
size="small"
color="default"
:disabled="isLoading"
@click="model = false"
/>
</template>
</v-card-item>
</v-sheet>
<v-card-item class="pl-7 py-4">
<template #prepend>
<img class="d-block" src="@/../static/images/team/teamMembers.svg" alt="Team members">
</template>

<v-card-title class="font-weight-bold">
{{ isPaidTier ? 'Add Member' : 'Upgrade to Pro' }}
</v-card-title>

<template #append>
<v-btn
icon="$close"
variant="text"
size="small"
color="default"
:disabled="isLoading"
@click="model = false"
/>
</template>
</v-card-item>

<v-divider />

<v-form v-model="valid" class="pa-7 pb-4" @submit.prevent="onInviteClick">
<v-form v-model="valid" class="pa-7 pb-4" @submit.prevent="onPrimaryClick">
<v-row>
<v-col cols="12">
<p class="mb-5">Invite a team member to join you in this project.</p>
<v-alert
variant="tonal"
color="info"
title="Important Information"
text="All team members should use the same passphrase to access the same data."
rounded="lg"
density="comfortable"
border
/>
</v-col>
<v-col cols="12">
<v-text-field
v-model="email"
variant="outlined"
:rules="emailRules"
label="Enter e-mail"
hint="Members will have read & write permissions."
required
autofocus
class="my-2"
/>
<template v-if="isPaidTier">
<v-col cols="12">
<p class="mb-5">Invite a team member to join you in this project.</p>
<v-alert
variant="tonal"
color="info"
title="Important Information"
text="All team members should use the same passphrase to access the same data."
rounded="lg"
density="comfortable"
border
/>
</v-col>
<v-col cols="12">
<v-text-field
v-model="email"
variant="outlined"
:rules="emailRules"
label="Enter e-mail"
hint="Members will have read & write permissions."
required
autofocus
class="my-2"
/>
</v-col>
</template>
<v-col v-else>
Upgrade now to unlock collaboration and bring your team together in this project.
</v-col>
</v-row>
</v-form>
Expand All @@ -70,20 +77,40 @@
<v-btn variant="outlined" color="default" block :disabled="isLoading" @click="model = false">Cancel</v-btn>
</v-col>
<v-col>
<v-btn color="primary" variant="flat" block :loading="isLoading" @click="onInviteClick">Send Invite</v-btn>
<v-btn
color="primary"
variant="flat"
block
:loading="isLoading"
:append-icon="!isPaidTier ? 'mdi-arrow-right' : undefined"
@click="onPrimaryClick"
>
{{ isPaidTier ? 'Send Invite' : 'Upgrade' }}
</v-btn>
</v-col>
</v-row>
</v-card-actions>
</v-card>
</v-dialog>

<upgrade-account-dialog
:scrim="false"
:model-value="model && isUpgradeDialogShown"
@update:model-value="v => model = isUpgradeDialogShown = v"
/>

<teleport to="body">
<v-fade-transition>
<div v-show="model" class="v-overlay__scrim custom-scrim" />
</v-fade-transition>
</teleport>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import {
VDialog,
VCard,
VSheet,
VCardItem,
VCardTitle,
VBtn,
Expand All @@ -94,6 +121,7 @@ import {
VAlert,
VTextField,
VCardActions,
VFadeTransition,
} from 'vuetify/components';
import { RequiredRule, ValidationRule } from '@poc/types/common';
Expand All @@ -102,38 +130,55 @@ import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { useProjectMembersStore } from '@/store/modules/projectMembersStore';
import { useNotify } from '@/utils/hooks';
import { useLoading } from '@/composables/useLoading';
import { useUsersStore } from '@/store/modules/usersStore';
import UpgradeAccountDialog from '@poc/components/dialogs/upgradeAccountFlow/UpgradeAccountDialog.vue';
const props = defineProps<{
modelValue: boolean,
projectId: string,
modelValue: boolean;
projectId: string;
}>();
const emit = defineEmits<{
'update:modelValue': [value: boolean],
'update:modelValue': [value: boolean];
}>();
const model = computed<boolean>({
get: () => props.modelValue,
set: value => emit('update:modelValue', value),
});
const usersStore = useUsersStore();
const analyticsStore = useAnalyticsStore();
const pmStore = useProjectMembersStore();
const notify = useNotify();
const { isLoading, withLoading } = useLoading();
const valid = ref<boolean>(false);
const email = ref<string>('');
const isUpgradeDialogShown = ref<boolean>(false);
const emailRules: ValidationRule<string>[] = [
RequiredRule,
v => ((/.+@.+\..+/.test(v)) || 'E-mail must be valid.'),
];
/**
* Sends a project invitation to the input email.
* Returns user's paid tier status from store.
*/
async function onInviteClick(): Promise<void> {
const isPaidTier = computed<boolean>(() => {
return usersStore.state.user.paidTier;
});
/**
* Handles primary button click.
*/
async function onPrimaryClick(): Promise<void> {
if (!isPaidTier.value) {
isUpgradeDialogShown.value = true;
return;
}
if (!valid.value) return;
await withLoading(async () => {
Expand Down

0 comments on commit 24ae793

Please sign in to comment.