@openeventkit/event-site 2.0.103 → 2.0.105
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/env.template +1 -0
- package/gatsby-browser.js +6 -0
- package/gatsby-config.js +11 -0
- package/gatsby-node.js +5 -3
- package/package.json +51 -4
- package/src/__mocks__/fileMock.js +1 -0
- package/src/actions/fetch-entities-actions.js +3 -0
- package/src/cms/config/collections/configurationsCollection/siteSettings/index.js +21 -0
- package/src/cms/config/collections/configurationsCollection/siteSettings/typeDefs.js +6 -0
- package/src/components/AttendeeToAttendeeWidgetComponent.js +7 -7
- package/src/components/AuthComponent.js +5 -2
- package/src/components/RegistrationLiteComponent.js +8 -1
- package/src/content/site-settings/index.json +5 -0
- package/src/utils/__test__/dataNormalization.test.js +163 -0
- package/src/utils/analytics/AnalyticsManager.js +28 -0
- package/src/utils/analytics/AnalyticsProvider.js +7 -0
- package/src/utils/analytics/events.js +1 -0
- package/src/utils/analytics/providers/GoogleTagManagerProvider.js +38 -0
- package/src/utils/customEvents/CustomEventManager.js +22 -0
- package/src/utils/customEvents/index.js +15 -0
- package/src/utils/customEvents/useCustomEvent.js +13 -0
- package/src/utils/dataNormalization.js +36 -0
- package/src/utils/envVariables.js +3 -0
- package/src/utils/useSiteSettings.js +9 -0
- package/src/utils/customEvents.js +0 -8
package/env.template
CHANGED
package/gatsby-browser.js
CHANGED
|
@@ -2,6 +2,9 @@ import * as Sentry from "@sentry/gatsby";
|
|
|
2
2
|
import { RewriteFrames as RewriteFramesIntegration } from "@sentry/integrations";
|
|
3
3
|
import ReduxWrapper from "./src/state/ReduxWrapper";
|
|
4
4
|
|
|
5
|
+
import AnalyticsManager from "./src/utils/analytics/AnalyticsManager";
|
|
6
|
+
import GoogleTagManagerProvider from "./src/utils/analytics/providers/GoogleTagManagerProvider";
|
|
7
|
+
|
|
5
8
|
import smoothscroll from "smoothscroll-polyfill";
|
|
6
9
|
import "what-input";
|
|
7
10
|
|
|
@@ -15,6 +18,9 @@ import marketingSettings from "data/marketing-settings.json";
|
|
|
15
18
|
// smooth scroll polyfill needed for Safari
|
|
16
19
|
smoothscroll.polyfill();
|
|
17
20
|
|
|
21
|
+
const googleTagManagerProvider = new GoogleTagManagerProvider();
|
|
22
|
+
const analyticsManager = new AnalyticsManager(googleTagManagerProvider);
|
|
23
|
+
|
|
18
24
|
export const wrapRootElement = ReduxWrapper;
|
|
19
25
|
|
|
20
26
|
export const onClientEntry = () => {
|
package/gatsby-config.js
CHANGED
|
@@ -39,6 +39,16 @@ const manifestPlugin = faviconAsset ? [
|
|
|
39
39
|
}
|
|
40
40
|
] : [];
|
|
41
41
|
|
|
42
|
+
const googleTagManagerPlugin = process.env.GATSBY_GOOGLE_TAGMANAGER_ID ? [
|
|
43
|
+
{
|
|
44
|
+
resolve: "gatsby-plugin-google-tagmanager",
|
|
45
|
+
options: {
|
|
46
|
+
id: process.env.GATSBY_GOOGLE_TAGMANAGER_ID,
|
|
47
|
+
includeInDevelopment: true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
] : [];
|
|
51
|
+
|
|
42
52
|
const plugins = [
|
|
43
53
|
...manifestPlugin,
|
|
44
54
|
{
|
|
@@ -177,6 +187,7 @@ const plugins = [
|
|
|
177
187
|
}
|
|
178
188
|
}
|
|
179
189
|
},
|
|
190
|
+
...googleTagManagerPlugin,
|
|
180
191
|
"gatsby-plugin-netlify", // make sure to keep it last in the array
|
|
181
192
|
];
|
|
182
193
|
|
package/gatsby-node.js
CHANGED
|
@@ -87,7 +87,8 @@ const SSR_getEvents = async (baseUrl, summitId, accessToken) => {
|
|
|
87
87
|
access_token: accessToken,
|
|
88
88
|
per_page: 50,
|
|
89
89
|
page: 1,
|
|
90
|
-
expand: "slides,
|
|
90
|
+
expand: "slides,links,videos,media_uploads,type,track,track.subtracks,track.allowed_access_levels,location,location.venue,location.floor,speakers,moderator,sponsors,current_attendance,groups,rsvp_template,tags",
|
|
91
|
+
relations: "speakers.badge_features,speakers.affiliations,speakers.languages,speakers.other_presentation_links,speakers.areas_of_expertise,speakers.travel_preferences,speakers.organizational_roles,speakers.all_presentations,speakers.all_moderated_presentations",
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
return await axios.get(endpoint, { params }).then(async ({data}) => {
|
|
@@ -155,6 +156,7 @@ const SSR_getSpeakers = async (baseUrl, summitId, accessToken, filter = null) =>
|
|
|
155
156
|
access_token: accessToken,
|
|
156
157
|
per_page: 30,
|
|
157
158
|
page: 1,
|
|
159
|
+
relations: 'badge_features,affiliations,languages,other_presentation_links,areas_of_expertise,travel_preferences,organizational_roles,all_presentations,all_moderated_presentations',
|
|
158
160
|
};
|
|
159
161
|
|
|
160
162
|
const endpoint = `${baseUrl}/api/v1/summits/${summitId}/speakers/on-schedule`;
|
|
@@ -180,7 +182,7 @@ const SSR_getSpeakers = async (baseUrl, summitId, accessToken, filter = null) =>
|
|
|
180
182
|
const SSR_getSummit = async (baseUrl, summitId) => {
|
|
181
183
|
|
|
182
184
|
const params = {
|
|
183
|
-
expand: "event_types,tracks,
|
|
185
|
+
expand: "event_types,tracks,tracks.subtracks,track_groups,presentation_levels,locations.rooms,locations.floors,order_extra_questions.values,schedule_settings,schedule_settings.filters,schedule_settings.pre_filters",
|
|
184
186
|
t: Date.now()
|
|
185
187
|
};
|
|
186
188
|
|
|
@@ -201,7 +203,7 @@ const SSR_getVoteablePresentations = async (baseUrl, summitId, accessToken) => {
|
|
|
201
203
|
per_page: 50,
|
|
202
204
|
page: 1,
|
|
203
205
|
filter: "published==1",
|
|
204
|
-
expand: "slides,
|
|
206
|
+
expand: "slides,links,videos,media_uploads,type,track,track.allowed_access_levels,location,location.venue,location.floor,speakers,moderator,sponsors,current_attendance,groups,rsvp_template,tags",
|
|
205
207
|
};
|
|
206
208
|
|
|
207
209
|
return await axios.get(endpoint,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openeventkit/event-site",
|
|
3
3
|
"description": "Event Site",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.105",
|
|
5
5
|
"author": "Tipit LLC",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@mui/base": "^5.0.0-alpha.114",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"full-schedule-widget": "3.0.5",
|
|
47
47
|
"gatsby": "^5.8.1",
|
|
48
48
|
"gatsby-alias-imports": "^1.0.6",
|
|
49
|
+
"gatsby-plugin-google-tagmanager": "^5.13.1",
|
|
49
50
|
"gatsby-plugin-image": "^3.8.0",
|
|
50
51
|
"gatsby-plugin-manifest": "^5.12.3",
|
|
51
52
|
"gatsby-plugin-netlify": "^5.1.0",
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
"moment-timezone": "^0.5.31",
|
|
78
79
|
"netlify-cms-app": "^2.15.72",
|
|
79
80
|
"netlify-cms-lib-widgets": "^1.8.0",
|
|
80
|
-
"openstack-uicore-foundation": "4.1.
|
|
81
|
+
"openstack-uicore-foundation": "4.1.76",
|
|
81
82
|
"path-browserify": "^1.0.1",
|
|
82
83
|
"prop-types": "^15.6.0",
|
|
83
84
|
"react": "^18.2.0",
|
|
@@ -121,7 +122,7 @@
|
|
|
121
122
|
"stream-browserify": "^3.0.0",
|
|
122
123
|
"stream-chat": "^2.7.2",
|
|
123
124
|
"stream-chat-react": "3.1.7",
|
|
124
|
-
"summit-registration-lite": "5.0.
|
|
125
|
+
"summit-registration-lite": "5.0.26",
|
|
125
126
|
"superagent": "8.0.9",
|
|
126
127
|
"sweetalert2": "^9.17.0",
|
|
127
128
|
"upcoming-events-widget": "3.0.5",
|
|
@@ -148,12 +149,58 @@
|
|
|
148
149
|
"build": "NODE_ENV=production NODE_OPTIONS=--max-old-space-size=10240 cross-env node --trace-warnings node_modules/.bin/gatsby build --log-pages",
|
|
149
150
|
"develop": "NODE_OPTIONS=--max-old-space-size=8192 npm run gatsby-clean && node --trace-warnings node_modules/.bin/gatsby develop --S -H 0.0.0.0",
|
|
150
151
|
"format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"{gatsby-*.js,src/**/*.js}\"",
|
|
151
|
-
"test": "
|
|
152
|
+
"test": "jest",
|
|
152
153
|
"update-libs": "yarn upgrade live-event-widget openstack-uicore-foundation schedule-lite simple-chat-widget speakers-widget attendee-to-attendee-widget"
|
|
153
154
|
},
|
|
154
155
|
"devDependencies": {
|
|
156
|
+
"@testing-library/dom": "^10.0.0",
|
|
157
|
+
"@testing-library/jest-dom": "^6.4.2",
|
|
158
|
+
"@testing-library/react": "^15.0.2",
|
|
159
|
+
"@testing-library/user-event": "^14.5.2",
|
|
160
|
+
"babel-jest": "^29.7.0",
|
|
155
161
|
"devcert": "1.1.0",
|
|
156
162
|
"gatsby-plugin-webpack-speed-measure": "^0.1.1",
|
|
163
|
+
"identity-obj-proxy": "^3.0.0",
|
|
164
|
+
"jest": "^28.1.0",
|
|
165
|
+
"jest-environment-jsdom": "^28.1.0",
|
|
166
|
+
"jest-transform-stub": "^2.0.0",
|
|
167
|
+
"js-yaml": "^4.1.0",
|
|
168
|
+
"js-yaml-loader": "^1.2.2",
|
|
157
169
|
"prettier": "^2.0.5"
|
|
170
|
+
},
|
|
171
|
+
"jest": {
|
|
172
|
+
"collectCoverageFrom": [
|
|
173
|
+
"src/**/*.{js,jsx,mjs}"
|
|
174
|
+
],
|
|
175
|
+
"moduleNameMapper": {
|
|
176
|
+
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
|
|
177
|
+
"\\.(css|scss)$": "identity-obj-proxy"
|
|
178
|
+
},
|
|
179
|
+
"testMatch": [
|
|
180
|
+
"<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}",
|
|
181
|
+
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}"
|
|
182
|
+
],
|
|
183
|
+
"transform": {
|
|
184
|
+
"\\.[jt]sx?$": "babel-jest",
|
|
185
|
+
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub"
|
|
186
|
+
},
|
|
187
|
+
"moduleDirectories": [
|
|
188
|
+
"node_modules",
|
|
189
|
+
"src"
|
|
190
|
+
],
|
|
191
|
+
"moduleFileExtensions": [
|
|
192
|
+
"web.js",
|
|
193
|
+
"js",
|
|
194
|
+
"json",
|
|
195
|
+
"web.jsx",
|
|
196
|
+
"jsx",
|
|
197
|
+
"node",
|
|
198
|
+
"mjs"
|
|
199
|
+
],
|
|
200
|
+
"globals": {
|
|
201
|
+
"window": {},
|
|
202
|
+
"console": {}
|
|
203
|
+
},
|
|
204
|
+
"testEnvironment": "jsdom"
|
|
158
205
|
}
|
|
159
206
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -16,6 +16,8 @@ export const fetchEventById = async (summitId, eventId, accessToken = null) => {
|
|
|
16
16
|
|
|
17
17
|
apiUrl.addQuery('expand', 'slides, links, videos, media_uploads, type, track, track.allowed_access_levels, location, location.venue, location.floor, speakers, moderator, sponsors, current_attendance, groups, rsvp_template, tags');
|
|
18
18
|
apiUrl.addQuery('evict_cache', 1);
|
|
19
|
+
apiUrl.addQuery('relations', "speakers.badge_features,speakers.affiliations,speakers.languages,speakers.other_presentation_links,speakers.areas_of_expertise,speakers.travel_preferences,speakers.organizational_roles,speakers.all_presentations,speakers.all_moderated_presentations");
|
|
20
|
+
|
|
19
21
|
return fetch(apiUrl.toString(), {
|
|
20
22
|
method: 'GET'
|
|
21
23
|
}).then(async (response) => {
|
|
@@ -73,6 +75,7 @@ export const fetchSpeakerById = async(summitId, speakerId, accessToken = null) =
|
|
|
73
75
|
apiUrl.addQuery('access_token', accessToken);
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
apiUrl.addQuery('relations', 'badge_features,affiliations,languages,other_presentation_links,areas_of_expertise,travel_preferences,organizational_roles,all_presentations,all_moderated_presentations');
|
|
76
79
|
apiUrl.addQuery('evict_cache', 1);
|
|
77
80
|
|
|
78
81
|
return fetch(apiUrl.toString(), {
|
|
@@ -176,6 +176,27 @@ const siteSettings = {
|
|
|
176
176
|
}),
|
|
177
177
|
]
|
|
178
178
|
}),
|
|
179
|
+
objectField({
|
|
180
|
+
label: "IDP Logo",
|
|
181
|
+
name: "idpLogo",
|
|
182
|
+
fields: [
|
|
183
|
+
imageField({
|
|
184
|
+
label: "Logo Dark",
|
|
185
|
+
name: "idpLogoDark",
|
|
186
|
+
required: false
|
|
187
|
+
}),
|
|
188
|
+
imageField({
|
|
189
|
+
label: "Logo Light",
|
|
190
|
+
name: "idpLogoLight",
|
|
191
|
+
required: false
|
|
192
|
+
}),
|
|
193
|
+
stringField({
|
|
194
|
+
label: "Logo Alt",
|
|
195
|
+
name: "idpLogoAlt",
|
|
196
|
+
required: false
|
|
197
|
+
}),
|
|
198
|
+
]
|
|
199
|
+
}),
|
|
179
200
|
listField({
|
|
180
201
|
label: "Identity Provider Buttons",
|
|
181
202
|
name: "identityProviderButtons",
|
|
@@ -21,6 +21,11 @@ module.exports = `
|
|
|
21
21
|
schedule: Schedule
|
|
22
22
|
chat: Chat
|
|
23
23
|
}
|
|
24
|
+
type IdpLogo {
|
|
25
|
+
idpLogoDark: File @fileByRelativePath
|
|
26
|
+
idpLogoLight: File @fileByRelativePath
|
|
27
|
+
idpLogoAlt: String
|
|
28
|
+
}
|
|
24
29
|
type IdentityProviderButton {
|
|
25
30
|
buttonColor: String
|
|
26
31
|
buttonBorderColor: String
|
|
@@ -33,6 +38,7 @@ module.exports = `
|
|
|
33
38
|
siteMetadata: SiteMetadata
|
|
34
39
|
favicon: Favicon
|
|
35
40
|
widgets: Widgets
|
|
41
|
+
idpLogo: IdpLogo
|
|
36
42
|
identityProviderButtons: [IdentityProviderButton]
|
|
37
43
|
}
|
|
38
44
|
`;
|
|
@@ -23,7 +23,10 @@ import { PHASES } from "../utils/phasesUtils";
|
|
|
23
23
|
import "attendee-to-attendee-widget/dist/index.css";
|
|
24
24
|
|
|
25
25
|
import { SentryFallbackFunction } from "./SentryErrorComponent";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
useCustomEvent,
|
|
28
|
+
INIT_LOGOUT_EVENT
|
|
29
|
+
} from "../utils/customEvents";
|
|
27
30
|
|
|
28
31
|
const sbAuthProps = {
|
|
29
32
|
supabaseUrl: getEnvVariable(SUPABASE_URL),
|
|
@@ -187,18 +190,15 @@ const AccessTracker = ({ user, isLoggedUser, summitPhase, chatSettings }) => {
|
|
|
187
190
|
const trackerRef = useRef();
|
|
188
191
|
|
|
189
192
|
const handleLogout = useCallback(() => {
|
|
190
|
-
if(trackerRef.current)
|
|
193
|
+
if (trackerRef.current)
|
|
191
194
|
trackerRef.current.signOut();
|
|
192
195
|
},[]);
|
|
193
196
|
|
|
194
|
-
|
|
195
|
-
window.addEventListener(onInitLogoutEvent, handleLogout)
|
|
196
|
-
return () => window.removeEventListener(onInitLogoutEvent, handleLogout);
|
|
197
|
-
},[]);
|
|
197
|
+
useCustomEvent(INIT_LOGOUT_EVENT, handleLogout);
|
|
198
198
|
|
|
199
199
|
useEffect(() => {
|
|
200
200
|
if (!isLoggedUser) {
|
|
201
|
-
if(trackerRef.current)
|
|
201
|
+
if (trackerRef.current)
|
|
202
202
|
trackerRef.current.signOut();
|
|
203
203
|
}
|
|
204
204
|
}, [isLoggedUser]);
|
|
@@ -143,7 +143,7 @@ const AuthComponent = ({
|
|
|
143
143
|
allowsOtpAuth: allowsOtpAuth,
|
|
144
144
|
initialEmailValue: initialEmailValue,
|
|
145
145
|
title: 'Sign in using the email associated with your account:',
|
|
146
|
-
summitData: summit
|
|
146
|
+
summitData: summit,
|
|
147
147
|
};
|
|
148
148
|
|
|
149
149
|
const passwordlessLoginProps = {
|
|
@@ -158,6 +158,9 @@ const AuthComponent = ({
|
|
|
158
158
|
codeError: otpError,
|
|
159
159
|
goToLogin: () => setOtpLogin(false),
|
|
160
160
|
getLoginCode: (email) => sendCode(email),
|
|
161
|
+
idpLogoLight: siteSettings?.idpLogo?.idpLogoLight?.publicURL,
|
|
162
|
+
idpLogoDark: siteSettings?.idpLogo?.idpLogoDark?.publicURL,
|
|
163
|
+
idpLogoAlt: siteSettings?.idpLogo?.idpLogoAlt
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
const { loginButton } = marketingPageSettings.hero.buttons;
|
|
@@ -242,4 +245,4 @@ AuthComponent.defaultProps = {
|
|
|
242
245
|
AuthComponent.propTypes = {
|
|
243
246
|
location: PropTypes.object.isRequired,
|
|
244
247
|
ignoreAutoOpen: PropTypes.bool,
|
|
245
|
-
}
|
|
248
|
+
}
|
|
@@ -25,6 +25,9 @@ import useMarketingSettings, { MARKETING_SETTINGS_KEYS } from "@utils/useMarket
|
|
|
25
25
|
import useSiteSettings from "@utils/useSiteSettings";
|
|
26
26
|
import { SentryFallbackFunction } from "./SentryErrorComponent";
|
|
27
27
|
|
|
28
|
+
import { triggerAnalyticsTrackEvent } from "@utils/customEvents";
|
|
29
|
+
import { PURCHASE_COMPLETE } from "@utils/analytics/events";
|
|
30
|
+
|
|
28
31
|
import styles from "../styles/marketing-hero.module.scss";
|
|
29
32
|
|
|
30
33
|
const RegistrationLiteComponent = ({
|
|
@@ -153,11 +156,12 @@ const RegistrationLiteComponent = ({
|
|
|
153
156
|
goToRegistration: () => navigate(`${getEnvVariable(REGISTRATION_BASE_URL)}/a/${summit.slug}`),
|
|
154
157
|
goToMyOrders: () => navigate("/a/my-tickets"),
|
|
155
158
|
completedExtraQuestions: async (attendee) => {
|
|
156
|
-
if(!attendee) return true;
|
|
159
|
+
if (!attendee) return true;
|
|
157
160
|
await getExtraQuestions(attendee?.id);
|
|
158
161
|
return checkRequireExtraQuestionsByAttendee(attendee);
|
|
159
162
|
},
|
|
160
163
|
onPurchaseComplete: (order) => {
|
|
164
|
+
triggerAnalyticsTrackEvent(PURCHASE_COMPLETE, { order });
|
|
161
165
|
// check if it"s necessary to update profile
|
|
162
166
|
setUserOrder(order).then(()=> checkOrderData(order));
|
|
163
167
|
},
|
|
@@ -191,6 +195,9 @@ const RegistrationLiteComponent = ({
|
|
|
191
195
|
noAllowedTicketsMessage: noAllowedTicketsMessage,
|
|
192
196
|
showCompanyInput: showCompanyInput,
|
|
193
197
|
showCompanyInputDefaultOptions: showCompanyInputDefaultOptions,
|
|
198
|
+
idpLogoLight: siteSettings?.idpLogo?.idpLogoLight?.publicURL,
|
|
199
|
+
idpLogoDark: siteSettings?.idpLogo?.idpLogoDark?.publicURL,
|
|
200
|
+
idpLogoAlt: siteSettings?.idpLogo?.idpLogoAlt
|
|
194
201
|
};
|
|
195
202
|
|
|
196
203
|
const { registerButton } = marketingPageSettings.hero.buttons;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { normalizeData } from "../dataNormalization";
|
|
6
|
+
|
|
7
|
+
const testOrder = {
|
|
8
|
+
"id": 43545,
|
|
9
|
+
"created": 1713391444,
|
|
10
|
+
"last_edited": 1713391444,
|
|
11
|
+
"number": "ORDER_2023OCPGLOBALSUMMIT2023_662047548D72D773046574",
|
|
12
|
+
"status": "Paid",
|
|
13
|
+
"payment_method": "Online",
|
|
14
|
+
"owner_first_name": "Sebastian",
|
|
15
|
+
"owner_last_name": "Marcet",
|
|
16
|
+
"owner_email": "test@gmail.com",
|
|
17
|
+
"owner_company": "Tipit",
|
|
18
|
+
"owner_company_id": 3496,
|
|
19
|
+
"owner_id": 26391,
|
|
20
|
+
"summit_id": 50,
|
|
21
|
+
"currency": "USD",
|
|
22
|
+
"currency_symbol": "$",
|
|
23
|
+
"extra_questions": [],
|
|
24
|
+
"tickets": [{
|
|
25
|
+
"id": 50175,
|
|
26
|
+
"created": 1713391444,
|
|
27
|
+
"last_edited": 1713409486,
|
|
28
|
+
"number": "TICKET_2023OCPGLOBALSUMMIT2023_6620475493A78111038846",
|
|
29
|
+
"status": "Paid",
|
|
30
|
+
"external_order_id": null,
|
|
31
|
+
"external_attendee_id": null,
|
|
32
|
+
"bought_date": 1713391485,
|
|
33
|
+
"order_id": 43545,
|
|
34
|
+
"promo_code_id": 0,
|
|
35
|
+
"raw_cost": 900,
|
|
36
|
+
"net_selling_cost": 900,
|
|
37
|
+
"raw_cost_in_cents": 90000,
|
|
38
|
+
"final_amount": 900,
|
|
39
|
+
"final_amount_in_cents": 90000,
|
|
40
|
+
"discount": 0,
|
|
41
|
+
"discount_rate": 0,
|
|
42
|
+
"discount_in_cents": 0,
|
|
43
|
+
"refunded_amount": 0,
|
|
44
|
+
"refunded_amount_in_cents": 0,
|
|
45
|
+
"total_refunded_amount": 0,
|
|
46
|
+
"total_refunded_amount_in_cents": 0,
|
|
47
|
+
"currency": "USD",
|
|
48
|
+
"currency_symbol": "$",
|
|
49
|
+
"taxes_amount": 0,
|
|
50
|
+
"taxes_amount_in_cents": 0,
|
|
51
|
+
"is_active": true,
|
|
52
|
+
"qr_code": "TICKET_2023OCPGLOBALSUMMIT2023|TICKET_2023OCPGLOBALSUMMIT2023_6620475493A78111038846|smarcet@gmail.com",
|
|
53
|
+
"owner": {
|
|
54
|
+
"id": 28608,
|
|
55
|
+
"created": 1685574339,
|
|
56
|
+
"last_edited": 1713409445,
|
|
57
|
+
"summit_hall_checked_in": false,
|
|
58
|
+
"summit_hall_checked_in_date": null,
|
|
59
|
+
"summit_virtual_checked_in_date": null,
|
|
60
|
+
"shared_contact_info": false,
|
|
61
|
+
"member_id": 26391,
|
|
62
|
+
"summit_id": 50,
|
|
63
|
+
"first_name": "Sebastian",
|
|
64
|
+
"last_name": "Marcet",
|
|
65
|
+
"email": "test@gmail.com",
|
|
66
|
+
"company": "Tipit",
|
|
67
|
+
"company_id": 3496,
|
|
68
|
+
"disclaimer_accepted_date": null,
|
|
69
|
+
"disclaimer_accepted": false,
|
|
70
|
+
"status": "Incomplete",
|
|
71
|
+
"tickets": [50175],
|
|
72
|
+
"presentation_votes": [],
|
|
73
|
+
"votes_count": 0,
|
|
74
|
+
"ticket_types": [{"type_id": 109, "qty": 1, "type_name": "Standard Ticket"}],
|
|
75
|
+
"allowed_access_levels": [149],
|
|
76
|
+
"allowed_features": [],
|
|
77
|
+
"tags": [],
|
|
78
|
+
"extra_questions": []
|
|
79
|
+
},
|
|
80
|
+
"badge": {
|
|
81
|
+
"id": 50183,
|
|
82
|
+
"created": 1713391444,
|
|
83
|
+
"last_edited": 1713391444,
|
|
84
|
+
"print_date": null,
|
|
85
|
+
"qr_code": null,
|
|
86
|
+
"is_void": false,
|
|
87
|
+
"ticket_id": 50175,
|
|
88
|
+
"printed_times": 0,
|
|
89
|
+
"features": [],
|
|
90
|
+
"print_excerpt": [],
|
|
91
|
+
"type": {
|
|
92
|
+
"id": 112,
|
|
93
|
+
"created": 1685036153,
|
|
94
|
+
"last_edited": 1685036153,
|
|
95
|
+
"name": "General Attendee",
|
|
96
|
+
"description": "This is for all in-person general attendees. This Badge Type can have various Badge Features (icons/titles). Badge Features may be dictated by promo codes or can be added manually by Admins. The badge needs a QR code for LR. This badge needs T-Shirt Size Indicator from extra question answer (dots/dashes) on the design.",
|
|
97
|
+
"template_content": "",
|
|
98
|
+
"is_default": true,
|
|
99
|
+
"summit_id": 50,
|
|
100
|
+
"badge_features": [],
|
|
101
|
+
"allowed_view_types": [49],
|
|
102
|
+
"access_levels": [{
|
|
103
|
+
"id": 149,
|
|
104
|
+
"created": 1683216137,
|
|
105
|
+
"last_edited": 1683216137,
|
|
106
|
+
"name": "IN_PERSON",
|
|
107
|
+
"description": "Allows in person show access.",
|
|
108
|
+
"template_content": "",
|
|
109
|
+
"is_default": true,
|
|
110
|
+
"summit_id": 50
|
|
111
|
+
}]
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"ticket_type": {
|
|
115
|
+
"id": 109,
|
|
116
|
+
"created": 1685036568,
|
|
117
|
+
"last_edited": 1713391444,
|
|
118
|
+
"name": "Standard Ticket",
|
|
119
|
+
"description": "This is for general admission. Needs QR Code on Badge. Symposium included (first come, first served until capacity is met). Early bird pricing ended 7/31/23. In-Person Ticket: Standard, is on sale 8/1/23- 10/15/23",
|
|
120
|
+
"external_id": null,
|
|
121
|
+
"summit_id": 50,
|
|
122
|
+
"cost": 900,
|
|
123
|
+
"currency": "USD",
|
|
124
|
+
"currency_symbol": "$",
|
|
125
|
+
"quantity_2_sell": 10000,
|
|
126
|
+
"max_quantity_per_order": 100,
|
|
127
|
+
"sales_start_date": 1690873200,
|
|
128
|
+
"sales_end_date": 1722581940,
|
|
129
|
+
"badge_type_id": 112,
|
|
130
|
+
"quantity_sold": 3289,
|
|
131
|
+
"audience": "All",
|
|
132
|
+
"applied_taxes": [],
|
|
133
|
+
"sub_type": "Regular"
|
|
134
|
+
},
|
|
135
|
+
"applied_taxes": [],
|
|
136
|
+
"refund_requests": []
|
|
137
|
+
}]
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const testArrayOrderData = [testOrder];
|
|
141
|
+
|
|
142
|
+
describe("data normalization", () => {
|
|
143
|
+
test("remove email from order data", () => {
|
|
144
|
+
expect(testOrder.hasOwnProperty("owner_email")).toBeTruthy();
|
|
145
|
+
expect(testOrder.tickets[0].owner.hasOwnProperty("email") ).toBeTruthy();
|
|
146
|
+
const data = normalizeData(testOrder);
|
|
147
|
+
expect(data.hasOwnProperty("owner_email")).toBeFalsy();
|
|
148
|
+
expect(data.tickets[0].owner.hasOwnProperty("email") ).toBeFalsy();
|
|
149
|
+
});
|
|
150
|
+
test("remove qr from order data", () => {
|
|
151
|
+
expect(testOrder.tickets[0].hasOwnProperty("qr_code") ).toBeTruthy();
|
|
152
|
+
const data = normalizeData(testOrder);
|
|
153
|
+
expect(data.tickets[0].hasOwnProperty("qr_code") ).toBeFalsy();
|
|
154
|
+
});
|
|
155
|
+
test("remove qr from array of orders", () => {
|
|
156
|
+
expect(Array.isArray(testArrayOrderData)).toBeTruthy();
|
|
157
|
+
expect(testArrayOrderData[0].tickets[0].hasOwnProperty("qr_code") ).toBeTruthy();
|
|
158
|
+
const data = normalizeData(testArrayOrderData);
|
|
159
|
+
expect(Array.isArray(data)).toBeTruthy();
|
|
160
|
+
expect(data[0].tickets[0].hasOwnProperty("qr_code") ).toBeFalsy();
|
|
161
|
+
});
|
|
162
|
+
})
|
|
163
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import AnalyticsProvider from "./AnalyticsProvider";
|
|
2
|
+
import { CustomEventManager, ANALYTICS_TRACK_EVENT } from "../customEvents";
|
|
3
|
+
import { normalizeData } from "@utils/dataNormalization";
|
|
4
|
+
|
|
5
|
+
class AnalyticsManager {
|
|
6
|
+
constructor(analyticsProvider) {
|
|
7
|
+
if (!(analyticsProvider instanceof AnalyticsProvider)) {
|
|
8
|
+
throw new Error("An instance of AnalyticsProvider is required.");
|
|
9
|
+
}
|
|
10
|
+
this.analyticsProvider = analyticsProvider;
|
|
11
|
+
CustomEventManager.addEventListener(ANALYTICS_TRACK_EVENT, this.handleTrackEvent.bind(this));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get provider() {
|
|
15
|
+
return this.analyticsProvider;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
handleTrackEvent = (event) => {
|
|
19
|
+
const { eventName, eventParams } = event.detail;
|
|
20
|
+
this.trackEvent(eventName, eventParams);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
trackEvent = (eventName, eventParams) => {
|
|
24
|
+
this.analyticsProvider.trackEvent(eventName, normalizeData(eventParams));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default AnalyticsManager;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PURCHASE_COMPLETE = "purchase_complete";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import AnalyticsProvider from "../AnalyticsProvider";
|
|
2
|
+
import { getEnvVariable, GOOGLE_TAGMANAGER_ID } from "@utils/envVariables";
|
|
3
|
+
|
|
4
|
+
class GoogleTagManagerProvider extends AnalyticsProvider {
|
|
5
|
+
constructor() {
|
|
6
|
+
super();
|
|
7
|
+
if (!getEnvVariable(GOOGLE_TAGMANAGER_ID)) {
|
|
8
|
+
console.warn("GoogleTagManagerProvider: GOOGLE_TAGMANAGER_ID environment variable is not set. Tracking will be disabled.");
|
|
9
|
+
}
|
|
10
|
+
this.dataLayer = (typeof window !== "undefined" && window.dataLayer) || [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
gtag() {
|
|
14
|
+
this.dataLayer.push(arguments);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
trackEvent = (eventName, eventParams) => {
|
|
18
|
+
this.gtag("event", eventName, eventParams);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
config = (targetId, additionalConfig) => {
|
|
22
|
+
this.gtag("config", targetId, additionalConfig);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
set = (parameters) => {
|
|
26
|
+
this.gtag("set", parameters);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
get = (target, fieldName, callback) => {
|
|
30
|
+
this.gtag("get", target, fieldName, callback);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
consent = (consentArg, consentParams) => {
|
|
34
|
+
this.gtag("consent", consentArg, consentParams);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default GoogleTagManagerProvider;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class CustomEventManager {
|
|
2
|
+
static dispatchEvent = (eventName, eventData, target = window) => {
|
|
3
|
+
if (typeof target !== "undefined") {
|
|
4
|
+
const event = new CustomEvent(eventName, { detail: eventData });
|
|
5
|
+
target.dispatchEvent(event);
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
static addEventListener = (eventName, callback, target = window) => {
|
|
10
|
+
if (typeof target !== "undefined") {
|
|
11
|
+
target.addEventListener(eventName, callback);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
static removeEventListener = (eventName, callback, target = window) => {
|
|
16
|
+
if (typeof target !== "undefined") {
|
|
17
|
+
target.removeEventListener(eventName, callback);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default CustomEventManager;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import useCustomEvent from "./useCustomEvent";
|
|
2
|
+
import CustomEventManager from "./CustomEventManager";
|
|
3
|
+
|
|
4
|
+
export { useCustomEvent, CustomEventManager };
|
|
5
|
+
|
|
6
|
+
export const INIT_LOGOUT_EVENT = "site_logout";
|
|
7
|
+
export const ANALYTICS_TRACK_EVENT = "analytics_track_event";
|
|
8
|
+
|
|
9
|
+
export const triggerOnInitLogout = () => {
|
|
10
|
+
CustomEventManager.dispatchEvent(INIT_LOGOUT_EVENT);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const triggerAnalyticsTrackEvent = (eventName, eventParams) => {
|
|
14
|
+
CustomEventManager.dispatchEvent(ANALYTICS_TRACK_EVENT, { eventName, eventParams });
|
|
15
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import CustomEventManager from "./CustomEventManager";
|
|
3
|
+
|
|
4
|
+
const useCustomEvent = (eventName, callback) => {
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
CustomEventManager.addEventListener(eventName, callback);
|
|
7
|
+
return () => {
|
|
8
|
+
CustomEventManager.removeEventListener(eventName, callback);
|
|
9
|
+
};
|
|
10
|
+
}, [eventName, callback]);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default useCustomEvent;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
|
|
3
|
+
const FIRST_NAME_KEY = "first_name";
|
|
4
|
+
const LAST_NAME_KEY = "last_name";
|
|
5
|
+
const EMAIL_KEY = "email";
|
|
6
|
+
const OWNER_FIRST_NAME_KEY = `owner_${FIRST_NAME_KEY}`;
|
|
7
|
+
const OWNER_LAST_NAME_KEY = `owner_${LAST_NAME_KEY}`;
|
|
8
|
+
const OWNER_EMAIL_KEY = `owner_${EMAIL_KEY}`;
|
|
9
|
+
const QR_CODE_KEY = "qr_code";
|
|
10
|
+
|
|
11
|
+
const excludeKeys = [
|
|
12
|
+
FIRST_NAME_KEY,
|
|
13
|
+
LAST_NAME_KEY,
|
|
14
|
+
EMAIL_KEY,
|
|
15
|
+
OWNER_FIRST_NAME_KEY,
|
|
16
|
+
OWNER_LAST_NAME_KEY,
|
|
17
|
+
OWNER_EMAIL_KEY,
|
|
18
|
+
QR_CODE_KEY,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const deepOmit = (obj, keysToOmit) => {
|
|
22
|
+
let keysToOmitIndex = _.keyBy(Array.isArray(keysToOmit) ? keysToOmit : [keysToOmit] ); // create an index object of the keys that should be omitted
|
|
23
|
+
|
|
24
|
+
const omitFromObject = (obj) => { // the inner function which will be called recursively
|
|
25
|
+
return _.transform(obj, function(result, value, key) { // transform to a new object
|
|
26
|
+
if (key in keysToOmitIndex) { // if the key is in the index skip it
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
result[key] = _.isObject(value) ? omitFromObject(value) : value; // if the key is an object run it through the inner function - omitFromObject
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return omitFromObject(obj); // return the inner function result
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const normalizeData = (data) => deepOmit(data, excludeKeys);
|
|
@@ -25,6 +25,7 @@ export const WS_PUB_SERVER_URL = 'WS_PUB_SERVER_URL';
|
|
|
25
25
|
export const REAL_TIME_UPDATES_STRATEGY = 'REAL_TIME_UPDATES_STRATEGY';
|
|
26
26
|
export const TIMEINTERVALSINCE1970_API_URL = 'TIMEINTERVALSINCE1970_API_URL';
|
|
27
27
|
export const ABLY_API_KEY = 'ABLY_API_KEY';
|
|
28
|
+
export const GOOGLE_TAGMANAGER_ID = 'GOOGLE_TAGMANAGER_ID';
|
|
28
29
|
|
|
29
30
|
const processEnv = {
|
|
30
31
|
/**
|
|
@@ -61,6 +62,7 @@ const processEnv = {
|
|
|
61
62
|
REAL_TIME_UPDATES_STRATEGY: process.env.GATSBY_REAL_TIME_UPDATES_STRATEGY,
|
|
62
63
|
TIMEINTERVALSINCE1970_API_URL: process.env.GATSBY_TIMEINTERVALSINCE1970_API_URL,
|
|
63
64
|
ABLY_API_KEY: process.env.GATSBY_ABLY_API_KEY,
|
|
65
|
+
GOOGLE_TAGMANAGER_ID: process.env.GATSBY_GOOGLE_TAGMANAGER_ID,
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
export const getEnvVariable = (name) => {
|
|
@@ -97,4 +99,5 @@ if (typeof window === 'object') {
|
|
|
97
99
|
window.REAL_TIME_UPDATES_STRATEGY = processEnv[REAL_TIME_UPDATES_STRATEGY];
|
|
98
100
|
window.TIMEINTERVALSINCE1970_API_URL = processEnv[TIMEINTERVALSINCE1970_API_URL];
|
|
99
101
|
window.ABLY_API_KEY = processEnv[ABLY_API_KEY];
|
|
102
|
+
window.GOOGLE_TAGMANAGER_ID = processEnv[GOOGLE_TAGMANAGER_ID];
|
|
100
103
|
}
|