@getlupa/client 0.14.3 → 0.15.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.
@@ -3,6 +3,7 @@ import { ProductGrid, SearchResultsDidYouMeanLabels, SearchResultsOptions, Searc
3
3
  import { FilterGroup, PublicQuery, SearchQueryResult } from "@getlupa/client-sdk/Types";
4
4
  import Vue from "vue";
5
5
  import { AnalyticsEventType } from "@/types/AnalyticsOptions";
6
+ import { TrackableEventData } from "@/types/search-box/Common";
6
7
  export default class SearchResults extends Vue {
7
8
  options: SearchResultsOptions;
8
9
  initialFilters: FilterGroup;
@@ -24,6 +25,10 @@ export default class SearchResults extends Vue {
24
25
  queryKey: string;
25
26
  results: SearchQueryResult;
26
27
  }) => void;
28
+ trackEvent: ({ queryKey, data, }: {
29
+ queryKey: string;
30
+ data: TrackableEventData;
31
+ }) => void;
27
32
  enhanceData: ({ result, }: {
28
33
  result: SearchQueryResult;
29
34
  }) => Promise<void>;
@@ -53,6 +58,7 @@ export default class SearchResults extends Vue {
53
58
  }) => void;
54
59
  mounted(): void;
55
60
  beforeDestroy(): void;
61
+ trackItemListView(title: string, items?: Record<string, unknown>[]): void;
56
62
  handleMounted(): void;
57
63
  handleParamsChange(): void;
58
64
  addSearchResult: (searchResult: SearchQueryResult) => SearchQueryResult;
@@ -1,4 +1,5 @@
1
1
  import { DocumentElement } from "@/types/DocumentElement";
2
+ import { TrackingOptions } from "@/types/General";
2
3
  import { TrackableEventData } from "@/types/search-box/Common";
3
4
  import { BadgeOptions } from "@/types/search-results/BadgeOptions";
4
5
  import { ResultsLayout } from "@/types/search-results/ResultsLayout";
@@ -15,6 +16,7 @@ export default class SearchResultsProductCard extends Vue {
15
16
  layout: ResultsLayout;
16
17
  searchResultsRoutingBehavior: RoutingBehavior;
17
18
  searchResultOptions: SearchResultsOptions;
19
+ trackingOptions: TrackingOptions;
18
20
  query: string;
19
21
  trackClick: ({ queryKey, data, }: {
20
22
  queryKey: string;
@@ -12429,6 +12429,7 @@ let SearchBoxProduct = class SearchBoxProduct extends Vue$1 {
12429
12429
  analytics: {
12430
12430
  type: "autocomplete_product_click",
12431
12431
  label: (_a = this.title) !== null && _a !== void 0 ? _a : this.link,
12432
+ items: [this.item],
12432
12433
  },
12433
12434
  },
12434
12435
  });
@@ -31119,6 +31120,7 @@ let SearchBox = class SearchBox extends Vue$1 {
31119
31120
  analytics: {
31120
31121
  type: "autocomplete_product_click",
31121
31122
  label: doc.title || doc.id,
31123
+ items: [doc],
31122
31124
  },
31123
31125
  },
31124
31126
  });
@@ -31257,7 +31259,7 @@ __vue_render__$V._withStripped = true;
31257
31259
  /* style */
31258
31260
  const __vue_inject_styles__$V = function (inject) {
31259
31261
  if (!inject) return
31260
- inject("data-v-39831122_0", { source: "\n#lupa-search-box {\n width: 100%;\n}\n.lupa-search-box-wrapper {\n position: relative;\n}\n", map: undefined, media: undefined });
31262
+ inject("data-v-1d0dc04c_0", { source: "\n#lupa-search-box {\n width: 100%;\n}\n.lupa-search-box-wrapper {\n position: relative;\n}\n", map: undefined, media: undefined });
31261
31263
 
31262
31264
  };
31263
31265
  /* scoped */
@@ -35682,9 +35684,11 @@ let SearchResultsProductCard = class SearchResultsProductCard extends Vue$1 {
35682
35684
  searchQuery: this.query,
35683
35685
  type: "itemClick",
35684
35686
  analytics: {
35685
- type: "search_product_click",
35687
+ type: this.query ? "search_product_click" : "select_item",
35686
35688
  label: this.title || this.id || this.link,
35689
+ items: [this.product],
35687
35690
  },
35691
+ options: { allowEmptySearchQuery: true },
35688
35692
  },
35689
35693
  });
