@cuemath/web-utils 0.0.1 → 0.0.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +0 -0
- package/dist/index.js +1 -0
- package/package.json +7 -4
- package/src/cookie/index.ts +39 -1
- package/src/growth-source/index.ts +172 -0
- package/src/growth-source/types.ts +33 -0
- package/src/index.ts +1 -0
package/.husky/pre-commit
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -15,4 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./cookie"), exports);
|
|
18
|
+
__exportStar(require("./growth-source"), exports);
|
|
18
19
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cuemath/web-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2-beta.0",
|
|
4
4
|
"description": "Shareable utils package",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -24,16 +24,19 @@
|
|
|
24
24
|
"@cuemath/eslint-config": "1.0.9",
|
|
25
25
|
"@cuemath/prettier-config": "1.0.3",
|
|
26
26
|
"@cuemath/ts-config": "0.0.5",
|
|
27
|
+
"@react-native-community/eslint-config": "3.2.0",
|
|
27
28
|
"@types/uuid": "9.0.0",
|
|
28
29
|
"@typescript-eslint/eslint-plugin": "5.47.0",
|
|
29
30
|
"@typescript-eslint/parser": "5.47.0",
|
|
30
|
-
"eslint": "
|
|
31
|
+
"eslint": "8.1.0",
|
|
31
32
|
"eslint-config-prettier": "8.5.0",
|
|
32
33
|
"eslint-plugin-import": "2.26.0",
|
|
34
|
+
"eslint-plugin-jest": "^27.2.1",
|
|
33
35
|
"eslint-plugin-prettier": "4.2.1",
|
|
34
36
|
"husky": "8.0.2",
|
|
35
|
-
"prettier": "2.
|
|
37
|
+
"prettier": "2.4.1",
|
|
36
38
|
"typescript": "4.9.4"
|
|
37
39
|
},
|
|
38
|
-
"dependencies": {}
|
|
40
|
+
"dependencies": {},
|
|
41
|
+
"side-effects": false
|
|
39
42
|
}
|
package/src/cookie/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const getCookie = (name: string, cookie: string): string => {
|
|
1
|
+
export const getCookie = (name: string, cookie: string = document.cookie): string => {
|
|
2
2
|
if (!cookie) {
|
|
3
3
|
return '';
|
|
4
4
|
}
|
|
@@ -17,3 +17,41 @@ export const getCookie = (name: string, cookie: string): string => {
|
|
|
17
17
|
|
|
18
18
|
return '';
|
|
19
19
|
};
|
|
20
|
+
|
|
21
|
+
export function removeCookie(name: string): void {
|
|
22
|
+
const date = new Date();
|
|
23
|
+
|
|
24
|
+
// Set it expire in -1 days
|
|
25
|
+
date.setTime(date.getTime() + -1 * 24 * 60 * 60 * 1000);
|
|
26
|
+
|
|
27
|
+
// Set it
|
|
28
|
+
document.cookie = `${name}=; expires=${date.toUTCString()}; path=/`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createCookie(
|
|
32
|
+
name: string,
|
|
33
|
+
value: string,
|
|
34
|
+
days: number,
|
|
35
|
+
subdomain: boolean,
|
|
36
|
+
): void {
|
|
37
|
+
let expires = '';
|
|
38
|
+
|
|
39
|
+
if (days) {
|
|
40
|
+
const date = new Date();
|
|
41
|
+
|
|
42
|
+
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
|
|
43
|
+
expires = `; expires=${date.toUTCString()}`;
|
|
44
|
+
}
|
|
45
|
+
document.cookie = `${name}=${encodeURIComponent(value)}${expires}; path=/${
|
|
46
|
+
subdomain ? '; .cuemath.com' : ''
|
|
47
|
+
}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function createCookieWithMidnightExp(name: string, value: string): void {
|
|
51
|
+
const date = new Date();
|
|
52
|
+
|
|
53
|
+
date.setHours(24, 0, 0, 0);
|
|
54
|
+
const expires = `; expires=${date}`;
|
|
55
|
+
|
|
56
|
+
document.cookie = `${name}=${value}${expires}; path=/`;
|
|
57
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SourceDetails,
|
|
3
|
+
Platform,
|
|
4
|
+
SignupFlow,
|
|
5
|
+
UTMParams,
|
|
6
|
+
Experiments,
|
|
7
|
+
RevenueChannel,
|
|
8
|
+
UTMParamsKeys,
|
|
9
|
+
} from './types';
|
|
10
|
+
|
|
11
|
+
import { createCookie, getCookie } from '../cookie';
|
|
12
|
+
|
|
13
|
+
const SOURCE_DETAILS_COOKIE = 'source_details_v1';
|
|
14
|
+
const UTM_PARAMS: UTMParamsKeys[] = [
|
|
15
|
+
'utm_source',
|
|
16
|
+
'utm_medium',
|
|
17
|
+
'utm_campaign',
|
|
18
|
+
'utm_term',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const readSourceCookie = () => {
|
|
22
|
+
const sourceDetails = getCookie(SOURCE_DETAILS_COOKIE);
|
|
23
|
+
|
|
24
|
+
return sourceDetails && JSON.parse(sourceDetails);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const writeSourceCookie = (sourceDetails: SourceDetails) => {
|
|
28
|
+
createCookie(SOURCE_DETAILS_COOKIE, JSON.stringify(sourceDetails), 365, true);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const updateSourceCookie = (sourceDetail: Partial<SourceDetails>) => {
|
|
32
|
+
const sourceDetails: SourceDetails = readSourceCookie();
|
|
33
|
+
const updatedDetails: SourceDetails = {
|
|
34
|
+
...sourceDetails,
|
|
35
|
+
...sourceDetail,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
writeSourceCookie(updatedDetails);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const getRevenueChannel = (platform: Platform, utmParams?: UTMParams) => {
|
|
42
|
+
let revenueChannel: RevenueChannel = 'UNKNOWN';
|
|
43
|
+
|
|
44
|
+
if (platform === 'ASTRO' && utmParams) {
|
|
45
|
+
const { utm_source: utmSource = '' } = utmParams;
|
|
46
|
+
|
|
47
|
+
switch (true) {
|
|
48
|
+
case /^affiliate-/.test(utmSource):
|
|
49
|
+
revenueChannel = 'AFFILIATE';
|
|
50
|
+
break;
|
|
51
|
+
case /^offline-/.test(utmSource):
|
|
52
|
+
revenueChannel = 'OFFLINE';
|
|
53
|
+
break;
|
|
54
|
+
case /^referral-/.test(utmSource):
|
|
55
|
+
revenueChannel = 'REFERRAL';
|
|
56
|
+
break;
|
|
57
|
+
case /^performance-/.test(utmSource):
|
|
58
|
+
default:
|
|
59
|
+
revenueChannel = 'PERFORMANCE';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (platform === 'WEBSITE') {
|
|
64
|
+
revenueChannel = 'ORGANIC';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return revenueChannel;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/*
|
|
71
|
+
if Platform = website, then decide based on experiment key
|
|
72
|
+
if Platform = Astro, decide logic once BAU is started
|
|
73
|
+
if Platform = APP, decide logic
|
|
74
|
+
if Platform = chatbot, decide based on chatbot experiment key
|
|
75
|
+
*/
|
|
76
|
+
const getFlow = (platform: Platform, experiments?: Experiments) => {
|
|
77
|
+
let flow: SignupFlow = 'REGULAR';
|
|
78
|
+
|
|
79
|
+
if (platform === 'WEBSITE') {
|
|
80
|
+
if (experiments) {
|
|
81
|
+
const ecnaExp = Object.keys(experiments).find(
|
|
82
|
+
experiment => experiment.indexOf('ecna') !== -1,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
flow = ecnaExp && experiments[ecnaExp] === 'b' ? 'ECNA' : 'REGULAR';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return flow;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getUTMParams = () => {
|
|
93
|
+
if (typeof window === undefined) return;
|
|
94
|
+
|
|
95
|
+
const search = new URLSearchParams(window.location.search);
|
|
96
|
+
const utmParams = UTM_PARAMS.reduce((acc: UTMParams, param) => {
|
|
97
|
+
const utmParam = search.get(param) as string;
|
|
98
|
+
|
|
99
|
+
if (utmParam) {
|
|
100
|
+
acc[param] = utmParam;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return acc;
|
|
104
|
+
}, {});
|
|
105
|
+
|
|
106
|
+
return utmParams;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const getFirstPage = () => {
|
|
110
|
+
if (typeof window === undefined) return '';
|
|
111
|
+
|
|
112
|
+
return window.location.pathname;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/* TODO: Align Marketing Team on UTM source convention
|
|
116
|
+
affiliate-<channel>
|
|
117
|
+
performance-<channel>
|
|
118
|
+
organic-<channel>
|
|
119
|
+
offline-<channel>
|
|
120
|
+
*/
|
|
121
|
+
const getChannel = (utmParams: UTMParams | undefined) => {
|
|
122
|
+
// TODO: UTM source patter for FB, GOOGLE...
|
|
123
|
+
if (!utmParams) return '';
|
|
124
|
+
|
|
125
|
+
const { utm_source: utmSource = '' } = utmParams;
|
|
126
|
+
|
|
127
|
+
return utmSource.split('-')[1];
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
Set Static Source Details of user on Visit of Platform
|
|
132
|
+
*/
|
|
133
|
+
export const initSourceDetails = async ({
|
|
134
|
+
platform,
|
|
135
|
+
experiments,
|
|
136
|
+
utmParams,
|
|
137
|
+
flow,
|
|
138
|
+
affiliateChannel,
|
|
139
|
+
offlineChannel,
|
|
140
|
+
organicChannel,
|
|
141
|
+
performanceChannel,
|
|
142
|
+
revenueChannel,
|
|
143
|
+
}: SourceDetails) => {
|
|
144
|
+
if (!readSourceCookie()) {
|
|
145
|
+
const leadChannel = getChannel(utmParams);
|
|
146
|
+
|
|
147
|
+
const sourceDetails: SourceDetails = {
|
|
148
|
+
platform,
|
|
149
|
+
experiments,
|
|
150
|
+
utmParams: getUTMParams(),
|
|
151
|
+
flow: flow || getFlow(platform, experiments),
|
|
152
|
+
affiliateChannel: affiliateChannel || leadChannel,
|
|
153
|
+
offlineChannel: offlineChannel || leadChannel,
|
|
154
|
+
organicChannel: organicChannel || leadChannel,
|
|
155
|
+
performanceChannel: performanceChannel || leadChannel,
|
|
156
|
+
revenueChannel: revenueChannel || getRevenueChannel(platform, utmParams),
|
|
157
|
+
firstPage: getFirstPage(),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
writeSourceCookie(sourceDetails);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Last Page which user visited signup before
|
|
165
|
+
export const setLastPage = async (pagePath: string) => {
|
|
166
|
+
updateSourceCookie({ lastPage: pagePath });
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// set Entry point where user clicked to Signup
|
|
170
|
+
export const setFlowEntryPoint = async (flowEntry: string) => {
|
|
171
|
+
updateSourceCookie({ flowEntry });
|
|
172
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type Platform = 'WEBSITE' | 'APP' | 'ASTRO' | 'LEAP_TEACHER' | 'LEAP_ADMIN';
|
|
2
|
+
export type RevenueChannel =
|
|
3
|
+
| 'ORGANIC'
|
|
4
|
+
| 'PERFORMANCE'
|
|
5
|
+
| 'AFFILIATE'
|
|
6
|
+
| 'OFFLINE'
|
|
7
|
+
| 'REFERRAL'
|
|
8
|
+
| 'UNKNOWN';
|
|
9
|
+
export type SignupFlow = 'REGULAR' | 'ECNA' | 'ESALES';
|
|
10
|
+
export type Experiments = Record<string, unknown>;
|
|
11
|
+
export type UTMParams = {
|
|
12
|
+
utm_source?: string;
|
|
13
|
+
utm_medium?: string;
|
|
14
|
+
utm_campaign?: string;
|
|
15
|
+
utm_term?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type UTMParamsKeys = keyof UTMParams;
|
|
19
|
+
|
|
20
|
+
export type SourceDetails = {
|
|
21
|
+
platform: Platform;
|
|
22
|
+
experiments?: Experiments;
|
|
23
|
+
flow?: SignupFlow;
|
|
24
|
+
utmParams?: UTMParams;
|
|
25
|
+
affiliateChannel?: string;
|
|
26
|
+
offlineChannel?: string;
|
|
27
|
+
performanceChannel: string;
|
|
28
|
+
revenueChannel: RevenueChannel;
|
|
29
|
+
organicChannel?: string;
|
|
30
|
+
flowEntry?: string;
|
|
31
|
+
firstPage?: string;
|
|
32
|
+
lastPage?: string;
|
|
33
|
+
};
|
package/src/index.ts
CHANGED