@athoscommerce/snap-tracker 1.0.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/LICENSE +21 -0
- package/README.md +13 -0
- package/dist/cjs/Tracker.d.ts +25 -0
- package/dist/cjs/Tracker.d.ts.map +1 -0
- package/dist/cjs/Tracker.js +355 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/types.d.ts +79 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +2 -0
- package/dist/esm/Tracker.d.ts +25 -0
- package/dist/esm/Tracker.d.ts.map +1 -0
- package/dist/esm/Tracker.js +308 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/types.d.ts +79 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +1 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Athos Commerce
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Snap Tracker
|
|
2
|
+
|
|
3
|
+
The Snap Tracker is a core service available on each controller via `controller.tracker`. It is responsible for sending beacon events. Its class directly extends the [beacon.js](https://github.com/searchspring/beacon.js) `Beacon` class, therefore all methods and properties of the Beacon class are available on the Tracker class.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Snap Integration Usage
|
|
7
|
+
See [Integration Tracking](https://github.com/athoscommerce/snap/tree/main/docs/SNAP_TRACKING.md) for how and where to implement tracking events.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
<!-- TODO: Include when beacon.js is public -->
|
|
11
|
+
<!-- ## `events` methods -->
|
|
12
|
+
<!-- See [beacon.js Tracking Events](https://github.com/searchspring/beacon.js) for a list of available events. -->
|
|
13
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Beacon } from '@athoscommerce/beacon';
|
|
2
|
+
import { TrackerGlobals, TrackMethods, TrackerConfig } from './types';
|
|
3
|
+
export declare class Tracker extends Beacon {
|
|
4
|
+
private localStorage;
|
|
5
|
+
private doNotTrack;
|
|
6
|
+
config: TrackerConfig;
|
|
7
|
+
private targeters;
|
|
8
|
+
constructor(globals: TrackerGlobals, config?: TrackerConfig);
|
|
9
|
+
getGlobals(): TrackerGlobals;
|
|
10
|
+
retarget(): void;
|
|
11
|
+
track: TrackMethods;
|
|
12
|
+
cookies: {
|
|
13
|
+
cart: {
|
|
14
|
+
get: () => string[];
|
|
15
|
+
set: (items: string[]) => void;
|
|
16
|
+
add: (items: string[]) => void;
|
|
17
|
+
remove: (items: string[]) => void;
|
|
18
|
+
clear: () => void;
|
|
19
|
+
};
|
|
20
|
+
viewed: {
|
|
21
|
+
get: () => string[];
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=Tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tracker.d.ts","sourceRoot":"","sources":["../../src/Tracker.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAG/C,OAAO,EACN,cAAc,EACd,YAAY,EAKZ,aAAa,EAEb,MAAM,SAAS,CAAC;AAQjB,qBAAa,OAAQ,SAAQ,MAAM;IAClC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,UAAU,CAAkB;IAE7B,MAAM,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,SAAS,CAAqB;gBAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,aAAa;IA4KpD,UAAU,IAAI,cAAc;IAI5B,QAAQ,IAAI,IAAI;IAMvB,KAAK,EAAE,YAAY,CA+GjB;IAEF,OAAO;;uBAEI,MAAM,EAAE;yBAIJ,MAAM,EAAE,KAAG,IAAI;yBAKf,MAAM,EAAE,KAAG,IAAI;4BAMZ,MAAM,EAAE,KAAG,IAAI;;;;uBAWtB,MAAM,EAAE;;MAKjB;CACF"}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
var __assign = (this && this.__assign) || function () {
|
|
18
|
+
__assign = Object.assign || function(t) {
|
|
19
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
20
|
+
s = arguments[i];
|
|
21
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
22
|
+
t[p] = s[p];
|
|
23
|
+
}
|
|
24
|
+
return t;
|
|
25
|
+
};
|
|
26
|
+
return __assign.apply(this, arguments);
|
|
27
|
+
};
|
|
28
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
29
|
+
var t = {};
|
|
30
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
31
|
+
t[p] = s[p];
|
|
32
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
33
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
34
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
35
|
+
t[p[i]] = s[p[i]];
|
|
36
|
+
}
|
|
37
|
+
return t;
|
|
38
|
+
};
|
|
39
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
40
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
|
+
};
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.Tracker = void 0;
|
|
44
|
+
var deepmerge_1 = __importDefault(require("deepmerge"));
|
|
45
|
+
var snap_store_mobx_1 = require("@athoscommerce/snap-store-mobx");
|
|
46
|
+
var snap_toolbox_1 = require("@athoscommerce/snap-toolbox");
|
|
47
|
+
var snap_toolbox_2 = require("@athoscommerce/snap-toolbox");
|
|
48
|
+
var beacon_1 = require("@athoscommerce/beacon");
|
|
49
|
+
var defaultConfig = {
|
|
50
|
+
id: 'track',
|
|
51
|
+
framework: 'snap',
|
|
52
|
+
mode: snap_toolbox_2.AppMode.production,
|
|
53
|
+
};
|
|
54
|
+
var Tracker = /** @class */ (function (_super) {
|
|
55
|
+
__extends(Tracker, _super);
|
|
56
|
+
function Tracker(globals, config) {
|
|
57
|
+
var _this = this;
|
|
58
|
+
var _a, _b;
|
|
59
|
+
config = (0, deepmerge_1.default)(defaultConfig, config || {});
|
|
60
|
+
config.initiator = "athos/".concat(config.framework, "/").concat(snap_toolbox_1.version);
|
|
61
|
+
if (typeof globals != 'object' || typeof globals.siteId != 'string') {
|
|
62
|
+
throw new Error("Invalid config passed to tracker. The \"siteId\" attribute must be provided.");
|
|
63
|
+
}
|
|
64
|
+
_this = _super.call(this, globals, config) || this;
|
|
65
|
+
_this.targeters = [];
|
|
66
|
+
_this.track = {
|
|
67
|
+
error: function (data, siteId) {
|
|
68
|
+
var _a;
|
|
69
|
+
if (((_a = _this.doNotTrack) === null || _a === void 0 ? void 0 : _a.includes('error')) || _this.mode === snap_toolbox_2.AppMode.development) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!(data === null || data === void 0 ? void 0 : data.stack) && !(data === null || data === void 0 ? void 0 : data.message)) {
|
|
73
|
+
// no console log
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
var stack = data.stack, message = data.message, details = __rest(data, ["stack", "message"]);
|
|
77
|
+
var pageUrl = _this.getContext().pageUrl;
|
|
78
|
+
// prevent sending of errors when on localhost or CDN
|
|
79
|
+
if ((message === null || message === void 0 ? void 0 : message.includes('Profile is currently paused')) ||
|
|
80
|
+
pageUrl.includes('//localhost') ||
|
|
81
|
+
pageUrl.includes('//snapui.searchspring.io/') ||
|
|
82
|
+
pageUrl.includes('//snapui.athoscommerce.io/')) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
_this.events.error.snap({
|
|
86
|
+
data: {
|
|
87
|
+
message: message || 'unknown',
|
|
88
|
+
stack: stack,
|
|
89
|
+
details: details,
|
|
90
|
+
},
|
|
91
|
+
siteId: siteId,
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
shopper: {
|
|
95
|
+
login: function (data, siteId) {
|
|
96
|
+
var _a;
|
|
97
|
+
if ((_a = _this.doNotTrack) === null || _a === void 0 ? void 0 : _a.includes('shopper.login')) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
_this.events.shopper.login({ data: { id: data.id }, siteId: siteId });
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
product: {
|
|
104
|
+
view: function (data, siteId) {
|
|
105
|
+
var _a;
|
|
106
|
+
if ((_a = _this.doNotTrack) === null || _a === void 0 ? void 0 : _a.includes('product.view')) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
var dataPayload = {
|
|
110
|
+
result: {
|
|
111
|
+
parentId: data.parentId || data.uid || '',
|
|
112
|
+
uid: data.uid || data.parentId || data.sku || '',
|
|
113
|
+
sku: data.sku,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
if (data.childSku || data.childUid) {
|
|
117
|
+
dataPayload = {
|
|
118
|
+
result: {
|
|
119
|
+
parentId: data.parentId || data.uid || data.childUid || '',
|
|
120
|
+
uid: data.childUid || data.uid || '',
|
|
121
|
+
sku: data.childSku || data.sku,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
_this.events.product.pageView({ data: dataPayload, siteId: siteId });
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated tracker.track.product.click() is deprecated and will be removed. Use tracker.events['search' | 'category'].clickThrough() instead
|
|
129
|
+
*/
|
|
130
|
+
click: function () {
|
|
131
|
+
console.warn("tracker.track.product.click() is deprecated and is no longer functional. Use tracker.events['search' | 'category'].clickThrough() instead");
|
|
132
|
+
_this.events.error.snap({ data: { message: "tracker.track.product.click was called" } });
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
cart: {
|
|
136
|
+
view: function () {
|
|
137
|
+
console.warn('tracker.cart.view is deprecated and no longer functional. Use tracker.events.cart.add() and tracker.events.cart.remove() instead');
|
|
138
|
+
_this.events.error.snap({ data: { message: "tracker.track.cart.view was called" } });
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
order: {
|
|
142
|
+
transaction: function (data, siteId) {
|
|
143
|
+
var _a;
|
|
144
|
+
if ((_a = _this.doNotTrack) === null || _a === void 0 ? void 0 : _a.includes('order.transaction')) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
var order = data.order;
|
|
148
|
+
var items = data.items;
|
|
149
|
+
var orderTransactionData = {
|
|
150
|
+
orderId: "".concat((order === null || order === void 0 ? void 0 : order.id) || ''),
|
|
151
|
+
transactionTotal: Number((order === null || order === void 0 ? void 0 : order.transactionTotal) || 0),
|
|
152
|
+
total: Number((order === null || order === void 0 ? void 0 : order.total) || 0),
|
|
153
|
+
city: order === null || order === void 0 ? void 0 : order.city,
|
|
154
|
+
state: order === null || order === void 0 ? void 0 : order.state,
|
|
155
|
+
country: order === null || order === void 0 ? void 0 : order.country,
|
|
156
|
+
results: items.map(function (item) {
|
|
157
|
+
return {
|
|
158
|
+
parentId: item.parentId || item.uid || '',
|
|
159
|
+
uid: item.uid || item.parentId || item.sku || '',
|
|
160
|
+
sku: item.sku,
|
|
161
|
+
qty: Number(item.qty),
|
|
162
|
+
price: Number(item.price),
|
|
163
|
+
};
|
|
164
|
+
}),
|
|
165
|
+
};
|
|
166
|
+
_this.events.order.transaction({ data: orderTransactionData, siteId: siteId });
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
_this.cookies = {
|
|
171
|
+
cart: {
|
|
172
|
+
get: function () {
|
|
173
|
+
var data = _this.storage.cart.get();
|
|
174
|
+
return data.map(function (item) { return _this.getProductId(item); });
|
|
175
|
+
},
|
|
176
|
+
set: function (items) {
|
|
177
|
+
var cartItems = items.map(function (item) { return "".concat(item).trim(); });
|
|
178
|
+
var uniqueCartItems = Array.from(new Set(cartItems)).map(function (uid) { return ({ parentId: uid, uid: uid, sku: uid, price: 0, qty: 1 }); });
|
|
179
|
+
_this.storage.cart.set(uniqueCartItems);
|
|
180
|
+
},
|
|
181
|
+
add: function (items) {
|
|
182
|
+
if (items.length) {
|
|
183
|
+
var itemsToAdd = items.map(function (item) { return "".concat(item).trim(); }).map(function (uid) { return ({ parentId: uid, uid: uid, sku: uid, price: 0, qty: 1 }); });
|
|
184
|
+
_this.storage.cart.add(itemsToAdd);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
remove: function (items) {
|
|
188
|
+
if (items.length) {
|
|
189
|
+
var itemsToRemove = items.map(function (item) { return "".concat(item).trim(); }).map(function (uid) { return ({ parentId: uid, uid: uid, sku: uid, price: 0, qty: 1 }); });
|
|
190
|
+
_this.storage.cart.remove(itemsToRemove);
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
clear: function () {
|
|
194
|
+
_this.storage.cart.clear();
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
viewed: {
|
|
198
|
+
get: function () {
|
|
199
|
+
var viewedItems = _this.storage.viewed.get();
|
|
200
|
+
return viewedItems.map(function (item) { return _this.getProductId(item); });
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
_this.config = config;
|
|
205
|
+
_this.doNotTrack = _this.config.doNotTrack || [];
|
|
206
|
+
if (Object.values(snap_toolbox_2.AppMode).includes(_this.config.mode)) {
|
|
207
|
+
_this.mode = _this.config.mode;
|
|
208
|
+
}
|
|
209
|
+
_this.localStorage = new snap_store_mobx_1.StorageStore({
|
|
210
|
+
type: 'local',
|
|
211
|
+
key: "athos-".concat(_this.config.id),
|
|
212
|
+
});
|
|
213
|
+
_this.localStorage.set('siteId', _this.globals.siteId);
|
|
214
|
+
var currency = (_a = _this.globals) === null || _a === void 0 ? void 0 : _a.currency;
|
|
215
|
+
if (currency) {
|
|
216
|
+
_this.setCurrency(currency);
|
|
217
|
+
}
|
|
218
|
+
if (!((_b = window.athos) === null || _b === void 0 ? void 0 : _b.tracker)) {
|
|
219
|
+
window.athos = window.athos || {};
|
|
220
|
+
window.athos.tracker = _this;
|
|
221
|
+
window.athos.version = snap_toolbox_1.version;
|
|
222
|
+
}
|
|
223
|
+
// since this is in the constructor, setTimeout is required for jest.spyOn
|
|
224
|
+
setTimeout(function () {
|
|
225
|
+
_this.targeters.push(new snap_toolbox_1.DomTargeter([{ selector: 'script[type^="athos/track/"], script[type^="searchspring/track/"]', emptyTarget: false }], function (target, elem) {
|
|
226
|
+
var _a = (0, snap_toolbox_1.getContext)(['item', 'items', 'siteId', 'shopper', 'order', 'type', 'currency'], elem), item = _a.item, items = _a.items, siteId = _a.siteId, shopper = _a.shopper, order = _a.order, type = _a.type, currency = _a.currency;
|
|
227
|
+
_this.setCurrency(currency);
|
|
228
|
+
switch (type) {
|
|
229
|
+
case 'searchspring/track/shopper/login':
|
|
230
|
+
case 'athos/track/shopper/login':
|
|
231
|
+
_this.track.shopper.login(shopper, siteId);
|
|
232
|
+
break;
|
|
233
|
+
case 'searchspring/track/product/view':
|
|
234
|
+
case 'athos/track/product/view':
|
|
235
|
+
_this.track.product.view(item, siteId);
|
|
236
|
+
break;
|
|
237
|
+
case 'searchspring/track/cart/view':
|
|
238
|
+
case 'athos/track/cart/view':
|
|
239
|
+
_this.track.cart.view();
|
|
240
|
+
break;
|
|
241
|
+
case 'searchspring/track/order/transaction':
|
|
242
|
+
case 'athos/track/order/transaction':
|
|
243
|
+
_this.track.order.transaction({ order: order, items: items }, siteId);
|
|
244
|
+
break;
|
|
245
|
+
default:
|
|
246
|
+
console.error("event '".concat(type, "' is not supported"));
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}));
|
|
250
|
+
});
|
|
251
|
+
var cart = _this.globals.cart;
|
|
252
|
+
if (Array.isArray(cart)) {
|
|
253
|
+
if (cart.length === 0) {
|
|
254
|
+
// cart is empty, clear storage and send remove event if storage had items
|
|
255
|
+
var storedCart = _this.storage.cart.get();
|
|
256
|
+
if (storedCart.length) {
|
|
257
|
+
_this.events.cart.remove({
|
|
258
|
+
data: {
|
|
259
|
+
results: storedCart,
|
|
260
|
+
cart: [],
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
_this.storage.cart.clear();
|
|
265
|
+
}
|
|
266
|
+
else if (cart.length) {
|
|
267
|
+
// length check here to be able to sent error event if invalid array of objects is provided
|
|
268
|
+
var currentCart = cart
|
|
269
|
+
.filter(function (item) { return typeof item === 'object' && (item.parentId || item.uid || item.sku) && item.qty !== undefined && item.price !== undefined; })
|
|
270
|
+
.map(function (item) {
|
|
271
|
+
return {
|
|
272
|
+
parentId: item.parentId || item.uid,
|
|
273
|
+
uid: item.uid,
|
|
274
|
+
sku: item.sku,
|
|
275
|
+
price: item.price,
|
|
276
|
+
qty: item.qty,
|
|
277
|
+
};
|
|
278
|
+
});
|
|
279
|
+
// beacon 2.0 requires all parameters to be present
|
|
280
|
+
// send error to keep track of integrations to be updated
|
|
281
|
+
if (!currentCart.length) {
|
|
282
|
+
_this.events.error.snap({
|
|
283
|
+
data: {
|
|
284
|
+
message: 'cart globals missing properties',
|
|
285
|
+
details: { cart: cart },
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
var storedCart_1 = _this.storage.cart.get();
|
|
290
|
+
var toAdd_1 = [];
|
|
291
|
+
var toRemove_1 = [];
|
|
292
|
+
if (!(storedCart_1 === null || storedCart_1 === void 0 ? void 0 : storedCart_1.length) && currentCart.length) {
|
|
293
|
+
// no stored cart, add all items
|
|
294
|
+
toAdd_1.push.apply(toAdd_1, currentCart);
|
|
295
|
+
}
|
|
296
|
+
else if (currentCart.length) {
|
|
297
|
+
currentCart.forEach(function (item) {
|
|
298
|
+
var existingItem = storedCart_1.find(function (existingItem) {
|
|
299
|
+
return existingItem.parentId === item.parentId && existingItem.uid === item.uid && existingItem.sku === item.sku;
|
|
300
|
+
});
|
|
301
|
+
if (!existingItem) {
|
|
302
|
+
// item does not exist in cart, add it
|
|
303
|
+
toAdd_1.push(item);
|
|
304
|
+
}
|
|
305
|
+
else if (existingItem) {
|
|
306
|
+
// item already exists in cart, check if qty has changed
|
|
307
|
+
if (item.qty > existingItem.qty) {
|
|
308
|
+
toAdd_1.push(__assign(__assign({}, item), { qty: item.qty - existingItem.qty }));
|
|
309
|
+
}
|
|
310
|
+
else if (item.qty < existingItem.qty) {
|
|
311
|
+
toRemove_1.push(__assign(__assign({}, existingItem), { qty: existingItem.qty - item.qty }));
|
|
312
|
+
}
|
|
313
|
+
// remove from existing cart
|
|
314
|
+
var index = storedCart_1.indexOf(existingItem);
|
|
315
|
+
if (index !== -1) {
|
|
316
|
+
storedCart_1.splice(index, 1);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// any remaining items in existing cart should be removed
|
|
321
|
+
if (storedCart_1.length) {
|
|
322
|
+
toRemove_1.push.apply(toRemove_1, storedCart_1);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (toAdd_1.length) {
|
|
326
|
+
_this.events.cart.add({
|
|
327
|
+
data: {
|
|
328
|
+
results: toAdd_1,
|
|
329
|
+
cart: currentCart,
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
if (toRemove_1.length) {
|
|
334
|
+
_this.events.cart.remove({
|
|
335
|
+
data: {
|
|
336
|
+
results: toRemove_1,
|
|
337
|
+
cart: currentCart,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return _this;
|
|
344
|
+
}
|
|
345
|
+
Tracker.prototype.getGlobals = function () {
|
|
346
|
+
return JSON.parse(JSON.stringify(this.globals));
|
|
347
|
+
};
|
|
348
|
+
Tracker.prototype.retarget = function () {
|
|
349
|
+
this.targeters.forEach(function (target) {
|
|
350
|
+
target.retarget();
|
|
351
|
+
});
|
|
352
|
+
};
|
|
353
|
+
return Tracker;
|
|
354
|
+
}(beacon_1.Beacon));
|
|
355
|
+
exports.Tracker = Tracker;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Tracker = void 0;
|
|
18
|
+
var Tracker_1 = require("./Tracker");
|
|
19
|
+
Object.defineProperty(exports, "Tracker", { enumerable: true, get: function () { return Tracker_1.Tracker; } });
|
|
20
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { AppMode } from '@athoscommerce/snap-toolbox';
|
|
2
|
+
import type { BeaconConfig, Currency, Product } from '@athoscommerce/beacon';
|
|
3
|
+
export type TrackerGlobals = {
|
|
4
|
+
siteId: string;
|
|
5
|
+
currency?: Currency;
|
|
6
|
+
cart?: Product[];
|
|
7
|
+
};
|
|
8
|
+
export type TrackerEvents = 'error' | 'shopper.login' | 'product.view' | 'product.click' | 'cart.view' | 'order.transaction';
|
|
9
|
+
export type TrackerConfig = BeaconConfig & {
|
|
10
|
+
id?: string;
|
|
11
|
+
framework?: string;
|
|
12
|
+
mode?: keyof typeof AppMode | AppMode;
|
|
13
|
+
doNotTrack?: TrackerEvents[];
|
|
14
|
+
};
|
|
15
|
+
export interface ShopperLoginEvent {
|
|
16
|
+
id: string;
|
|
17
|
+
}
|
|
18
|
+
export interface TrackErrorEvent {
|
|
19
|
+
href?: string;
|
|
20
|
+
filename?: string;
|
|
21
|
+
stack?: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
colno?: number;
|
|
24
|
+
lineno?: number;
|
|
25
|
+
errortimestamp?: number;
|
|
26
|
+
context?: {
|
|
27
|
+
controller?: {
|
|
28
|
+
type: string;
|
|
29
|
+
id: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
details?: {
|
|
33
|
+
[any: string]: unknown;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface ProductViewEvent {
|
|
37
|
+
uid?: string;
|
|
38
|
+
parentId?: string;
|
|
39
|
+
sku?: string;
|
|
40
|
+
childUid?: string;
|
|
41
|
+
childSku?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ProductData extends ProductViewEvent {
|
|
44
|
+
qty: string | number;
|
|
45
|
+
price: string | number;
|
|
46
|
+
}
|
|
47
|
+
export interface OrderTransactionData {
|
|
48
|
+
order?: {
|
|
49
|
+
id?: string | number;
|
|
50
|
+
total?: string | number;
|
|
51
|
+
transactionTotal?: string | number;
|
|
52
|
+
city?: string;
|
|
53
|
+
state?: string;
|
|
54
|
+
country?: string;
|
|
55
|
+
};
|
|
56
|
+
items: ProductData[];
|
|
57
|
+
}
|
|
58
|
+
export interface TrackMethods {
|
|
59
|
+
error: (data: TrackErrorEvent) => undefined;
|
|
60
|
+
shopper: {
|
|
61
|
+
login: (data: ShopperLoginEvent, siteId?: string) => undefined;
|
|
62
|
+
};
|
|
63
|
+
product: {
|
|
64
|
+
view: (data: ProductViewEvent, siteId?: string) => undefined;
|
|
65
|
+
click: () => void;
|
|
66
|
+
};
|
|
67
|
+
cart: {
|
|
68
|
+
view: () => void;
|
|
69
|
+
};
|
|
70
|
+
order: {
|
|
71
|
+
transaction: (data: OrderTransactionData, siteId?: string) => undefined;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
declare global {
|
|
75
|
+
interface Window {
|
|
76
|
+
athos?: any;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Beacon } from '@athoscommerce/beacon';
|
|
2
|
+
import { TrackerGlobals, TrackMethods, TrackerConfig } from './types';
|
|
3
|
+
export declare class Tracker extends Beacon {
|
|
4
|
+
private localStorage;
|
|
5
|
+
private doNotTrack;
|
|
6
|
+
config: TrackerConfig;
|
|
7
|
+
private targeters;
|
|
8
|
+
constructor(globals: TrackerGlobals, config?: TrackerConfig);
|
|
9
|
+
getGlobals(): TrackerGlobals;
|
|
10
|
+
retarget(): void;
|
|
11
|
+
track: TrackMethods;
|
|
12
|
+
cookies: {
|
|
13
|
+
cart: {
|
|
14
|
+
get: () => string[];
|
|
15
|
+
set: (items: string[]) => void;
|
|
16
|
+
add: (items: string[]) => void;
|
|
17
|
+
remove: (items: string[]) => void;
|
|
18
|
+
clear: () => void;
|
|
19
|
+
};
|
|
20
|
+
viewed: {
|
|
21
|
+
get: () => string[];
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=Tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tracker.d.ts","sourceRoot":"","sources":["../../src/Tracker.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAG/C,OAAO,EACN,cAAc,EACd,YAAY,EAKZ,aAAa,EAEb,MAAM,SAAS,CAAC;AAQjB,qBAAa,OAAQ,SAAQ,MAAM;IAClC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,UAAU,CAAkB;IAE7B,MAAM,EAAE,aAAa,CAAC;IAC7B,OAAO,CAAC,SAAS,CAAqB;gBAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,aAAa;IA4KpD,UAAU,IAAI,cAAc;IAI5B,QAAQ,IAAI,IAAI;IAMvB,KAAK,EAAE,YAAY,CA+GjB;IAEF,OAAO;;uBAEI,MAAM,EAAE;yBAIJ,MAAM,EAAE,KAAG,IAAI;yBAKf,MAAM,EAAE,KAAG,IAAI;4BAMZ,MAAM,EAAE,KAAG,IAAI;;;;uBAWtB,MAAM,EAAE;;MAKjB;CACF"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import deepmerge from 'deepmerge';
|
|
2
|
+
import { StorageStore } from '@athoscommerce/snap-store-mobx';
|
|
3
|
+
import { version, DomTargeter, getContext } from '@athoscommerce/snap-toolbox';
|
|
4
|
+
import { AppMode } from '@athoscommerce/snap-toolbox';
|
|
5
|
+
import { Beacon } from '@athoscommerce/beacon';
|
|
6
|
+
const defaultConfig = {
|
|
7
|
+
id: 'track',
|
|
8
|
+
framework: 'snap',
|
|
9
|
+
mode: AppMode.production,
|
|
10
|
+
};
|
|
11
|
+
export class Tracker extends Beacon {
|
|
12
|
+
constructor(globals, config) {
|
|
13
|
+
config = deepmerge(defaultConfig, config || {});
|
|
14
|
+
config.initiator = `athos/${config.framework}/${version}`;
|
|
15
|
+
if (typeof globals != 'object' || typeof globals.siteId != 'string') {
|
|
16
|
+
throw new Error(`Invalid config passed to tracker. The "siteId" attribute must be provided.`);
|
|
17
|
+
}
|
|
18
|
+
super(globals, config);
|
|
19
|
+
this.targeters = [];
|
|
20
|
+
this.track = {
|
|
21
|
+
error: (data, siteId) => {
|
|
22
|
+
if (this.doNotTrack?.includes('error') || this.mode === AppMode.development) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (!data?.stack && !data?.message) {
|
|
26
|
+
// no console log
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const { stack, message, ...details } = data;
|
|
30
|
+
const { pageUrl } = this.getContext();
|
|
31
|
+
// prevent sending of errors when on localhost or CDN
|
|
32
|
+
if (message?.includes('Profile is currently paused') ||
|
|
33
|
+
pageUrl.includes('//localhost') ||
|
|
34
|
+
pageUrl.includes('//snapui.searchspring.io/') ||
|
|
35
|
+
pageUrl.includes('//snapui.athoscommerce.io/')) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.events.error.snap({
|
|
39
|
+
data: {
|
|
40
|
+
message: message || 'unknown',
|
|
41
|
+
stack,
|
|
42
|
+
details,
|
|
43
|
+
},
|
|
44
|
+
siteId,
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
shopper: {
|
|
48
|
+
login: (data, siteId) => {
|
|
49
|
+
if (this.doNotTrack?.includes('shopper.login')) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.events.shopper.login({ data: { id: data.id }, siteId });
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
product: {
|
|
56
|
+
view: (data, siteId) => {
|
|
57
|
+
if (this.doNotTrack?.includes('product.view')) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
let dataPayload = {
|
|
61
|
+
result: {
|
|
62
|
+
parentId: data.parentId || data.uid || '',
|
|
63
|
+
uid: data.uid || data.parentId || data.sku || '',
|
|
64
|
+
sku: data.sku,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
if (data.childSku || data.childUid) {
|
|
68
|
+
dataPayload = {
|
|
69
|
+
result: {
|
|
70
|
+
parentId: data.parentId || data.uid || data.childUid || '',
|
|
71
|
+
uid: data.childUid || data.uid || '',
|
|
72
|
+
sku: data.childSku || data.sku,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
this.events.product.pageView({ data: dataPayload, siteId });
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* @deprecated tracker.track.product.click() is deprecated and will be removed. Use tracker.events['search' | 'category'].clickThrough() instead
|
|
80
|
+
*/
|
|
81
|
+
click: () => {
|
|
82
|
+
console.warn(`tracker.track.product.click() is deprecated and is no longer functional. Use tracker.events['search' | 'category'].clickThrough() instead`);
|
|
83
|
+
this.events.error.snap({ data: { message: `tracker.track.product.click was called` } });
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
cart: {
|
|
87
|
+
view: () => {
|
|
88
|
+
console.warn('tracker.cart.view is deprecated and no longer functional. Use tracker.events.cart.add() and tracker.events.cart.remove() instead');
|
|
89
|
+
this.events.error.snap({ data: { message: `tracker.track.cart.view was called` } });
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
order: {
|
|
93
|
+
transaction: (data, siteId) => {
|
|
94
|
+
if (this.doNotTrack?.includes('order.transaction')) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const order = data.order;
|
|
98
|
+
const items = data.items;
|
|
99
|
+
const orderTransactionData = {
|
|
100
|
+
orderId: `${order?.id || ''}`,
|
|
101
|
+
transactionTotal: Number(order?.transactionTotal || 0),
|
|
102
|
+
total: Number(order?.total || 0),
|
|
103
|
+
city: order?.city,
|
|
104
|
+
state: order?.state,
|
|
105
|
+
country: order?.country,
|
|
106
|
+
results: items.map((item) => {
|
|
107
|
+
return {
|
|
108
|
+
parentId: item.parentId || item.uid || '',
|
|
109
|
+
uid: item.uid || item.parentId || item.sku || '',
|
|
110
|
+
sku: item.sku,
|
|
111
|
+
qty: Number(item.qty),
|
|
112
|
+
price: Number(item.price),
|
|
113
|
+
};
|
|
114
|
+
}),
|
|
115
|
+
};
|
|
116
|
+
this.events.order.transaction({ data: orderTransactionData, siteId });
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
this.cookies = {
|
|
121
|
+
cart: {
|
|
122
|
+
get: () => {
|
|
123
|
+
const data = this.storage.cart.get();
|
|
124
|
+
return data.map((item) => this.getProductId(item));
|
|
125
|
+
},
|
|
126
|
+
set: (items) => {
|
|
127
|
+
const cartItems = items.map((item) => `${item}`.trim());
|
|
128
|
+
const uniqueCartItems = Array.from(new Set(cartItems)).map((uid) => ({ parentId: uid, uid, sku: uid, price: 0, qty: 1 }));
|
|
129
|
+
this.storage.cart.set(uniqueCartItems);
|
|
130
|
+
},
|
|
131
|
+
add: (items) => {
|
|
132
|
+
if (items.length) {
|
|
133
|
+
const itemsToAdd = items.map((item) => `${item}`.trim()).map((uid) => ({ parentId: uid, uid, sku: uid, price: 0, qty: 1 }));
|
|
134
|
+
this.storage.cart.add(itemsToAdd);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
remove: (items) => {
|
|
138
|
+
if (items.length) {
|
|
139
|
+
const itemsToRemove = items.map((item) => `${item}`.trim()).map((uid) => ({ parentId: uid, uid, sku: uid, price: 0, qty: 1 }));
|
|
140
|
+
this.storage.cart.remove(itemsToRemove);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
clear: () => {
|
|
144
|
+
this.storage.cart.clear();
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
viewed: {
|
|
148
|
+
get: () => {
|
|
149
|
+
const viewedItems = this.storage.viewed.get();
|
|
150
|
+
return viewedItems.map((item) => this.getProductId(item));
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
this.config = config;
|
|
155
|
+
this.doNotTrack = this.config.doNotTrack || [];
|
|
156
|
+
if (Object.values(AppMode).includes(this.config.mode)) {
|
|
157
|
+
this.mode = this.config.mode;
|
|
158
|
+
}
|
|
159
|
+
this.localStorage = new StorageStore({
|
|
160
|
+
type: 'local',
|
|
161
|
+
key: `athos-${this.config.id}`,
|
|
162
|
+
});
|
|
163
|
+
this.localStorage.set('siteId', this.globals.siteId);
|
|
164
|
+
const currency = this.globals?.currency;
|
|
165
|
+
if (currency) {
|
|
166
|
+
this.setCurrency(currency);
|
|
167
|
+
}
|
|
168
|
+
if (!window.athos?.tracker) {
|
|
169
|
+
window.athos = window.athos || {};
|
|
170
|
+
window.athos.tracker = this;
|
|
171
|
+
window.athos.version = version;
|
|
172
|
+
}
|
|
173
|
+
// since this is in the constructor, setTimeout is required for jest.spyOn
|
|
174
|
+
setTimeout(() => {
|
|
175
|
+
this.targeters.push(new DomTargeter([{ selector: 'script[type^="athos/track/"], script[type^="searchspring/track/"]', emptyTarget: false }], (target, elem) => {
|
|
176
|
+
const { item, items, siteId, shopper, order, type, currency } = getContext(['item', 'items', 'siteId', 'shopper', 'order', 'type', 'currency'], elem);
|
|
177
|
+
this.setCurrency(currency);
|
|
178
|
+
switch (type) {
|
|
179
|
+
case 'searchspring/track/shopper/login':
|
|
180
|
+
case 'athos/track/shopper/login':
|
|
181
|
+
this.track.shopper.login(shopper, siteId);
|
|
182
|
+
break;
|
|
183
|
+
case 'searchspring/track/product/view':
|
|
184
|
+
case 'athos/track/product/view':
|
|
185
|
+
this.track.product.view(item, siteId);
|
|
186
|
+
break;
|
|
187
|
+
case 'searchspring/track/cart/view':
|
|
188
|
+
case 'athos/track/cart/view':
|
|
189
|
+
this.track.cart.view();
|
|
190
|
+
break;
|
|
191
|
+
case 'searchspring/track/order/transaction':
|
|
192
|
+
case 'athos/track/order/transaction':
|
|
193
|
+
this.track.order.transaction({ order, items }, siteId);
|
|
194
|
+
break;
|
|
195
|
+
default:
|
|
196
|
+
console.error(`event '${type}' is not supported`);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}));
|
|
200
|
+
});
|
|
201
|
+
const cart = this.globals.cart;
|
|
202
|
+
if (Array.isArray(cart)) {
|
|
203
|
+
if (cart.length === 0) {
|
|
204
|
+
// cart is empty, clear storage and send remove event if storage had items
|
|
205
|
+
const storedCart = this.storage.cart.get();
|
|
206
|
+
if (storedCart.length) {
|
|
207
|
+
this.events.cart.remove({
|
|
208
|
+
data: {
|
|
209
|
+
results: storedCart,
|
|
210
|
+
cart: [],
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
this.storage.cart.clear();
|
|
215
|
+
}
|
|
216
|
+
else if (cart.length) {
|
|
217
|
+
// length check here to be able to sent error event if invalid array of objects is provided
|
|
218
|
+
const currentCart = cart
|
|
219
|
+
.filter((item) => typeof item === 'object' && (item.parentId || item.uid || item.sku) && item.qty !== undefined && item.price !== undefined)
|
|
220
|
+
.map((item) => {
|
|
221
|
+
return {
|
|
222
|
+
parentId: item.parentId || item.uid,
|
|
223
|
+
uid: item.uid,
|
|
224
|
+
sku: item.sku,
|
|
225
|
+
price: item.price,
|
|
226
|
+
qty: item.qty,
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
// beacon 2.0 requires all parameters to be present
|
|
230
|
+
// send error to keep track of integrations to be updated
|
|
231
|
+
if (!currentCart.length) {
|
|
232
|
+
this.events.error.snap({
|
|
233
|
+
data: {
|
|
234
|
+
message: 'cart globals missing properties',
|
|
235
|
+
details: { cart },
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const storedCart = this.storage.cart.get();
|
|
240
|
+
const toAdd = [];
|
|
241
|
+
const toRemove = [];
|
|
242
|
+
if (!storedCart?.length && currentCart.length) {
|
|
243
|
+
// no stored cart, add all items
|
|
244
|
+
toAdd.push(...currentCart);
|
|
245
|
+
}
|
|
246
|
+
else if (currentCart.length) {
|
|
247
|
+
currentCart.forEach((item) => {
|
|
248
|
+
const existingItem = storedCart.find((existingItem) => {
|
|
249
|
+
return existingItem.parentId === item.parentId && existingItem.uid === item.uid && existingItem.sku === item.sku;
|
|
250
|
+
});
|
|
251
|
+
if (!existingItem) {
|
|
252
|
+
// item does not exist in cart, add it
|
|
253
|
+
toAdd.push(item);
|
|
254
|
+
}
|
|
255
|
+
else if (existingItem) {
|
|
256
|
+
// item already exists in cart, check if qty has changed
|
|
257
|
+
if (item.qty > existingItem.qty) {
|
|
258
|
+
toAdd.push({
|
|
259
|
+
...item,
|
|
260
|
+
qty: item.qty - existingItem.qty,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
else if (item.qty < existingItem.qty) {
|
|
264
|
+
toRemove.push({
|
|
265
|
+
...existingItem,
|
|
266
|
+
qty: existingItem.qty - item.qty,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
// remove from existing cart
|
|
270
|
+
const index = storedCart.indexOf(existingItem);
|
|
271
|
+
if (index !== -1) {
|
|
272
|
+
storedCart.splice(index, 1);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
// any remaining items in existing cart should be removed
|
|
277
|
+
if (storedCart.length) {
|
|
278
|
+
toRemove.push(...storedCart);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (toAdd.length) {
|
|
282
|
+
this.events.cart.add({
|
|
283
|
+
data: {
|
|
284
|
+
results: toAdd,
|
|
285
|
+
cart: currentCart,
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (toRemove.length) {
|
|
290
|
+
this.events.cart.remove({
|
|
291
|
+
data: {
|
|
292
|
+
results: toRemove,
|
|
293
|
+
cart: currentCart,
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
getGlobals() {
|
|
301
|
+
return JSON.parse(JSON.stringify(this.globals));
|
|
302
|
+
}
|
|
303
|
+
retarget() {
|
|
304
|
+
this.targeters.forEach((target) => {
|
|
305
|
+
target.retarget();
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { AppMode } from '@athoscommerce/snap-toolbox';
|
|
2
|
+
import type { BeaconConfig, Currency, Product } from '@athoscommerce/beacon';
|
|
3
|
+
export type TrackerGlobals = {
|
|
4
|
+
siteId: string;
|
|
5
|
+
currency?: Currency;
|
|
6
|
+
cart?: Product[];
|
|
7
|
+
};
|
|
8
|
+
export type TrackerEvents = 'error' | 'shopper.login' | 'product.view' | 'product.click' | 'cart.view' | 'order.transaction';
|
|
9
|
+
export type TrackerConfig = BeaconConfig & {
|
|
10
|
+
id?: string;
|
|
11
|
+
framework?: string;
|
|
12
|
+
mode?: keyof typeof AppMode | AppMode;
|
|
13
|
+
doNotTrack?: TrackerEvents[];
|
|
14
|
+
};
|
|
15
|
+
export interface ShopperLoginEvent {
|
|
16
|
+
id: string;
|
|
17
|
+
}
|
|
18
|
+
export interface TrackErrorEvent {
|
|
19
|
+
href?: string;
|
|
20
|
+
filename?: string;
|
|
21
|
+
stack?: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
colno?: number;
|
|
24
|
+
lineno?: number;
|
|
25
|
+
errortimestamp?: number;
|
|
26
|
+
context?: {
|
|
27
|
+
controller?: {
|
|
28
|
+
type: string;
|
|
29
|
+
id: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
details?: {
|
|
33
|
+
[any: string]: unknown;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface ProductViewEvent {
|
|
37
|
+
uid?: string;
|
|
38
|
+
parentId?: string;
|
|
39
|
+
sku?: string;
|
|
40
|
+
childUid?: string;
|
|
41
|
+
childSku?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ProductData extends ProductViewEvent {
|
|
44
|
+
qty: string | number;
|
|
45
|
+
price: string | number;
|
|
46
|
+
}
|
|
47
|
+
export interface OrderTransactionData {
|
|
48
|
+
order?: {
|
|
49
|
+
id?: string | number;
|
|
50
|
+
total?: string | number;
|
|
51
|
+
transactionTotal?: string | number;
|
|
52
|
+
city?: string;
|
|
53
|
+
state?: string;
|
|
54
|
+
country?: string;
|
|
55
|
+
};
|
|
56
|
+
items: ProductData[];
|
|
57
|
+
}
|
|
58
|
+
export interface TrackMethods {
|
|
59
|
+
error: (data: TrackErrorEvent) => undefined;
|
|
60
|
+
shopper: {
|
|
61
|
+
login: (data: ShopperLoginEvent, siteId?: string) => undefined;
|
|
62
|
+
};
|
|
63
|
+
product: {
|
|
64
|
+
view: (data: ProductViewEvent, siteId?: string) => undefined;
|
|
65
|
+
click: () => void;
|
|
66
|
+
};
|
|
67
|
+
cart: {
|
|
68
|
+
view: () => void;
|
|
69
|
+
};
|
|
70
|
+
order: {
|
|
71
|
+
transaction: (data: OrderTransactionData, siteId?: string) => undefined;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
declare global {
|
|
75
|
+
interface Window {
|
|
76
|
+
athos?: any;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@athoscommerce/snap-tracker",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Snap Tracker",
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"author": "AthosCommerce",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"repository": "https://github.com/athoscommerce/snap",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "rm -rf ./dist && rm -fr ./components/dist && tsc & p1=$!; tsc -p tsconfig.cjs.json & p2=$!; wait $p1 && wait $p2",
|
|
15
|
+
"build:docs": "typedoc --out docs src/index.ts",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'",
|
|
18
|
+
"lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
|
|
19
|
+
"test": "jest",
|
|
20
|
+
"test:watch": "jest --watch"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@athoscommerce/beacon": "1.1.0",
|
|
24
|
+
"@athoscommerce/snap-store-mobx": "1.0.0",
|
|
25
|
+
"@athoscommerce/snap-toolbox": "1.0.0",
|
|
26
|
+
"@types/uuid": "8.3.4",
|
|
27
|
+
"deepmerge": "4.3.1",
|
|
28
|
+
"uuid": "9.0.1"
|
|
29
|
+
},
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"files": [
|
|
32
|
+
"dist/**/*"
|
|
33
|
+
],
|
|
34
|
+
"gitHead": "714fb2d140d44581a559a6025310f8df9e33e216"
|
|
35
|
+
}
|