satellite/analytics: Added segment.io page calls to track all the pages (#4880)

satellite/analytics: send analytics 'page visit' api requests when the user navigates around the UI
This commit is contained in:
prerna-parashar 2022-06-09 11:54:23 -07:00 committed by GitHub
parent b4c77d89a6
commit cc0518f473
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 358 additions and 3 deletions

View File

@ -374,3 +374,24 @@ func (service *Service) TrackCreditCardAdded(userID uuid.UUID, email string) {
})
}
// PageVisitEvent sends a page visit event associated with user ID to Segment.
// It is used for tracking occurrences of client-side events.
func (service *Service) PageVisitEvent(pageName string, userID uuid.UUID, email string) {
if !service.config.Enabled {
return
}
props := segment.NewProperties()
props.Set("email", email)
props.Set("path", pageName)
props.Set("user_id", userID.String())
props.Set("satellite", service.satelliteName)
service.enqueueMessage(segment.Page{
UserId: userID.String(),
Name: "Page Requested",
Properties: props,
})
}

View File

@ -39,6 +39,10 @@ type eventTriggeredBody struct {
Link string `json:"link"`
}
type pageVisitBody struct {
PageName string `json:"pageName"`
}
// EventTriggered tracks the occurrence of an arbitrary event on the client.
func (a *Analytics) EventTriggered(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
@ -68,6 +72,33 @@ func (a *Analytics) EventTriggered(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
// PageEventTriggered tracks the occurrence of an arbitrary page visit event on the client.
func (a *Analytics) PageEventTriggered(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.serveJSONError(w, http.StatusInternalServerError, err)
}
var pv pageVisitBody
err = json.Unmarshal(body, &pv)
if err != nil {
a.serveJSONError(w, http.StatusInternalServerError, err)
}
auth, err := console.GetAuth(ctx)
if err != nil {
a.serveJSONError(w, http.StatusUnauthorized, err)
return
}
a.analytics.PageVisitEvent(pv.PageName, auth.User.ID, auth.User.Email)
w.WriteHeader(http.StatusOK)
}
// serveJSONError writes JSON error to response output stream.
func (a *Analytics) serveJSONError(w http.ResponseWriter, status int, err error) {
serveJSONError(a.log, w, status, err)

View File

@ -293,6 +293,7 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc
analyticsRouter := router.PathPrefix("/api/v0/analytics").Subrouter()
analyticsRouter.Use(server.withAuth)
analyticsRouter.HandleFunc("/event", analyticsController.EventTriggered).Methods(http.MethodPost)
analyticsRouter.HandleFunc("/page", analyticsController.PageEventTriggered).Methods(http.MethodPost)
if server.config.StaticDir != "" {
oidc := oidc.NewEndpoint(server.config.ExternalAddress, logger, oidcService, service,

View File

@ -56,4 +56,26 @@ export class AnalyticsHttpApi {
console.error('Could not notify satellite about ' + eventName + ' event occurrence (most likely blocked by browser).');
}
}
/**
* Used to notify the satellite about arbitrary page visits that occur.
* Does not throw any errors so that expected UI behavior is not interrupted if the API call fails.
*
* @param pageName - name of the page
*/
public async pageVisit(pageName: string): Promise<void> {
try {
const path = `${this.ROOT_PATH}/page`;
const body = {
pageName: pageName,
};
const response = await this.http.post(path, JSON.stringify(body));
if (response.ok) {
return;
}
console.error('Attempted to notify Satellite that ' + pageName + ' occurred. Got bad response status code: ' + response.status);
} catch (error) {
console.error('Could not notify satellite about ' + pageName + ' event occurrence (most likely blocked by browser).');
}
}
}

View File

@ -236,6 +236,7 @@ import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { AccessGrant, AccessGrantsOrderBy } from '@/types/accessGrants';
import { SortDirection } from '@/types/common';
import { AnalyticsHttpApi } from '@/api/analytics';
const {
FETCH,
@ -271,6 +272,8 @@ export default class AccessGrants extends Vue {
private FIRST_PAGE = 1;
private isDeleteClicked = false;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Indicates if the access modal should be shown and what the defaulted type of access should be defaulted.
*/
@ -355,6 +358,7 @@ export default class AccessGrants extends Vue {
* Starts create access grant flow.
*/
public onCreateClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
}
/**

View File

@ -23,6 +23,8 @@ import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -31,11 +33,15 @@ import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
},
})
export default class CreateAccessGrant extends Vue {
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Closes popup.
*/
public onCloseClick(): void {
this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CLEAR_SELECTION);
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
this.$router.push(RouteConfig.AccessGrants.path);
}

View File

@ -27,6 +27,8 @@ import Key from '@/../static/images/accessGrants/key.svg';
import { RouteConfig } from '@/router';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -35,10 +37,14 @@ import { RouteConfig } from '@/router';
},
})
export default class EmptyState extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Starts create access grant flow.
*/
public onCreateClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
}
}

View File

@ -60,6 +60,8 @@ import BackIcon from '@/../static/images/accessGrants/back.svg';
import { RouteConfig } from '@/router';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -76,12 +78,15 @@ export default class CLIStep extends Vue {
addressContainer: HTMLElement;
};
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Sets local key from props value.
*/
public mounted(): void {
if (!this.$route.params.key && !this.$route.params.restrictedKey) {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
return;
@ -96,6 +101,7 @@ export default class CLIStep extends Vue {
* Redirects to previous step.
*/
public onBackClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).path);
this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).name,
params: {
@ -109,6 +115,7 @@ export default class CLIStep extends Vue {
* Redirects to upload step.
*/
public onDoneClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.path)
this.isOnboardingTour ? this.$router.push(RouteConfig.ProjectDashboard.path) : this.$router.push(RouteConfig.AccessGrants.path);
}

