kitchen-simulator
Version:
It is a kitchen simulator (self-contained micro-frontend).
188 lines (180 loc) • 7.43 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import ReactGA from 'react-ga4';
// ---------- Helpers ----------
function nowMs() {
return Date.now();
}
function getClientName() {
return sessionStorage.getItem('visualizerName');
}
function isDesktopUA() {
// Lightweight heuristic; GA4 also provides device.category. This flag can help ad-hoc filtering.
var ua = navigator.userAgent.toLowerCase();
var isMobile = /mobi|android|iphone|ipad|ipod|windows phone/.test(ua);
return !isMobile;
}
// ---------- Keys for storage ----------
var SSN_KEYS = {
sessionStartMs: 'ga4_session_start_ms',
introChoiceMs: 'ga4_intro_choice_ms',
firstCabinetPlaced: 'ga4_first_cabinet_placed',
contextBooted: 'ga4_context_booted',
enteredCanvasSent: 'ga4_entered_canvas_sent'
};
// ---------- Core GA wrapper ----------
export var GA = {
init: function init(_ref) {
var measurementId = _ref.measurementId;
ReactGA.initialize([{
trackingId: measurementId
}], {
testMode: false
});
},
resetSessionKeys: function resetSessionKeys() {
Object.values(SSN_KEYS).forEach(function (key) {
sessionStorage.removeItem(key);
});
},
/**
* Must be called ONCE per page load, *before* other events.
* Sets user properties (project_entry, cross_auth) and sends an initial page_view with client_name.
*/
bootSessionContext: function bootSessionContext(_ref2) {
var projectEntry = _ref2.projectEntry,
crossAuth = _ref2.crossAuth;
try {
var resolvedClient = getClientName();
// Persist session start for timing metrics (if not already set for this tab)
if (!sessionStorage.getItem(SSN_KEYS.sessionStartMs)) {
sessionStorage.setItem(SSN_KEYS.sessionStartMs, String(nowMs()));
}
// Mark context as booted to avoid duplicate property sets on hot reloads
if (!sessionStorage.getItem(SSN_KEYS.contextBooted)) {
sessionStorage.setItem(SSN_KEYS.contextBooted, '1');
// Set GA4 user_properties (user-scoped dimensions)
ReactGA.gtag('set', 'user_properties', {
project_entry: projectEntry,
// user scope dimension
cross_auth: crossAuth // user scope dimension (boolean serialized)
});
}
// Send first page_view hit enriched with event-scoped client_name
ReactGA.send({
hitType: 'pageview',
page: window.location.pathname + window.location.search,
// @ts-expect-error react-ga4 passes along additional params into gtag
client_name: resolvedClient
});
} catch (e) {
// eslint-disable-next-line no-console
console.warn('GA.bootSessionContext error', e);
}
},
// Utility to derive cross_auth from URL (token or details query params)
deriveCrossAuthFromUrl: function deriveCrossAuthFromUrl(search) {
var sp = new URLSearchParams(search);
return sp.has('token') || sp.has('details');
},
// Utility to derive project entry (best-effort). You can also pass explicitly from router logic.
deriveProjectEntry: function deriveProjectEntry() {
var projectId = sessionStorage.getItem('projectId');
// If a project id exists, consider it "open_existing"; otherwise fallback to "new"
if (projectId) return 'open_existing';
return 'new';
},
// ---------- Event API ----------
events: {
/**
* intro_choice(option) — records the user's entry choice.
* Also stores a timestamp for later time_to_canvas calculation.
*/
introChoice: function introChoice(option, extra) {
var client_name = getClientName();
sessionStorage.setItem(SSN_KEYS.introChoiceMs, String(nowMs()));
ReactGA.event('intro_choice', _objectSpread({
client_name: client_name,
option: option
}, extra || {}));
},
/**
* entered_canvas(time_to_canvas) — compute (now - intro_choice)
*/
enteredCanvas: function enteredCanvas() {
// fire only once per tab/session
if (sessionStorage.getItem(SSN_KEYS.enteredCanvasSent)) return;
var client_name = getClientName();
var introMs = Number(sessionStorage.getItem(SSN_KEYS.introChoiceMs) || 0);
var timeSec = introMs ? Math.max(0, Math.round((Date.now() - introMs) / 1000)) : undefined;
ReactGA.event('entered_canvas', _objectSpread({
client_name: client_name
}, typeof timeSec === 'number' ? {
time_to_canvas: timeSec
} : {}));
sessionStorage.setItem(SSN_KEYS.enteredCanvasSent, '1'); // 👈 lock it
},
/**
* cabinet_placed(time_to_first_cabinet) — send only for the *first* cabinet of the session.
*/
cabinetPlaced: function cabinetPlaced() {
var already = sessionStorage.getItem(SSN_KEYS.firstCabinetPlaced);
var client_name = getClientName();
var params = {
client_name: client_name
};
if (!already) {
var startMs = Number(sessionStorage.getItem(SSN_KEYS.sessionStartMs) || 0);
var timeSec = startMs ? Math.max(0, Math.round((nowMs() - startMs) / 1000)) : undefined;
if (typeof timeSec === 'number') params.time_to_first_cabinet = timeSec; // custom metric (seconds)
sessionStorage.setItem(SSN_KEYS.firstCabinetPlaced, '1');
}
ReactGA.event('cabinet_placed', params);
},
/**
* door_changed(door_id)
*/
doorChanged: function doorChanged(door_id) {
var client_name = getClientName();
ReactGA.event('door_changed', {
client_name: client_name,
door_id: door_id
});
},
/**
* project_saved(project_id, save_method) — mark as conversion in GA UI.
*/
projectSaved: function projectSaved(project_id, save_method) {
var client_name = getClientName();
ReactGA.event('project_saved', {
client_name: client_name,
project_id: project_id,
save_method: save_method
});
},
/**
* assistance_requested(method, project_id) — mark as conversion in GA UI.
*/
assistanceRequested: function assistanceRequested(method, project_id) {
var client_name = getClientName();
ReactGA.event('assistance_requested', {
client_name: client_name,
method: method,
project_id: project_id
});
},
/**
* add_to_cart(project_id, sku_count, price_total) — mark as conversion in GA UI.
*/
addToCart: function addToCart(project_id, sku_count, price_total) {
var client_name = getClientName();
ReactGA.event('add_to_cart', {
client_name: client_name,
project_id: project_id,
sku_count: sku_count,
price_total: price_total
});
}
}
};