35690
35694
  (_b = (_a = this.searchResultOptions.callbacks) === null || _a === void 0 ? void 0 : _a.onProductClick) === null || _b === void 0 ? void 0 : _b.call(_a, {
@@ -35730,6 +35734,9 @@ __decorate([
35730
35734
  __decorate([
35731
35735
  options$7.State((o) => o.searchResultOptions)
35732
35736
  ], SearchResultsProductCard.prototype, "searchResultOptions", void 0);
35737
+ __decorate([
35738
+ options$7.State((o) => o.trackingOptions)
35739
+ ], SearchResultsProductCard.prototype, "trackingOptions", void 0);
35733
35740
  __decorate([
35734
35741
  params$a.Getter("query")
35735
35742
  ], SearchResultsProductCard.prototype, "query", void 0);
@@ -36066,6 +36073,15 @@ const trackAnalyticsEvent = (data) => {
36066
36073
  console.error("Unable to send an event to google analytics");
36067
36074
  }
36068
36075
  };
36076
+ const parseEcommerceData = (data, title) => {
36077
+ var _a, _b;
36078
+ return ((_a = data.analytics) === null || _a === void 0 ? void 0 : _a.items)
36079
+ ? {
36080
+ item_list_name: title,
36081
+ items: (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.items,
36082
+ }
36083
+ : undefined;
36084
+ };
36069
36085
  const sendUaAnalyticsEvent = (data, options) => {
36070
36086
  var _a, _b, _c, _d;
36071
36087
  const ga = window.ga;
@@ -36076,34 +36092,47 @@ const sendUaAnalyticsEvent = (data, options) => {
36076
36092
  sendGa("send", "event", options.parentEventName, (_b = (_a = data.analytics) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "", (_d = (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : "");
36077
36093
  };
36078
36094
  const sendGa4AnalyticsEvent = (data, options) => {
36079
- var _a, _b, _c, _d;
36095
+ var _a, _b, _c, _d, _e;
36080
36096
  if (!window || !window.dataLayer) {
36081
36097
  console.error("dataLayer object not found.");
36082
36098
  return;
36083
36099
  }
36084
36100
  const sendItemTitle = data.searchQuery !== ((_a = data.analytics) === null || _a === void 0 ? void 0 : _a.label);
36101
+ const title = sendItemTitle ? (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.label : undefined;
36085
36102
  const params = {
36086
36103
  search_text: data.searchQuery,
36087
- item_title: sendItemTitle ? (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.label : undefined,
36104
+ item_title: sendItemTitle ? (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.label : undefined,
36105
+ ecommerce: parseEcommerceData(data, title),
36088
36106
  };
36089
- window.dataLayer.push(Object.assign({ event: (_d = (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : options.parentEventName }, params));
36107
+ window.dataLayer.push(Object.assign({ event: (_e = (_d = data.analytics) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : options.parentEventName }, params));
36090
36108
  };
36091
36109
  const processDebugEvent = (data) => {
36092
36110
  var _a, _b, _c;
36093
36111
  const sendItemTitle = data.searchQuery !== ((_a = data.analytics) === null || _a === void 0 ? void 0 : _a.label);
36112
+ const title = sendItemTitle ? (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.label : undefined;
36094
36113
  const params = {
36095
- event: (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.type,
36114
+ event: (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.type,
36096
36115
  search_text: data.searchQuery,
36097
- item_title: sendItemTitle ? (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.label : undefined,
36116
+ item_title: title,
36117
+ ecommerce: parseEcommerceData(data, title),
36098
36118
  };
36099
36119
  console.debug("Analytics debug event:", params);
36100
36120
  };
36101
36121
  const track = (queryKey, data = {}, options) => {
36102
- if (!isTrackingEnabled() || !data.searchQuery) {
36122
+ var _a;
36123
+ if (!isTrackingEnabled()) {
36124
+ return;
36125
+ }
36126
+ const hasSearchQuery = data.searchQuery;
36127
+ if (!hasSearchQuery && !((_a = data.options) === null || _a === void 0 ? void 0 : _a.allowEmptySearchQuery)) {
36103
36128
  return;
36104
36129
  }
36105
- trackLupaEvent(queryKey, data, options);
36106
36130
  trackAnalyticsEvent(data);
36131
+ // Lupa events are only tracked if search query is set
36132
+ if (!hasSearchQuery) {
36133
+ return;
36134
+ }
36135
+ trackLupaEvent(queryKey, data, options);
36107
36136
  };
36108
36137
 
36109
36138
  const params$9 = namespace("params");
@@ -38456,11 +38485,25 @@ let SearchResults = class SearchResults extends Vue$1 {
38456
38485
  beforeDestroy() {
38457
38486
  window.removeEventListener("resize", this.handleResize);
38458
38487
  }
38488
+ trackItemListView(title, items = []) {
38489
+ this.trackEvent({
38490
+ queryKey: this.options.queryKey,
38491
+ data: {
38492
+ analytics: {
38493
+ type: "view_item_list",
38494
+ label: title,
38495
+ items,
38496
+ },
38497
+ options: { allowEmptySearchQuery: true },
38498
+ },
38499
+ });
38500
+ }
38459
38501
  handleMounted() {
38460
38502
  var _a;
38461
38503
  this.handleResize();
38462
38504
  if (this.isProductList) {
38463
- setDocumentTitle(this.options.labels.htmlTitleTemplate, "");
38505
+ const pageTitle = this.options.labels.htmlTitleTemplate;
38506
+ setDocumentTitle(pageTitle, "");
38464
38507
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
38465
38508
  (_a = this.$refs.searchResultsFilters) === null || _a === void 0 ? void 0 : _a.fetch();
38466
38509
  }
@@ -38531,6 +38574,7 @@ let SearchResults = class SearchResults extends Vue$1 {
38531
38574
  if (!hasResults) {
38532
38575
  return;
38533
38576
  }
38577
+ this.trackItemListView(this.options.labels.htmlTitleTemplate, results.items);
38534
38578
  yield this.enhanceData({ result: results });
38535
38579
  });
38536
38580
  }
@@ -38568,6 +38612,9 @@ __decorate([
38568
38612
  __decorate([
38569
38613
  tracking$2.Action("trackResults")
38570
38614
  ], SearchResults.prototype, "trackResults", void 0);
38615
+ __decorate([
38616
+ tracking$2.Action("track")
38617
+ ], SearchResults.prototype, "trackEvent", void 0);
38571
38618
  __decorate([
38572
38619
  dynamicData$1.Action("enhanceSearchResultsWithDynamicData")
38573
38620
  ], SearchResults.prototype, "enhanceData", void 0);
@@ -40368,6 +40415,7 @@ let OptionsModule = class OptionsModule extends VuexModule {
40368
40415
  super(...arguments);
40369
40416
  this.searchBoxOptions = DEFAULT_SEARCH_BOX_OPTIONS;
40370
40417
  this.searchResultOptions = DEFAULT_OPTIONS_RESULTS;
40418
+ this.trackingOptions = {};
40371
40419
  this.searchResultInitialFilters = {};
40372
40420
  }
40373
40421
  get envOptions() {
@@ -40416,6 +40464,9 @@ let OptionsModule = class OptionsModule extends VuexModule {
40416
40464
  setSearchBoxOptions({ options }) {
40417
40465
  this.searchBoxOptions = options;
40418
40466
  }
40467
+ setTrackingOptions({ options }) {
40468
+ this.trackingOptions = options !== null && options !== void 0 ? options : {};
40469
+ }
40419
40470
  setSearchResultOptions({ options }) {
40420
40471
  this.searchResultOptions = options;
40421
40472
  }
@@ -40426,6 +40477,9 @@ let OptionsModule = class OptionsModule extends VuexModule {
40426
40477
  __decorate([
40427
40478
  Mutation
40428
40479
  ], OptionsModule.prototype, "setSearchBoxOptions", null);
40480
+ __decorate([
40481
+ Mutation
40482
+ ], OptionsModule.prototype, "setTrackingOptions", null);
40429
40483
  __decorate([
40430
40484
  Mutation
40431
40485
  ], OptionsModule.prototype, "setSearchResultOptions", null);
@@ -40470,9 +40524,15 @@ let TrackingModule = class TrackingModule extends VuexModule {
40470
40524
  }
40471
40525
  }
40472
40526
  track({ queryKey, data, }) {
40473
- var _a;
40527
+ var _a, _b, _c, _d, _e;
40474
40528
  const options = (_a = this.context.rootGetters["options/envOptions"]) !== null && _a !== void 0 ? _a : {};
40475
- track(queryKey, data, options);
40529
+ const trackingOptions = (_b = this.context.rootState["options"].trackingOptions) !== null && _b !== void 0 ? _b : {};
40530
+ const items = (_d = (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.items) !== null && _d !== void 0 ? _d : [];
40531
+ const mappedItems = ((_e = trackingOptions.analytics) === null || _e === void 0 ? void 0 : _e.itemMap)
40532
+ ? items.map(trackingOptions.analytics.itemMap)
40533
+ : items;
40534
+ track(queryKey, Object.assign(Object.assign({}, data), { analytics: data.analytics
40535
+ ? Object.assign(Object.assign({}, data.analytics), { items: mappedItems }) : undefined }), options);
40476
40536
  }
40477
40537
  };
40478
40538
  __decorate([
@@ -40647,6 +40707,7 @@ const app = {
40647
40707
  };
40648
40708
  const tracking = (options) => {
40649
40709
  initTracking(options);
40710
+ store.commit("options/setTrackingOptions", { options });
40650
40711
  };
40651
40712
  const applySearchBox = (options, mountOptions) => {
40652
40713
  const existingInstance = app.box[options.inputSelector];
@@ -1,3 +1,4 @@
1
+ import { TrackingOptions } from "@/types/General";
1
2
  import { SearchBoxOptions } from "@/types/search-box/SearchBoxOptions";
2
3
  import { RoutingBehavior } from "@/types/search-results/RoutingBehavior";
3
4
  import { SearchResultsOptions } from "@/types/search-results/SearchResultsOptions";
@@ -6,6 +7,7 @@ import { VuexModule } from "vuex-module-decorators";
6
7
  export default class OptionsModule extends VuexModule {
7
8
  searchBoxOptions: SearchBoxOptions;
8
9
  searchResultOptions: SearchResultsOptions;
10
+ trackingOptions: TrackingOptions;
9
11
  searchResultInitialFilters: FilterGroup;
10
12
  get envOptions(): Options;
11
13
  get classMap(): Record<string, string>;
@@ -17,6 +19,9 @@ export default class OptionsModule extends VuexModule {
17
19
  setSearchBoxOptions({ options }: {
18
20
  options: SearchBoxOptions;
19
21
  }): void;
22
+ setTrackingOptions({ options }: {
23
+ options: TrackingOptions;
24
+ }): void;
20
25
  setSearchResultOptions({ options }: {
21
26
  options: SearchResultsOptions;
22
27
  }): void;
@@ -1,8 +1,9 @@
1
1
  export declare const PARENT_EVENT_NAME = "GetLupa";
2
- export declare type AnalyticsEventType = "search_query" | "search_form_submit" | "autocomplete_suggestion_click" | "autocomplete_product_click" | "search_product_click" | "search_zero_results" | "search_filters" | "search_add_to_cart";
2
+ export declare type AnalyticsEventType = "search_query" | "search_form_submit" | "autocomplete_suggestion_click" | "autocomplete_product_click" | "search_product_click" | "search_zero_results" | "search_filters" | "search_add_to_cart" | "view_item_list" | "select_item";
3
3
  export declare type AnalyticsOptions = {
4
4
  type: "ua" | "ga4" | "debug";
5
5
  enabled: boolean;
6
6
  parentEventName: string;
7
7
  ignoreEvents?: AnalyticsEventType[];
8
+ itemMap?: (item: Record<string, unknown>) => Record<string, unknown>;
8
9
  };
@@ -34,6 +34,10 @@ export declare type TrackableEventData = {
34
34
  analytics?: {
35
35
  type: AnalyticsEventType;
36
36
  label: string;
37
+ items?: Record<string, unknown>[];
38
+ };
39
+ options?: {
40
+ allowEmptySearchQuery: boolean;
37
41
  };
38
42
  };
39
43
  export declare type HighlightedDocInfo = {
@@ -3,6 +3,7 @@ import { ProductGrid, SearchResultsDidYouMeanLabels, SearchResultsOptions, Searc
3
3
  import { FilterGroup, PublicQuery, SearchQueryResult } from "@getlupa/client-sdk/Types";
4
4
  import Vue from "vue";
5
5
  import { AnalyticsEventType } from "@/types/AnalyticsOptions";
6
+ import { TrackableEventData } from "@/types/search-box/Common";
6
7
  export default class SearchResults extends Vue {
7
8
  options: SearchResultsOptions;
8
9
  initialFilters: FilterGroup;
@@ -24,6 +25,10 @@ export default class SearchResults extends Vue {
24
25
  queryKey: string;
25
26
  results: SearchQueryResult;
26
27
  }) => void;
28
+ trackEvent: ({ queryKey, data, }: {
29
+ queryKey: string;
30
+ data: TrackableEventData;
31
+ }) => void;
27
32
  enhanceData: ({ result, }: {
28
33
  result: SearchQueryResult;
29
34
  }) => Promise<void>;
@@ -53,6 +58,7 @@ export default class SearchResults extends Vue {
53
58
  }) => void;
54
59
  mounted(): void;
55
60
  beforeDestroy(): void;
61
+ trackItemListView(title: string, items?: Record<string, unknown>[]): void;
56
62
  handleMounted(): void;
57
63
  handleParamsChange(): void;
58
64
  addSearchResult: (searchResult: SearchQueryResult) => SearchQueryResult;
@@ -1,4 +1,5 @@
1
1
  import { DocumentElement } from "@/types/DocumentElement";
2
+ import { TrackingOptions } from "@/types/General";
2
3
  import { TrackableEventData } from "@/types/search-box/Common";
3
4
  import { BadgeOptions } from "@/types/search-results/BadgeOptions";
4
5
  import { ResultsLayout } from "@/types/search-results/ResultsLayout";
@@ -15,6 +16,7 @@ export default class SearchResultsProductCard extends Vue {
15
16
  layout: ResultsLayout;
16
17
  searchResultsRoutingBehavior: RoutingBehavior;
17
18
  searchResultOptions: SearchResultsOptions;
19
+ trackingOptions: TrackingOptions;
18
20
  query: string;
19
21
  trackClick: ({ queryKey, data, }: {
20
22
  queryKey: string;
@@ -12425,6 +12425,7 @@ let SearchBoxProduct = class SearchBoxProduct extends Vue$1 {
12425
12425
  analytics: {
12426
12426
  type: "autocomplete_product_click",
12427
12427
  label: (_a = this.title) !== null && _a !== void 0 ? _a : this.link,
12428
+ items: [this.item],
12428
12429
  },
12429
12430
  },
12430
12431
  });
@@ -31115,6 +31116,7 @@ let SearchBox = class SearchBox extends Vue$1 {
31115
31116
  analytics: {
31116
31117
  type: "autocomplete_product_click",
31117
31118
  label: doc.title || doc.id,
31119
+ items: [doc],
31118
31120
  },
31119
31121
  },
31120
31122
  });
@@ -31253,7 +31255,7 @@ __vue_render__$V._withStripped = true;
31253
31255
  /* style */
31254
31256
  const __vue_inject_styles__$V = function (inject) {
31255
31257
  if (!inject) return
31256
- inject("data-v-39831122_0", { source: "\n#lupa-search-box {\n width: 100%;\n}\n.lupa-search-box-wrapper {\n position: relative;\n}\n", map: undefined, media: undefined });
31258
+ inject("data-v-1d0dc04c_0", { source: "\n#lupa-search-box {\n width: 100%;\n}\n.lupa-search-box-wrapper {\n position: relative;\n}\n", map: undefined, media: undefined });
31257
31259
 
31258
31260
  };
31259
31261
  /* scoped */
@@ -35678,9 +35680,11 @@ let SearchResultsProductCard = class SearchResultsProductCard extends Vue$1 {
35678
35680
  searchQuery: this.query,
35679
35681
  type: "itemClick",
35680
35682
  analytics: {
35681
- type: "search_product_click",
35683
+ type: this.query ? "search_product_click" : "select_item",
35682
35684
  label: this.title || this.id || this.link,
35685
+ items: [this.product],
35683
35686
  },
35687
+ options: { allowEmptySearchQuery: true },
35684
35688
  },
35685
35689
  });
35686
35690
  (_b = (_a = this.searchResultOptions.callbacks) === null || _a === void 0 ? void 0 : _a.onProductClick) === null || _b === void 0 ? void 0 : _b.call(_a, {
@@ -35726,6 +35730,9 @@ __decorate([
35726
35730
  __decorate([
35727
35731
  options$7.State((o) => o.searchResultOptions)
35728
35732
  ], SearchResultsProductCard.prototype, "searchResultOptions", void 0);
35733
+ __decorate([
35734
+ options$7.State((o) => o.trackingOptions)
35735
+ ], SearchResultsProductCard.prototype, "trackingOptions", void 0);
35729
35736
  __decorate([
35730
35737
  params$a.Getter("query")
35731
35738
  ], SearchResultsProductCard.prototype, "query", void 0);
@@ -36062,6 +36069,15 @@ const trackAnalyticsEvent = (data) => {
36062
36069
  console.error("Unable to send an event to google analytics");
36063
36070
  }
36064
36071
  };
36072
+ const parseEcommerceData = (data, title) => {
36073
+ var _a, _b;
36074
+ return ((_a = data.analytics) === null || _a === void 0 ? void 0 : _a.items)
36075
+ ? {
36076
+ item_list_name: title,
36077
+ items: (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.items,
36078
+ }
36079
+ : undefined;
36080
+ };
36065
36081
  const sendUaAnalyticsEvent = (data, options) => {
36066
36082
  var _a, _b, _c, _d;
36067
36083
  const ga = window.ga;
@@ -36072,34 +36088,47 @@ const sendUaAnalyticsEvent = (data, options) => {
36072
36088
  sendGa("send", "event", options.parentEventName, (_b = (_a = data.analytics) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "", (_d = (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : "");
36073
36089
  };
36074
36090
  const sendGa4AnalyticsEvent = (data, options) => {
36075
- var _a, _b, _c, _d;
36091
+ var _a, _b, _c, _d, _e;
36076
36092
  if (!window || !window.dataLayer) {
36077
36093
  console.error("dataLayer object not found.");
36078
36094
  return;
36079
36095
  }
36080
36096
  const sendItemTitle = data.searchQuery !== ((_a = data.analytics) === null || _a === void 0 ? void 0 : _a.label);
36097
+ const title = sendItemTitle ? (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.label : undefined;
36081
36098
  const params = {
36082
36099
  search_text: data.searchQuery,
36083
- item_title: sendItemTitle ? (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.label : undefined,
36100
+ item_title: sendItemTitle ? (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.label : undefined,
36101
+ ecommerce: parseEcommerceData(data, title),
36084
36102
  };
36085
- window.dataLayer.push(Object.assign({ event: (_d = (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : options.parentEventName }, params));
36103
+ window.dataLayer.push(Object.assign({ event: (_e = (_d = data.analytics) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : options.parentEventName }, params));
36086
36104
  };
36087
36105
  const processDebugEvent = (data) => {
36088
36106
  var _a, _b, _c;
36089
36107
  const sendItemTitle = data.searchQuery !== ((_a = data.analytics) === null || _a === void 0 ? void 0 : _a.label);
36108
+ const title = sendItemTitle ? (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.label : undefined;
36090
36109
  const params = {
36091
- event: (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.type,
36110
+ event: (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.type,
36092
36111
  search_text: data.searchQuery,
36093
- item_title: sendItemTitle ? (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.label : undefined,
36112
+ item_title: title,
36113
+ ecommerce: parseEcommerceData(data, title),
36094
36114
  };
36095
36115
  console.debug("Analytics debug event:", params);
36096
36116
  };
36097
36117
  const track = (queryKey, data = {}, options) => {
36098
- if (!isTrackingEnabled() || !data.searchQuery) {
36118
+ var _a;
36119
+ if (!isTrackingEnabled()) {
36120
+ return;
36121
+ }
36122
+ const hasSearchQuery = data.searchQuery;
36123
+ if (!hasSearchQuery && !((_a = data.options) === null || _a === void 0 ? void 0 : _a.allowEmptySearchQuery)) {
36099
36124
  return;
36100
36125
  }
36101
- trackLupaEvent(queryKey, data, options);
36102
36126
  trackAnalyticsEvent(data);
36127
+ // Lupa events are only tracked if search query is set
36128
+ if (!hasSearchQuery) {
36129
+ return;
36130
+ }
36131
+ trackLupaEvent(queryKey, data, options);
36103
36132
  };
36104
36133
 
36105
36134
  const params$9 = namespace("params");
@@ -38452,11 +38481,25 @@ let SearchResults = class SearchResults extends Vue$1 {
38452
38481
  beforeDestroy() {
38453
38482
  window.removeEventListener("resize", this.handleResize);
38454
38483
  }
38484
+ trackItemListView(title, items = []) {
38485
+ this.trackEvent({
38486
+ queryKey: this.options.queryKey,
38487
+ data: {
38488
+ analytics: {
38489
+ type: "view_item_list",
38490
+ label: title,
38491
+ items,
38492
+ },
38493
+ options: { allowEmptySearchQuery: true },
38494
+ },
38495
+ });
38496
+ }
38455
38497
  handleMounted() {
38456
38498
  var _a;
38457
38499
  this.handleResize();
38458
38500
  if (this.isProductList) {
38459
- setDocumentTitle(this.options.labels.htmlTitleTemplate, "");
38501
+ const pageTitle = this.options.labels.htmlTitleTemplate;
38502
+ setDocumentTitle(pageTitle, "");
38460
38503
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
38461
38504
  (_a = this.$refs.searchResultsFilters) === null || _a === void 0 ? void 0 : _a.fetch();
38462
38505
  }
@@ -38527,6 +38570,7 @@ let SearchResults = class SearchResults extends Vue$1 {
38527
38570
  if (!hasResults) {
38528
38571
  return;
38529
38572
  }
38573
+ this.trackItemListView(this.options.labels.htmlTitleTemplate, results.items);
38530
38574
  yield this.enhanceData({ result: results });
38531
38575
  });
38532
38576
  }
@@ -38564,6 +38608,9 @@ __decorate([
38564
38608
  __decorate([
38565
38609
  tracking$2.Action("trackResults")
38566
38610
  ], SearchResults.prototype, "trackResults", void 0);
38611
+ __decorate([
38612
+ tracking$2.Action("track")
38613
+ ], SearchResults.prototype, "trackEvent", void 0);
38567
38614
  __decorate([
38568
38615
  dynamicData$1.Action("enhanceSearchResultsWithDynamicData")
38569
38616
  ], SearchResults.prototype, "enhanceData", void 0);
@@ -40364,6 +40411,7 @@ let OptionsModule = class OptionsModule extends VuexModule {
40364
40411
  super(...arguments);
40365
40412
  this.searchBoxOptions = DEFAULT_SEARCH_BOX_OPTIONS;
40366
40413
  this.searchResultOptions = DEFAULT_OPTIONS_RESULTS;
40414
+ this.trackingOptions = {};
40367
40415
  this.searchResultInitialFilters = {};
40368
40416
  }
40369
40417
  get envOptions() {
@@ -40412,6 +40460,9 @@ let OptionsModule = class OptionsModule extends VuexModule {
40412
40460
  setSearchBoxOptions({ options }) {
40413
40461
  this.searchBoxOptions = options;
40414
40462
  }
40463
+ setTrackingOptions({ options }) {
40464
+ this.trackingOptions = options !== null && options !== void 0 ? options : {};
40465
+ }
40415
40466
  setSearchResultOptions({ options }) {
40416
40467
  this.searchResultOptions = options;
40417
40468
  }
@@ -40422,6 +40473,9 @@ let OptionsModule = class OptionsModule extends VuexModule {
40422
40473
  __decorate([
40423
40474
  Mutation
40424
40475
  ], OptionsModule.prototype, "setSearchBoxOptions", null);
40476
+ __decorate([
40477
+ Mutation
40478
+ ], OptionsModule.prototype, "setTrackingOptions", null);
40425
40479
  __decorate([
40426
40480
  Mutation
40427
40481
  ], OptionsModule.prototype, "setSearchResultOptions", null);
@@ -40466,9 +40520,15 @@ let TrackingModule = class TrackingModule extends VuexModule {
40466
40520
  }
40467
40521
  }
40468
40522
  track({ queryKey, data, }) {
40469
- var _a;
40523
+ var _a, _b, _c, _d, _e;
40470
40524
  const options = (_a = this.context.rootGetters["options/envOptions"]) !== null && _a !== void 0 ? _a : {};
40471
- track(queryKey, data, options);
40525
+ const trackingOptions = (_b = this.context.rootState["options"].trackingOptions) !== null && _b !== void 0 ? _b : {};
40526
+ const items = (_d = (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.items) !== null && _d !== void 0 ? _d : [];
40527
+ const mappedItems = ((_e = trackingOptions.analytics) === null || _e === void 0 ? void 0 : _e.itemMap)
40528
+ ? items.map(trackingOptions.analytics.itemMap)
40529
+ : items;
40530
+ track(queryKey, Object.assign(Object.assign({}, data), { analytics: data.analytics
40531
+ ? Object.assign(Object.assign({}, data.analytics), { items: mappedItems }) : undefined }), options);
40472
40532
  }
40473
40533
  };
40474
40534
  __decorate([
@@ -40643,6 +40703,7 @@ const app = {
40643
40703
  };
40644
40704
  const tracking = (options) => {
40645
40705
  initTracking(options);
40706
+ store.commit("options/setTrackingOptions", { options });
40646
40707
  };
40647
40708
  const applySearchBox = (options, mountOptions) => {
40648
40709
  const existingInstance = app.box[options.inputSelector];
@@ -1,3 +1,4 @@
1
+ import { TrackingOptions } from "@/types/General";
1
2
  import { SearchBoxOptions } from "@/types/search-box/SearchBoxOptions";
2
3
  import { RoutingBehavior } from "@/types/search-results/RoutingBehavior";
3
4
  import { SearchResultsOptions } from "@/types/search-results/SearchResultsOptions";
@@ -6,6 +7,7 @@ import { VuexModule } from "vuex-module-decorators";
6
7
  export default class OptionsModule extends VuexModule {
7
8
  searchBoxOptions: SearchBoxOptions;
8
9
  searchResultOptions: SearchResultsOptions;
10
+ trackingOptions: TrackingOptions;
9
11
  searchResultInitialFilters: FilterGroup;
10
12
  get envOptions(): Options;
11
13
  get classMap(): Record<string, string>;
@@ -17,6 +19,9 @@ export default class OptionsModule extends VuexModule {
17
19
  setSearchBoxOptions({ options }: {
18
20
  options: SearchBoxOptions;
19
21
  }): void;
22
+ setTrackingOptions({ options }: {
23
+ options: TrackingOptions;
24
+ }): void;
20
25
  setSearchResultOptions({ options }: {
21
26
  options: SearchResultsOptions;
22
27
  }): void;
@@ -1,8 +1,9 @@
1
1
  export declare const PARENT_EVENT_NAME = "GetLupa";
2
- export declare type AnalyticsEventType = "search_query" | "search_form_submit" | "autocomplete_suggestion_click" | "autocomplete_product_click" | "search_product_click" | "search_zero_results" | "search_filters" | "search_add_to_cart";
2
+ export declare type AnalyticsEventType = "search_query" | "search_form_submit" | "autocomplete_suggestion_click" | "autocomplete_product_click" | "search_product_click" | "search_zero_results" | "search_filters" | "search_add_to_cart" | "view_item_list" | "select_item";
3
3
  export declare type AnalyticsOptions = {
4
4
  type: "ua" | "ga4" | "debug";
5
5
  enabled: boolean;
6
6
  parentEventName: string;
7
7
  ignoreEvents?: AnalyticsEventType[];
8
+ itemMap?: (item: Record<string, unknown>) => Record<string, unknown>;
8
9
  };
@@ -34,6 +34,10 @@ export declare type TrackableEventData = {
34
34
  analytics?: {
35
35
  type: AnalyticsEventType;
36
36
  label: string;
37
+ items?: Record<string, unknown>[];
38
+ };
39
+ options?: {
40
+ allowEmptySearchQuery: boolean;
37
41
  };
38
42
  };
39
43
  export declare type HighlightedDocInfo = {
@@ -3,6 +3,7 @@ import { ProductGrid, SearchResultsDidYouMeanLabels, SearchResultsOptions, Searc
3
3
  import { FilterGroup, PublicQuery, SearchQueryResult } from "@getlupa/client-sdk/Types";
4
4
  import Vue from "vue";
5
5
  import { AnalyticsEventType } from "@/types/AnalyticsOptions";
6
+ import { TrackableEventData } from "@/types/search-box/Common";
6
7
  export default class SearchResults extends Vue {
7
8
  options: SearchResultsOptions;
8
9
  initialFilters: FilterGroup;
@@ -24,6 +25,10 @@ export default class SearchResults extends Vue {
24
25
  queryKey: string;
25
26
  results: SearchQueryResult;
26
27
  }) => void;
28
+ trackEvent: ({ queryKey, data, }: {
29
+ queryKey: string;
30
+ data: TrackableEventData;
31
+ }) => void;
27
32
  enhanceData: ({ result, }: {
28
33
  result: SearchQueryResult;
29
34
  }) => Promise<void>;
@@ -53,6 +58,7 @@ export default class SearchResults extends Vue {
53
58
  }) => void;
54
59
  mounted(): void;
55
60
  beforeDestroy(): void;
61
+ trackItemListView(title: string, items?: Record<string, unknown>[]): void;
56
62
  handleMounted(): void;
57
63
  handleParamsChange(): void;
58
64
  addSearchResult: (searchResult: SearchQueryResult) => SearchQueryResult;
@@ -1,4 +1,5 @@
1
1
  import { DocumentElement } from "@/types/DocumentElement";
2
+ import { TrackingOptions } from "@/types/General";
2
3
  import { TrackableEventData } from "@/types/search-box/Common";
3
4
  import { BadgeOptions } from "@/types/search-results/BadgeOptions";
4
5
  import { ResultsLayout } from "@/types/search-results/ResultsLayout";
@@ -15,6 +16,7 @@ export default class SearchResultsProductCard extends Vue {
15
16
  layout: ResultsLayout;
16
17
  searchResultsRoutingBehavior: RoutingBehavior;
17
18
  searchResultOptions: SearchResultsOptions;
19
+ trackingOptions: TrackingOptions;
18
20
  query: string;
19
21
  trackClick: ({ queryKey, data, }: {
20
22
  queryKey: string;