View File

@ -128,6 +128,7 @@ export default class CreatePassphraseStep extends Vue {
*/
public async mounted(): Promise<void> {
if (!this.$route.params.key && !this.$route.params.restrictedKey) {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
await this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
return;
@ -207,6 +208,7 @@ export default class CreatePassphraseStep extends Vue {
this.isLoading = false;
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).path);
await this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).name,
params: {
@ -272,6 +274,7 @@ export default class CreatePassphraseStep extends Vue {
* Redirects to previous step.
*/
public onBackClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).path);
this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).name,
params: {

View File

@ -34,6 +34,8 @@ import BackIcon from '@/../static/images/accessGrants/back.svg';
import { RouteConfig } from '@/router';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -52,12 +54,15 @@ export default class EnterPassphraseStep extends Vue {
public passphrase = '';
public errorMessage = '';
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Sets local key from props value.
*/
public async mounted(): Promise<void> {
if (!this.$route.params.key && !this.$route.params.restrictedKey) {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
await this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
return;
@ -116,6 +121,7 @@ export default class EnterPassphraseStep extends Vue {
this.isLoading = false;
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).path);
await this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).name,
params: {
@ -142,6 +148,7 @@ export default class EnterPassphraseStep extends Vue {
* Redirects to previous step.
*/
public onBackClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).path);
this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).name,
params: {

View File

@ -143,6 +143,7 @@ export default class GatewayStep extends Vue {
*/
public mounted(): void {
if (!this.$route.params.access && !this.$route.params.key && !this.$route.params.resctrictedKey) {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
return;
@ -185,6 +186,7 @@ export default class GatewayStep extends Vue {
* Redirects to previous step.
*/
public onBackClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).path);
this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.ResultStep)).name,
params: {
@ -200,6 +202,7 @@ export default class GatewayStep extends Vue {
* Proceed to upload data step.
*/
public onDoneClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
this.isOnboardingTour ? this.$router.push(RouteConfig.ProjectDashboard.path) : this.$router.push(RouteConfig.AccessGrants.path);
}

View File

@ -43,6 +43,7 @@ import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { AccessGrant } from '@/types/accessGrants';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
@ -59,6 +60,8 @@ export default class NameStep extends Vue {
private readonly FIRST_PAGE = 1;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Changes name data from input value.
* @param value
@ -73,6 +76,7 @@ export default class NameStep extends Vue {
*/
public onCancelClick(): void {
this.onChangeName('');
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
this.$router.push(RouteConfig.AccessGrants.path);
}
@ -128,6 +132,7 @@ export default class NameStep extends Vue {
this.isLoading = false;
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).path);
await this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.PermissionsStep)).name,
params: {

View File

@ -82,6 +82,8 @@ import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { DurationPermission } from '@/types/accessGrants';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -105,6 +107,8 @@ export default class PermissionsStep extends Vue {
public isDelete = true;
public areBucketNamesFetching = true;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Sets local key from props value.
@ -137,6 +141,7 @@ export default class PermissionsStep extends Vue {
* Redirects to previous step.
*/
public onBackClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
}
@ -170,6 +175,7 @@ export default class PermissionsStep extends Vue {
this.isLoading = false;
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CLIStep)).path);
await this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CLIStep)).name,
params: {
@ -199,6 +205,7 @@ export default class PermissionsStep extends Vue {
this.isLoading = false;
if (this.accessGrantsAmount > 1) {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.EnterPassphraseStep)).path);
await this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.EnterPassphraseStep)).name,
params: {
@ -210,6 +217,7 @@ export default class PermissionsStep extends Vue {
return;
}
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CreatePassphraseStep)).path);
await this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CreatePassphraseStep)).name,
params: {

View File

@ -59,6 +59,8 @@ import { RouteConfig } from '@/router';
import { MetaUtils } from '@/utils/meta';
import { Download } from "@/utils/download";
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -74,12 +76,15 @@ export default class ResultStep extends Vue {
public access = '';
public isGatewayLinkVisible = false;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Sets local access from props value.
*/
public mounted(): void {
if (!this.$route.params.access && !this.$route.params.key && !this.$route.params.resctrictedKey) {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.NameStep)).path);
return;
@ -121,6 +126,7 @@ export default class ResultStep extends Vue {
*/
public onBackClick(): void {
if (this.accessGrantsAmount > 1) {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.EnterPassphraseStep)).path);
this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.EnterPassphraseStep)).name,
params: {
@ -132,6 +138,7 @@ export default class ResultStep extends Vue {
return;
}
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CreatePassphraseStep)).path);
this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.CreatePassphraseStep)).name,
params: {
@ -146,6 +153,7 @@ export default class ResultStep extends Vue {
* Proceed to upload data step.
*/
public onDoneClick(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.path);
this.isOnboardingTour ? this.$router.push(RouteConfig.ProjectDashboard.path) : this.$router.push(RouteConfig.AccessGrants.path);
}
@ -154,6 +162,7 @@ export default class ResultStep extends Vue {
* Proceed to gateway step.
*/
public navigateToGatewayStep(): void {
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.GatewayStep)).path);
this.$router.push({
name: RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant.with(RouteConfig.GatewayStep)).name,
params: {

View File

@ -11,6 +11,7 @@
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component
@ -20,10 +21,13 @@ export default class HistoryDropdown extends Vue {
@Prop({ default: '' })
public readonly route: string;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds logic to redirect user to history page.
*/
public redirect(): void {
this.analytics.pageVisit(this.route);
this.$router.push(this.route);
}

View File

@ -27,6 +27,8 @@ import CloseIcon from '@/../static/images/common/closeCross.svg';
import { RouteConfig } from '@/router';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -41,10 +43,13 @@ export default class AddCouponCode extends Vue {
@Prop({default: false})
protected readonly error: boolean;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Closes add coupon modal.
*/
public onCloseClick(): void {
this.analytics.pageVisit(RouteConfig.Account.with(RouteConfig.Billing).path);
this.$router.push(RouteConfig.Account.with(RouteConfig.Billing).path);
}
}

View File

@ -63,6 +63,8 @@ import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { Coupon, CouponDuration } from '@/types/payments';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -74,6 +76,7 @@ import { Coupon, CouponDuration } from '@/types/payments';
})
export default class CouponArea extends Vue {
public isCouponFetching = true;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
@ -92,6 +95,7 @@ export default class CouponArea extends Vue {
* Opens Add Coupon modal.
*/
public onCreateClick(): void {
this.analytics.pageVisit(RouteConfig.Billing.with(RouteConfig.AddCouponCode).path);
this.$router.push(RouteConfig.Billing.with(RouteConfig.AddCouponCode).path);
}

View File

@ -37,6 +37,8 @@ import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -48,6 +50,7 @@ import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
})
export default class DetailedHistory extends Vue {
public isDataFetching = true;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
@ -89,6 +92,7 @@ export default class DetailedHistory extends Vue {
* Replaces location to root billing route.
*/
public onBackToBillingClick(): void {
this.analytics.pageVisit(RouteConfig.Billing.path);
this.$router.push(RouteConfig.Billing.path);
}
}

View File

@ -40,6 +40,8 @@ import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -57,6 +59,8 @@ export default class PeriodSelection extends Vue {
];
public currentOption: string = this.periodOptions[0];
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Indicates if periods dropdown is shown.
*/
@ -123,6 +127,7 @@ export default class PeriodSelection extends Vue {
* Holds logic to redirect user to billing history page.
*/
public redirect(): void {
this.analytics.pageVisit(RouteConfig.Account.with(RouteConfig.BillingHistory).path);
this.$router.push(RouteConfig.Account.with(RouteConfig.BillingHistory).path);
}

View File

@ -25,6 +25,8 @@ import SortingHeader from '@/components/account/billing/depositAndBillingHistory
import { RouteConfig } from '@/router';
import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -33,10 +35,13 @@ import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
},
})
export default class SmallDepositHistory extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Changes location to deposit history route.
*/
public onViewAllClick(): void {
this.analytics.pageVisit(RouteConfig.Account.with(RouteConfig.DepositHistory).path);
this.$router.push(RouteConfig.Account.with(RouteConfig.DepositHistory).path);
}

