Skip to content

Commit fb23715

Browse files
committed
feat: add method and signupTypeFlags to login tracking
1 parent 2aff84d commit fb23715

10 files changed

Lines changed: 253 additions & 17 deletions

File tree

src/App.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66
import { useThemeStore, DARK_SCHEME_QUERY } from '@/stores/theme'
77
import { storeToRefs } from 'pinia'
88
import { themeApply } from '@/helpers'
9+
import { captureFirstSessionUrl } from '@/helpers/first-session-url'
910
import Layout from '@/layout'
1011
import '@modules/real-time-metrics/helpers/convert-date'
1112
import '@/helpers/store-handler'
1213
14+
// Capture the first session URL as early as possible
15+
captureFirstSessionUrl()
16+
1317
const DEFAULT_TITLE = 'Azion Console'
1418
1519
/** @type {import('@/plugins/analytics/AnalyticsTrackerAdapter').AnalyticsTrackerAdapter} */

src/helpers/first-session-url.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const FIRST_SESSION_URL_KEY = 'azion_first_session_url'
2+
3+
/**
4+
* Captures the first session URL and stores it in localStorage.
5+
* This should be called early in the application lifecycle.
6+
* The URL is only stored once per user session.
7+
*/
8+
export const captureFirstSessionUrl = () => {
9+
if (typeof window === 'undefined') return null
10+
11+
// Only capture if not already stored
12+
if (!localStorage.getItem(FIRST_SESSION_URL_KEY)) {
13+
const currentUrl = window.location.href
14+
localStorage.setItem(FIRST_SESSION_URL_KEY, currentUrl)
15+
return currentUrl
16+
}
17+
18+
return getFirstSessionUrl()
19+
}
20+
21+
/**
22+
* Retrieves the first session URL from localStorage.
23+
* @returns {string|null} The first session URL or null if not set.
24+
*/
25+
export const getFirstSessionUrl = () => {
26+
if (typeof window === 'undefined') return null
27+
return localStorage.getItem(FIRST_SESSION_URL_KEY)
28+
}
29+
30+
/**
31+
* Clears the first session URL from localStorage.
32+
* This can be called after the signup flow is complete.
33+
*/
34+
export const clearFirstSessionUrl = () => {
35+
if (typeof window === 'undefined') return
36+
localStorage.removeItem(FIRST_SESSION_URL_KEY)
37+
}

src/plugins/analytics/trackers/SignInTracker.js

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,63 @@ export class SignInTracker {
1414
}
1515

1616
/**
17+
* @param {Object} payload
18+
* @param {'google'|'azure'|'github'|'email'} payload.method
19+
* @param {Object} [payload.signupTypeFlags] - Flags for signup type tracking
20+
*
1721
* @returns {AnalyticsTrackerAdapter}
1822
*/
19-
userSignedIn() {
23+
userSignedIn(payload) {
2024
this.#trackerAdapter.addEvent({
2125
eventName: 'User Signed In',
22-
props: {}
26+
props: {
27+
method: payload.method,
28+
login_sso_google: payload.signupTypeFlags?.login_sso_google ?? false,
29+
login_sso_github: payload.signupTypeFlags?.login_sso_github ?? false,
30+
login_email: payload.signupTypeFlags?.login_email ?? false,
31+
signup_sso_google: payload.signupTypeFlags?.signup_sso_google ?? false,
32+
signup_sso_github: payload.signupTypeFlags?.signup_sso_github ?? false,
33+
signup_email: payload.signupTypeFlags?.signup_email ?? false
34+
}
2335
})
2436
return this.#trackerAdapter
2537
}
2638

2739
/**
40+
* @param {Object} payload
41+
* @param {'google'|'azure'|'github'|'email'} payload.method
42+
*
2843
* @returns {AnalyticsTrackerAdapter}
2944
*/
30-
userFailedSignIn() {
45+
userClickedSignIn(payload) {
46+
this.#trackerAdapter.addEvent({
47+
eventName: 'User Clicked to Sign In',
48+
props: {
49+
method: payload.method
50+
}
51+
})
52+
return this.#trackerAdapter
53+
}
54+
55+
/**
56+
* @param {Object} payload
57+
* @param {'google'|'azure'|'github'|'email'} payload.method
58+
* @param {Object} [payload.signupTypeFlags] - Flags for signup type tracking
59+
*
60+
* @returns {AnalyticsTrackerAdapter}
61+
*/
62+
userFailedSignIn(payload) {
3163
this.#trackerAdapter.addEvent({
3264
eventName: 'User Failed to Sign In',
33-
props: {}
65+
props: {
66+
method: payload.method,
67+
login_sso_google: payload.signupTypeFlags?.login_sso_google ?? false,
68+
login_sso_github: payload.signupTypeFlags?.login_sso_github ?? false,
69+
login_email: payload.signupTypeFlags?.login_email ?? false,
70+
signup_sso_google: payload.signupTypeFlags?.signup_sso_google ?? false,
71+
signup_sso_github: payload.signupTypeFlags?.signup_sso_github ?? false,
72+
signup_email: payload.signupTypeFlags?.signup_email ?? false
73+
}
3474
})
3575
return this.#trackerAdapter
3676
}

