@searchspring/snap-controller 0.63.5 → 0.65.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.
@@ -37,12 +37,62 @@ export class AutocompleteController extends AbstractController {
37
37
  constructor(config, { client, store, urlManager, eventManager, profiler, logger, tracker }, context) {
38
38
  super(config, { client, store, urlManager, eventManager, profiler, logger, tracker }, context);
39
39
  this.type = ControllerTypes.autocomplete;
40
+ this.events = {
41
+ product: {},
42
+ };
40
43
  this.track = {
41
- // TODO: add in future when autocomplete supports result click tracking
42
44
  product: {
43
- click: () => {
44
- this.log.warn('product.click tracking is not currently supported in this controller type');
45
+ clickThrough: (e, result) => {
46
+ if (this.events.product[result.id]?.clickThrough) {
47
+ return;
48
+ }
49
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
50
+ this.tracker.events.autocomplete.clickThrough({ data, siteId: this.config.globals?.siteId });
51
+ this.events.product[result.id] = this.events.product[result.id] || {};
52
+ this.events.product[result.id].clickThrough = true;
53
+ this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, products: [result], trackEvent: data });
54
+ },
55
+ click: (e, result) => {
56
+ if (result.type === 'banner') {
57
+ return;
58
+ }
59
+ // TODO: closest might be going too far - write own function to only go n levels up - additionally check that href includes result.url
60
+ const href = e.target?.getAttribute('href') || e.target?.closest('a')?.getAttribute('href');
61
+ if (href) {
62
+ this.track.product.clickThrough(e, result);
63
+ }
64
+ else {
65
+ // TODO: in future, send as an interaction event
66
+ }
67
+ },
68
+ render: (result) => {
69
+ if (this.events.product[result.id]?.render)
70
+ return;
71
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
72
+ this.tracker.events.autocomplete.render({ data, siteId: this.config.globals?.siteId });
73
+ this.events.product[result.id] = this.events.product[result.id] || {};
74
+ this.events.product[result.id].render = true;
75
+ this.eventManager.fire('track.product.render', { controller: this, products: [result], trackEvent: data });
76
+ },
77
+ impression: (result) => {
78
+ if (this.events.product[result.id]?.impression)
79
+ return;
80
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
81
+ this.tracker.events.autocomplete.impression({ data, siteId: this.config.globals?.siteId });
82
+ this.events.product[result.id] = this.events.product[result.id] || {};
83
+ this.events.product[result.id].impression = true;
84
+ this.eventManager.fire('track.product.impression', { controller: this, products: [result], trackEvent: data });
45
85
  },
86
+ addToCart: (result) => {
87
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
88
+ this.tracker.events.autocomplete.addToCart({ data, siteId: this.config.globals?.siteId });
89
+ this.eventManager.fire('track.product.addToCart', { controller: this, products: [result], trackEvent: data });
90
+ },
91
+ },
92
+ redirect: (redirectURL) => {
93
+ const data = getAutocompleteRedirectSchemaData({ redirectURL });
94
+ this.tracker.events.autocomplete.redirect({ data, siteId: this.config.globals?.siteId });
95
+ this.eventManager.fire('track.product.redirect', { controller: this, redirectURL, trackEvent: data });
46
96
  },
47
97
  };
48
98
  this.handlers = {
@@ -257,6 +307,8 @@ export class AutocompleteController extends AbstractController {
257
307
  if (!this.initialized) {
258
308
  await this.init();
259
309
  }
310
+ // reset events for new search
311
+ this.events = { product: {} };
260
312
  // if urlManager has no query, there will be no need to get params and no query
261
313
  if (!this.urlManager.state.query) {
262
314
  return;
@@ -382,6 +434,10 @@ export class AutocompleteController extends AbstractController {
382
434
  this.store.loading = false;
383
435
  }
384
436
  };
437
+ this.addToCart = async (product) => {
438
+ this.track.product.addToCart(product);
439
+ this.eventManager.fire('addToCart', { controller: this, products: [product] });
440
+ };
385
441
  // deep merge config with defaults
386
442
  this.config = deepmerge(defaultConfig, this.config);
387
443
  this.store.setConfig(this.config);
@@ -409,6 +465,7 @@ export class AutocompleteController extends AbstractController {
409
465
  await next();
410
466
  const redirectURL = ac.controller.store.merchandising?.redirect;
411
467
  if (redirectURL && this.config?.settings?.redirects?.merchandising) {
468
+ this.track.redirect(redirectURL);
412
469
  window.location.href = redirectURL;
413
470
  return false;
414
471
  }
@@ -429,9 +486,7 @@ export class AutocompleteController extends AbstractController {
429
486
  get params() {
430
487
  const urlState = this.urlManager.state;
431
488
  const params = deepmerge({ ...getSearchParams(urlState) }, this.config.globals);
432
- const userId = this.tracker.getUserId();
433
- const sessionId = this.tracker.getContext().sessionId;
434
- const pageLoadId = this.tracker.getContext().pageLoadId;
489
+ const { userId, sessionId, pageLoadId, shopperId } = this.tracker.getContext();
435
490
  params.tracking = params.tracking || {};
436
491
  params.tracking.domain = window.location.href;
437
492
  if (userId) {
@@ -454,7 +509,6 @@ export class AutocompleteController extends AbstractController {
454
509
  params.personalization = params.personalization || {};
455
510
  params.personalization.lastViewed = lastViewedItems.join(',');
456
511
  }
457
- const shopperId = this.tracker.getShopperId();
458
512
  if (shopperId) {
459
513
  params.personalization = params.personalization || {};
460
514
  params.personalization.shopper = shopperId;
@@ -633,3 +687,69 @@ function unbindFormParameters(form, fn) {
633
687
  }
634
688
  }
635
689
  }
690
+ function getAutocompleteRedirectSchemaData({ redirectURL }) {
691
+ return {
692
+ redirect: redirectURL,
693
+ };
694
+ }
695
+ function getAutocompleteSchemaData({ params, store, results, }) {
696
+ const filters = params.filters?.reduce((acc, filter) => {
697
+ const key = filter.background ? 'bgfilter' : 'filter';
698
+ acc[key] = acc[key] || [];
699
+ const value = filter.type === 'range' &&
700
+ !isNaN(filter.value?.low) &&
701
+ !isNaN(filter.value?.low)
702
+ ? [`low=${filter.value?.low}`, `high=${filter.value?.high}`]
703
+ : [`${filter.value}`];
704
+ const existing = acc[key].find((item) => item.field === filter.field);
705
+ if (existing && !existing.value.includes(value[0])) {
706
+ existing.value.push(...value);
707
+ }
708
+ else {
709
+ acc[key].push({
710
+ field: filter.field,
711
+ value,
712
+ });
713
+ }
714
+ return acc;
715
+ }, {});
716
+ return {
717
+ q: params.search?.query?.string || '',
718
+ correctedQuery: params.search?.originalQuery || '',
719
+ ...filters,
720
+ sort: [
721
+ {
722
+ field: store.sorting.current?.field,
723
+ dir: store.sorting.current?.direction,
724
+ },
725
+ ],
726
+ pagination: {
727
+ totalResults: store.pagination.totalResults,
728
+ page: store.pagination.page,
729
+ resultsPerPage: store.pagination.pageSize,
730
+ },
731
+ merchandising: {
732
+ personalized: store.merchandising.personalized,
733
+ redirect: store.merchandising.redirect,
734
+ triggeredCampaigns: (store.merchandising.campaigns?.length &&
735
+ store.merchandising.campaigns?.map((campaign) => {
736
+ const experiement = store.merchandising.experiments.find((experiment) => experiment.campaignId === campaign.id);
737
+ return {
738
+ id: campaign.id,
739
+ experimentId: experiement?.experimentId,
740
+ variationId: experiement?.variationId,
741
+ };
742
+ })) ||
743
+ undefined,
744
+ },
745
+ results: results?.map((result) => {
746
+ const core = result.mappings.core;
747
+ return {
748
+ uid: core.uid || '',
749
+ // childUid: core.uid,
750
+ sku: core.sku,
751
+ // childSku: core.sku,
752
+ };
753
+ }) || [],
754
+ };
755
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"FinderController.d.ts","sourceRoot":"","sources":["../../../src/Finder/FinderController.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,KAAK,EAAE,sBAAsB,EAAkB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAiB7G,qBAAa,gBAAiB,SAAQ,kBAAkB;IAChD,IAAI,kBAA0B;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,sBAAsB,CAAC;gBAGtC,MAAM,EAAE,sBAAsB,EAC9B,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA+B3B,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA+BhC;IAED,IAAI,QAAa,QAAQ,IAAI,CAAC,CAkB5B;IAEF,KAAK,QAAO,IAAI,CAId;IAEF,MAAM,QAAa,QAAQ,IAAI,CAAC,CAoI9B;CACF"}
1
+ {"version":3,"file":"FinderController.d.ts","sourceRoot":"","sources":["../../../src/Finder/FinderController.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,KAAK,EAAE,sBAAsB,EAAkB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAiB7G,qBAAa,gBAAiB,SAAQ,kBAAkB;IAChD,IAAI,kBAA0B;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,sBAAsB,CAAC;gBAGtC,MAAM,EAAE,sBAAsB,EAC9B,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA+B3B,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA6BhC;IAED,IAAI,QAAa,QAAQ,IAAI,CAAC,CAkB5B;IAEF,KAAK,QAAO,IAAI,CAId;IAEF,MAAM,QAAa,QAAQ,IAAI,CAAC,CAoI9B;CACF"}
@@ -194,9 +194,7 @@ export class FinderController extends AbstractController {
194
194
  }
195
195
  get params() {
196
196
  const urlState = this.urlManager.state;
197
- const userId = this.tracker.getUserId();
198
- const sessionId = this.tracker.getContext().sessionId;
199
- const pageLoadId = this.tracker.getContext().pageLoadId;
197
+ const { userId, sessionId, pageLoadId } = this.tracker.getContext();
200
198
  const tracking = {};
201
199
  if (userId) {
202
200
  tracking.userId = userId;
@@ -1,5 +1,4 @@
1
1
  import { Product } from '@searchspring/snap-store-mobx';
2
- import { BeaconEvent } from '@searchspring/snap-tracker';
3
2
  import { AbstractController } from '../Abstract/AbstractController';
4
3
  import { ControllerTypes } from '../types';
5
4
  import type { RecommendationStore } from '@searchspring/snap-store-mobx';
@@ -7,28 +6,25 @@ import type { RecommendRequestModel } from '@searchspring/snap-client';
7
6
  import type { RecommendationControllerConfig, ControllerServices, ContextVariables } from '../types';
8
7
  type RecommendationTrackMethods = {
9
8
  product: {
10
- click: (e: MouseEvent, result: Product) => BeaconEvent | undefined;
11
- render: (result: Product) => BeaconEvent | undefined;
12
- impression: (result: Product) => BeaconEvent | undefined;
13
- removedFromBundle: (result: Product) => BeaconEvent | undefined;
14
- addedToBundle: (result: Product) => BeaconEvent | undefined;
9
+ clickThrough: (e: MouseEvent, result: Product) => void;
10
+ click: (e: MouseEvent, result: Product) => void;
11
+ render: (result: Product) => void;
12
+ impression: (result: Product) => void;
13
+ addToCart: (result: Product) => void;
14
+ };
15
+ bundle: {
16
+ addToCart: (results: Product[]) => void;
15
17
  };
16
- click: (e: MouseEvent) => BeaconEvent | undefined;
17
- addBundle: (e: MouseEvent, results: Product[]) => BeaconEvent | undefined;
18
- impression: () => BeaconEvent | undefined;
19
- render: (results?: Product[]) => BeaconEvent | undefined;
20
18
  };
21
19
  export declare class RecommendationController extends AbstractController {
22
20
  type: ControllerTypes;
23
21
  store: RecommendationStore;
24
22
  config: RecommendationControllerConfig;
25
23
  events: {
26
- click?: BeaconEvent;
27
- impression?: BeaconEvent;
28
- render?: BeaconEvent;
29
- product?: Record<string, {
30
- impression?: BeaconEvent;
31
- render?: BeaconEvent;
24
+ product: Record<string, {
25
+ clickThrough?: boolean;
26
+ impression?: boolean;
27
+ render?: boolean;
32
28
  }>;
33
29
  };
34
30
  constructor(config: RecommendationControllerConfig, { client, store, urlManager, eventManager, profiler, logger, tracker }: ControllerServices, context?: ContextVariables);
@@ -1 +1 @@
1
- {"version":3,"file":"RecommendationController.d.ts","sourceRoot":"","sources":["../../../src/Recommendation/RecommendationController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,8BAA8B,EAAiB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEpH,KAAK,0BAA0B,GAAG;IACjC,OAAO,EAAE;QACR,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;QACnE,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;QACrD,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;QACzD,iBAAiB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;QAChE,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,CAAC;KAC5D,CAAC;IACF,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,WAAW,GAAG,SAAS,CAAC;IAClD,SAAS,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,GAAG,SAAS,CAAC;IAC1E,UAAU,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;IAC1C,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,WAAW,GAAG,SAAS,CAAC;CACzD,CAAC;AAUF,qBAAa,wBAAyB,SAAQ,kBAAkB;IACxD,IAAI,kBAAkC;IACrC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,8BAA8B,CAAC;IAE/C,MAAM,EAAE;QACP,KAAK,CAAC,EAAE,WAAW,CAAC;QACpB,UAAU,CAAC,EAAE,WAAW,CAAC;QACzB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,UAAU,CAAC,EAAE,WAAW,CAAC;YAAC,MAAM,CAAC,EAAE,WAAW,CAAA;SAAE,CAAC,CAAC;KAC7E,CAKC;gBAGD,MAAM,EAAE,8BAA8B,EACtC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA4C3B,KAAK,EAAE,0BAA0B,CAwS5B;IAEL,IAAI,MAAM,IAAI,qBAAqB,CA2BlC;IAED,MAAM,QAAa,QAAQ,IAAI,CAAC,CA0H9B;CACF"}
1
+ {"version":3,"file":"RecommendationController.d.ts","sourceRoot":"","sources":["../../../src/Recommendation/RecommendationController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,8BAA8B,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAIrG,KAAK,0BAA0B,GAAG;IACjC,OAAO,EAAE;QACR,YAAY,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QACvD,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QAChD,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QAClC,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QACtC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;KACrC,CAAC;IACF,MAAM,EAAE;QACP,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;KACxC,CAAC;CACF,CAAC;AAUF,qBAAa,wBAAyB,SAAQ,kBAAkB;IACxD,IAAI,kBAAkC;IACrC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,8BAA8B,CAAC;IAE/C,MAAM,EAAE;QACP,OAAO,EAAE,MAAM,CACd,MAAM,EACN;YACC,YAAY,CAAC,EAAE,OAAO,CAAC;YACvB,UAAU,CAAC,EAAE,OAAO,CAAC;YACrB,MAAM,CAAC,EAAE,OAAO,CAAC;SACjB,CACD,CAAC;KACF,CAEC;gBAGD,MAAM,EAAE,8BAA8B,EACtC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IAwC3B,KAAK,EAAE,0BAA0B,CAyD/B;IAEF,IAAI,MAAM,IAAI,qBAAqB,CA4BlC;IAED,MAAM,QAAa,QAAQ,IAAI,CAAC,CA6H9B;CACF"}