View File

@ -22,6 +22,8 @@ import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { USER_ACTIONS } from '@/store/modules/users';
import { AnalyticsHttpApi } from '@/api/analytics';
const {
ADD_CREDIT_CARD,
GET_CREDIT_CARDS,
@ -42,6 +44,8 @@ export default class AddCardForm extends Vue {
stripeCardInput: StripeCardInput & StripeForm;
};
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Adds card after Stripe confirmation.
*
@ -80,6 +84,7 @@ export default class AddCardForm extends Vue {
setTimeout(() => {
if (!this.userHasOwnProject) {
this.analytics.pageVisit(RouteConfig.CreateProject.path);
this.$router.push(RouteConfig.CreateProject.path);
}
}, 500);

View File

@ -629,6 +629,7 @@ export default class FileBrowser extends Vue {
const path = this.isNewObjectsFlow ? RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path :
RouteConfig.Buckets.with(RouteConfig.EncryptData).path;
this.analytics.pageVisit(path);
await this.$router.push(path);
return;
@ -639,6 +640,7 @@ export default class FileBrowser extends Vue {
if (!this.routePath) {
try {
this.analytics.pageVisit(`${this.$store.state.files.browserRoot}${this.path}`);
await this.$router.push({
path: `${this.$store.state.files.browserRoot}${this.path}`
});

View File

@ -108,6 +108,7 @@ export default class AccountArea extends Vue {
*/
public navigateToSettings(): void {
this.closeDropdown();
this.analytics.pageVisit(RouteConfig.Account.with(RouteConfig.Settings).path);
this.$router.push(RouteConfig.Account.with(RouteConfig.Settings).path).catch(() => {return;});
}
@ -115,6 +116,7 @@ export default class AccountArea extends Vue {
* Logouts user and navigates to login page.
*/
public async onLogout(): Promise<void> {
this.analytics.pageVisit(RouteConfig.Login.path);
await this.$router.push(RouteConfig.Login.path);
try {

View File

@ -17,7 +17,7 @@
:aria-label="navItem.name"
class="navigation-area__container__wrap__item-container"
:to="navItem.path"
@click.native="trackClickEvent(navItem.name)"
@click.native="trackClickEvent(navItem.path)"
>
<div class="navigation-area__container__wrap__item-container__left">
<component :is="navItem.icon" class="navigation-area__container__wrap__item-container__left__image" />
@ -246,6 +246,7 @@ export default class NavigationArea extends Vue {
public navigateToCreateAG(): void {
this.analytics.eventTriggered(AnalyticsEvent.CREATE_AN_ACCESS_GRANT_CLICKED);
this.closeDropdowns();
this.analytics.pageVisit(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).path).catch(() => {return;});
}
@ -255,6 +256,7 @@ export default class NavigationArea extends Vue {
public navigateToBuckets(): void {
this.analytics.eventTriggered(AnalyticsEvent.UPLOAD_IN_WEB_CLICKED);
this.closeDropdowns();
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
this.$router.push(RouteConfig.Buckets.path).catch(() => {return;});
}
@ -265,6 +267,7 @@ export default class NavigationArea extends Vue {
this.analytics.eventTriggered(AnalyticsEvent.UPLOAD_USING_CLI_CLICKED);
this.closeDropdowns();
this.$store.commit(APP_STATE_MUTATIONS.SET_ONB_AG_NAME_STEP_BACK_ROUTE, this.$route.path);
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.AGName)).path);
this.$router.push({name: RouteConfig.AGName.name});
}
@ -274,6 +277,7 @@ export default class NavigationArea extends Vue {
public navigateToNewProject(): void {
this.analytics.eventTriggered(AnalyticsEvent.NEW_PROJECT_CLICKED);
this.closeDropdowns();
this.analytics.pageVisit(RouteConfig.CreateProject.path);
this.$router.push(RouteConfig.CreateProject.path);
}
@ -361,6 +365,7 @@ export default class NavigationArea extends Vue {
* Sends "View Docs" event to segment and opens link.
*/
public trackViewDocsEvent(link: string): void {
this.analytics.pageVisit(link);
this.analytics.eventTriggered(AnalyticsEvent.VIEW_DOCS_CLICKED);
window.open(link)
}
@ -369,6 +374,7 @@ export default class NavigationArea extends Vue {
* Sends "View Forum" event to segment and opens link.
*/
public trackViewForumEvent(link: string): void {
this.analytics.pageVisit(link);
this.analytics.eventTriggered(AnalyticsEvent.VIEW_FORUM_CLICKED);
window.open(link)
}
@ -377,6 +383,7 @@ export default class NavigationArea extends Vue {
* Sends "View Support" event to segment and opens link.
*/
public trackViewSupportEvent(link: string): void {
this.analytics.pageVisit(link);
this.analytics.eventTriggered(AnalyticsEvent.VIEW_SUPPORT_CLICKED);
window.open(link)
}
@ -384,8 +391,8 @@ export default class NavigationArea extends Vue {
/**
* Sends new path click event to segment.
*/
public trackClickEvent(name: string): void {
this.analytics.linkEventTriggered(AnalyticsEvent.PATH_SELECTED, name);
public trackClickEvent(path: string): void {
this.analytics.pageVisit(path);
}
}
</script>

View File

@ -145,6 +145,7 @@ export default class ProjectSelection extends Vue {
if (this.isBucketsView) {
await this.$store.dispatch(OBJECTS_ACTIONS.CLEAR);
this.analytics.pageVisit(RouteConfig.Buckets.path);
await this.$router.push(RouteConfig.Buckets.path).catch(() => {return; });
}
@ -213,6 +214,7 @@ export default class ProjectSelection extends Vue {
*/
public onProjectsLinkClick(): void {
if (this.$route.name !== RouteConfig.ProjectsList.name) {
this.analytics.pageVisit(RouteConfig.ProjectsList.path);
this.analytics.eventTriggered(AnalyticsEvent.MANAGE_PROJECTS_CLICKED);
this.$router.push(RouteConfig.ProjectsList.path);
}
@ -233,6 +235,7 @@ export default class ProjectSelection extends Vue {
if (!user.paidTier && user.projectLimit === ownProjectsCount) {
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_CREATE_PROJECT_PROMPT_POPUP);
} else {
this.analytics.pageVisit(RouteConfig.CreateProject.path);
this.$router.push(RouteConfig.CreateProject.path);
}
}

View File

@ -31,6 +31,8 @@ import BucketCreationGeneratePassphrase from "@/components/objects/BucketCreatio
import BucketCreationNameStep from "@/components/objects/BucketCreationNameStep.vue";
import BucketCreationProgress from "@/components/objects/BucketCreationProgress.vue";
import { AnalyticsHttpApi } from '@/api/analytics';
export enum BucketCreationSteps {
Name = 0,
Passphrase,
@ -55,6 +57,8 @@ export default class BucketCreation extends Vue {
public bucketName = '';
public passphrase = '';
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Sets bucket name from child component.
*/
@ -94,6 +98,7 @@ export default class BucketCreation extends Vue {
await this.$store.dispatch(OBJECTS_ACTIONS.CREATE_BUCKET, this.bucketName);
await this.$store.dispatch(OBJECTS_ACTIONS.FETCH_BUCKETS);
await this.$store.dispatch(OBJECTS_ACTIONS.SET_FILE_COMPONENT_BUCKET_NAME, this.bucketName);
this.analytics.pageVisit(RouteConfig.UploadFile.path);
await this.$router.push(RouteConfig.UploadFile.path);
} catch (e) {
await this.$notify.error(e.message);

View File

@ -59,6 +59,8 @@ import VLoader from "@/components/common/VLoader.vue";
import BucketIcon from "@/../static/images/objects/bucketCreation.svg";
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -73,6 +75,8 @@ export default class BucketCreationNameStep extends Vue {
public nameError = '';
public isLoading = true;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
public async mounted(): Promise<void> {
try {
await this.$store.dispatch(BUCKET_ACTIONS.FETCH_ALL_BUCKET_NAMES)
@ -111,6 +115,7 @@ export default class BucketCreationNameStep extends Vue {
*/
public onCancelClick(): void {
LocalData.setDemoBucketCreatedStatus();
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
}

View File

@ -80,6 +80,8 @@ import ObjectsPopup from '@/components/objects/ObjectsPopup.vue';
import BucketIcon from '@/../static/images/objects/bucket.svg';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -104,6 +106,8 @@ export default class BucketsView extends Vue {
public errorMessage = '';
public activeDropdown = -1;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Setup gateway credentials.
@ -146,6 +150,7 @@ export default class BucketsView extends Vue {
if (!this.bucketsPage.buckets.length && !wasDemoBucketCreated) {
if (this.isNewObjectsFlow) {
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
await this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
return;
}
@ -229,6 +234,7 @@ export default class BucketsView extends Vue {
}
public onNewBucketButtonClick(): void {
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path);
this.isNewObjectsFlow
? this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path)
: this.showCreateBucketPopup();
@ -390,6 +396,7 @@ export default class BucketsView extends Vue {
*/
public openBucket(bucketName: string): void {
this.$store.dispatch(OBJECTS_ACTIONS.SET_FILE_COMPONENT_BUCKET_NAME, bucketName);
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.EncryptData).path);
this.isNewObjectsFlow
? this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_OPEN_BUCKET_MODAL_SHOWN)
: this.$router.push(RouteConfig.Buckets.with(RouteConfig.EncryptData).path);

View File

@ -72,6 +72,8 @@ import { MetaUtils } from "@/utils/meta";
import GeneratePassphrase from "@/components/common/GeneratePassphrase.vue";
import FAQBullet from "@/components/objects/FAQBullet.vue";
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -85,11 +87,14 @@ export default class EncryptData extends Vue {
public isLoading = false;
public passphrase = '';
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Sets passphrase from child component.
*/
public navigateToCLIFlow(): void {
this.$store.commit(APP_STATE_MUTATIONS.SET_ONB_AG_NAME_STEP_BACK_ROUTE, this.$route.path);
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.AGName)).path);
this.$router.push({name: RouteConfig.AGName.name});
}
@ -99,6 +104,7 @@ export default class EncryptData extends Vue {
*/
public mounted(): void {
if (!this.apiKey) {
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path)
}
@ -147,6 +153,7 @@ export default class EncryptData extends Vue {
try {
await this.setAccess();
this.analytics.pageVisit(RouteConfig.UploadFile.path);
await this.$router.push(RouteConfig.UploadFile.path);
} catch (e) {
await this.$notify.error(e.message);
@ -159,6 +166,7 @@ export default class EncryptData extends Vue {
* Holds on back button click logic.
*/
public onBackClick(): void {
this.analytics.pageVisit(RouteConfig.BucketsManagement.path);
this.$router.push(RouteConfig.BucketsManagement.path);
}

View File

@ -14,9 +14,14 @@ import { RouteConfig } from '@/router';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component
export default class ObjectsArea extends Vue {
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Redirects if flow is disabled.
@ -24,6 +29,7 @@ export default class ObjectsArea extends Vue {
public async mounted(): Promise<void> {
const isFileBrowserFlowDisabled = MetaUtils.getMetaContent('file-browser-flow-disabled');
if (isFileBrowserFlowDisabled === "true") {
this.analytics.pageVisit(RouteConfig.ProjectDashboard.path);
await this.$router.push(RouteConfig.ProjectDashboard.path);
}
}

View File

@ -35,6 +35,8 @@ import WarningIcon from '@/../static/images/objects/cancelWarning.svg';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -43,10 +45,14 @@ import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
},
})
export default class UploadCancelPopup extends Vue {
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on leave click logic.
*/
public onLeaveClick(): void {
this.analytics.pageVisit(this.leaveRoute);
this.$router.push(this.leaveRoute);
this.closePopup();
}

View File

@ -67,6 +67,7 @@ export default class UploadFile extends Vue {
* Redirects to buckets list view.
*/
public goToBuckets(): void {
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
}

View File

@ -87,6 +87,7 @@ export default class OverviewStep extends Vue {
this.isLoading = true;
await this.analytics.linkEventTriggered(AnalyticsEvent.PATH_SELECTED, 'CLI');
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.CLIStep).with(RouteConfig.AGName).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.CLIStep).with(RouteConfig.AGName).path);
this.isLoading = false;
@ -101,6 +102,7 @@ export default class OverviewStep extends Vue {
this.isLoading = true;
await this.analytics.linkEventTriggered(AnalyticsEvent.PATH_SELECTED, 'Continue in Browser');
this.analytics.pageVisit(RouteConfig.Buckets.path);
await this.$router.push(RouteConfig.Buckets.path).catch(() => {return; });
this.isLoading = false;

View File

@ -37,6 +37,8 @@ import VInput from "@/components/common/VInput.vue";
import Icon from '@/../static/images/onboardingTour/accessGrant.svg';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -50,6 +52,8 @@ export default class AGName extends Vue {
private errorMessage = '';
private isLoading = false;
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Changes name data from input value.
* @param value
@ -64,6 +68,7 @@ export default class AGName extends Vue {
* Navigates to previous screen.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OverviewStep.path);
this.backRoute ?
await this.$router.push(this.backRoute).catch(() => {return; }) :
await this.$router.push({name: RouteConfig.OverviewStep.name});
@ -97,6 +102,7 @@ export default class AGName extends Vue {
this.$store.commit(APP_STATE_MUTATIONS.SET_ONB_CLEAN_API_KEY, createdAccessGrant.secret);
this.name = '';
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.AGPermissions)).path);
await this.$router.push({name: RouteConfig.AGPermissions.name});
}

View File

@ -120,6 +120,7 @@ export default class AGPermissions extends Vue {
public async onBackClick(): Promise<void> {
if (this.isLoading) return;
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.AGName)).path);
await this.$router.push({name: RouteConfig.AGName.name})
}
@ -144,6 +145,7 @@ export default class AGPermissions extends Vue {
}
await this.$store.commit(APP_STATE_MUTATIONS.SET_ONB_API_KEY_STEP_BACK_ROUTE, this.$route.path)
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.APIKey)).path);
await this.$router.push({name: RouteConfig.APIKey.name});
}

View File

@ -31,6 +31,8 @@ import ValueWithCopy from "@/components/onboardingTour/steps/common/ValueWithCop
import Icon from '@/../static/images/onboardingTour/apiKeyStep.svg';
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -42,12 +44,15 @@ import Icon from '@/../static/images/onboardingTour/apiKeyStep.svg';
export default class APIKey extends Vue {
public satelliteAddress: string = MetaUtils.getMetaContent('satellite-nodeurl');
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Checks if api key was generated during previous step.
*/
public mounted(): void {
if (!this.storedAPIKey) {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.AGName)).path);
this.$router.push({name: RouteConfig.AGName.name});
}
}
@ -58,11 +63,13 @@ export default class APIKey extends Vue {
*/
public async onBackClick(): Promise<void> {
if (this.backRoute) {
this.analytics.pageVisit(this.backRoute);
await this.$router.push(this.backRoute).catch(() => {return; });
return;
}
this.analytics.pageVisit(RouteConfig.OnboardingTour.path);
await this.$router.push(RouteConfig.OnboardingTour.path).catch(() => {return; })
}
@ -70,6 +77,7 @@ export default class APIKey extends Vue {
* Holds on next button click logic.
*/
public async onNextClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CLIInstall)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CLIInstall)).path);
}

View File

@ -134,6 +134,8 @@ import TabWithCopy from "@/components/onboardingTour/steps/common/TabWithCopy.vu
import Icon from '@/../static/images/onboardingTour/cliSetupStep.svg';
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -144,10 +146,14 @@ import Icon from '@/../static/images/onboardingTour/cliSetupStep.svg';
}
})
export default class CLIInstall extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on back button click logic.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.APIKey)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.APIKey)).path);
}
@ -155,6 +161,7 @@ export default class CLIInstall extends Vue {
* Holds on next button click logic.
*/
public async onNextClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CLISetup)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CLISetup)).path);
}
}

