Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 106 additions & 87 deletions packages/utils/src/tracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,106 +11,125 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//@ts-nocheck
import ReactGA4 from 'react-ga4';
import ReactGA from 'react-ga4';

import type { Tenant, User } from '@northern.tech/types/MenderTypes';

const cookieConsentCSS = 'https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.css';
const cookieConsentJS = 'https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js';

const ReactGA = ReactGA4.default;
interface TrackingState {
initialized: boolean;
organization: Tenant | null;
trackingEnabled: boolean;
user: User | null;
}

const state: TrackingState = {
initialized: false,
organization: null,
trackingEnabled: true,
user: null
};

class Tracker {
constructor() {
this.initialized = false;
this.trackingEnabled = true;
this.currentPageView = null;
this.currentOrganizationUser = null;
}
cookieconsent() {
return new Promise(resolve => {
const style = document.createElement('link');
style.href = cookieConsentCSS;
style.rel = 'stylesheet';
style.async = true;
document.head.appendChild(style);
//
const script = document.createElement('script');
script.src = cookieConsentJS;
script.async = false;
script.addEventListener('load', () => {
window.cookieconsent.initialise({
palette: {
popup: {
background: '#5d0f43',
text: '#ffffff'
},
button: {
background: '#73a4ad',
text: '#ffffff'
}
},
position: 'bottom-left',
type: 'opt-out',
content: {
message: 'We use cookies to analyze our traffic so we can improve our website and give you a better experience.',
link: 'View our cookie policy',
href: 'https://northern.tech/legal/cookies'
},
autoOpen: true,
revokable: false,
law: {
regionalLaw: false
},
onStatusChange: status => {
const hasConsented = status == 'allow';
resolve({ trackingConsentGiven: hasConsented });
}
});
export const cookieconsent = (): Promise<{ trackingConsentGiven: boolean }> =>
new Promise(resolve => {
const style = document.createElement('link');
style.href = cookieConsentCSS;
style.rel = 'stylesheet';
style.async = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link element does not have async property

document.head.appendChild(style);

const script = document.createElement('script');
script.src = cookieConsentJS;
script.async = false;
script.addEventListener('load', () => {
(window as any).cookieconsent.initialise({
palette: {
popup: { background: '#5d0f43', text: '#ffffff' },
button: { background: '#73a4ad', text: '#ffffff' }
},
position: 'bottom-left',
type: 'opt-out',
content: {
message: 'We use cookies to analyze our traffic so we can improve our website and give you a better experience.',
link: 'View our cookie policy',
href: 'https://northern.tech/legal/cookies'
},
autoOpen: true,
revokable: false,
law: { regionalLaw: false },
onStatusChange: (status: string) => {
resolve({ trackingConsentGiven: status === 'allow' });
}
});
document.body.appendChild(script);
});
document.body.appendChild(script);
});

export const initialize = (trackingCode: string): boolean => {
if (state.initialized && state.trackingEnabled) {
return false;
}
event(data) {
if (this.initialized && this.trackingEnabled) {
ReactGA.event(data);
}
}
exception(error) {
if (this.initialized && this.trackingEnabled) {
ReactGA.event('error', error);
}
ReactGA.initialize(trackingCode);
state.initialized = true;
return true;
};

export const trackEvent = (data: { action: string; category: string; label?: string; value?: number }) => {
if (state.initialized && state.trackingEnabled) {
ReactGA.event(data);
}
initialize(trackingCode) {
if (this.initialized && this.trackingEnabled) {
return false;
}
ReactGA.initialize(trackingCode);
this.initialized = true;
return true;
};

export const trackException = (error: { description: string; fatal?: boolean }) => {
if (state.initialized && state.trackingEnabled) {
ReactGA.event('error', error);
}
pageview(data) {
if (data) {
this.currentPageView = data;
}
};

export const pageview = (page?: string) => {
// currently a no-op stored for later use, matching original behavior
if (page) {
// page view tracking placeholder
}
set(value) {
if (this.initialized && this.trackingEnabled) {
ReactGA.set(value);
}
};

export const setGA = (value: Record<string, unknown>) => {
if (state.initialized && state.trackingEnabled) {
ReactGA.set(value);
}
setOrganizationUser(organization, user) {
if (this.initialized && this.trackingEnabled && this.currentOrganizationUser != { organization, user }) {
this.currentOrganizationUser = { organization, user };
this.set({ dimension1: organization.plan });
this.set({ dimension2: organization.id });
this.set({ dimension3: user.id });
this.set({ userId: user.id });
}
};

export const setOrganizationUser = (organization: Tenant, user: User) => {
if (!state.initialized || !state.trackingEnabled) {
return;
}
setTrackingEnabled(trackingEnabled) {
this.trackingEnabled = trackingEnabled;
const { user: currentUser, organization: currentOrganization } = state;
if (currentOrganization?.id === organization.id && currentUser?.id === user.id && currentOrganization.plan === organization.plan) {
return;
}
}
state.organization = organization;
state.user = user;
ReactGA.set({ dimension1: organization.plan });
ReactGA.set({ dimension2: organization.id });
ReactGA.set({ dimension3: user.id });
ReactGA.set({ userId: user.id });
Comment on lines +114 to +117

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe

ReactGA.set({
  dimension1: organization.plan,
  dimension2: organization.id,
  dimension3: user.id,
  userId: user.id,
});

};

export const setTrackingEnabled = (trackingEnabled: boolean) => {
state.trackingEnabled = trackingEnabled;
};

const Tracking = {
cookieconsent,
event: trackEvent,
exception: trackException,
initialize,
pageview,
set: setGA,
setOrganizationUser,
setTrackingEnabled
};

const Tracking = new Tracker();
export default Tracking;