web/satellite/vuetify: added analytics events for currently implemented functionality

Populated already implemented functionality with analytics events.

Issue:
https://github.com/storj/storj/issues/6122

Change-Id: I1e250ca02debc9bedbf78024f74e0dbfa9dfbcb9
This commit is contained in:
Vitalii 2023-08-03 15:06:37 +03:00 committed by Storj Robot
parent e2e437dd95
commit a4f7f0634d
10 changed files with 91 additions and 17 deletions

View File

@ -98,6 +98,8 @@ import { ProjectItemModel, PROJECT_ROLE_COLORS } from '@poc/types/projects';
import { ProjectInvitationResponse } from '@/types/projects'; import { ProjectInvitationResponse } from '@/types/projects';
import { ProjectRole } from '@/types/projectMembers'; import { ProjectRole } from '@/types/projectMembers';
import { useProjectsStore } from '@/store/modules/projectsStore'; import { useProjectsStore } from '@/store/modules/projectsStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import IconProject from '@poc/components/icons/IconProject.vue'; import IconProject from '@poc/components/icons/IconProject.vue';
import IconSettings from '@poc/components/icons/IconSettings.vue'; import IconSettings from '@poc/components/icons/IconSettings.vue';
@ -111,6 +113,7 @@ const emit = defineEmits<{
(event: 'joinClick'): void; (event: 'joinClick'): void;
}>(); }>();
const analyticsStore = useAnalyticsStore();
const projectsStore = useProjectsStore(); const projectsStore = useProjectsStore();
const router = useRouter(); const router = useRouter();
@ -123,6 +126,7 @@ function openProject(): void {
if (!props.item) return; if (!props.item) return;
projectsStore.selectProject(props.item.id); projectsStore.selectProject(props.item.id);
router.push(`/projects/${props.item.id}/dashboard`); router.push(`/projects/${props.item.id}/dashboard`);
analyticsStore.pageVisit('/projects/dashboard');
} }
/** /**
@ -132,7 +136,11 @@ async function declineInvitation(): Promise<void> {
if (!props.item || isDeclining.value) return; if (!props.item || isDeclining.value) return;
isDeclining.value = true; isDeclining.value = true;
await projectsStore.respondToInvitation(props.item.id, ProjectInvitationResponse.Decline).catch(_ => {}); try {
await projectsStore.respondToInvitation(props.item.id, ProjectInvitationResponse.Decline);
analyticsStore.eventTriggered(AnalyticsEvent.PROJECT_INVITATION_DECLINED);
} catch { /* empty */ }
await projectsStore.getUserInvitations().catch(_ => {}); await projectsStore.getUserInvitations().catch(_ => {});
await projectsStore.getProjects().catch(_ => {}); await projectsStore.getProjects().catch(_ => {});

View File