View File

@ -42,6 +42,8 @@ import TabWithCopy from "@/components/onboardingTour/steps/common/TabWithCopy.vu
import Icon from '@/../static/images/onboardingTour/cliSetupStep.svg';
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -52,10 +54,14 @@ import Icon from '@/../static/images/onboardingTour/cliSetupStep.svg';
}
})
export default class CLISetup extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on back button click logic.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CLIInstall)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CLIInstall)).path);
}
@ -63,6 +69,7 @@ export default class CLISetup extends Vue {
* Holds on next button click logic.
*/
public async onNextClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CreateBucket)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CreateBucket)).path);
}
}

View File

@ -41,6 +41,8 @@ import TabWithCopy from "@/components/onboardingTour/steps/common/TabWithCopy.vu
import Icon from "@/../static/images/onboardingTour/bucketStep.svg";
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -51,10 +53,14 @@ import Icon from "@/../static/images/onboardingTour/bucketStep.svg";
}
})
export default class CreateBucket extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on back button click logic.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CLISetup)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CLISetup)).path);
}
@ -62,6 +68,7 @@ export default class CreateBucket extends Vue {
* Holds on next button click logic.
*/
public async onNextClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.UploadObject)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.UploadObject)).path);
}
}

View File

@ -40,6 +40,8 @@ import TabWithCopy from "@/components/onboardingTour/steps/common/TabWithCopy.vu
import Icon from "@/../static/images/onboardingTour/downloadObjectStep.svg";
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -50,10 +52,14 @@ import Icon from "@/../static/images/onboardingTour/downloadObjectStep.svg";
}
})
export default class DownloadObject extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on back button click logic.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.ListObject)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.ListObject)).path);
}
@ -61,6 +67,7 @@ export default class DownloadObject extends Vue {
* Holds on next button click logic.
*/
public async onNextClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.ShareObject)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.ShareObject)).path);
}
}