src/plugins/analytics/trackers/SignUpTracker.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,25 @@ export class SignUpTracker {
1616
/**
1717
* @param {Object} payload
1818
* @param {'google'|'azure'|'github'|'email'} payload.method
19+
* @param {string} [payload.firstSessionUrl] - The first session URL
20+
* @param {string} [payload.signupType] - 'sso' or 'email'
21+
* @param {Object} [payload.signupTypeFlags] - Flags for signup type tracking
1922
*
2023
* @returns {AnalyticsTrackerAdapter}
2124
*/
2225
userSignedUp(payload) {
2326
this.#trackerAdapter.addEvent({
2427
eventName: 'User Signed Up',
2528
props: {
26-
method: payload.method
29+
method: payload.method,
30+
first_session_url: payload.firstSessionUrl,
31+
signup_type: payload.signupType,
32+
login_sso_google: payload.signupTypeFlags?.login_sso_google ?? false,
33+
login_sso_github: payload.signupTypeFlags?.login_sso_github ?? false,
34+
login_email: payload.signupTypeFlags?.login_email ?? false,
35+
signup_sso_google: payload.signupTypeFlags?.signup_sso_google ?? false,
36+
signup_sso_github: payload.signupTypeFlags?.signup_sso_github ?? false,
37+
signup_email: payload.signupTypeFlags?.signup_email ?? false
2738
}
2839
})
2940
return this.#trackerAdapter
@@ -48,14 +59,25 @@ export class SignUpTracker {
4859
/**
4960
* @param {Object} payload
5061
* @param {'google'|'azure'|'github'} payload.method
62+
* @param {string} [payload.firstSessionUrl] - The first session URL
63+
* @param {string} [payload.signupType] - 'sso' or 'email'
64+
* @param {Object} [payload.signupTypeFlags] - Flags for signup type tracking
5165
*
5266
* @returns {AnalyticsTrackerAdapter}
5367
*/
5468
userAuthorizedSso(payload) {
5569
this.#trackerAdapter.addEvent({
5670
eventName: 'User Authorized SSO',
5771
props: {
58-
method: payload.method
72+
method: payload.method,
73+
first_session_url: payload.firstSessionUrl,
74+
signup_type: payload.signupType,
75+
login_sso_google: payload.signupTypeFlags?.login_sso_google ?? false,
76+
login_sso_github: payload.signupTypeFlags?.login_sso_github ?? false,
77+
login_email: payload.signupTypeFlags?.login_email ?? false,
78+
signup_sso_google: payload.signupTypeFlags?.signup_sso_google ?? false,
79+
signup_sso_github: payload.signupTypeFlags?.signup_sso_github ?? false,
80+
signup_email: payload.signupTypeFlags?.signup_email ?? false
5981
}
6082
})
6183
return this.#trackerAdapter

src/router/routes/signup-routes/index.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as SignupService from '@/services/signup-services'
22
import { inject } from 'vue'
33
import { useAccountStore } from '@/stores/account'
44
import SignupView from '@/views/Signup/SignupView.vue'
5+
import { getFirstSessionUrl } from '@/helpers/first-session-url'
56