@ -130,6 +130,8 @@ import { ProjectInvitationResponse } from '@/types/projects';
import { ProjectRole } from '@/types/projectMembers'; import { ProjectRole } from '@/types/projectMembers';
import { SHORT_MONTHS_NAMES } from '@/utils/constants/date'; import { SHORT_MONTHS_NAMES } from '@/utils/constants/date';
import { useProjectsStore } from '@/store/modules/projectsStore'; import { useProjectsStore } from '@/store/modules/projectsStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import IconSettings from '@poc/components/icons/IconSettings.vue'; import IconSettings from '@poc/components/icons/IconSettings.vue';
import IconTeam from '@poc/components/icons/IconTeam.vue'; import IconTeam from '@poc/components/icons/IconTeam.vue';
@ -145,6 +147,7 @@ const emit = defineEmits<{
const search = ref<string>(''); const search = ref<string>('');
const decliningIds = ref(new Set<string>()); const decliningIds = ref(new Set<string>());
const analyticsStore = useAnalyticsStore();
const projectsStore = useProjectsStore(); const projectsStore = useProjectsStore();
const router = useRouter(); const router = useRouter();
@ -170,6 +173,7 @@ function getFormattedDate(date: Date): string {
function openProject(item: ProjectItemModel): void { function openProject(item: ProjectItemModel): void {
projectsStore.selectProject(item.id); projectsStore.selectProject(item.id);
router.push(`/projects/${item.id}/dashboard`); router.push(`/projects/${item.id}/dashboard`);
analyticsStore.pageVisit('/projects/dashboard');
} }
/** /**
@ -179,7 +183,11 @@ async function declineInvitation(item: ProjectItemModel): Promise<void> {
if (decliningIds.value.has(item.id)) return; if (decliningIds.value.has(item.id)) return;
decliningIds.value.add(item.id); decliningIds.value.add(item.id);
await projectsStore.respondToInvitation(item.id, ProjectInvitationResponse.Decline).catch(_ => {}); try {
await projectsStore.respondToInvitation(item.id, ProjectInvitationResponse.Decline);
analyticsStore.eventTriggered(AnalyticsEvent.PROJECT_INVITATION_DECLINED);
} catch { /* empty */ }
await projectsStore.getUserInvitations().catch(_ => {}); await projectsStore.getUserInvitations().catch(_ => {});
await projectsStore.getProjects().catch(_ => {}); await projectsStore.getProjects().catch(_ => {});

View File

@ -93,11 +93,14 @@ import {
import { useLoading } from '@/composables/useLoading'; import { useLoading } from '@/composables/useLoading';
import { useUsersStore } from '@/store/modules/usersStore'; import { useUsersStore } from '@/store/modules/usersStore';
import { UpdatedUser } from '@/types/users'; import { UpdatedUser } from '@/types/users';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
const rules = [ const rules = [
(value: string) => (!!value || 'Can\'t be empty'), (value: string) => (!!value || 'Can\'t be empty'),
]; ];
const analyticsStore = useAnalyticsStore();
const userStore = useUsersStore(); const userStore = useUsersStore();
const { isLoading, withLoading } = useLoading(); const { isLoading, withLoading } = useLoading();
@ -125,6 +128,8 @@ async function onChangeName(): Promise<void> {
await withLoading(async () => { await withLoading(async () => {
try { try {
await userStore.updateUser(new UpdatedUser(name.value, name.value)); await userStore.updateUser(new UpdatedUser(name.value, name.value));
analyticsStore.eventTriggered(AnalyticsEvent.PROFILE_UPDATED);
} catch (error) { } catch (error) {
return; return;
} }

View File

@ -119,6 +119,8 @@ import { useLoading } from '@/composables/useLoading';
import { useConfigStore } from '@/store/modules/configStore'; import { useConfigStore } from '@/store/modules/configStore';
import { AuthHttpApi } from '@/api/auth'; import { AuthHttpApi } from '@/api/auth';
import { RouteConfig } from '@/types/router'; import { RouteConfig } from '@/types/router';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
const DELAY_BEFORE_REDIRECT = 2000; // 2 sec const DELAY_BEFORE_REDIRECT = 2000; // 2 sec
const auth: AuthHttpApi = new AuthHttpApi(); const auth: AuthHttpApi = new AuthHttpApi();
@ -134,6 +136,7 @@ const repeatRules = [
(value: string) => (value && value === newPassword.value || 'Passwords are not the same.'), (value: string) => (value && value === newPassword.value || 'Passwords are not the same.'),
]; ];
const analyticsStore = useAnalyticsStore();
const { config } = useConfigStore().state; const { config } = useConfigStore().state;
const { isLoading, withLoading } = useLoading(); const { isLoading, withLoading } = useLoading();
const router = useRouter(); const router = useRouter();
@ -163,6 +166,8 @@ async function onChangePassword(): Promise<void> {
await withLoading(async () => { await withLoading(async () => {
try { try {
await auth.changePassword(oldPassword.value, newPassword.value); await auth.changePassword(oldPassword.value, newPassword.value);
analyticsStore.eventTriggered(AnalyticsEvent.PASSWORD_CHANGED);
} catch (error) { } catch (error) {
return; return;
} }

View File

@ -162,6 +162,8 @@ import QRCode from 'qrcode';
import { useLoading } from '@/composables/useLoading'; import { useLoading } from '@/composables/useLoading';
import { useConfigStore } from '@/store/modules/configStore'; import { useConfigStore } from '@/store/modules/configStore';
import { useUsersStore } from '@/store/modules/usersStore'; import { useUsersStore } from '@/store/modules/usersStore';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
const rules = [ const rules = [
(value: string) => (!!value || 'Can\'t be empty'), (value: string) => (!!value || 'Can\'t be empty'),
@ -170,6 +172,7 @@ const rules = [
(value: string) => (value.length === 6 || 'Can only be 6 numbers long'), (value: string) => (value.length === 6 || 'Can only be 6 numbers long'),
]; ];
const analyticsStore = useAnalyticsStore();
const { config } = useConfigStore().state; const { config } = useConfigStore().state;
const usersStore = useUsersStore(); const usersStore = useUsersStore();
const { isLoading, withLoading } = useLoading(); const { isLoading, withLoading } = useLoading();
@ -234,6 +237,8 @@ function enable(): void {
await usersStore.enableUserMFA(confirmPasscode.value); await usersStore.enableUserMFA(confirmPasscode.value);
await usersStore.getUser(); await usersStore.getUser();
await showCodes(); await showCodes();
analyticsStore.eventTriggered(AnalyticsEvent.MFA_ENABLED);
} catch (error) { } catch (error) {
isError.value = true; isError.value = true;
} }

View File

@ -77,6 +77,9 @@ import {
import { ProjectInvitationResponse } from '@/types/projects'; import { ProjectInvitationResponse } from '@/types/projects';
import { useProjectsStore } from '@/store/modules/projectsStore'; import { useProjectsStore } from '@/store/modules/projectsStore';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { RouteConfig } from '@/types/router';
const props = defineProps<{ const props = defineProps<{
modelValue: boolean, modelValue: boolean,
@ -93,6 +96,7 @@ const emit = defineEmits<{
(event: 'update:modelValue', value: boolean): void, (event: 'update:modelValue', value: boolean): void,
}>(); }>();
const analyticsStore = useAnalyticsStore();
const projectsStore = useProjectsStore(); const projectsStore = useProjectsStore();
const router = useRouter(); const router = useRouter();
@ -105,6 +109,7 @@ const isDeclining = ref<boolean>(false);
function openProject(): void { function openProject(): void {
projectsStore.selectProject(props.id); projectsStore.selectProject(props.id);
router.push(`/projects/${props.id}/dashboard`); router.push(`/projects/${props.id}/dashboard`);
analyticsStore.pageVisit('/projects/dashboard');
} }
/** /**
@ -117,7 +122,16 @@ async function respondToInvitation(response: ProjectInvitationResponse): Promise
isLoading.value = true; isLoading.value = true;
let success = false; let success = false;
await projectsStore.respondToInvitation(props.id, response).then(() => { success = true; }).catch(_ => {}); try {
await projectsStore.respondToInvitation(props.id, response);
success = true;
analyticsStore.eventTriggered(
response === ProjectInvitationResponse.Accept ?
AnalyticsEvent.PROJECT_INVITATION_ACCEPTED :
AnalyticsEvent.PROJECT_INVITATION_DECLINED,
);
} catch { /* empty */ }
await projectsStore.getUserInvitations().catch(_ => {}); await projectsStore.getUserInvitations().catch(_ => {});
await projectsStore.getProjects().catch(_ => { success = false; }); await projectsStore.getProjects().catch(_ => { success = false; });

View File

@ -6,7 +6,7 @@
<v-sheet> <v-sheet>
<v-list class="px-2" color="default" variant="flat"> <v-list class="px-2" color="default" variant="flat">
<template v-if="pathBeforeAccountPage"> <template v-if="pathBeforeAccountPage">
<v-list-item class="pa-4 rounded-lg" link router-link :to="pathBeforeAccountPage"> <v-list-item class="pa-4 rounded-lg" link router-link :to="pathBeforeAccountPage" @click="() => trackPageVisitEvent(pathBeforeAccountPage)">
<template #prepend> <template #prepend>
<img src="@poc/assets/icon-back-tonal.svg" alt="Project"> <img src="@poc/assets/icon-back-tonal.svg" alt="Project">
<!-- <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <!-- <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -22,7 +22,7 @@
</template> </template>
<!-- All Projects --> <!-- All Projects -->
<v-list-item class="pa-4 rounded-lg" link router-link to="/projects"> <v-list-item class="pa-4 rounded-lg" link router-link to="/projects" @click="() => trackPageVisitEvent('/projects')">
<template #prepend> <template #prepend>
<!-- <img src="@poc/assets/icon-prosject.svg" alt="Project" class="mr-3"> --> <!-- <img src="@poc/assets/icon-prosject.svg" alt="Project" class="mr-3"> -->
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -40,7 +40,7 @@
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item link router-link to="settings" class="my-1 py-3" rounded="lg"> <v-list-item link router-link to="settings" class="my-1 py-3" rounded="lg" @click="() => trackPageVisitEvent('/settings')">
<template #prepend> <template #prepend>
<!-- <img src="@poc/assets/icon-settings.svg" alt="Account Settings" class="mr-3"> --> <!-- <img src="@poc/assets/icon-settings.svg" alt="Account Settings" class="mr-3"> -->
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -52,7 +52,7 @@
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item link router-link to="billing" class="my-1" rounded="lg"> <v-list-item link router-link to="billing" class="my-1" rounded="lg" @click="() => trackPageVisitEvent('/billing')">
<template #prepend> <template #prepend>
<!-- <img src="@poc/assets/icon-card.svg" alt="Billing" class="mr-3"> --> <!-- <img src="@poc/assets/icon-card.svg" alt="Billing" class="mr-3"> -->
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -82,7 +82,9 @@ import {
} from 'vuetify/components'; } from 'vuetify/components';
import { useAppStore } from '@poc/store/appStore'; import { useAppStore } from '@poc/store/appStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
const analyticsStore = useAnalyticsStore();
const appStore = useAppStore(); const appStore = useAppStore();
/** /**
@ -93,4 +95,11 @@ const pathBeforeAccountPage = computed((): string | null => {
if (!path || path === '/projects') return null; if (!path || path === '/projects') return null;
return path; return path;
}); });
/**
* Sends "Page Visit" event to segment and opens link.
*/
function trackPageVisitEvent(page: string | null): void {
if (page) analyticsStore.pageVisit(page);
}
</script> </script>

View File

@ -30,10 +30,12 @@ import { useUsersStore } from '@/store/modules/usersStore';
import { useABTestingStore } from '@/store/modules/abTestingStore'; import { useABTestingStore } from '@/store/modules/abTestingStore';
import { useProjectsStore } from '@/store/modules/projectsStore'; import { useProjectsStore } from '@/store/modules/projectsStore';
import { useAppStore } from '@poc/store/appStore'; import { useAppStore } from '@poc/store/appStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const analyticsStore = useAnalyticsStore();
const billingStore = useBillingStore(); const billingStore = useBillingStore();
const usersStore = useUsersStore(); const usersStore = useUsersStore();
const abTestingStore = useABTestingStore(); const abTestingStore = useABTestingStore();
@ -53,11 +55,15 @@ async function selectProject(projectId: string): Promise<void> {
try { try {
projects = await projectsStore.getProjects(); projects = await projectsStore.getProjects();
} catch (_) { } catch (_) {
router.push('/projects'); const path = '/projects';
router.push(path);
analyticsStore.pageVisit(path);
return; return;
} }
if (!projects.some(p => p.id === projectId)) { if (!projects.some(p => p.id === projectId)) {
router.push('/projects'); const path = '/projects';
router.push(path);
analyticsStore.pageVisit(path);
return; return;
} }
projectsStore.selectProject(projectId); projectsStore.selectProject(projectId);

View File

@ -11,7 +11,7 @@
<!-- Project Menu --> <!-- Project Menu -->
<v-list class="pa-2"> <v-list class="pa-2">
<!-- My Projects --> <!-- My Projects -->
<v-list-item rounded="lg" link router-link to="/projects"> <v-list-item rounded="lg" link router-link to="/projects" @click="() => trackPageVisitEvent('/projects')">
<template #prepend> <template #prepend>
<!-- <img src="@poc/assets/icon-project.svg" alt="Projects"> --> <!-- <img src="@poc/assets/icon-project.svg" alt="Projects"> -->
<IconProject /> <IconProject />
@ -36,7 +36,7 @@
<v-divider class="my-2" /> <v-divider class="my-2" />
<!-- Shared With Me --> <!-- Shared With Me -->
<v-list-item rounded="lg" link router-link to="/projects"> <v-list-item rounded="lg" link router-link to="/projects" @click="() => trackPageVisitEvent('/projects')">
<template #prepend> <template #prepend>
<IconProject /> <IconProject />
</template> </template>
@ -69,7 +69,7 @@
<!-- <v-divider class="my-2"></v-divider> --> <!-- <v-divider class="my-2"></v-divider> -->
<!-- View All Projects --> <!-- View All Projects -->
<v-list-item link rounded="lg" router-link to="/projects"> <v-list-item link rounded="lg" router-link to="/projects" @click="() => trackPageVisitEvent('/projects')">
<template #prepend> <template #prepend>
<IconAllProjects /> <IconAllProjects />
</template> </template>
@ -117,7 +117,7 @@
<v-divider class="my-2" /> <v-divider class="my-2" />
<v-list-item link router-link to="dashboard" class="my-1 py-3" rounded="lg"> <v-list-item link router-link to="dashboard" class="my-1 py-3" rounded="lg" @click="() => trackPageVisitEvent('/dashboard')">
<template #prepend> <template #prepend>
<IconDashboard /> <IconDashboard />
</template> </template>
@ -126,7 +126,7 @@
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item link router-link to="buckets" class="my-1" rounded="lg"> <v-list-item link router-link to="buckets" class="my-1" rounded="lg" @click="() => trackPageVisitEvent('/buckets')">
<template #prepend> <template #prepend>
<IconBucket /> <IconBucket />
</template> </template>
@ -135,7 +135,7 @@
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item link router-link to="bucket" class="my-1" rounded="lg"> <v-list-item link router-link to="bucket" class="my-1" rounded="lg" @click="() => trackPageVisitEvent('/bucket')">
<template #prepend> <template #prepend>
<IconBrowse /> <IconBrowse />
</template> </template>
@ -144,7 +144,7 @@
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item link router-link to="access" class="my-1" rounded="lg"> <v-list-item link router-link to="access" class="my-1" rounded="lg" @click="() => trackPageVisitEvent('/access')">
<template #prepend> <template #prepend>
<IconAccess /> <IconAccess />
</template> </template>
@ -153,7 +153,7 @@
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item link router-link to="team" class="my-1" rounded="lg"> <v-list-item link router-link to="team" class="my-1" rounded="lg" @click="() => trackPageVisitEvent('/team')">
<template #prepend> <template #prepend>
<IconTeam /> <IconTeam />
</template> </template>
@ -249,6 +249,7 @@ import {
import { Project } from '@/types/projects'; import { Project } from '@/types/projects';
import { useProjectsStore } from '@/store/modules/projectsStore'; import { useProjectsStore } from '@/store/modules/projectsStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import IconProject from '@poc/components/icons/IconProject.vue'; import IconProject from '@poc/components/icons/IconProject.vue';
import IconSettings from '@poc/components/icons/IconSettings.vue'; import IconSettings from '@poc/components/icons/IconSettings.vue';
@ -265,6 +266,7 @@ import IconForum from '@poc/components/icons/IconForum.vue';
import IconSupport from '@poc/components/icons/IconSupport.vue'; import IconSupport from '@poc/components/icons/IconSupport.vue';
import IconResources from '@poc/components/icons/IconResources.vue'; import IconResources from '@poc/components/icons/IconResources.vue';
const analyticsStore = useAnalyticsStore();
const projectsStore = useProjectsStore(); const projectsStore = useProjectsStore();
/** /**
@ -273,4 +275,11 @@ const projectsStore = useProjectsStore();
const selectedProject = computed((): Project => { const selectedProject = computed((): Project => {
return projectsStore.state.selectedProject; return projectsStore.state.selectedProject;
}); });
/**
* Sends "Page Visit" event to segment and opens link.
*/
function trackPageVisitEvent(page: string): void {
analyticsStore.pageVisit(page);
}
</script> </script>

View File

@ -134,11 +134,14 @@ import {
import { useProjectMembersStore } from '@/store/modules/projectMembersStore'; import { useProjectMembersStore } from '@/store/modules/projectMembersStore';
import { useProjectsStore } from '@/store/modules/projectsStore'; import { useProjectsStore } from '@/store/modules/projectsStore';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import PageTitleComponent from '@poc/components/PageTitleComponent.vue'; import PageTitleComponent from '@poc/components/PageTitleComponent.vue';
import PageSubtitleComponent from '@poc/components/PageSubtitleComponent.vue'; import PageSubtitleComponent from '@poc/components/PageSubtitleComponent.vue';
import TeamTableComponent from '@poc/components/TeamTableComponent.vue'; import TeamTableComponent from '@poc/components/TeamTableComponent.vue';
const analyticsStore = useAnalyticsStore();
const pmStore = useProjectMembersStore(); const pmStore = useProjectMembersStore();
const projectsStore = useProjectsStore(); const projectsStore = useProjectsStore();
@ -169,6 +172,8 @@ async function onAddUsersClick(): Promise<void> {
return; return;
} }
analyticsStore.eventTriggered(AnalyticsEvent.PROJECT_MEMBERS_INVITE_SENT);
try { try {
await pmStore.getProjectMembers(1, selectedProjectID.value); await pmStore.getProjectMembers(1, selectedProjectID.value);
} catch (error) { /* empty */ } } catch (error) { /* empty */ }