View File

@ -40,6 +40,8 @@ import TabWithCopy from "@/components/onboardingTour/steps/common/TabWithCopy.vu
import Icon from "@/../static/images/onboardingTour/listObjectStep.svg";
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -50,10 +52,14 @@ import Icon from "@/../static/images/onboardingTour/listObjectStep.svg";
}
})
export default class ListObject extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on back button click logic.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.UploadObject)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.UploadObject)).path);
}
@ -61,6 +67,7 @@ export default class ListObject extends Vue {
* Holds on next button click logic.
*/
public async onNextClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.DownloadObject)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.DownloadObject)).path);
}
}

View File

@ -48,6 +48,8 @@ import CLIFlowContainer from "@/components/onboardingTour/steps/common/CLIFlowCo
import OSContainer from "@/components/onboardingTour/steps/common/OSContainer.vue";
import TabWithCopy from "@/components/onboardingTour/steps/common/TabWithCopy.vue";
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -57,10 +59,14 @@ import TabWithCopy from "@/components/onboardingTour/steps/common/TabWithCopy.vu
}
})
export default class ShareObject extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on back button click logic.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.DownloadObject)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.DownloadObject)).path);
}
@ -68,6 +74,7 @@ export default class ShareObject extends Vue {
* Holds on next button click logic.
*/
public async onNextClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.SuccessScreen)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.SuccessScreen)).path);
}
}

