@legalplace/wizardx-core 2.6.2 → 2.6.4
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/CHANGELOG.md +16 -0
- package/dist/constants/phoneValidation.d.ts +0 -1
- package/dist/constants/phoneValidation.js +0 -1
- package/dist/libs/EventsTracking.d.ts +0 -1
- package/dist/libs/EventsTracking.js +0 -18
- package/dist/redux/sagas/saveData.js +1 -46
- package/dist/redux/sagas/setUserEmail.js +1 -28
- package/package.json +4 -5
- package/src/constants/phoneValidation.ts +0 -2
- package/src/libs/EventsTracking.ts +0 -26
- package/src/redux/sagas/saveData.ts +1 -62
- package/src/redux/sagas/setUserEmail.ts +1 -47
- package/dist/helpers/sendinblueHelper.d.ts +0 -4
- package/dist/helpers/sendinblueHelper.js +0 -46
- package/dist/libs/Extracts.d.ts +0 -20
- package/dist/libs/Extracts.js +0 -110
- package/src/helpers/sendinblueHelper.ts +0 -65
- package/src/libs/Extracts.ts +0 -159
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [2.6.4](https://git.legalplace.eu/legalplace/monorepo/compare/@legalplace/wizardx-core@2.6.3...@legalplace/wizardx-core@2.6.4) (2022-01-05)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @legalplace/wizardx-core
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [2.6.3](https://git.legalplace.eu/legalplace/monorepo/compare/@legalplace/wizardx-core@2.6.2...@legalplace/wizardx-core@2.6.3) (2021-12-13)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @legalplace/wizardx-core
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
## [2.6.2](https://git.legalplace.eu/legalplace/monorepo/compare/@legalplace/wizardx-core@2.6.1...@legalplace/wizardx-core@2.6.2) (2021-12-10)
|
|
7
23
|
|
|
8
24
|
|
|
@@ -12,7 +12,6 @@ declare class _EventsTracking {
|
|
|
12
12
|
private executeQueue;
|
|
13
13
|
private queueTimeout;
|
|
14
14
|
trackMix(eventName: string, eventProps?: EventsPropsType, doNotPushToQueue?: boolean): boolean;
|
|
15
|
-
trackSIB(eventName: string, eventProps?: EventsPropsType, doNotPushToQueue?: boolean): boolean;
|
|
16
15
|
private trackFB;
|
|
17
16
|
private trackGA;
|
|
18
17
|
private registerMix;
|
|
@@ -13,7 +13,6 @@ class _EventsTracking {
|
|
|
13
13
|
this.trackMix(eventName, Object.assign({}, eventProps));
|
|
14
14
|
this.trackFB(eventName, Object.assign({}, eventProps));
|
|
15
15
|
this.trackGA(eventName, Object.assign({}, eventProps));
|
|
16
|
-
this.trackSIB(eventName, Object.assign({}, eventProps));
|
|
17
16
|
}
|
|
18
17
|
register(props) {
|
|
19
18
|
if (!this.initiated)
|
|
@@ -74,9 +73,6 @@ class _EventsTracking {
|
|
|
74
73
|
case "trackGA":
|
|
75
74
|
fn = this.trackGA.bind(this);
|
|
76
75
|
break;
|
|
77
|
-
case "trackSIB":
|
|
78
|
-
fn = this.trackSIB.bind(this);
|
|
79
|
-
break;
|
|
80
76
|
case "registerMix":
|
|
81
77
|
fn = this.registerMix.bind(this);
|
|
82
78
|
break;
|
|
@@ -117,20 +113,6 @@ class _EventsTracking {
|
|
|
117
113
|
this.queueTimeout();
|
|
118
114
|
return false;
|
|
119
115
|
}
|
|
120
|
-
trackSIB(eventName, eventProps, doNotPushToQueue = false) {
|
|
121
|
-
if (!this.initiated)
|
|
122
|
-
this.init();
|
|
123
|
-
if (typeof window.sendinblue !== "undefined") {
|
|
124
|
-
if (this.queue.length > 0 && !doNotPushToQueue)
|
|
125
|
-
this.executeQueue();
|
|
126
|
-
window.sendinblue.track(eventName, eventProps);
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
if (doNotPushToQueue === false)
|
|
130
|
-
this.queue.push(["trackSIB", [eventName, eventProps]]);
|
|
131
|
-
this.queueTimeout();
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
116
|
trackFB(eventName, eventProps, doNotPushToQueue = false) {
|
|
135
117
|
if (typeof window.fbq !== "undefined") {
|
|
136
118
|
if (this.queue.length > 0 && !doNotPushToQueue)
|
|
@@ -1,39 +1,10 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { call, put, takeLatest } from "redux-saga/effects";
|
|
11
|
-
import { v4 as uuid } from "uuid";
|
|
12
2
|
import { setDataStatus } from "../actions/app";
|
|
13
|
-
import { selectPermalink, selectInstanceUniqid
|
|
3
|
+
import { selectPermalink, selectInstanceUniqid } from "../selectors/app";
|
|
14
4
|
import { SAVE_DATA } from "../constants/sagas/data";
|
|
15
5
|
import { selectInputs } from "../selectors/inputs";
|
|
16
6
|
import { OvcConverter } from "../../libs/OvcConverter";
|
|
17
7
|
import { updateInstanceArguments } from "../../service/api.manager";
|
|
18
|
-
import { getSendinblueIdentifyAttributes } from "../../helpers/sendinblueHelper";
|
|
19
|
-
import { getConfig } from "../../config";
|
|
20
|
-
import { selectUserEmail } from "../selectors/user";
|
|
21
|
-
const getSendinblueData = (uniqid, paid) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
-
const email = selectUserEmail();
|
|
23
|
-
const apiEndpoint = getConfig().apiEndpoint + (/\/$/.test(getConfig().apiEndpoint) ? "" : "/");
|
|
24
|
-
const ovc = OvcConverter.convertToOvc(selectInputs());
|
|
25
|
-
const nbFilledVars = Object.keys(ovc.v).reduce((p, c) => {
|
|
26
|
-
const currentValue = ovc.v[c];
|
|
27
|
-
const o = Array.isArray(currentValue)
|
|
28
|
-
? currentValue.reduce((_p, _c) => _p + (_c !== "" ? 1 : 0), 0)
|
|
29
|
-
: currentValue !== ""
|
|
30
|
-
? 1
|
|
31
|
-
: 0;
|
|
32
|
-
return p + o;
|
|
33
|
-
}, 0);
|
|
34
|
-
const attributes = yield getSendinblueIdentifyAttributes(uniqid, selectDocumentTitle(), nbFilledVars, paid, window.location.host);
|
|
35
|
-
return [apiEndpoint, email, attributes];
|
|
36
|
-
});
|
|
37
8
|
function* saveDataDecorator(action) {
|
|
38
9
|
try {
|
|
39
10
|
yield put(setDataStatus("saving"));
|
|
@@ -43,22 +14,6 @@ function* saveDataDecorator(action) {
|
|
|
43
14
|
const responseData = yield call([response, "json"]);
|
|
44
15
|
if (action.redirect === true) {
|
|
45
16
|
if (responseData.redirect_to) {
|
|
46
|
-
try {
|
|
47
|
-
const [apiEndpoint, email, attributes] = yield call(getSendinblueData, [uniqid, selectInstanceIsPaid()]);
|
|
48
|
-
yield call(fetch, `${apiEndpoint}event/create/${uniqid}/${uuid()}`, {
|
|
49
|
-
method: "POST",
|
|
50
|
-
credentials: "include",
|
|
51
|
-
headers: {
|
|
52
|
-
"Content-Type": "application/json",
|
|
53
|
-
},
|
|
54
|
-
body: JSON.stringify({
|
|
55
|
-
email,
|
|
56
|
-
attributes,
|
|
57
|
-
}),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
}
|
|
62
17
|
window.onbeforeunload = () => { };
|
|
63
18
|
window.location.href = responseData.redirect_to;
|
|
64
19
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { call,
|
|
1
|
+
import { call, put, takeLatest } from "redux-saga/effects";
|
|
2
2
|
import { getContractFunnelGAEvent } from "@legalplace/lp-events";
|
|
3
|
-
import { v4 as uuid } from "uuid";
|
|
4
3
|
import { getConfig } from "../../config";
|
|
5
4
|
import { selectPermalink, selectInstanceUniqid, selectDocumentTitle, selectAvailableAppStates, selectDocumentModelVersion, } from "../selectors/app";
|
|
6
5
|
import { selectInputs } from "../selectors/inputs";
|
|
@@ -8,7 +7,6 @@ import { SET_USER_EMAIL } from "../constants/sagas/user";
|
|
|
8
7
|
import { setUserStatusAction, initUserAction } from "../actions/user";
|
|
9
8
|
import { updateAvailableAppStatesAction, initInstanceAction, goNextPageAction, } from "../actions/app";
|
|
10
9
|
import { OvcConverter } from "../../libs/OvcConverter";
|
|
11
|
-
import { getSendinblueIdentifyAttributes } from "../../helpers/sendinblueHelper";
|
|
12
10
|
import { setUserEmailSucceeded } from "../actions/sagas/user";
|
|
13
11
|
import { EMAIL_REGEX } from "../../constants/emailValidation";
|
|
14
12
|
const canalHeaders = {
|
|
@@ -75,31 +73,6 @@ function* setUserEmailDecorator(action) {
|
|
|
75
73
|
yield put(initUserAction(Object.assign(Object.assign({}, user), { level: user.role || user.level })));
|
|
76
74
|
}
|
|
77
75
|
}
|
|
78
|
-
if (typeof window.sendinblue !== "undefined" && response.user) {
|
|
79
|
-
const attributes = yield getSendinblueIdentifyAttributes(uniqid, selectDocumentTitle(), nbFilledVars, false, window.location.host);
|
|
80
|
-
window.sendinblue.identify(email, attributes);
|
|
81
|
-
const fetchParams = {
|
|
82
|
-
method: "POST",
|
|
83
|
-
credentials: "include",
|
|
84
|
-
headers: {
|
|
85
|
-
"Content-Type": "application/json",
|
|
86
|
-
},
|
|
87
|
-
body: JSON.stringify({
|
|
88
|
-
email,
|
|
89
|
-
attributes,
|
|
90
|
-
}),
|
|
91
|
-
};
|
|
92
|
-
try {
|
|
93
|
-
if (typeof redirectTo === "string" && !action.disableRedirect) {
|
|
94
|
-
yield call(fetch, `${apiEndpoint}event/create/${uniqid}/${uuid()}`, fetchParams);
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
yield fork(fetch, `${apiEndpoint}event/create/${uniqid}/${uuid()}`, fetchParams);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
76
|
try {
|
|
104
77
|
if (redirectTo && redirectTo.includes("generation"))
|
|
105
78
|
window.dataLayer.push(getContractFunnelGAEvent({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@legalplace/wizardx-core",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.4",
|
|
4
4
|
"author": "Moncef Hammou (moncef@legalplace.fr)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"exports": {
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"@legalplace/sb-experiment": "^0.1.0-alpha.3",
|
|
28
28
|
"@loadable/component": "^5.15.0",
|
|
29
29
|
"@redux-saga/core": "^1.1.3",
|
|
30
|
-
"awesome-phonenumber": "^2.62.1",
|
|
31
30
|
"connected-react-router": "^6.8.0",
|
|
32
31
|
"crypto-js": "^4.1.1",
|
|
33
32
|
"history": "4.10.1",
|
|
@@ -51,9 +50,9 @@
|
|
|
51
50
|
"xss": "^1.0.9"
|
|
52
51
|
},
|
|
53
52
|
"devDependencies": {
|
|
54
|
-
"@legalplace/eslint-config": "^2.1.
|
|
53
|
+
"@legalplace/eslint-config": "^2.1.3",
|
|
55
54
|
"@legalplace/models-v3-types": "^3.6.0",
|
|
56
|
-
"@legalplace/prettier-config": "^2.1.
|
|
55
|
+
"@legalplace/prettier-config": "^2.1.3",
|
|
57
56
|
"@swc-node/jest": "^1.3.2",
|
|
58
57
|
"@swc/core": "^1.2.93",
|
|
59
58
|
"@swc/jest": "^0.2.4",
|
|
@@ -92,5 +91,5 @@
|
|
|
92
91
|
"*.test.ts",
|
|
93
92
|
"*.test.tsx"
|
|
94
93
|
],
|
|
95
|
-
"gitHead": "
|
|
94
|
+
"gitHead": "8fb640031a007e1e39a5710b22e271c65bd3a29a"
|
|
96
95
|
}
|
|
@@ -2,5 +2,3 @@ export const phoneNumberRegX =
|
|
|
2
2
|
/^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/im;
|
|
3
3
|
export const phonePatternFR =
|
|
4
4
|
/^(?:(?:\+|00)33|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/gim;
|
|
5
|
-
export const FRENCH_OVERSEAS_NUMBERS_REGEX =
|
|
6
|
-
/\+33(?:6(?:39|9[0123467])|7(?:[0-2]\d))\d{6}/;
|
|
@@ -26,9 +26,6 @@ class _EventsTracking {
|
|
|
26
26
|
|
|
27
27
|
// Google Analytics
|
|
28
28
|
this.trackGA(eventName, { ...eventProps });
|
|
29
|
-
|
|
30
|
-
// Sendinblue
|
|
31
|
-
this.trackSIB(eventName, { ...eventProps });
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
public register(props: EventsPropsType) {
|
|
@@ -117,9 +114,6 @@ class _EventsTracking {
|
|
|
117
114
|
case "trackGA":
|
|
118
115
|
fn = this.trackGA.bind(this);
|
|
119
116
|
break;
|
|
120
|
-
case "trackSIB":
|
|
121
|
-
fn = this.trackSIB.bind(this);
|
|
122
|
-
break;
|
|
123
117
|
case "registerMix":
|
|
124
118
|
fn = this.registerMix.bind(this);
|
|
125
119
|
break;
|
|
@@ -172,26 +166,6 @@ class _EventsTracking {
|
|
|
172
166
|
return false;
|
|
173
167
|
}
|
|
174
168
|
|
|
175
|
-
public trackSIB(
|
|
176
|
-
eventName: string,
|
|
177
|
-
eventProps?: EventsPropsType,
|
|
178
|
-
doNotPushToQueue = false
|
|
179
|
-
) {
|
|
180
|
-
if (!this.initiated) this.init();
|
|
181
|
-
|
|
182
|
-
if (typeof window.sendinblue !== "undefined") {
|
|
183
|
-
if (this.queue.length > 0 && !doNotPushToQueue) this.executeQueue();
|
|
184
|
-
window.sendinblue.track(eventName, eventProps);
|
|
185
|
-
return true;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (doNotPushToQueue === false)
|
|
189
|
-
this.queue.push(["trackSIB", [eventName, eventProps]]);
|
|
190
|
-
|
|
191
|
-
this.queueTimeout();
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
169
|
private trackFB(
|
|
196
170
|
eventName: string,
|
|
197
171
|
eventProps?: EventsPropsType,
|
|
@@ -1,50 +1,11 @@
|
|
|
1
1
|
import { call, put, takeLatest } from "redux-saga/effects";
|
|
2
|
-
import { v4 as uuid } from "uuid";
|
|
3
2
|
import { setDataStatus } from "../actions/app";
|
|
4
|
-
import {
|
|
5
|
-
selectPermalink,
|
|
6
|
-
selectInstanceUniqid,
|
|
7
|
-
selectDocumentTitle,
|
|
8
|
-
selectInstanceIsPaid,
|
|
9
|
-
} from "../selectors/app";
|
|
3
|
+
import { selectPermalink, selectInstanceUniqid } from "../selectors/app";
|
|
10
4
|
import { SAVE_DATA } from "../constants/sagas/data";
|
|
11
5
|
import { selectInputs } from "../selectors/inputs";
|
|
12
6
|
import { OvcConverter } from "../../libs/OvcConverter";
|
|
13
7
|
import { ActionsType } from "../../types/Actions.type";
|
|
14
8
|
import { updateInstanceArguments } from "../../service/api.manager";
|
|
15
|
-
import { getSendinblueIdentifyAttributes } from "../../helpers/sendinblueHelper";
|
|
16
|
-
import { getConfig } from "../../config";
|
|
17
|
-
import { selectUserEmail } from "../selectors/user";
|
|
18
|
-
|
|
19
|
-
const getSendinblueData = async (uniqid: string, paid: boolean) => {
|
|
20
|
-
const email = selectUserEmail();
|
|
21
|
-
const apiEndpoint =
|
|
22
|
-
getConfig().apiEndpoint + (/\/$/.test(getConfig().apiEndpoint) ? "" : "/");
|
|
23
|
-
|
|
24
|
-
// Getting OVC
|
|
25
|
-
const ovc = OvcConverter.convertToOvc(selectInputs());
|
|
26
|
-
|
|
27
|
-
// Counting filled variables
|
|
28
|
-
const nbFilledVars = Object.keys(ovc.v).reduce((p, c) => {
|
|
29
|
-
const currentValue = ovc.v[c];
|
|
30
|
-
// eslint-disable-next-line no-nested-ternary
|
|
31
|
-
const o: number = Array.isArray(currentValue)
|
|
32
|
-
? currentValue.reduce((_p: number, _c) => _p + (_c !== "" ? 1 : 0), 0)
|
|
33
|
-
: currentValue !== ""
|
|
34
|
-
? 1
|
|
35
|
-
: 0;
|
|
36
|
-
return p + o;
|
|
37
|
-
}, 0);
|
|
38
|
-
// if (typeof window.sendinblue !== 'undefined' && response.user) {
|
|
39
|
-
const attributes = await getSendinblueIdentifyAttributes(
|
|
40
|
-
uniqid as string,
|
|
41
|
-
selectDocumentTitle(),
|
|
42
|
-
nbFilledVars,
|
|
43
|
-
paid,
|
|
44
|
-
window.location.host
|
|
45
|
-
);
|
|
46
|
-
return [apiEndpoint, email, attributes];
|
|
47
|
-
};
|
|
48
9
|
|
|
49
10
|
function* saveDataDecorator(
|
|
50
11
|
action: ActionsType.Sagas.Data.saveData
|
|
@@ -71,28 +32,6 @@ function* saveDataDecorator(
|
|
|
71
32
|
// Redirecting if we recieved order to
|
|
72
33
|
if (action.redirect === true) {
|
|
73
34
|
if (responseData.redirect_to) {
|
|
74
|
-
try {
|
|
75
|
-
// Get Data to send to sendinblue
|
|
76
|
-
const [apiEndpoint, email, attributes] = yield call(
|
|
77
|
-
// @ts-ignore: due to saga implementation
|
|
78
|
-
getSendinblueData,
|
|
79
|
-
[uniqid as string, selectInstanceIsPaid()]
|
|
80
|
-
);
|
|
81
|
-
yield call(fetch, `${apiEndpoint}event/create/${uniqid}/${uuid()}`, {
|
|
82
|
-
method: "POST",
|
|
83
|
-
credentials: "include",
|
|
84
|
-
headers: {
|
|
85
|
-
"Content-Type": "application/json",
|
|
86
|
-
},
|
|
87
|
-
body: JSON.stringify({
|
|
88
|
-
email,
|
|
89
|
-
attributes,
|
|
90
|
-
}),
|
|
91
|
-
});
|
|
92
|
-
} catch (error) {
|
|
93
|
-
// keep going in case the request fail for now
|
|
94
|
-
}
|
|
95
|
-
|
|
96
35
|
window.onbeforeunload = () => {};
|
|
97
36
|
window.location.href = responseData.redirect_to;
|
|
98
37
|
} else if (action.redirectFallback) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { call,
|
|
1
|
+
import { call, put, takeLatest } from "redux-saga/effects";
|
|
2
2
|
import { getContractFunnelGAEvent } from "@legalplace/lp-events";
|
|
3
|
-
import { v4 as uuid } from "uuid";
|
|
4
3
|
import { getConfig } from "../../config";
|
|
5
4
|
import {
|
|
6
5
|
selectPermalink,
|
|
@@ -21,7 +20,6 @@ import {
|
|
|
21
20
|
} from "../actions/app";
|
|
22
21
|
|
|
23
22
|
import { OvcConverter } from "../../libs/OvcConverter";
|
|
24
|
-
import { getSendinblueIdentifyAttributes } from "../../helpers/sendinblueHelper";
|
|
25
23
|
import { setUserEmailSucceeded } from "../actions/sagas/user";
|
|
26
24
|
import { EMAIL_REGEX } from "../../constants/emailValidation";
|
|
27
25
|
|
|
@@ -131,50 +129,6 @@ function* setUserEmailDecorator(action: ActionsType.User.setUserEmail): any {
|
|
|
131
129
|
);
|
|
132
130
|
}
|
|
133
131
|
}
|
|
134
|
-
|
|
135
|
-
// Identifying user
|
|
136
|
-
if (typeof window.sendinblue !== "undefined" && response.user) {
|
|
137
|
-
const attributes = yield getSendinblueIdentifyAttributes(
|
|
138
|
-
uniqid,
|
|
139
|
-
selectDocumentTitle(),
|
|
140
|
-
nbFilledVars,
|
|
141
|
-
false,
|
|
142
|
-
window.location.host
|
|
143
|
-
);
|
|
144
|
-
window.sendinblue.identify(email, attributes);
|
|
145
|
-
const fetchParams = {
|
|
146
|
-
method: "POST",
|
|
147
|
-
credentials: "include",
|
|
148
|
-
headers: {
|
|
149
|
-
"Content-Type": "application/json",
|
|
150
|
-
},
|
|
151
|
-
body: JSON.stringify({
|
|
152
|
-
email,
|
|
153
|
-
attributes,
|
|
154
|
-
}),
|
|
155
|
-
};
|
|
156
|
-
try {
|
|
157
|
-
// if we redirect afterwards we wait for the call to finish otherwise we do it in background
|
|
158
|
-
if (typeof redirectTo === "string" && !action.disableRedirect) {
|
|
159
|
-
yield call(
|
|
160
|
-
// @ts-ignore for some reason saga doesn't like fetchParams being in a seperate variable
|
|
161
|
-
fetch,
|
|
162
|
-
`${apiEndpoint}event/create/${uniqid}/${uuid()}`,
|
|
163
|
-
fetchParams
|
|
164
|
-
);
|
|
165
|
-
} else {
|
|
166
|
-
yield fork(
|
|
167
|
-
// @ts-ignore for some reason saga doesn't like fetchParams being in a seperate variable
|
|
168
|
-
fetch,
|
|
169
|
-
`${apiEndpoint}event/create/${uniqid}/${uuid()}`,
|
|
170
|
-
fetchParams
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
} catch (error) {
|
|
174
|
-
// keep going in case the request fail for now
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
132
|
try {
|
|
179
133
|
if (redirectTo && redirectTo.includes("generation"))
|
|
180
134
|
window.dataLayer.push(
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare const getSIBExtract: () => Promise<{
|
|
2
|
-
[k: string]: string;
|
|
3
|
-
} | undefined>;
|
|
4
|
-
export declare const getSendinblueIdentifyAttributes: (uniqid: string, documentTitle: string, nbFilledVars: number, VALIDATION_Q2?: boolean, domain?: string) => Promise<Record<string, string | number | undefined | boolean | null>>;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { FRENCH_OVERSEAS_NUMBERS_REGEX } from "../constants/phoneValidation";
|
|
11
|
-
import Extracts from "../libs/Extracts";
|
|
12
|
-
import { pardotAttributes } from "../service/pardot.service";
|
|
13
|
-
const formatMobilePhone = (number) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
|
-
const phoneNumber = new (yield (yield import("awesome-phonenumber")).default)(number, "FR");
|
|
15
|
-
const sanitizedNumber = phoneNumber.getNumber();
|
|
16
|
-
return !(sanitizedNumber === null || sanitizedNumber === void 0 ? void 0 : sanitizedNumber.length) ||
|
|
17
|
-
(!phoneNumber.isValid() &&
|
|
18
|
-
!FRENCH_OVERSEAS_NUMBERS_REGEX.test(sanitizedNumber))
|
|
19
|
-
? ""
|
|
20
|
-
: sanitizedNumber;
|
|
21
|
-
});
|
|
22
|
-
export const getSIBExtract = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
-
let sibExtract;
|
|
24
|
-
try {
|
|
25
|
-
Extracts.extractData();
|
|
26
|
-
sibExtract = Extracts.getSibExtractedData();
|
|
27
|
-
if (sibExtract.TEL) {
|
|
28
|
-
try {
|
|
29
|
-
sibExtract.SMS = yield formatMobilePhone(sibExtract.TEL);
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
console.error("Error while extracting sendiblue data", error);
|
|
37
|
-
}
|
|
38
|
-
return sibExtract;
|
|
39
|
-
});
|
|
40
|
-
export const getSendinblueIdentifyAttributes = (uniqid, documentTitle, nbFilledVars, VALIDATION_Q2 = false, domain = "www.legalplace.fr") => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
|
-
const sendinblueVars = Object.assign(Object.assign(Object.assign({ UNIQID: uniqid, AB: Math.floor(Math.random() * 2), TELECHARGEMENT: `${domain}/rt/${uniqid}`, DOCUMENT: documentTitle, WIZZARD_FILLED_FIELDS: nbFilledVars }, (yield getSIBExtract())), pardotAttributes()), { VALIDATION_Q2 });
|
|
42
|
-
if (localStorage && localStorage.getItem("entranceUrl")) {
|
|
43
|
-
sendinblueVars.URL_ENTREE = localStorage.getItem("entranceUrl");
|
|
44
|
-
}
|
|
45
|
-
return sendinblueVars;
|
|
46
|
-
});
|
package/dist/libs/Extracts.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export interface ExtractObject {
|
|
2
|
-
name: string;
|
|
3
|
-
value: string;
|
|
4
|
-
}
|
|
5
|
-
declare class Extracts {
|
|
6
|
-
private extracts;
|
|
7
|
-
private slackExtracts;
|
|
8
|
-
private sendinblueExtracts;
|
|
9
|
-
private mixpanelExtracts;
|
|
10
|
-
extractData(): void;
|
|
11
|
-
getSibExtractedData(): {
|
|
12
|
-
[k: string]: string;
|
|
13
|
-
};
|
|
14
|
-
private prepareExtracts;
|
|
15
|
-
private sendSlackMessages;
|
|
16
|
-
private sendMixpanel;
|
|
17
|
-
private formatSlackMessage;
|
|
18
|
-
}
|
|
19
|
-
declare const _default: Extracts;
|
|
20
|
-
export default _default;
|
package/dist/libs/Extracts.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { selectCustomizationMeta } from "../redux/selectors/app";
|
|
11
|
-
import { selectUserEmail } from "../redux/selectors/user";
|
|
12
|
-
import { selectOptionInput, selectVariableInput, } from "../redux/selectors/inputs";
|
|
13
|
-
import { selectOptionReference, selectVariableReference, } from "../redux/selectors/references";
|
|
14
|
-
import { EventsTracking } from "./EventsTracking";
|
|
15
|
-
class Extracts {
|
|
16
|
-
constructor() {
|
|
17
|
-
this.extracts = [];
|
|
18
|
-
this.slackExtracts = {};
|
|
19
|
-
this.sendinblueExtracts = {};
|
|
20
|
-
this.mixpanelExtracts = {};
|
|
21
|
-
}
|
|
22
|
-
extractData() {
|
|
23
|
-
const customizationMeta = selectCustomizationMeta();
|
|
24
|
-
if (!(typeof customizationMeta === "object" &&
|
|
25
|
-
Array.isArray(customizationMeta.extracts) &&
|
|
26
|
-
customizationMeta.extracts.length > 0))
|
|
27
|
-
return;
|
|
28
|
-
this.extracts = customizationMeta.extracts;
|
|
29
|
-
this.prepareExtracts();
|
|
30
|
-
this.sendSlackMessages();
|
|
31
|
-
this.sendMixpanel();
|
|
32
|
-
}
|
|
33
|
-
getSibExtractedData() {
|
|
34
|
-
return this.sendinblueExtracts;
|
|
35
|
-
}
|
|
36
|
-
prepareExtracts() {
|
|
37
|
-
this.extracts.forEach((extract) => {
|
|
38
|
-
const { id, type, destination } = extract;
|
|
39
|
-
let inputs;
|
|
40
|
-
let defaultName;
|
|
41
|
-
if (type === "option") {
|
|
42
|
-
inputs = selectOptionInput(id).map((value) => value === true ? "True" : "False");
|
|
43
|
-
defaultName = selectOptionReference(id).meta.label;
|
|
44
|
-
}
|
|
45
|
-
else if (type === "variable") {
|
|
46
|
-
inputs = selectVariableInput(id).map((value) => value.toString());
|
|
47
|
-
defaultName = selectVariableReference(id, true).label;
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
throw new Error(`Unkown extract type ${type}`);
|
|
51
|
-
}
|
|
52
|
-
defaultName =
|
|
53
|
-
defaultName.trim().length > 0 ? defaultName : `${type} ${id}`;
|
|
54
|
-
const value = inputs.length > 1 ? inputs.join(", ").slice(0, -2) : inputs[0];
|
|
55
|
-
if (typeof destination.slack === "object" &&
|
|
56
|
-
typeof destination.slack.channel === "string" &&
|
|
57
|
-
destination.slack.channel.trim().length > 0) {
|
|
58
|
-
const { name, channel } = destination.slack;
|
|
59
|
-
if (this.slackExtracts[channel] === undefined)
|
|
60
|
-
this.slackExtracts[channel] = [];
|
|
61
|
-
this.slackExtracts[channel].push({
|
|
62
|
-
name: name || defaultName,
|
|
63
|
-
value,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
if (typeof destination.sendinblue === "object" && value && value.trim()) {
|
|
67
|
-
const name = destination.sendinblue.name || defaultName;
|
|
68
|
-
this.sendinblueExtracts = Object.assign(Object.assign({}, this.sendinblueExtracts), { [name]: value });
|
|
69
|
-
}
|
|
70
|
-
if (typeof destination.mixpanel === "object") {
|
|
71
|
-
const name = destination.mixpanel.name || defaultName;
|
|
72
|
-
this.mixpanelExtracts = Object.assign(Object.assign({}, this.mixpanelExtracts), { [name]: value });
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
sendSlackMessages() {
|
|
77
|
-
const channels = Object.keys(this.slackExtracts);
|
|
78
|
-
channels.forEach((currentChannel) => {
|
|
79
|
-
const text = this.formatSlackMessage(currentChannel);
|
|
80
|
-
const json = {
|
|
81
|
-
username: "WizardX Extract Bot",
|
|
82
|
-
icon_emoji: ":robot_face:",
|
|
83
|
-
channel: currentChannel,
|
|
84
|
-
attachments: [
|
|
85
|
-
{
|
|
86
|
-
text,
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
};
|
|
90
|
-
fetch("https://hooks.slack.com/services/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", {
|
|
91
|
-
body: JSON.stringify(json),
|
|
92
|
-
method: "POST",
|
|
93
|
-
}).catch((err) => __awaiter(this, void 0, void 0, function* () {
|
|
94
|
-
throw new Error(err);
|
|
95
|
-
}));
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
sendMixpanel() {
|
|
99
|
-
EventsTracking.trackMix("Variable Extraction", this.mixpanelExtracts);
|
|
100
|
-
}
|
|
101
|
-
formatSlackMessage(channel) {
|
|
102
|
-
const lines = [`Email: ${selectUserEmail()}`];
|
|
103
|
-
this.slackExtracts[channel].forEach((extract) => {
|
|
104
|
-
const { name, value } = extract;
|
|
105
|
-
lines.push(`*${name}:* ${value}`);
|
|
106
|
-
});
|
|
107
|
-
return lines.join("\n");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
export default new Extracts();
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { FRENCH_OVERSEAS_NUMBERS_REGEX } from "../constants/phoneValidation";
|
|
2
|
-
import Extracts from "../libs/Extracts";
|
|
3
|
-
import { pardotAttributes } from "../service/pardot.service";
|
|
4
|
-
|
|
5
|
-
const formatMobilePhone = async (number: string): Promise<string> => {
|
|
6
|
-
const phoneNumber = new (await (
|
|
7
|
-
await import("awesome-phonenumber")
|
|
8
|
-
).default)(number, "FR");
|
|
9
|
-
const sanitizedNumber = phoneNumber.getNumber();
|
|
10
|
-
|
|
11
|
-
return !sanitizedNumber?.length ||
|
|
12
|
-
(!phoneNumber.isValid() &&
|
|
13
|
-
// Overseas number aren't considerate as valid mobile number by the core library whereas it should
|
|
14
|
-
!FRENCH_OVERSEAS_NUMBERS_REGEX.test(sanitizedNumber))
|
|
15
|
-
? ""
|
|
16
|
-
: sanitizedNumber;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const getSIBExtract = async () => {
|
|
20
|
-
let sibExtract;
|
|
21
|
-
try {
|
|
22
|
-
Extracts.extractData();
|
|
23
|
-
sibExtract = Extracts.getSibExtractedData();
|
|
24
|
-
if (sibExtract.TEL) {
|
|
25
|
-
try {
|
|
26
|
-
sibExtract.SMS = await formatMobilePhone(sibExtract.TEL);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
// the lib we use to format phone can throw exception
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
} catch (error) {
|
|
32
|
-
// eslint-disable-next-line no-console
|
|
33
|
-
console.error("Error while extracting sendiblue data", error);
|
|
34
|
-
}
|
|
35
|
-
return sibExtract;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const getSendinblueIdentifyAttributes = async (
|
|
39
|
-
uniqid: string,
|
|
40
|
-
documentTitle: string,
|
|
41
|
-
nbFilledVars: number,
|
|
42
|
-
VALIDATION_Q2 = false,
|
|
43
|
-
domain = "www.legalplace.fr"
|
|
44
|
-
): Promise<Record<string, string | number | undefined | boolean | null>> => {
|
|
45
|
-
// Identifying client into Sendinblue
|
|
46
|
-
const sendinblueVars: {
|
|
47
|
-
[k: string]: string | undefined | boolean | number | null;
|
|
48
|
-
} = {
|
|
49
|
-
// TODO: Client_type: lpApp.getState().contract.client_type ? 'pro' : 'ind',
|
|
50
|
-
UNIQID: uniqid,
|
|
51
|
-
AB: Math.floor(Math.random() * 2),
|
|
52
|
-
TELECHARGEMENT: `${domain}/rt/${uniqid}`,
|
|
53
|
-
DOCUMENT: documentTitle,
|
|
54
|
-
WIZZARD_FILLED_FIELDS: nbFilledVars,
|
|
55
|
-
...(await getSIBExtract()),
|
|
56
|
-
...pardotAttributes(),
|
|
57
|
-
VALIDATION_Q2,
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
if (localStorage && localStorage.getItem("entranceUrl")) {
|
|
61
|
-
sendinblueVars.URL_ENTREE = localStorage.getItem("entranceUrl");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return sendinblueVars;
|
|
65
|
-
};
|
package/src/libs/Extracts.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import type { ExtractV3 } from "@legalplace/models-v3-types";
|
|
2
|
-
import { selectCustomizationMeta } from "../redux/selectors/app";
|
|
3
|
-
import { selectUserEmail } from "../redux/selectors/user";
|
|
4
|
-
import {
|
|
5
|
-
selectOptionInput,
|
|
6
|
-
selectVariableInput,
|
|
7
|
-
} from "../redux/selectors/inputs";
|
|
8
|
-
import {
|
|
9
|
-
selectOptionReference,
|
|
10
|
-
selectVariableReference,
|
|
11
|
-
} from "../redux/selectors/references";
|
|
12
|
-
import { EventsTracking } from "./EventsTracking";
|
|
13
|
-
|
|
14
|
-
export interface ExtractObject {
|
|
15
|
-
name: string;
|
|
16
|
-
value: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
class Extracts {
|
|
20
|
-
private extracts: Readonly<ExtractV3[]> = [];
|
|
21
|
-
|
|
22
|
-
private slackExtracts: {
|
|
23
|
-
[k: string]: ExtractObject[];
|
|
24
|
-
} = {};
|
|
25
|
-
|
|
26
|
-
private sendinblueExtracts: { [k: string]: string } = {};
|
|
27
|
-
|
|
28
|
-
private mixpanelExtracts: { [k: string]: string } = {};
|
|
29
|
-
|
|
30
|
-
extractData() {
|
|
31
|
-
const customizationMeta = selectCustomizationMeta();
|
|
32
|
-
if (
|
|
33
|
-
!(
|
|
34
|
-
typeof customizationMeta === "object" &&
|
|
35
|
-
Array.isArray(customizationMeta.extracts) &&
|
|
36
|
-
customizationMeta.extracts.length > 0
|
|
37
|
-
)
|
|
38
|
-
)
|
|
39
|
-
return;
|
|
40
|
-
this.extracts = customizationMeta.extracts;
|
|
41
|
-
this.prepareExtracts();
|
|
42
|
-
this.sendSlackMessages();
|
|
43
|
-
// this.sendSendinblue(); Remove SIB data sent there we send all in the identify
|
|
44
|
-
this.sendMixpanel();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
getSibExtractedData() {
|
|
48
|
-
return this.sendinblueExtracts;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private prepareExtracts() {
|
|
52
|
-
this.extracts.forEach((extract) => {
|
|
53
|
-
const { id, type, destination } = extract;
|
|
54
|
-
let inputs: Readonly<string[]>;
|
|
55
|
-
let defaultName: string;
|
|
56
|
-
|
|
57
|
-
// Getting inputs
|
|
58
|
-
if (type === "option") {
|
|
59
|
-
inputs = selectOptionInput(id).map((value) =>
|
|
60
|
-
value === true ? "True" : "False"
|
|
61
|
-
);
|
|
62
|
-
defaultName = selectOptionReference(id).meta.label;
|
|
63
|
-
} else if (type === "variable") {
|
|
64
|
-
inputs = selectVariableInput(id).map((value) => value.toString());
|
|
65
|
-
defaultName = selectVariableReference(id, true).label;
|
|
66
|
-
} else {
|
|
67
|
-
throw new Error(`Unkown extract type ${type}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Making sure default name is never empty
|
|
71
|
-
defaultName =
|
|
72
|
-
defaultName.trim().length > 0 ? defaultName : `${type} ${id}`;
|
|
73
|
-
|
|
74
|
-
// Joining inputs
|
|
75
|
-
const value =
|
|
76
|
-
inputs.length > 1 ? inputs.join(", ").slice(0, -2) : inputs[0];
|
|
77
|
-
|
|
78
|
-
// Slack
|
|
79
|
-
if (
|
|
80
|
-
typeof destination.slack === "object" &&
|
|
81
|
-
typeof destination.slack.channel === "string" &&
|
|
82
|
-
destination.slack.channel.trim().length > 0
|
|
83
|
-
) {
|
|
84
|
-
const { name, channel } = destination.slack;
|
|
85
|
-
|
|
86
|
-
if (this.slackExtracts[channel] === undefined)
|
|
87
|
-
this.slackExtracts[channel] = [];
|
|
88
|
-
|
|
89
|
-
this.slackExtracts[channel].push({
|
|
90
|
-
name: name || defaultName,
|
|
91
|
-
value,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Sendinblue
|
|
96
|
-
if (typeof destination.sendinblue === "object" && value && value.trim()) {
|
|
97
|
-
const name = destination.sendinblue.name || defaultName;
|
|
98
|
-
|
|
99
|
-
this.sendinblueExtracts = {
|
|
100
|
-
...this.sendinblueExtracts,
|
|
101
|
-
[name]: value,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Mixpanel
|
|
106
|
-
if (typeof destination.mixpanel === "object") {
|
|
107
|
-
const name = destination.mixpanel.name || defaultName;
|
|
108
|
-
|
|
109
|
-
this.mixpanelExtracts = {
|
|
110
|
-
...this.mixpanelExtracts,
|
|
111
|
-
[name]: value,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
private sendSlackMessages() {
|
|
118
|
-
const channels = Object.keys(this.slackExtracts);
|
|
119
|
-
channels.forEach((currentChannel) => {
|
|
120
|
-
const text = this.formatSlackMessage(currentChannel);
|
|
121
|
-
const json = {
|
|
122
|
-
username: "WizardX Extract Bot",
|
|
123
|
-
icon_emoji: ":robot_face:",
|
|
124
|
-
channel: currentChannel,
|
|
125
|
-
attachments: [
|
|
126
|
-
{
|
|
127
|
-
text,
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// Sending Slack Message
|
|
133
|
-
fetch(
|
|
134
|
-
"https://hooks.slack.com/services/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",
|
|
135
|
-
{
|
|
136
|
-
body: JSON.stringify(json),
|
|
137
|
-
method: "POST",
|
|
138
|
-
}
|
|
139
|
-
).catch(async (err) => {
|
|
140
|
-
throw new Error(err);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private sendMixpanel() {
|
|
146
|
-
EventsTracking.trackMix("Variable Extraction", this.mixpanelExtracts);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private formatSlackMessage(channel: string) {
|
|
150
|
-
const lines = [`Email: ${selectUserEmail()}`];
|
|
151
|
-
this.slackExtracts[channel].forEach((extract) => {
|
|
152
|
-
const { name, value } = extract;
|
|
153
|
-
lines.push(`*${name}:* ${value}`);
|
|
154
|
-
});
|
|
155
|
-
return lines.join("\n");
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export default new Extracts();
|