@roomstay/frontend 2.6.102 → 2.6.103-0
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/dist/177.bundle.js +1 -1
- package/dist/211.bundle.js +1 -0
- package/dist/288.bundle.js +1 -1
- package/dist/326.bundle.js +2 -0
- package/dist/326.bundle.js.LICENSE.txt +16 -0
- package/dist/328.bundle.js +1 -1
- package/dist/main.bundle.js +1 -1
- package/dist/src/api/AnalyticsEventsAPI.d.ts +13 -0
- package/dist/src/api/AnalyticsEventsAPI.js +55 -0
- package/dist/src/api/AnalyticsEventsAPI.js.map +1 -0
- package/dist/src/components/generic/PromotionalCodeInput.js +1 -1
- package/dist/src/components/generic/PromotionalCodeInput.js.map +1 -1
- package/dist/src/components/steps/confirmation/PayPalPaymentOption/PayPalPaymentOption.d.ts +2 -0
- package/dist/src/components/steps/confirmation/PayPalPaymentOption/PayPalPaymentOption.js +86 -0
- package/dist/src/components/steps/confirmation/PayPalPaymentOption/PayPalPaymentOption.js.map +1 -0
- package/dist/src/components/steps/confirmation/RoomContactDetails.js +3 -1
- package/dist/src/components/steps/confirmation/RoomContactDetails.js.map +1 -1
- package/dist/src/components/steps/confirmation/StepConfirmationCountrySelector.d.ts +2 -1
- package/dist/src/components/steps/confirmation/StepConfirmationCountrySelector.js +1 -0
- package/dist/src/components/steps/confirmation/StepConfirmationCountrySelector.js.map +1 -1
- package/dist/src/components/steps/confirmation/StepConfirmationForm.js +1 -1
- package/dist/src/components/steps/confirmation/StepConfirmationForm.js.map +1 -1
- package/dist/src/components/steps/date/PeoplePicker.js +4 -2
- package/dist/src/components/steps/date/PeoplePicker.js.map +1 -1
- package/dist/src/components/steps/date/PeoplePickerRow.js +2 -2
- package/dist/src/components/steps/date/PeoplePickerRow.js.map +1 -1
- package/dist/src/components/steps/room/RoomListCrossSellBlock.js +1 -0
- package/dist/src/components/steps/room/RoomListCrossSellBlock.js.map +1 -1
- package/dist/src/components/steps/room/UserSearchSummary/UserSearchSummary.js +5 -3
- package/dist/src/components/steps/room/UserSearchSummary/UserSearchSummary.js.map +1 -1
- package/dist/src/components/steps/room/UserSearchSummary/UserSearchSummaryRow.js +2 -2
- package/dist/src/components/steps/room/UserSearchSummary/UserSearchSummaryRow.js.map +1 -1
- package/dist/src/components/steps/room/roomBuilderProgress/NumberOfAdultsPicker.js +3 -3
- package/dist/src/components/steps/room/roomBuilderProgress/NumberOfAdultsPicker.js.map +1 -1
- package/dist/src/components/steps/room/roomBuilderProgress/RoomBuilderProgress.js +3 -3
- package/dist/src/components/steps/room/roomBuilderProgress/RoomBuilderProgress.js.map +1 -1
- package/dist/src/components/steps/room/roomBuilderProgress/RoomBuilderProgressRow.js +2 -0
- package/dist/src/components/steps/room/roomBuilderProgress/RoomBuilderProgressRow.js.map +1 -1
- package/dist/src/components/summary/BESummaryRoomRow.js.map +1 -1
- package/dist/src/contexts/BasketContext/BasketContextType.d.ts +4 -0
- package/dist/src/contexts/BasketContext/BasketContextType.js.map +1 -1
- package/dist/src/contexts/BasketContext/BasketContextWrapper.js +47 -7
- package/dist/src/contexts/BasketContext/BasketContextWrapper.js.map +1 -1
- package/dist/src/contexts/CompanyContext/CompanyContextWrapper.js +10 -0
- package/dist/src/contexts/CompanyContext/CompanyContextWrapper.js.map +1 -1
- package/dist/src/contexts/ConfirmationStepContext/ConfirmationStepContextWrapper.js +21 -3
- package/dist/src/contexts/ConfirmationStepContext/ConfirmationStepContextWrapper.js.map +1 -1
- package/dist/src/contexts/ConfirmationStepContext/ConfirmationStepPriceQuoteErrorModal.d.ts +9 -0
- package/dist/src/contexts/ConfirmationStepContext/ConfirmationStepPriceQuoteErrorModal.js +67 -0
- package/dist/src/contexts/ConfirmationStepContext/ConfirmationStepPriceQuoteErrorModal.js.map +1 -0
- package/dist/src/contexts/FullPageEngineContext/FullPageEngineContextWrapper.js +43 -7
- package/dist/src/contexts/FullPageEngineContext/FullPageEngineContextWrapper.js.map +1 -1
- package/dist/src/handlers/payment/PayPalPaymentHandler.d.ts +2 -0
- package/dist/src/handlers/payment/PayPalPaymentHandler.js +41 -0
- package/dist/src/handlers/payment/PayPalPaymentHandler.js.map +1 -0
- package/dist/src/hooks/AutoFocusOnSelect.d.ts +1 -2
- package/dist/src/hooks/AutoFocusOnSelect.js.map +1 -1
- package/dist/src/hooks/useSearchScopeHook.d.ts +5 -2
- package/dist/src/hooks/useSearchScopeHook.js +57 -14
- package/dist/src/hooks/useSearchScopeHook.js.map +1 -1
- package/dist/src/models/Addon/Addon.js +4 -0
- package/dist/src/models/Addon/Addon.js.map +1 -1
- package/dist/src/models/Api/HotelDTO.d.ts +1 -1
- package/dist/src/models/Api/HotelDTO.js.map +1 -1
- package/dist/src/models/BasketRow.d.ts +5 -0
- package/dist/src/models/BasketRow.js +13 -1
- package/dist/src/models/BasketRow.js.map +1 -1
- package/dist/src/models/Client/Hotel/Hotel.d.ts +12 -0
- package/dist/src/models/Client/Hotel/Hotel.js.map +1 -1
- package/dist/src/pages/findReservation/FindReservationResults.js +2 -2
- package/dist/src/pages/findReservation/FindReservationResults.js.map +1 -1
- package/dist/src/pages/hotel/HotelInfo.js +7 -6
- package/dist/src/pages/hotel/HotelInfo.js.map +1 -1
- package/dist/src/pages/steps/StepAddon/StepAddonValidator.js +1 -1
- package/dist/src/pages/steps/StepAddon/StepAddonValidator.js.map +1 -1
- package/dist/src/pages/steps/StepConfirmation/StepConfirmationValidator.js +1 -1
- package/dist/src/pages/steps/StepConfirmation/StepConfirmationValidator.js.map +1 -1
- package/dist/src/pages/steps/StepRoom/StepRoomValidator.js +1 -1
- package/dist/src/pages/steps/StepRoom/StepRoomValidator.js.map +1 -1
- package/dist/src/pages/steps/StepThanks/StepThanksComponent.js +2 -2
- package/dist/src/pages/steps/StepThanks/StepThanksComponent.js.map +1 -1
- package/dist/src/providers/FeatureProvider.js +2 -0
- package/dist/src/providers/FeatureProvider.js.map +1 -1
- package/dist/src/providers/RatePillProvider.d.ts +1 -0
- package/dist/src/providers/RatePillProvider.js +11 -4
- package/dist/src/providers/RatePillProvider.js.map +1 -1
- package/dist/src/providers/feature/AnalyticsEventsFeature.d.ts +23 -0
- package/dist/src/providers/feature/AnalyticsEventsFeature.js +83 -0
- package/dist/src/providers/feature/AnalyticsEventsFeature.js.map +1 -0
- package/dist/src/translations/Translation.d.ts +5 -0
- package/dist/src/translations/Translation.js +5 -0
- package/dist/src/translations/Translation.js.map +1 -1
- package/dist/src/translations/languages/en-gb.js +9 -4
- package/dist/src/translations/languages/en-gb.js.map +1 -1
- package/dist/src/util/Analytics/Analytics.d.ts +7 -0
- package/dist/src/util/Analytics/Analytics.js +8 -1
- package/dist/src/util/Analytics/Analytics.js.map +1 -1
- package/dist/src/util/AnalyticsEvents/AnalyticsEvents.d.ts +61 -0
- package/dist/src/util/AnalyticsEvents/AnalyticsEvents.js +329 -0
- package/dist/src/util/AnalyticsEvents/AnalyticsEvents.js.map +1 -0
- package/dist/src/util/AnalyticsEvents/helpers.d.ts +30 -0
- package/dist/src/util/AnalyticsEvents/helpers.js +96 -0
- package/dist/src/util/AnalyticsEvents/helpers.js.map +1 -0
- package/dist/src/util/DataLayer.js +4 -4
- package/dist/src/util/DataLayer.js.map +1 -1
- package/dist/test.bundle.js +1 -1
- package/dist/vendors.bundle.js +1 -1
- package/package.json +3 -2
- package/dist/180.bundle.js +0 -1
- package/dist/570.bundle.js +0 -1
|
@@ -32,6 +32,13 @@ export declare abstract class Analytics {
|
|
|
32
32
|
withUserData: <T extends IAnalyticMessageEvent>(input: T) => T & Partial<{
|
|
33
33
|
user_id: string;
|
|
34
34
|
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Merges the current company name/property name into the provided input object.
|
|
37
|
+
*/
|
|
38
|
+
withClientDetailsData: <T extends IAnalyticMessageEvent>(input: T) => T & {
|
|
39
|
+
company?: string;
|
|
40
|
+
property?: string;
|
|
41
|
+
};
|
|
35
42
|
updateCurrency(current: string, original: string): void;
|
|
36
43
|
/** Assemble the message for a room impression post based on impressions added */
|
|
37
44
|
abstract generateRoomImpressionMessageObject(impressions: GenericAnalyticsAccommodation[]): IAnalyticMessageEvent;
|
|
@@ -15,7 +15,14 @@ class Analytics {
|
|
|
15
15
|
* Ensures the resulting object always includes user_id when available.
|
|
16
16
|
*/
|
|
17
17
|
this.withUserData = (input) => {
|
|
18
|
-
return Object.assign(Object.assign({}, input), (('ecommerce' in input || input.event === 'login') && Object.assign({}, this.getUserData())));
|
|
18
|
+
return this.withClientDetailsData(Object.assign(Object.assign({}, input), (('ecommerce' in input || input.event === 'login') && Object.assign({}, this.getUserData()))));
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Merges the current company name/property name into the provided input object.
|
|
22
|
+
*/
|
|
23
|
+
this.withClientDetailsData = (input) => {
|
|
24
|
+
var _a, _b;
|
|
25
|
+
return Object.assign(Object.assign({}, input), { company: (_a = this.company) === null || _a === void 0 ? void 0 : _a.name, property: (_b = this.hotel) === null || _b === void 0 ? void 0 : _b.name });
|
|
19
26
|
};
|
|
20
27
|
}
|
|
21
28
|
initialise(company, hotel) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Analytics.js","sourceRoot":"/","sources":["src/util/Analytics/Analytics.ts"],"names":[],"mappings":";;;AAWA;;;;GAIG;AACH,MAAsB,SAAS;IAA/B;QAKc,aAAQ,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QACzC,iBAAY,GAAG,EAAE,CAAC;QAoB5B;;;WAGG;QACI,iBAAY,GAAG,CAAkC,KAAQ,EAAoC,EAAE;YAClG,
|
|
1
|
+
{"version":3,"file":"Analytics.js","sourceRoot":"/","sources":["src/util/Analytics/Analytics.ts"],"names":[],"mappings":";;;AAWA;;;;GAIG;AACH,MAAsB,SAAS;IAA/B;QAKc,aAAQ,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QACzC,iBAAY,GAAG,EAAE,CAAC;QAoB5B;;;WAGG;QACI,iBAAY,GAAG,CAAkC,KAAQ,EAAoC,EAAE;YAClG,OAAO,IAAI,CAAC,qBAAqB,iCAC1B,KAAK,GACL,CAAC,CAAC,WAAW,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,sBAC9C,IAAI,CAAC,WAAW,EAAE,CACxB,CAAC,EACJ,CAAC;QACP,CAAC,CAAC;QAEF;;WAEG;QACI,0BAAqB,GAAG,CAAkC,KAAQ,EAA+C,EAAE;;YACtH,uCACO,KAAK,KACR,OAAO,EAAE,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,EAC3B,QAAQ,EAAE,MAAA,IAAI,CAAC,KAAK,0CAAE,IAAI,IAC5B;QACN,CAAC,CAAC;IAoCN,CAAC;IA5EU,UAAU,CAAC,OAAuB,EAAE,KAAa;QACpD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAEM,SAAS,CAAC,EAAW;QACxB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;IAEM,eAAe,CAAC,YAAqB;QACxC,IAAI,CAAC,YAAY,GAAG,YAAY,IAAI,EAAE,CAAC;IAC3C,CAAC;IAED,iEAAiE;IACzD,WAAW;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,CAAC;IA0BM,cAAc,CAAC,OAAe,EAAE,QAAgB;QACnD,IAAI,CAAC,QAAQ,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC1C,CAAC;IAoBM,8CAA8C,CAAC,iBAAyB;QAC3E,OAAO;YACH,KAAK,EAAE,QAAQ;YACf,SAAS,EAAE;gBACP,MAAM,EAAE;oBACJ,WAAW,EAAE;wBACT,EAAE,EAAE,iBAAiB;qBACxB;iBACJ;aACJ;SACJ,CAAC;IACN,CAAC;CACJ;AApFD,8BAoFC","sourcesContent":["import { ReservationDTO, ReservationsDTO } from '@roomstay/core';\n\nimport BasketAddonRow from '@/models/BasketAddonRow';\nimport BasketRow from '@/models/BasketRow';\nimport { ConfigCompany } from '@/models/Client/Hotel/Company';\nimport { Hotel } from '@/models/Client/Hotel/Hotel';\n\nimport { DateRange, GenericAnalyticsAccommodation } from '../DataLayer';\nexport interface IAnalyticMessageEvent extends Record<string, any> {\n event: string;\n}\n/**\n * Abstract class used to transform roomstay data into either GA4 or UA analytics events\n *\n * All logging still happens from the {@link DataLayer} class.\n */\nexport abstract class Analytics {\n protected hotel?: Hotel;\n protected company?: ConfigCompany;\n protected userId?: string; // logged user id\n\n protected currency = { original: '', current: '' };\n protected itemListName = '';\n\n public initialise(company?: ConfigCompany, hotel?: Hotel) {\n this.company = company;\n this.hotel = hotel;\n }\n\n public setUserId(id?: string) {\n this.userId = id;\n }\n\n public setItemListName(itemListName?: string) {\n this.itemListName = itemListName || '';\n }\n\n // Returns an object containing the current user_id if available.\n private getUserData(): Partial<{ user_id: string }> {\n return this.userId ? { user_id: this.userId } : {};\n }\n\n /**\n * Merges the current user's data (user_id) into the provided input object.\n * Ensures the resulting object always includes user_id when available.\n */\n public withUserData = <T extends IAnalyticMessageEvent>(input: T): T & Partial<{ user_id: string }> => {\n return this.withClientDetailsData({\n ...input,\n ...(('ecommerce' in input || input.event === 'login') && {\n ...this.getUserData(),\n }),\n });\n };\n\n /**\n * Merges the current company name/property name into the provided input object.\n */\n public withClientDetailsData = <T extends IAnalyticMessageEvent>(input: T): T & { company?: string; property?: string } => {\n return {\n ...input,\n company: this.company?.name,\n property: this.hotel?.name,\n };\n };\n\n public updateCurrency(current: string, original: string) {\n this.currency = { current, original };\n }\n\n /** Assemble the message for a room impression post based on impressions added */\n public abstract generateRoomImpressionMessageObject(impressions: GenericAnalyticsAccommodation[]): IAnalyticMessageEvent;\n\n public abstract generateProductClickMessageObject(product: GenericAnalyticsAccommodation): IAnalyticMessageEvent;\n public abstract generateDateSelectionMessageObject(dateRange: DateRange, adults: number, children: number, infants?: number): IAnalyticMessageEvent;\n\n public abstract generateAddToCartMessageObject(product: GenericAnalyticsAccommodation): IAnalyticMessageEvent;\n public abstract generateAddonsAddToCartMessageObject(\n product: Required<Pick<GenericAnalyticsAccommodation, 'quantity' | 'dateRange' | 'basketAddonRow'>>\n ): IAnalyticMessageEvent;\n public abstract generateUpsellConfirmedMessageObject(oldProduct: GenericAnalyticsAccommodation, newProduct: GenericAnalyticsAccommodation): IAnalyticMessageEvent | null;\n\n public abstract generateUpsellCancelledMessageObject(product: GenericAnalyticsAccommodation): IAnalyticMessageEvent;\n\n public abstract generateCheckoutViewMessageObject(basketRows: BasketRow[], addonRows: BasketAddonRow[]): IAnalyticMessageEvent;\n public abstract generateRoomPurchaseMessageObject(basketRows: BasketRow[], addonRows: BasketAddonRow[], reservations: ReservationsDTO): IAnalyticMessageEvent[];\n public abstract generateRefundMessageObject(reservation: ReservationDTO): IAnalyticMessageEvent;\n\n public generateRefundMessageObjectByReservationNumber(reservationNumber: string) {\n return {\n event: 'refund',\n ecommerce: {\n refund: {\n actionField: {\n id: reservationNumber,\n },\n },\n },\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Events Utility
|
|
3
|
+
*/
|
|
4
|
+
import { ReservationsDTO } from '@roomstay/core';
|
|
5
|
+
import BasketRow from '../../models/BasketRow';
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for AnalyticsEvents
|
|
8
|
+
*/
|
|
9
|
+
export interface AnalyticsEventsConfig {
|
|
10
|
+
hotelId?: string;
|
|
11
|
+
currency?: string;
|
|
12
|
+
firePixelEvents?: boolean;
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
15
|
+
declare class AnalyticsEvents {
|
|
16
|
+
private session;
|
|
17
|
+
private config;
|
|
18
|
+
private initialized;
|
|
19
|
+
private listenersRegistered;
|
|
20
|
+
private sessionReady;
|
|
21
|
+
/**
|
|
22
|
+
* Initialize analytics with configuration
|
|
23
|
+
*/
|
|
24
|
+
initialize(config?: AnalyticsEventsConfig): void;
|
|
25
|
+
/**
|
|
26
|
+
* Create session on the server
|
|
27
|
+
*/
|
|
28
|
+
private createServerSession;
|
|
29
|
+
/**
|
|
30
|
+
* Register listeners for Roomstay events
|
|
31
|
+
*/
|
|
32
|
+
private registerEventListeners;
|
|
33
|
+
/**
|
|
34
|
+
* Track View/Checkout event
|
|
35
|
+
*/
|
|
36
|
+
trackInitiateCheckout(basketRows: BasketRow[]): void;
|
|
37
|
+
/**
|
|
38
|
+
* Track Basket/Add event
|
|
39
|
+
*/
|
|
40
|
+
trackAddToCart(basketRow: BasketRow): void;
|
|
41
|
+
/**
|
|
42
|
+
* Track Booking/Committed event
|
|
43
|
+
*/
|
|
44
|
+
trackPurchase(reservation: ReservationsDTO, basketRows: BasketRow[]): void;
|
|
45
|
+
/**
|
|
46
|
+
* Set external user ID (e.g. member portal ID)
|
|
47
|
+
*/
|
|
48
|
+
setExternalId(externalId: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* Ensure session is initialized and has required fields for tracking.
|
|
51
|
+
* Returns the session if tracking can proceed, null otherwise.
|
|
52
|
+
*/
|
|
53
|
+
private getValidatedSession;
|
|
54
|
+
private createEventPayload;
|
|
55
|
+
private sendEvent;
|
|
56
|
+
private loadSession;
|
|
57
|
+
private saveSession;
|
|
58
|
+
private log;
|
|
59
|
+
}
|
|
60
|
+
declare const _default: AnalyticsEvents;
|
|
61
|
+
export default _default;
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Analytics Events Utility
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const core_1 = require("@roomstay/core");
|
|
19
|
+
const AnalyticsEventsAPI_1 = require("../../api/AnalyticsEventsAPI");
|
|
20
|
+
const AddToCartEvent_1 = require("../../events/actions/AddToCartEvent");
|
|
21
|
+
const RoomstayEventManager_1 = __importDefault(require("../../events/RoomstayEventManager"));
|
|
22
|
+
const CommittedBookingEvent_1 = require("../../events/views/CommittedBookingEvent");
|
|
23
|
+
const StepCheckoutViewEvent_1 = require("../../events/views/StepCheckoutViewEvent");
|
|
24
|
+
const helpers_1 = require("./helpers");
|
|
25
|
+
/**
|
|
26
|
+
* Validate session from storage
|
|
27
|
+
*/
|
|
28
|
+
function isValidSession(obj) {
|
|
29
|
+
if (!obj || typeof obj !== 'object')
|
|
30
|
+
return false;
|
|
31
|
+
const session = obj;
|
|
32
|
+
return typeof session.sessionId === 'string' && typeof session.createdAt === 'number' && typeof session.hotelId === 'string' && typeof session.currency === 'string';
|
|
33
|
+
}
|
|
34
|
+
const STORAGE_KEY_PREFIX = 'roomstay-analytics-session';
|
|
35
|
+
const SESSION_MAX_AGE = 12 * 60 * 60 * 1000; // 12 hours
|
|
36
|
+
/**
|
|
37
|
+
* Get storage key for a specific hotel's session
|
|
38
|
+
*/
|
|
39
|
+
function getStorageKey(hotelId) {
|
|
40
|
+
return `${STORAGE_KEY_PREFIX}-${hotelId}`;
|
|
41
|
+
}
|
|
42
|
+
function generateUUID() {
|
|
43
|
+
return crypto.randomUUID();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fire a Meta Pixel event with Event ID for deduplication
|
|
47
|
+
*/
|
|
48
|
+
function firePixelEvent(eventName, eventId, customData) {
|
|
49
|
+
const metaPixelIsAvailable = typeof window !== 'undefined' && typeof window.fbq === 'function';
|
|
50
|
+
if (!metaPixelIsAvailable)
|
|
51
|
+
return;
|
|
52
|
+
try {
|
|
53
|
+
window.fbq('track', eventName, customData, { eventID: eventId });
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error('[AnalyticsEvents] Error firing pixel event:', error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
class AnalyticsEvents {
|
|
60
|
+
constructor() {
|
|
61
|
+
this.session = null;
|
|
62
|
+
this.config = {};
|
|
63
|
+
this.initialized = false;
|
|
64
|
+
this.listenersRegistered = false;
|
|
65
|
+
this.sessionReady = null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Initialize analytics with configuration
|
|
69
|
+
*/
|
|
70
|
+
initialize(config) {
|
|
71
|
+
var _a, _b, _c, _d;
|
|
72
|
+
if (this.initialized) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.config = config || {};
|
|
76
|
+
// Try to restore existing session
|
|
77
|
+
const existingSession = this.loadSession();
|
|
78
|
+
const cookies = (0, helpers_1.getMetaCookies)();
|
|
79
|
+
// Resolve hotelId and currency from config or existing session
|
|
80
|
+
const hotelFrontendId = (_a = this.config.hotelId) !== null && _a !== void 0 ? _a : existingSession === null || existingSession === void 0 ? void 0 : existingSession.hotelFrontendId;
|
|
81
|
+
const currency = (_b = this.config.currency) !== null && _b !== void 0 ? _b : existingSession === null || existingSession === void 0 ? void 0 : existingSession.currency;
|
|
82
|
+
// Cannot initialize without required fields
|
|
83
|
+
if (!hotelFrontendId || !currency) {
|
|
84
|
+
this.log('Cannot initialize: hotelId and currency are required');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (existingSession) {
|
|
88
|
+
this.session = Object.assign(Object.assign({}, existingSession), { fbc: (_c = cookies.fbc) !== null && _c !== void 0 ? _c : existingSession.fbc, fbp: (_d = cookies.fbp) !== null && _d !== void 0 ? _d : existingSession.fbp, hotelFrontendId,
|
|
89
|
+
currency });
|
|
90
|
+
this.sessionReady = Promise.resolve(true);
|
|
91
|
+
this.log('Session restored:', this.session.sessionId);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
this.session = {
|
|
95
|
+
sessionId: generateUUID(),
|
|
96
|
+
fbc: cookies.fbc,
|
|
97
|
+
fbp: cookies.fbp,
|
|
98
|
+
createdAt: Date.now(),
|
|
99
|
+
hotelFrontendId,
|
|
100
|
+
currency,
|
|
101
|
+
};
|
|
102
|
+
this.sessionReady = this.createServerSession();
|
|
103
|
+
}
|
|
104
|
+
this.saveSession();
|
|
105
|
+
this.initialized = true;
|
|
106
|
+
this.registerEventListeners();
|
|
107
|
+
this.log('Session initialized:', this.session.sessionId);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create session on the server
|
|
111
|
+
*/
|
|
112
|
+
createServerSession() {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
if (!this.session)
|
|
115
|
+
return false;
|
|
116
|
+
try {
|
|
117
|
+
const response = yield AnalyticsEventsAPI_1.analyticsEventsAPI.createSession({
|
|
118
|
+
sessionId: this.session.sessionId,
|
|
119
|
+
hotelFrontendId: this.session.hotelFrontendId,
|
|
120
|
+
fbc: this.session.fbc,
|
|
121
|
+
fbp: this.session.fbp,
|
|
122
|
+
});
|
|
123
|
+
if (!response.success) {
|
|
124
|
+
console.warn('[AnalyticsEvents] Failed to create server session:', response.message);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
this.log('Server session ready:', this.session.sessionId);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error('[AnalyticsEvents] Error creating server session:', error);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Register listeners for Roomstay events
|
|
138
|
+
*/
|
|
139
|
+
registerEventListeners() {
|
|
140
|
+
if (this.listenersRegistered)
|
|
141
|
+
return;
|
|
142
|
+
// View/Checkout event
|
|
143
|
+
RoomstayEventManager_1.default.addEvent(StepCheckoutViewEvent_1.StepCheckoutViewEvent, (event) => __awaiter(this, void 0, void 0, function* () {
|
|
144
|
+
this.trackInitiateCheckout(event.basketRows);
|
|
145
|
+
}), 'analytics-events-checkout');
|
|
146
|
+
// Basket/Add event
|
|
147
|
+
RoomstayEventManager_1.default.addEvent(AddToCartEvent_1.AddToCartEvent, (event) => __awaiter(this, void 0, void 0, function* () {
|
|
148
|
+
this.trackAddToCart(event.basketRow);
|
|
149
|
+
}), 'analytics-events-add-to-cart');
|
|
150
|
+
// Booking/Committed event
|
|
151
|
+
RoomstayEventManager_1.default.addEvent(CommittedBookingEvent_1.CommittedBookingEvent, (event) => __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
this.trackPurchase(event.reservation, event.basketRows);
|
|
153
|
+
}), 'analytics-events-purchase');
|
|
154
|
+
this.listenersRegistered = true;
|
|
155
|
+
this.log('Event listeners registered');
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Track View/Checkout event
|
|
159
|
+
*/
|
|
160
|
+
trackInitiateCheckout(basketRows) {
|
|
161
|
+
const session = this.getValidatedSession();
|
|
162
|
+
if (!session)
|
|
163
|
+
return;
|
|
164
|
+
const { currency } = session;
|
|
165
|
+
const items = (0, helpers_1.serializeBasketRows)(basketRows);
|
|
166
|
+
// Build event data in Roomstay format
|
|
167
|
+
const eventData = {
|
|
168
|
+
items,
|
|
169
|
+
currency,
|
|
170
|
+
totalValue: (0, helpers_1.calculateTotalValue)(items),
|
|
171
|
+
};
|
|
172
|
+
const payload = this.createEventPayload(core_1.ERoomstayEventName.ViewCheckout, eventData);
|
|
173
|
+
// Fire Meta Pixel with transformed data
|
|
174
|
+
if (this.config.firePixelEvents) {
|
|
175
|
+
const metaEventData = (0, core_1.transformToMetaEventData)(eventData);
|
|
176
|
+
firePixelEvent('InitiateCheckout', payload.eventId, metaEventData);
|
|
177
|
+
}
|
|
178
|
+
this.sendEvent(payload);
|
|
179
|
+
this.log('View/Checkout tracked:', payload);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Track Basket/Add event
|
|
183
|
+
*/
|
|
184
|
+
trackAddToCart(basketRow) {
|
|
185
|
+
const session = this.getValidatedSession();
|
|
186
|
+
if (!session)
|
|
187
|
+
return;
|
|
188
|
+
const { currency } = session;
|
|
189
|
+
// Build event data in Roomstay format
|
|
190
|
+
const eventData = {
|
|
191
|
+
item: (0, helpers_1.serializeBasketRow)(basketRow),
|
|
192
|
+
currency,
|
|
193
|
+
};
|
|
194
|
+
const payload = this.createEventPayload(core_1.ERoomstayEventName.BasketAdd, eventData);
|
|
195
|
+
// Fire Meta Pixel with transformed data
|
|
196
|
+
if (this.config.firePixelEvents) {
|
|
197
|
+
const metaEventData = (0, core_1.transformToMetaEventData)(eventData);
|
|
198
|
+
firePixelEvent('AddToCart', payload.eventId, metaEventData);
|
|
199
|
+
}
|
|
200
|
+
this.sendEvent(payload);
|
|
201
|
+
this.log('Basket/Add tracked:', payload);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Track Booking/Committed event
|
|
205
|
+
*/
|
|
206
|
+
trackPurchase(reservation, basketRows) {
|
|
207
|
+
const session = this.getValidatedSession();
|
|
208
|
+
if (!session)
|
|
209
|
+
return;
|
|
210
|
+
const { currency } = session;
|
|
211
|
+
const items = (0, helpers_1.serializeBasketRows)(basketRows);
|
|
212
|
+
// Build event data in Roomstay format with embedded guest data
|
|
213
|
+
const eventData = {
|
|
214
|
+
items,
|
|
215
|
+
currency,
|
|
216
|
+
totalValue: (0, helpers_1.calculateTotalValue)(items),
|
|
217
|
+
roomstayId: reservation.roomstayId,
|
|
218
|
+
itineraryNumber: reservation.itineraryNumber,
|
|
219
|
+
reservationNumbers: reservation.reservations.map((r) => r.reservationNumber),
|
|
220
|
+
guest: Object.assign(Object.assign({}, (0, helpers_1.extractGuestData)(reservation)), { externalId: session.externalId }),
|
|
221
|
+
};
|
|
222
|
+
const payload = this.createEventPayload(core_1.ERoomstayEventName.BookingCommitted, eventData);
|
|
223
|
+
// Fire Meta Pixel with transformed data
|
|
224
|
+
if (this.config.firePixelEvents) {
|
|
225
|
+
const metaEventData = (0, core_1.transformToMetaEventData)(eventData);
|
|
226
|
+
firePixelEvent('Purchase', payload.eventId, metaEventData);
|
|
227
|
+
}
|
|
228
|
+
this.sendEvent(payload);
|
|
229
|
+
this.log('Booking/Committed tracked:', payload);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Set external user ID (e.g. member portal ID)
|
|
233
|
+
*/
|
|
234
|
+
setExternalId(externalId) {
|
|
235
|
+
if (this.session) {
|
|
236
|
+
this.log('Setting external ID:', externalId);
|
|
237
|
+
this.session.externalId = externalId;
|
|
238
|
+
this.saveSession();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Ensure session is initialized and has required fields for tracking.
|
|
243
|
+
* Returns the session if tracking can proceed, null otherwise.
|
|
244
|
+
*/
|
|
245
|
+
getValidatedSession() {
|
|
246
|
+
if (!this.initialized) {
|
|
247
|
+
this.initialize(this.config);
|
|
248
|
+
}
|
|
249
|
+
if (!this.session) {
|
|
250
|
+
console.warn('[AnalyticsEvents] Cannot track: session not initialized');
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
return this.session;
|
|
254
|
+
}
|
|
255
|
+
createEventPayload(eventName, eventData) {
|
|
256
|
+
var _a, _b;
|
|
257
|
+
const session = this.session;
|
|
258
|
+
return {
|
|
259
|
+
eventId: generateUUID(),
|
|
260
|
+
sessionId: (_a = session === null || session === void 0 ? void 0 : session.sessionId) !== null && _a !== void 0 ? _a : '',
|
|
261
|
+
eventName,
|
|
262
|
+
eventTime: Math.floor(Date.now() / 1000),
|
|
263
|
+
hotelFrontendId: (_b = session === null || session === void 0 ? void 0 : session.hotelFrontendId) !== null && _b !== void 0 ? _b : '',
|
|
264
|
+
fbc: (session === null || session === void 0 ? void 0 : session.fbc) || undefined,
|
|
265
|
+
fbp: (session === null || session === void 0 ? void 0 : session.fbp) || undefined,
|
|
266
|
+
eventData,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
sendEvent(eventData) {
|
|
270
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
271
|
+
try {
|
|
272
|
+
// Wait for session to be created on server before sending events
|
|
273
|
+
if (!this.sessionReady) {
|
|
274
|
+
console.warn('[AnalyticsEvents] Skipping event - sessionReady not initialized');
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const sessionCreated = yield this.sessionReady;
|
|
278
|
+
if (!sessionCreated) {
|
|
279
|
+
console.warn('[AnalyticsEvents] Skipping event - session not created on server');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const response = yield AnalyticsEventsAPI_1.analyticsEventsAPI.sendEvent(eventData);
|
|
283
|
+
if (!response.success) {
|
|
284
|
+
console.warn('[AnalyticsEvents] Backend error:', response.message);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
console.error('[AnalyticsEvents] Failed to send event:', error);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
loadSession() {
|
|
293
|
+
if (typeof localStorage === 'undefined')
|
|
294
|
+
return null;
|
|
295
|
+
const hotelId = this.config.hotelId;
|
|
296
|
+
if (!hotelId)
|
|
297
|
+
return null;
|
|
298
|
+
try {
|
|
299
|
+
const stored = localStorage.getItem(getStorageKey(hotelId));
|
|
300
|
+
if (stored) {
|
|
301
|
+
const session = JSON.parse(stored);
|
|
302
|
+
if (isValidSession(session) && Date.now() - session.createdAt < SESSION_MAX_AGE) {
|
|
303
|
+
return session;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (e) {
|
|
308
|
+
this.log('Failed to load session:', e);
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
saveSession() {
|
|
313
|
+
if (typeof localStorage === 'undefined' || !this.session)
|
|
314
|
+
return;
|
|
315
|
+
try {
|
|
316
|
+
localStorage.setItem(getStorageKey(this.session.hotelFrontendId), JSON.stringify(this.session));
|
|
317
|
+
}
|
|
318
|
+
catch (e) {
|
|
319
|
+
this.log('Failed to save session:', e);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
log(...args) {
|
|
323
|
+
if (this.config.debug) {
|
|
324
|
+
console.log('[AnalyticsEvents]', ...args);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
exports.default = new AnalyticsEvents();
|
|
329
|
+
//# sourceMappingURL=AnalyticsEvents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnalyticsEvents.js","sourceRoot":"/","sources":["src/util/AnalyticsEvents/AnalyticsEvents.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Analytics Events Utility\n */\n\nimport {\n ERoomstayEventName,\n IAnalyticsEventRequest,\n IMetaEventData,\n IRoomstayBasketAddEventData,\n IRoomstayBookingCommittedEventData,\n IRoomstayCheckoutViewEventData,\n IRoomstayEventData,\n ReservationsDTO,\n transformToMetaEventData,\n} from '@roomstay/core';\n\nimport { analyticsEventsAPI } from '@/api/AnalyticsEventsAPI';\nimport { AddToCartEvent } from '@/events/actions/AddToCartEvent';\nimport RoomstayEventManager from '@/events/RoomstayEventManager';\nimport { CommittedBookingEvent } from '@/events/views/CommittedBookingEvent';\nimport { StepCheckoutViewEvent } from '@/events/views/StepCheckoutViewEvent';\nimport BasketRow from '@/models/BasketRow';\n\nimport { calculateTotalValue, extractGuestData, getMetaCookies, serializeBasketRow, serializeBasketRows } from './helpers';\n\n/**\n * Configuration for AnalyticsEvents\n */\nexport interface AnalyticsEventsConfig {\n hotelId?: string;\n currency?: string;\n firePixelEvents?: boolean;\n debug?: boolean;\n}\n\n/**\n * Analytics session data\n */\ninterface AnalyticsSession {\n sessionId: string;\n fbc?: string;\n fbp?: string;\n createdAt: number;\n hotelFrontendId: string;\n currency: string;\n externalId?: string;\n}\n\n/**\n * Validate session from storage\n */\nfunction isValidSession(obj: unknown): obj is AnalyticsSession {\n if (!obj || typeof obj !== 'object') return false;\n const session = obj as Record<string, unknown>;\n return typeof session.sessionId === 'string' && typeof session.createdAt === 'number' && typeof session.hotelId === 'string' && typeof session.currency === 'string';\n}\n\nconst STORAGE_KEY_PREFIX = 'roomstay-analytics-session';\nconst SESSION_MAX_AGE = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * Get storage key for a specific hotel's session\n */\nfunction getStorageKey(hotelId: string): string {\n return `${STORAGE_KEY_PREFIX}-${hotelId}`;\n}\n\nfunction generateUUID(): string {\n return crypto.randomUUID();\n}\n\n/**\n * Fire a Meta Pixel event with Event ID for deduplication\n */\nfunction firePixelEvent(eventName: string, eventId: string, customData?: IMetaEventData): void {\n const metaPixelIsAvailable = typeof window !== 'undefined' && typeof (window as any).fbq === 'function';\n if (!metaPixelIsAvailable) return;\n\n try {\n (window as any).fbq('track', eventName, customData, { eventID: eventId });\n } catch (error) {\n console.error('[AnalyticsEvents] Error firing pixel event:', error);\n }\n}\n\nclass AnalyticsEvents {\n private session: AnalyticsSession | null = null;\n private config: AnalyticsEventsConfig = {};\n private initialized = false;\n private listenersRegistered = false;\n private sessionReady: Promise<boolean> | null = null;\n\n /**\n * Initialize analytics with configuration\n */\n public initialize(config?: AnalyticsEventsConfig): void {\n if (this.initialized) {\n return;\n }\n\n this.config = config || {};\n\n // Try to restore existing session\n const existingSession = this.loadSession();\n const cookies = getMetaCookies();\n\n // Resolve hotelId and currency from config or existing session\n const hotelFrontendId = this.config.hotelId ?? existingSession?.hotelFrontendId;\n const currency = this.config.currency ?? existingSession?.currency;\n\n // Cannot initialize without required fields\n if (!hotelFrontendId || !currency) {\n this.log('Cannot initialize: hotelId and currency are required');\n return;\n }\n\n if (existingSession) {\n this.session = {\n ...existingSession,\n fbc: cookies.fbc ?? existingSession.fbc,\n fbp: cookies.fbp ?? existingSession.fbp,\n hotelFrontendId,\n currency,\n };\n this.sessionReady = Promise.resolve(true);\n this.log('Session restored:', this.session.sessionId);\n } else {\n this.session = {\n sessionId: generateUUID(),\n fbc: cookies.fbc,\n fbp: cookies.fbp,\n createdAt: Date.now(),\n hotelFrontendId,\n currency,\n };\n\n this.sessionReady = this.createServerSession();\n }\n\n this.saveSession();\n this.initialized = true;\n this.registerEventListeners();\n\n this.log('Session initialized:', this.session.sessionId);\n }\n\n /**\n * Create session on the server\n */\n private async createServerSession(): Promise<boolean> {\n if (!this.session) return false;\n\n try {\n const response = await analyticsEventsAPI.createSession({\n sessionId: this.session.sessionId,\n hotelFrontendId: this.session.hotelFrontendId,\n fbc: this.session.fbc,\n fbp: this.session.fbp,\n });\n\n if (!response.success) {\n console.warn('[AnalyticsEvents] Failed to create server session:', response.message);\n return false;\n }\n\n this.log('Server session ready:', this.session.sessionId);\n return true;\n } catch (error) {\n console.error('[AnalyticsEvents] Error creating server session:', error);\n return false;\n }\n }\n\n /**\n * Register listeners for Roomstay events\n */\n private registerEventListeners(): void {\n if (this.listenersRegistered) return;\n\n // View/Checkout event\n RoomstayEventManager.addEvent(\n StepCheckoutViewEvent,\n async (event: StepCheckoutViewEvent) => {\n this.trackInitiateCheckout(event.basketRows);\n },\n 'analytics-events-checkout'\n );\n\n // Basket/Add event\n RoomstayEventManager.addEvent(\n AddToCartEvent,\n async (event: AddToCartEvent) => {\n this.trackAddToCart(event.basketRow);\n },\n 'analytics-events-add-to-cart'\n );\n\n // Booking/Committed event\n RoomstayEventManager.addEvent(\n CommittedBookingEvent,\n async (event: CommittedBookingEvent) => {\n this.trackPurchase(event.reservation, event.basketRows);\n },\n 'analytics-events-purchase'\n );\n\n this.listenersRegistered = true;\n this.log('Event listeners registered');\n }\n\n /**\n * Track View/Checkout event\n */\n public trackInitiateCheckout(basketRows: BasketRow[]): void {\n const session = this.getValidatedSession();\n if (!session) return;\n\n const { currency } = session;\n const items = serializeBasketRows(basketRows);\n\n // Build event data in Roomstay format\n const eventData: IRoomstayCheckoutViewEventData = {\n items,\n currency,\n totalValue: calculateTotalValue(items),\n };\n\n const payload = this.createEventPayload(ERoomstayEventName.ViewCheckout, eventData);\n\n // Fire Meta Pixel with transformed data\n if (this.config.firePixelEvents) {\n const metaEventData = transformToMetaEventData(eventData);\n firePixelEvent('InitiateCheckout', payload.eventId, metaEventData);\n }\n\n this.sendEvent(payload);\n this.log('View/Checkout tracked:', payload);\n }\n\n /**\n * Track Basket/Add event\n */\n public trackAddToCart(basketRow: BasketRow): void {\n const session = this.getValidatedSession();\n if (!session) return;\n\n const { currency } = session;\n\n // Build event data in Roomstay format\n const eventData: IRoomstayBasketAddEventData = {\n item: serializeBasketRow(basketRow),\n currency,\n };\n\n const payload = this.createEventPayload(ERoomstayEventName.BasketAdd, eventData);\n\n // Fire Meta Pixel with transformed data\n if (this.config.firePixelEvents) {\n const metaEventData = transformToMetaEventData(eventData);\n firePixelEvent('AddToCart', payload.eventId, metaEventData);\n }\n\n this.sendEvent(payload);\n this.log('Basket/Add tracked:', payload);\n }\n\n /**\n * Track Booking/Committed event\n */\n public trackPurchase(reservation: ReservationsDTO, basketRows: BasketRow[]): void {\n const session = this.getValidatedSession();\n if (!session) return;\n\n const { currency } = session;\n const items = serializeBasketRows(basketRows);\n\n // Build event data in Roomstay format with embedded guest data\n const eventData: IRoomstayBookingCommittedEventData = {\n items,\n currency,\n totalValue: calculateTotalValue(items),\n roomstayId: reservation.roomstayId,\n itineraryNumber: reservation.itineraryNumber,\n reservationNumbers: reservation.reservations.map((r) => r.reservationNumber),\n guest: {\n ...extractGuestData(reservation),\n externalId: session.externalId,\n },\n };\n\n const payload = this.createEventPayload(ERoomstayEventName.BookingCommitted, eventData);\n\n // Fire Meta Pixel with transformed data\n if (this.config.firePixelEvents) {\n const metaEventData = transformToMetaEventData(eventData);\n firePixelEvent('Purchase', payload.eventId, metaEventData);\n }\n\n this.sendEvent(payload);\n this.log('Booking/Committed tracked:', payload);\n }\n\n /**\n * Set external user ID (e.g. member portal ID)\n */\n public setExternalId(externalId: string): void {\n if (this.session) {\n this.log('Setting external ID:', externalId);\n this.session.externalId = externalId;\n this.saveSession();\n }\n }\n\n /**\n * Ensure session is initialized and has required fields for tracking.\n * Returns the session if tracking can proceed, null otherwise.\n */\n private getValidatedSession(): AnalyticsSession | null {\n if (!this.initialized) {\n this.initialize(this.config);\n }\n\n if (!this.session) {\n console.warn('[AnalyticsEvents] Cannot track: session not initialized');\n return null;\n }\n\n return this.session;\n }\n\n private createEventPayload(eventName: ERoomstayEventName, eventData: IRoomstayEventData): IAnalyticsEventRequest {\n const session = this.session;\n return {\n eventId: generateUUID(),\n sessionId: session?.sessionId ?? '',\n eventName,\n eventTime: Math.floor(Date.now() / 1000),\n hotelFrontendId: session?.hotelFrontendId ?? '',\n fbc: session?.fbc || undefined,\n fbp: session?.fbp || undefined,\n eventData,\n };\n }\n\n private async sendEvent(eventData: IAnalyticsEventRequest): Promise<void> {\n try {\n // Wait for session to be created on server before sending events\n if (!this.sessionReady) {\n console.warn('[AnalyticsEvents] Skipping event - sessionReady not initialized');\n return;\n }\n\n const sessionCreated = await this.sessionReady;\n if (!sessionCreated) {\n console.warn('[AnalyticsEvents] Skipping event - session not created on server');\n return;\n }\n\n const response = await analyticsEventsAPI.sendEvent(eventData);\n if (!response.success) {\n console.warn('[AnalyticsEvents] Backend error:', response.message);\n }\n } catch (error) {\n console.error('[AnalyticsEvents] Failed to send event:', error);\n }\n }\n\n private loadSession(): AnalyticsSession | null {\n if (typeof localStorage === 'undefined') return null;\n\n const hotelId = this.config.hotelId;\n if (!hotelId) return null;\n\n try {\n const stored = localStorage.getItem(getStorageKey(hotelId));\n if (stored) {\n const session: unknown = JSON.parse(stored);\n if (isValidSession(session) && Date.now() - session.createdAt < SESSION_MAX_AGE) {\n return session;\n }\n }\n } catch (e) {\n this.log('Failed to load session:', e);\n }\n\n return null;\n }\n\n private saveSession(): void {\n if (typeof localStorage === 'undefined' || !this.session) return;\n try {\n localStorage.setItem(getStorageKey(this.session.hotelFrontendId), JSON.stringify(this.session));\n } catch (e) {\n this.log('Failed to save session:', e);\n }\n }\n\n private log(...args: any[]): void {\n if (this.config.debug) {\n console.log('[AnalyticsEvents]', ...args);\n }\n }\n}\n\nexport default new AnalyticsEvents();\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Events Helpers
|
|
3
|
+
*/
|
|
4
|
+
import { IRoomstayBasketItem, IRoomstayGuestData, ReservationsDTO } from '@roomstay/core';
|
|
5
|
+
import BasketRow from '../../models/BasketRow';
|
|
6
|
+
/**
|
|
7
|
+
* Get Meta cookies for tracking
|
|
8
|
+
* - fbc (_fbc): Click ID - tracks users who clicked on a Facebook ad
|
|
9
|
+
* - fbp (_fbp): Browser ID - unique identifier for the browser
|
|
10
|
+
*/
|
|
11
|
+
export declare function getMetaCookies(): {
|
|
12
|
+
fbc?: string;
|
|
13
|
+
fbp?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Serialize a BasketRow to IRoomstayBasketItem for analytics events.
|
|
17
|
+
*/
|
|
18
|
+
export declare function serializeBasketRow(basketRow: BasketRow): IRoomstayBasketItem;
|
|
19
|
+
/**
|
|
20
|
+
* Serialize multiple BasketRows
|
|
21
|
+
*/
|
|
22
|
+
export declare function serializeBasketRows(basketRows: BasketRow[]): IRoomstayBasketItem[];
|
|
23
|
+
/**
|
|
24
|
+
* Extract guest data from a ReservationsDTO
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractGuestData(reservation: ReservationsDTO): IRoomstayGuestData;
|
|
27
|
+
/**
|
|
28
|
+
* Calculate total value from basket items
|
|
29
|
+
*/
|
|
30
|
+
export declare function calculateTotalValue(items: IRoomstayBasketItem[]): number;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Analytics Events Helpers
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getMetaCookies = getMetaCookies;
|
|
7
|
+
exports.serializeBasketRow = serializeBasketRow;
|
|
8
|
+
exports.serializeBasketRows = serializeBasketRows;
|
|
9
|
+
exports.extractGuestData = extractGuestData;
|
|
10
|
+
exports.calculateTotalValue = calculateTotalValue;
|
|
11
|
+
/**
|
|
12
|
+
* Get a cookie value by name
|
|
13
|
+
*/
|
|
14
|
+
function getCookie(name) {
|
|
15
|
+
if (typeof document === 'undefined') {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const cookies = document.cookie.split(';');
|
|
19
|
+
for (const cookie of cookies) {
|
|
20
|
+
const [cookieName, cookieValue] = cookie.trim().split('=');
|
|
21
|
+
if (cookieName === name) {
|
|
22
|
+
return decodeURIComponent(cookieValue);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get Meta cookies for tracking
|
|
29
|
+
* - fbc (_fbc): Click ID - tracks users who clicked on a Facebook ad
|
|
30
|
+
* - fbp (_fbp): Browser ID - unique identifier for the browser
|
|
31
|
+
*/
|
|
32
|
+
function getMetaCookies() {
|
|
33
|
+
return {
|
|
34
|
+
fbc: getCookie('_fbc'),
|
|
35
|
+
fbp: getCookie('_fbp'),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Serialize a BasketRow to IRoomstayBasketItem for analytics events.
|
|
40
|
+
*/
|
|
41
|
+
function serializeBasketRow(basketRow) {
|
|
42
|
+
const room = basketRow.getRoom();
|
|
43
|
+
const rate = basketRow.getRate();
|
|
44
|
+
const startDate = basketRow.getStartDate();
|
|
45
|
+
const endDate = basketRow.getEndDate();
|
|
46
|
+
return {
|
|
47
|
+
roomCode: (room === null || room === void 0 ? void 0 : room.code) || '',
|
|
48
|
+
roomId: room === null || room === void 0 ? void 0 : room.id,
|
|
49
|
+
roomName: room === null || room === void 0 ? void 0 : room.name,
|
|
50
|
+
rateCode: (rate === null || rate === void 0 ? void 0 : rate.code) || '',
|
|
51
|
+
rateName: rate === null || rate === void 0 ? void 0 : rate.name,
|
|
52
|
+
startDate: (startDate === null || startDate === void 0 ? void 0 : startDate.format('YYYY-MM-DD')) || '',
|
|
53
|
+
endDate: (endDate === null || endDate === void 0 ? void 0 : endDate.format('YYYY-MM-DD')) || '',
|
|
54
|
+
numberOfNights: basketRow.getNumberOfDays(),
|
|
55
|
+
adults: basketRow.getAdults(),
|
|
56
|
+
children: basketRow.getChildren(),
|
|
57
|
+
infants: basketRow.getInfants(),
|
|
58
|
+
totalPrice: basketRow.getTotalPrice() || 0,
|
|
59
|
+
averageNightlyRate: (rate === null || rate === void 0 ? void 0 : rate.getAveragePrice()) || 0,
|
|
60
|
+
promoCode: basketRow.getPromoCode() || undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Serialize multiple BasketRows
|
|
65
|
+
*/
|
|
66
|
+
function serializeBasketRows(basketRows) {
|
|
67
|
+
return basketRows.map(serializeBasketRow);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Extract guest data from a ReservationsDTO
|
|
71
|
+
*/
|
|
72
|
+
function extractGuestData(reservation) {
|
|
73
|
+
var _a, _b, _c, _d, _e;
|
|
74
|
+
const firstReservation = (_a = reservation.reservations) === null || _a === void 0 ? void 0 : _a[0];
|
|
75
|
+
const guest = firstReservation === null || firstReservation === void 0 ? void 0 : firstReservation.guest;
|
|
76
|
+
if (!guest) {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
email: guest.email,
|
|
81
|
+
phone: guest.phone,
|
|
82
|
+
firstName: guest.firstName,
|
|
83
|
+
lastName: guest.lastName,
|
|
84
|
+
city: (_b = guest.address) === null || _b === void 0 ? void 0 : _b.city,
|
|
85
|
+
state: (_c = guest.address) === null || _c === void 0 ? void 0 : _c.state,
|
|
86
|
+
postalCode: (_d = guest.address) === null || _d === void 0 ? void 0 : _d.postalCode,
|
|
87
|
+
country: (_e = guest.address) === null || _e === void 0 ? void 0 : _e.country,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Calculate total value from basket items
|
|
92
|
+
*/
|
|
93
|
+
function calculateTotalValue(items) {
|
|
94
|
+
return items.reduce((sum, item) => sum + item.totalPrice, 0);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"/","sources":["src/util/AnalyticsEvents/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Analytics Events Helpers\n */\n\nimport { IRoomstayBasketItem, IRoomstayGuestData, ReservationsDTO } from '@roomstay/core';\n\nimport BasketRow from '@/models/BasketRow';\n\n/**\n * Get a cookie value by name\n */\nfunction getCookie(name: string): string | undefined {\n if (typeof document === 'undefined') {\n return undefined;\n }\n\n const cookies = document.cookie.split(';');\n for (const cookie of cookies) {\n const [cookieName, cookieValue] = cookie.trim().split('=');\n if (cookieName === name) {\n return decodeURIComponent(cookieValue);\n }\n }\n return undefined;\n}\n\n/**\n * Get Meta cookies for tracking\n * - fbc (_fbc): Click ID - tracks users who clicked on a Facebook ad\n * - fbp (_fbp): Browser ID - unique identifier for the browser\n */\nexport function getMetaCookies(): { fbc?: string; fbp?: string } {\n return {\n fbc: getCookie('_fbc'),\n fbp: getCookie('_fbp'),\n };\n}\n\n/**\n * Serialize a BasketRow to IRoomstayBasketItem for analytics events.\n */\nexport function serializeBasketRow(basketRow: BasketRow): IRoomstayBasketItem {\n const room = basketRow.getRoom();\n const rate = basketRow.getRate();\n const startDate = basketRow.getStartDate();\n const endDate = basketRow.getEndDate();\n\n return {\n roomCode: room?.code || '',\n roomId: room?.id,\n roomName: room?.name,\n\n rateCode: rate?.code || '',\n rateName: rate?.name,\n\n startDate: startDate?.format('YYYY-MM-DD') || '',\n endDate: endDate?.format('YYYY-MM-DD') || '',\n numberOfNights: basketRow.getNumberOfDays(),\n\n adults: basketRow.getAdults(),\n children: basketRow.getChildren(),\n infants: basketRow.getInfants(),\n\n totalPrice: basketRow.getTotalPrice() || 0,\n averageNightlyRate: rate?.getAveragePrice() || 0,\n\n promoCode: basketRow.getPromoCode() || undefined,\n };\n}\n\n/**\n * Serialize multiple BasketRows\n */\nexport function serializeBasketRows(basketRows: BasketRow[]): IRoomstayBasketItem[] {\n return basketRows.map(serializeBasketRow);\n}\n\n/**\n * Extract guest data from a ReservationsDTO\n */\nexport function extractGuestData(reservation: ReservationsDTO): IRoomstayGuestData {\n const firstReservation = reservation.reservations?.[0];\n const guest = firstReservation?.guest;\n\n if (!guest) {\n return {};\n }\n\n return {\n email: guest.email,\n phone: guest.phone,\n firstName: guest.firstName,\n lastName: guest.lastName,\n city: guest.address?.city,\n state: guest.address?.state,\n postalCode: guest.address?.postalCode,\n country: guest.address?.country,\n };\n}\n\n/**\n * Calculate total value from basket items\n */\nexport function calculateTotalValue(items: IRoomstayBasketItem[]): number {\n return items.reduce((sum, item) => sum + item.totalPrice, 0);\n}\n"]}
|