View File

@ -38,6 +38,8 @@ import VButton from "@/components/common/VButton.vue";
import Icon from "@/../static/images/onboardingTour/successStep.svg";
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -46,10 +48,14 @@ import Icon from "@/../static/images/onboardingTour/successStep.svg";
}
})
export default class SuccessScreen extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on back button click logic.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.ShareObject)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.ShareObject)).path);
}
@ -57,6 +63,7 @@ export default class SuccessScreen extends Vue {
* Holds on finish button click logic.
*/
public async onFinishClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.ProjectDashboard.path);
await this.$router.push(RouteConfig.ProjectDashboard.path);
}
}

View File

@ -45,6 +45,8 @@ import TabWithCopy from "@/components/onboardingTour/steps/common/TabWithCopy.vu
import Icon from "@/../static/images/onboardingTour/uploadStep.svg";
import { AnalyticsHttpApi } from "@/api/analytics";
// @vue/component
@Component({
components: {
@ -55,10 +57,14 @@ import Icon from "@/../static/images/onboardingTour/uploadStep.svg";
}
})
export default class UploadObject extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Holds on back button click logic.
*/
public async onBackClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CreateBucket)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.CreateBucket)).path);
}
@ -66,6 +72,7 @@ export default class UploadObject extends Vue {
* Holds on next button click logic.
*/
public async onNextClick(): Promise<void> {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.ListObject)).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OnbCLIStep.with(RouteConfig.ListObject)).path);
}
}