67
/** @type {import('vue-router').RouteRecordRaw} */
78
export const signupRoutes = {
@@ -42,9 +43,20 @@ export const signupRoutes = {
4243
if (isFirstLogin && accountStore.ssoSignUpMethod) {
4344
/** @type {import('@/plugins/adapters/AnalyticsTrackerAdapter').AnalyticsTrackerAdapter} */
4445
const tracker = inject('tracker')
45-
const signUpMethod = { method: accountStore.ssoSignUpMethod }
46+
const method = accountStore.ssoSignUpMethod
4647

47-
tracker.signUp.userSignedUp(signUpMethod).signUp.userAuthorizedSso(signUpMethod)
48+
// Set the appropriate signup flag
49+
const signupFlag = `signup_sso_${method}`
50+
accountStore.setSignupTypeFlag(signupFlag)
51+
52+
const signUpPayload = {
53+
method,
54+
firstSessionUrl: getFirstSessionUrl(),
55+
signupType: 'sso',
56+
signupTypeFlags: accountStore.getSignupTypeFlags()
57+
}
58+
59+
tracker.signUp.userSignedUp(signUpPayload).signUp.userAuthorizedSso(signUpPayload)
4860
}
4961

5062
if (isFirstLogin) {

src/stores/account.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@ import { defineStore } from 'pinia'
33
export const useAccountStore = defineStore({
44
id: 'account',
55
persist: {
6-
paths: ['identifySignUpProvider', 'hasSession']
6+
paths: ['identifySignUpProvider', 'hasSession', 'signupTypeFlags']
77
},
88
state: () => ({
99
account: {},
1010
hasSession: false,
1111
identifySignUpProvider: '',
12+
signupTypeFlags: {
13+
login_sso_google: false,
14+
login_sso_github: false,
15+
login_email: false,
16+
signup_sso_google: false,
17+
signup_sso_github: false,
18+
signup_email: false
19+
},
1220
accountStatuses: {
1321
BLOCKED: 'BLOCKED',
1422
DEFAULTING: 'DEFAULTING',
@@ -146,12 +154,36 @@ export const useAccountStore = defineStore({
146154
this.account = {}
147155
this.hasSession = false
148156
this.identifySignUpProvider = ''
157+
this.signupTypeFlags = {
158+
login_sso_google: false,
159+
login_sso_github: false,
160+
login_email: false,
161+
signup_sso_google: false,
162+
signup_sso_github: false,
163+
signup_email: false
164+
}
149165
},
150166
setSsoSignUpMethod(method) {
151167
this.identifySignUpProvider = method
152168
},
153169
resetSsoSignUpMethod() {
154170
this.identifySignUpProvider = ''
171+
},
172+
/**
173+
* Sets a signup type flag to true.
174+
* @param {string} flag - The flag name (e.g., 'signup_sso_google', 'login_email')
175+
*/
176+
setSignupTypeFlag(flag) {
177+
if (flag in this.signupTypeFlags) {
178+
this.signupTypeFlags[flag] = true
179+
}
180+
},
181+
/**
182+
* Gets all signup type flags.
183+
* @returns {Object} The signup type flags object
184+
*/
185+
getSignupTypeFlags() {
186+
return { ...this.signupTypeFlags }
155187
}
156188
}
157189
})

src/templates/sign-in-block/index.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@
7777
</Divider>
7878
</div>
7979

80-
<SocialIdpsBlock v-model:showSocialIdps="showSocialIdps" />
80+
<SocialIdpsBlock
81+
v-model:showSocialIdps="showSocialIdps"
82+
context="login"
83+
/>
8184
</div>
8285

8386
<!-- Password step -->
@@ -163,8 +166,10 @@
163166
import Divider from '@aziontech/webkit/divider'
164167
import * as yup from 'yup'
165168
import { useToast } from '@aziontech/webkit/use-toast'
169+
import { useAccountStore } from '@/stores/account'
166170
/**@type {import('@/plugins/analytics/AnalyticsTrackerAdapter').AnalyticsTrackerAdapter} */
167171
const tracker = inject('tracker')
172+
const accountStore = useAccountStore()
168173
169174
defineOptions({ name: 'signInBlock' })
170175
@@ -265,7 +270,8 @@
265270
266271
await props.authenticationLoginService(loginData)
267272
const { twoFactor, trustedDevice, user_tracking_info: userInfo } = await verify()
268-
tracker.signIn.userSignedIn()
273+
const signupTypeFlags = accountStore.getSignupTypeFlags()
274+
tracker.signIn.userSignedIn({ method: 'email', signupTypeFlags }).track()
269275
if (twoFactor) {
270276
const mfaRoute = trustedDevice ? 'authentication' : 'setup'
271277
router.push(`/mfa/${mfaRoute}`)
@@ -274,7 +280,8 @@
274280
275281
await switchClientAccount(userInfo.props)
276282
} catch {
277-
tracker.signIn.userFailedSignIn().track()
283+
const signupTypeFlags = accountStore.getSignupTypeFlags()
284+
tracker.signIn.userFailedSignIn({ method: 'email', signupTypeFlags }).track()
278285
hasRequestErrorMessage.value = new UserNotFoundError().message
279286
isButtonLoading.value = false
280287
}

src/templates/signup-block/login-with-email-block.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@
183183
const handleSignUpEmailClick = () => {
184184
const accountStore = useAccountStore()
185185
accountStore.resetSsoSignUpMethod()
186+
// Set signup_email flag when user chooses email signup
187+
accountStore.setSignupTypeFlag('login_email')
188+
accountStore.setSignupTypeFlag('signup_email')
186189
showForm.value = true
187190
labelButton.value = 'Sign Up'
188191
}

src/templates/social-idps-block/index.vue

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@
4444
4545
const tracker = inject('tracker')
4646
47+
const props = defineProps({
48+
context: {
49+
type: String,
50+
default: 'signup',
51+
validator: (value) => ['signup', 'login'].includes(value)
52+
}
53+
})
54+
4755
const idps = ref([])
4856
const submittedIdp = ref(null)
4957
@@ -86,8 +94,17 @@
8694
8795
if (validateOAuthRedirect(idp.loginUrl)) {
8896
accountStore.setSsoSignUpMethod(idp.slug)
97+
// Set the login_sso flag based on provider
98+
const loginFlag = `login_sso_${idp.slug}`
99+
accountStore.setSignupTypeFlag(loginFlag)
89100
window.location.assign(idp.loginUrl)
90-
tracker.signUp.userClickedSignedUp({ method: idp.slug }).track()
101+
102+
// Track based on context (signup or login)
103+
if (props.context === 'login') {
104+
tracker.signIn.userClickedSignIn({ method: idp.slug }).track()
105+
} else {
106+
tracker.signUp.userClickedSignedUp({ method: idp.slug }).track()
107+
}
91108
} else {
92109
loadingStore.finishLoading()
93110
submittedIdp.value = null

0 commit comments

Comments
 (0)