web/satellite: added ability to restart onboarding tour

Added an info icon which toggles a bubble where user can click a button to restrat onboarding tour

Change-Id: I8b02a1506684377c2887cc9d9c93b5450f1bfd4d
This commit is contained in:
Vitalii Shpital 2021-08-20 15:04:16 +03:00
parent ce6ae1b17a
commit a7e151e970
8 changed files with 112 additions and 84 deletions

View File

@ -5,10 +5,20 @@
<div class="info" @mouseenter="toggleVisibility" @mouseleave="toggleVisibility"> <div class="info" @mouseenter="toggleVisibility" @mouseleave="toggleVisibility">
<slot /> <slot />
<div v-if="isVisible" class="info__box"> <div v-if="isVisible" class="info__box">
<div v-if="buttonLabel" class="info__box__click-mock" />
<div class="info__box__arrow" /> <div class="info__box__arrow" />
<div class="info__box__message"> <div class="info__box__message">
<h1 v-if="title" class="info__box__message__title">{{ title }}</h1>
<p class="info__box__message__regular-text">{{ text }}</p> <p class="info__box__message__regular-text">{{ text }}</p>
<p class="info__box__message__bold-text">{{ boldText }}</p> <p class="info__box__message__bold-text">{{ boldText }}</p>
<VButton
v-if="buttonLabel"
class="info__box__message__button"
:label="buttonLabel"
height="42px"
border-radius="52px"
:on-press="onClick"
/>
</div> </div>
</div> </div>
</div> </div>
@ -17,15 +27,38 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'; import { Component, Prop, Vue } from 'vue-property-decorator';
@Component import VButton from '@/components/common/VButton.vue';
@Component({
components: {
VButton,
}
})
export default class VInfo extends Vue { export default class VInfo extends Vue {
@Prop({default: ''})
private readonly title: string;
@Prop({default: ''}) @Prop({default: ''})
private readonly text: string; private readonly text: string;
@Prop({default: ''}) @Prop({default: ''})
private readonly boldText: string; private readonly boldText: string;
@Prop({default: ''})
private readonly buttonLabel: string;
@Prop({default: () => false})
private readonly onButtonClick: () => unknown;
public isVisible = false; public isVisible = false;
/**
* Holds on button click logic.
*/
public onClick(): void {
this.onButtonClick();
this.toggleVisibility();
}
/**
* Toggles bubble visibility.
*/
public toggleVisibility(): void { public toggleVisibility(): void {
this.isVisible = !this.isVisible; this.isVisible = !this.isVisible;
} }
@ -47,6 +80,11 @@ export default class VInfo extends Vue {
filter: drop-shadow(0 0 34px #0a1b2c47); filter: drop-shadow(0 0 34px #0a1b2c47);
z-index: 1; z-index: 1;
&__click-mock {
height: 24px;
background: transparent;
}
&__arrow { &__arrow {
background-color: white; background-color: white;
width: 40px; width: 40px;
@ -62,6 +100,14 @@ export default class VInfo extends Vue {
padding: 24px; padding: 24px;
border-radius: 20px; border-radius: 20px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 14px;
line-height: 32px;
color: #000;
margin-bottom: 10px;
}
&__bold-text, &__bold-text,
&__regular-text { &__regular-text {
color: #586c86; color: #586c86;
@ -74,6 +120,10 @@ export default class VInfo extends Vue {
&__regular-text { &__regular-text {
font-family: 'font_regular', sans-serif; font-family: 'font_regular', sans-serif;
} }
&__button {
margin-top: 20px;
}
} }
} }
} }

View File

@ -24,6 +24,16 @@
<SettingsSelection class="settings-selection" /> <SettingsSelection class="settings-selection" />
</div> </div>
<div class="header-container__right-area"> <div class="header-container__right-area">
<VInfo
v-if="!isOnboardingTour"
class="header-container__right-area__info"
title="Need some help?"
text="You can always start the onboarding tour and go through all the steps to get you started again."
button-label="START TOUR"
:on-button-click="onStartTourButtonClick"
>
<InfoIcon />
</VInfo>
<div class="header-container__right-area__satellite-area"> <div class="header-container__right-area__satellite-area">
<div class="header-container__right-area__satellite-area__checkmark"> <div class="header-container__right-area__satellite-area__checkmark">
<CheckmarkIcon /> <CheckmarkIcon />
@ -47,8 +57,10 @@ import ProjectSelection from '@/components/header/projectsDropdown/ProjectSelect
import ResourcesSelection from '@/components/header/resourcesDropdown/ResourcesSelection.vue'; import ResourcesSelection from '@/components/header/resourcesDropdown/ResourcesSelection.vue';
import SettingsSelection from '@/components/header/settingsDropdown/SettingsSelection.vue'; import SettingsSelection from '@/components/header/settingsDropdown/SettingsSelection.vue';
import NavigationArea from '@/components/navigation/NavigationArea.vue'; import NavigationArea from '@/components/navigation/NavigationArea.vue';
import VInfo from '@/components/common/VInfo.vue';
import LogoIcon from '@/../static/images/logo.svg'; import LogoIcon from '@/../static/images/logo.svg';
import InfoIcon from '@/../static/images/header/info.svg';
import CheckmarkIcon from '@/../static/images/header/checkmark.svg'; import CheckmarkIcon from '@/../static/images/header/checkmark.svg';
import NavigationCloseIcon from '@/../static/images/header/navigationClose.svg'; import NavigationCloseIcon from '@/../static/images/header/navigationClose.svg';
import NavigationMenuIcon from '@/../static/images/header/navigationMenu.svg'; import NavigationMenuIcon from '@/../static/images/header/navigationMenu.svg';
@ -61,9 +73,11 @@ import AccountButton from './accountDropdown/AccountButton.vue';
components: { components: {
AccountButton, AccountButton,
NavigationArea, NavigationArea,
VInfo,
NavigationMenuIcon, NavigationMenuIcon,
NavigationCloseIcon, NavigationCloseIcon,
LogoIcon, LogoIcon,
InfoIcon,
CheckmarkIcon, CheckmarkIcon,
ProjectSelection, ProjectSelection,
ResourcesSelection, ResourcesSelection,
@ -90,6 +104,13 @@ export default class HeaderArea extends Vue {
location.reload(); location.reload();
} }
/**
* Redirects to onboarding tour.
*/
public async onStartTourButtonClick(): Promise<void> {
await this.$router.push(RouteConfig.OnboardingTour.path)
}
/** /**
* Indicates if current route is onboarding tour. * Indicates if current route is onboarding tour.
*/ */
@ -148,6 +169,11 @@ export default class HeaderArea extends Vue {
display: flex; display: flex;
align-items: center; align-items: center;
&__info {
max-height: 24px;
margin-right: 17px;
}
&__satellite-area { &__satellite-area {
height: 36px; height: 36px;
background: #f6f6fa; background: #f6f6fa;
@ -205,6 +231,19 @@ export default class HeaderArea extends Vue {
margin-right: 35px; margin-right: 35px;
} }
::v-deep .info__box {
top: calc(100% - 24px);
&__message {
min-width: 335px;
&__regular-text {
font-size: 12px;
line-height: 21px;
}
}
}
@media screen and (min-width: 1281px) { @media screen and (min-width: 1281px) {
.adapted-navigation, .adapted-navigation,

View File

@ -10,27 +10,8 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import { RouteConfig } from '@/router';
@Component @Component
export default class OnboardingTourArea extends Vue { export default class OnboardingTourArea extends Vue {}
/**
* Lifecycle hook after initial render.
* Sets area to needed state.
*/
public mounted(): void {
if (this.userHasProject) {
this.$router.push(RouteConfig.ProjectDashboard.path).catch(() => {return; });
}
}
/**
* Indicates if user has at least one project.
*/
private get userHasProject(): boolean {
return this.$store.state.projectsModule.projects.length > 0;
}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -51,7 +51,6 @@ import WelcomeRight from '@/../static/images/onboardingTour/welcome-right.svg';
import { AnalyticsHttpApi } from '@/api/analytics'; import { AnalyticsHttpApi } from '@/api/analytics';
import { RouteConfig } from '@/router'; import { RouteConfig } from '@/router';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames'; import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
@Component({ @Component({
@ -67,19 +66,9 @@ export default class OverviewStep extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi(); private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Sets area to needed state.
*/
public mounted(): void {
if (this.userHasProject) {
this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.AccessGrant).path).catch(() => {return; });
}
}
/** /**
* Holds button click logic. * Holds button click logic.
* Creates untitled project and redirects to next step (creating access grant). * Redirects to next step (creating access grant).
*/ */
public async onUplinkCLIClick(): Promise<void> { public async onUplinkCLIClick(): Promise<void> {
if (this.isLoading) return; if (this.isLoading) return;
@ -87,21 +76,13 @@ export default class OverviewStep extends Vue {
this.isLoading = true; this.isLoading = true;
await this.analytics.linkEventTriggered(AnalyticsEvent.PATH_SELECTED, 'CLI'); await this.analytics.linkEventTriggered(AnalyticsEvent.PATH_SELECTED, 'CLI');
try {
await this.$store.dispatch(PROJECTS_ACTIONS.CREATE_DEFAULT_PROJECT);
this.isLoading = false;
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.AccessGrant).with(RouteConfig.AccessGrantName).path); await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.AccessGrant).with(RouteConfig.AccessGrantName).path);
} catch (error) {
await this.$notify.error(error.message);
this.isLoading = false; this.isLoading = false;
} }
}
/** /**
* Creates untitled project and redirects to objects page. * Redirects to objects page.
*/ */
public async onUploadInBrowserClick(): Promise<void> { public async onUploadInBrowserClick(): Promise<void> {
if (this.isLoading) return; if (this.isLoading) return;
@ -109,45 +90,17 @@ export default class OverviewStep extends Vue {
this.isLoading = true; this.isLoading = true;
await this.analytics.linkEventTriggered(AnalyticsEvent.PATH_SELECTED, 'Continue in Browser'); await this.analytics.linkEventTriggered(AnalyticsEvent.PATH_SELECTED, 'Continue in Browser');
try {
await this.$store.dispatch(PROJECTS_ACTIONS.CREATE_DEFAULT_PROJECT);
this.isLoading = false;
await this.$router.push(RouteConfig.Objects.path).catch(() => {return; }); await this.$router.push(RouteConfig.Objects.path).catch(() => {return; });
} catch (error) {
await this.$notify.error(error.message);
this.isLoading = false; this.isLoading = false;
} }
}
/** /**
* Holds button click logic. * Holds button click logic.
* Creates untitled project and redirects to project dashboard. * Redirects to project dashboard.
*/ */
public async onSkipClick(): Promise<void> { public async onSkipClick(): Promise<void> {
if (this.isLoading) return;
this.isLoading = true;
try {
await this.$store.dispatch(PROJECTS_ACTIONS.CREATE_DEFAULT_PROJECT);
this.isLoading = false;
await this.$router.push(RouteConfig.ProjectDashboard.path); await this.$router.push(RouteConfig.ProjectDashboard.path);
} catch (error) {
await this.$notify.error(error.message);
this.isLoading = false;
}
}
/**
* Indicates if user has at least one project.
*/
private get userHasProject(): boolean {
return this.$store.state.projectsModule.projects.length > 0;
} }
} }
</script> </script>

View File

@ -132,10 +132,10 @@ export default class DashboardArea extends Vue {
} }
if (!projects.length) { if (!projects.length) {
await this.$store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, AppState.LOADED);
try { try {
await this.$store.dispatch(PROJECTS_ACTIONS.CREATE_DEFAULT_PROJECT);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path); await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
await this.$store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, AppState.LOADED);
} catch (error) { } catch (error) {
return; return;
} }

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 16V12" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 8H12.01" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 524 B

View File

@ -4,7 +4,7 @@ exports[`Team HeaderArea renders correctly 1`] = `
<div class="team-header-container"> <div class="team-header-container">
<div class="team-header-container__title-area"> <div class="team-header-container__title-area">
<h1 class="team-header-container__title-area__title">Project Members</h1> <h1 class="team-header-container__title-area__title">Project Members</h1>
<vinfo-stub text="" boldtext="The only project role currently available is Admin, which gives full access to the project." class="team-header-container__title-area__info-button"> <vinfo-stub title="" text="" boldtext="The only project role currently available is Admin, which gives full access to the project." buttonlabel="" class="team-header-container__title-area__info-button">
<infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub> <infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub>
</vinfo-stub> </vinfo-stub>
</div> </div>
@ -27,7 +27,7 @@ exports[`Team HeaderArea renders correctly with 1 selected user and delete click
<div class="team-header-container"> <div class="team-header-container">
<div class="team-header-container__title-area"> <div class="team-header-container__title-area">
<h1 class="team-header-container__title-area__title">Project Members</h1> <h1 class="team-header-container__title-area__title">Project Members</h1>
<vinfo-stub text="" boldtext="The only project role currently available is Admin, which gives full access to the project." class="team-header-container__title-area__info-button"> <vinfo-stub title="" text="" boldtext="The only project role currently available is Admin, which gives full access to the project." buttonlabel="" class="team-header-container__title-area__info-button">
<infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub> <infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub>
</vinfo-stub> </vinfo-stub>
</div> </div>
@ -53,7 +53,7 @@ exports[`Team HeaderArea renders correctly with 2 selected users and delete clic
<div class="team-header-container"> <div class="team-header-container">
<div class="team-header-container__title-area"> <div class="team-header-container__title-area">
<h1 class="team-header-container__title-area__title">Project Members</h1> <h1 class="team-header-container__title-area__title">Project Members</h1>
<vinfo-stub text="" boldtext="The only project role currently available is Admin, which gives full access to the project." class="team-header-container__title-area__info-button"> <vinfo-stub title="" text="" boldtext="The only project role currently available is Admin, which gives full access to the project." buttonlabel="" class="team-header-container__title-area__info-button">
<infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub> <infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub>
</vinfo-stub> </vinfo-stub>
</div> </div>
@ -79,7 +79,7 @@ exports[`Team HeaderArea renders correctly with opened Add team member popup 1`]
<div class="team-header-container"> <div class="team-header-container">
<div class="team-header-container__title-area"> <div class="team-header-container__title-area">
<h1 class="team-header-container__title-area__title">Project Members</h1> <h1 class="team-header-container__title-area__title">Project Members</h1>
<vinfo-stub text="" boldtext="The only project role currently available is Admin, which gives full access to the project." class="team-header-container__title-area__info-button"> <vinfo-stub title="" text="" boldtext="The only project role currently available is Admin, which gives full access to the project." buttonlabel="" class="team-header-container__title-area__info-button">
<infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub> <infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub>
</vinfo-stub> </vinfo-stub>
</div> </div>
@ -102,7 +102,7 @@ exports[`Team HeaderArea renders correctly with selected users 1`] = `
<div class="team-header-container"> <div class="team-header-container">
<div class="team-header-container__title-area"> <div class="team-header-container__title-area">
<h1 class="team-header-container__title-area__title">Project Members</h1> <h1 class="team-header-container__title-area__title">Project Members</h1>
<vinfo-stub text="" boldtext="The only project role currently available is Admin, which gives full access to the project." class="team-header-container__title-area__info-button"> <vinfo-stub title="" text="" boldtext="The only project role currently available is Admin, which gives full access to the project." buttonlabel="" class="team-header-container__title-area__info-button">
<infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub> <infoicon-stub class="team-header-container__title-area__info-button__image"></infoicon-stub>
</vinfo-stub> </vinfo-stub>
</div> </div>

View File

@ -5,7 +5,7 @@ exports[`Dashboard renders correctly when data is loaded 1`] = `
<!----> <!---->
<!----> <!---->
<div class="dashboard__wrap"> <div class="dashboard__wrap">
<!----> <paidtierbar-stub openaddpmmodal="function () { [native code] }"></paidtierbar-stub>
<!----> <!---->
<dashboardheader-stub></dashboardheader-stub> <dashboardheader-stub></dashboardheader-stub>
<div class="dashboard__wrap__main-area"> <div class="dashboard__wrap__main-area">