View File

@ -71,6 +71,8 @@ import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { ProjectFields } from '@/types/projects';
import { LocalData } from '@/utils/localData';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -87,6 +89,8 @@ export default class NewProjectPopup extends Vue {
public projectName = '';
public nameError = '';
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Sets project name from input value.
*/
@ -152,6 +156,7 @@ export default class NewProjectPopup extends Vue {
this.isLoading = false;
this.analytics.pageVisit(RouteConfig.ProjectDashboard.path);
await this.$router.push(RouteConfig.ProjectDashboard.path);
}

View File

@ -33,6 +33,8 @@ import { BUCKET_ACTIONS } from '@/store/modules/buckets';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PM_ACTIONS } from '@/utils/constants/actionNames';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -46,12 +48,15 @@ export default class ProjectDashboard extends Vue {
public areBucketsFetching = true;
public isSummaryDataFetching = true;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Fetches buckets, usage rollup, project members and access grants.
*/
public async mounted(): Promise<void> {
if (!this.$store.getters.selectedProject.id) {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
return;

View File

@ -29,6 +29,8 @@ import { RouteConfig } from "@/router";
import VButton from "@/components/common/VButton.vue";
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -36,10 +38,14 @@ import VButton from "@/components/common/VButton.vue";
}
})
export default class NoBucketArea extends Vue {
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Navigates user to welcome screen.
*/
public navigateToWelcomeScreen(): void {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
}
}

View File

