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;
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 });
};

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;