@ -196,6 +196,8 @@ import BucketArea from '@/components/project/buckets/BucketArea.vue';
import NewProjectIcon from "@/../static/images/project/newProject.svg";
import InfoIcon from '@/../static/images/project/infoIcon.svg';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -222,12 +224,15 @@ export default class NewProjectDashboard extends Vue {
dashboard: HTMLDivElement;
}
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Fetches project limits.
*/
public async mounted(): Promise<void> {
if (!this.$store.getters.selectedProject.id) {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
return;
@ -289,6 +294,7 @@ export default class NewProjectDashboard extends Vue {
* Holds on create project button click logic.
*/
public onCreateProjectClick(): void {
this.analytics.pageVisit(RouteConfig.CreateProject.path);
this.$router.push(RouteConfig.CreateProject.path);
}
@ -296,6 +302,7 @@ export default class NewProjectDashboard extends Vue {
* Holds on upload button click logic.
*/
public onUploadClick(): void {
this.analytics.pageVisit(RouteConfig.Buckets.path);
this.$router.push(RouteConfig.Buckets.path).catch(() => {return;})
}

View File

@ -52,6 +52,8 @@ import { Project, ProjectsPage } from '@/types/projects';
import { PM_ACTIONS } from '@/utils/constants/actionNames';
import { LocalData } from '@/utils/localData';
import { AnalyticsHttpApi } from '@/api/analytics';
const {
FETCH_OWNED,
} = PROJECTS_ACTIONS;
@ -73,6 +75,8 @@ export default class Projects extends Vue {
public areProjectsFetching = true;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render where list of existing owned projects is fetched.
*/
@ -103,6 +107,7 @@ export default class Projects extends Vue {
* Redirects to create project page.
*/
public onCreateClick(): void {
this.analytics.pageVisit(RouteConfig.CreateProject.path);
this.$router.push(RouteConfig.CreateProject.path);
}
@ -127,6 +132,7 @@ export default class Projects extends Vue {
await this.$store.dispatch(BUCKET_ACTIONS.FETCH, this.FIRST_PAGE);
await this.$store.dispatch(PROJECTS_ACTIONS.GET_LIMITS, this.$store.getters.selectedProject.id);
this.analytics.pageVisit(RouteConfig.EditProjectDetails.path);
await this.$router.push(RouteConfig.EditProjectDetails.path);
} catch (error) {
await this.$notify.error(`Unable to select project. ${error.message}`);

View File

@ -94,6 +94,8 @@ import { ProjectMemberHeaderState } from '@/types/projectMembers';
import { Project } from '@/types/projects';
import { APP_STATE_ACTIONS, PM_ACTIONS } from '@/utils/constants/actionNames';
import { AnalyticsHttpApi } from '@/api/analytics';
declare interface ClearSearch {
clearSearch(): void;
}
@ -118,6 +120,8 @@ export default class HeaderArea extends Vue {
private FIRST_PAGE = 1;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Indicates if state after first delete click is active.
*/
@ -215,6 +219,7 @@ export default class HeaderArea extends Vue {
private async setProjectState(): Promise<void> {
const projects: Project[] = await this.$store.dispatch(PROJECTS_ACTIONS.FETCH);
if (!projects.length) {
this.analytics.pageVisit(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
await this.$router.push(RouteConfig.OnboardingTour.with(RouteConfig.OverviewStep).path);
return;

View File

@ -104,6 +104,8 @@ import {ACCESS_GRANTS_ACTIONS} from '@/store/modules/accessGrants';
import {OAuthClient, OAuthClientsAPI} from '@/api/oauthClients';
import {URLSearchParams} from "url";
import { AnalyticsHttpApi } from '@/api/analytics';
const oauthClientsAPI = new OAuthClientsAPI();
// @vue/component
@ -150,6 +152,8 @@ export default class Authorize extends Vue {
private worker: Worker;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
private async ensureLogin(): Promise<void> {
try {
await this.$store.dispatch(USER_ACTIONS.GET);
@ -162,6 +166,7 @@ export default class Authorize extends Vue {
const query = new URLSearchParams(this.oauthData).toString();
const path = `${RouteConfig.Authorize.path}?${query}#${this.clientKey}`;
this.analytics.pageVisit(`${RouteConfig.Login.path}?return_url=${encodeURIComponent(path)}`);
await this.$router.push(`${RouteConfig.Login.path}?return_url=${encodeURIComponent(path)}`);
return;
}

View File

@ -62,6 +62,8 @@ import { User } from "@/types/users";
import { AuthHttpApi } from "@/api/auth";
import { MetaUtils } from "@/utils/meta";
import { AnalyticsHttpApi } from '@/api/analytics';
const {
SETUP_ACCOUNT,
GET_CREDIT_CARDS,
@ -90,6 +92,8 @@ export default class DashboardArea extends Vue {
public isMFACodesPopup = false;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Pre fetches user`s and project information.
@ -147,6 +151,7 @@ export default class DashboardArea extends Vue {
try {
await this.$store.dispatch(PROJECTS_ACTIONS.CREATE_DEFAULT_PROJECT);
this.analytics.pageVisit(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);
@ -305,6 +310,7 @@ export default class DashboardArea extends Vue {
this.resetActivityEvents.forEach((eventName: string) => {
document.removeEventListener(eventName, this.resetInactivityTimer);
});
this.analytics.pageVisit(RouteConfig.Login.path);
await this.$router.push(RouteConfig.Login.path);
await this.$notify.notify('Your session was timed out.');
} catch (error) {

View File

@ -59,6 +59,8 @@ import { PartneredSatellite } from '@/types/common';
import { Validator } from '@/utils/validation';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -74,6 +76,8 @@ export default class ForgotPassword extends Vue {
private readonly auth: AuthHttpApi = new AuthHttpApi();
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
// tardigrade logic
public isDropdownShown = false;
@ -138,6 +142,7 @@ export default class ForgotPassword extends Vue {
* Changes location to Login route.
*/
public onBackToLoginClick(): void {
this.analytics.pageVisit(RouteConfig.Login.path);
this.$router.push(RouteConfig.Login.path);
}

View File

@ -121,6 +121,8 @@ import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
import { ErrorBadRequest } from "@/api/errors/ErrorBadRequest";
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
interface ClearInput {
clearInput(): void;
}
@ -157,6 +159,8 @@ export default class Login extends Vue {
public isRecoveryCodeState = false;
public isBadLoginMessageShown = false;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
// Tardigrade logic
public isDropdownShown = false;
@ -320,6 +324,7 @@ export default class Login extends Vue {
await this.$store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, AppState.LOADING);
this.isLoading = false;
this.analytics.pageVisit(this.returnURL);
await this.$router.push(this.returnURL);
}

View File

@ -248,6 +248,8 @@ import { User } from '@/types/users';
import { MetaUtils } from '@/utils/meta';
import { Validator } from '@/utils/validation';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -316,6 +318,8 @@ export default class RegisterArea extends Vue {
hcaptcha: VueHcaptcha;
}
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Lifecycle hook after initial render.
* Sets up variables from route params.

View File

@ -92,6 +92,8 @@ import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { Validator } from '@/utils/validation';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
// @vue/component
@Component({
components: {
@ -124,6 +126,8 @@ export default class ResetPassword extends Vue {
public readonly loginPath: string = RouteConfig.Login.path;
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
public $refs!: {
mfaInput: ConfirmMFAInput;
};
@ -147,6 +151,7 @@ export default class ResetPassword extends Vue {
if (this.$route.query.token) {
this.token = this.$route.query.token.toString();
} else {
this.analytics.pageVisit(RouteConfig.Login.path);
this.$router.push(RouteConfig.Login.path);
}
}