cloudcommerce 0.33.1 → 0.33.3

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/ecomplus-stores/barra-doce/functions/many/package.json +3 -3
  3. package/ecomplus-stores/barra-doce/functions/ssr/package.json +6 -6
  4. package/ecomplus-stores/barra-doce/functions/with-apps/package.json +3 -3
  5. package/ecomplus-stores/barra-doce/package.json +2 -2
  6. package/ecomplus-stores/monocard/functions/many/package.json +3 -3
  7. package/ecomplus-stores/monocard/functions/ssr/package.json +6 -6
  8. package/ecomplus-stores/monocard/functions/with-apps/package.json +3 -3
  9. package/ecomplus-stores/monocard/package.json +2 -2
  10. package/package.json +6 -6
  11. package/packages/api/package.json +1 -1
  12. package/packages/apps/affilate-program/package.json +1 -1
  13. package/packages/apps/correios/package.json +2 -2
  14. package/packages/apps/custom-payment/package.json +1 -1
  15. package/packages/apps/custom-shipping/package.json +1 -1
  16. package/packages/apps/datafrete/package.json +2 -2
  17. package/packages/apps/discounts/package.json +1 -1
  18. package/packages/apps/emails/package.json +1 -1
  19. package/packages/apps/fb-conversions/package.json +1 -1
  20. package/packages/apps/flash-courier/package.json +2 -2
  21. package/packages/apps/frenet/package.json +2 -2
  22. package/packages/apps/galaxpay/package.json +2 -2
  23. package/packages/apps/google-analytics/package.json +2 -2
  24. package/packages/apps/jadlog/package.json +2 -2
  25. package/packages/apps/loyalty-points/package.json +1 -1
  26. package/packages/apps/mandae/package.json +2 -2
  27. package/packages/apps/melhor-envio/package.json +2 -2
  28. package/packages/apps/mercadopago/package.json +2 -2
  29. package/packages/apps/pagarme/package.json +2 -2
  30. package/packages/apps/pagarme-v5/package.json +2 -2
  31. package/packages/apps/paghiper/package.json +2 -2
  32. package/packages/apps/pix/package.json +2 -2
  33. package/packages/apps/tiny-erp/package.json +2 -2
  34. package/packages/apps/webhooks/package.json +2 -2
  35. package/packages/cli/package.json +1 -1
  36. package/packages/config/package.json +1 -1
  37. package/packages/emails/package.json +2 -2
  38. package/packages/eslint/package.json +4 -4
  39. package/packages/events/package.json +1 -1
  40. package/packages/feeds/package.json +1 -1
  41. package/packages/firebase/package.json +1 -1
  42. package/packages/i18n/package.json +1 -1
  43. package/packages/modules/package.json +2 -2
  44. package/packages/passport/package.json +1 -1
  45. package/packages/ssr/lib/analytics-events.js +26 -5
  46. package/packages/ssr/lib/analytics-events.js.map +1 -1
  47. package/packages/ssr/lib/firebase/serve-storefront.js +16 -12
  48. package/packages/ssr/lib/firebase/serve-storefront.js.map +1 -1
  49. package/packages/ssr/package.json +2 -1
  50. package/packages/ssr/src/analytics-events.ts +29 -5
  51. package/packages/ssr/src/firebase/serve-storefront.ts +17 -12
  52. package/packages/storefront/package.json +6 -5
  53. package/packages/storefront/src/analytics/event-to-fbq.ts +77 -10
  54. package/packages/storefront/src/analytics/event-to-ttq.ts +15 -0
  55. package/packages/storefront/src/helpers/sf-utils.ts +15 -0
  56. package/packages/storefront/src/lib/composables/use-product-card.ts +3 -2
  57. package/packages/storefront/src/lib/scripts/push-analytics-events.ts +31 -25
  58. package/packages/storefront/src/lib/state/shopping-cart.ts +35 -2
  59. package/packages/storefront/src/lib/state/use-analytics.ts +116 -75
  60. package/packages/test-base/package.json +1 -1
  61. package/packages/types/package.json +1 -1
@@ -1,9 +1,11 @@
1
1
  import type { Products, CartSet, SearchItem } from '@cloudcommerce/api/types';
2
- import { computed } from 'vue';
2
+ import { computed, watch } from 'vue';
3
+ import mitt from 'mitt';
3
4
  import useStorage from '@@sf/state/use-storage';
4
5
  import addItem from '@@sf/state/shopping-cart/add-cart-item';
5
6
  import parseProduct from '@@sf/state/shopping-cart/parse-product';
6
7
 
8
+ type CartItem = CartSet['items'][0];
7
9
  const storageKey = 'ecomShoppingCart';
8
10
  const emptyCart = {
9
11
  items: [],
@@ -64,7 +66,7 @@ const shoppingCart = computed({
64
66
  },
65
67
  });
66
68
 
67
- const addCartItem = (newItem: CartSet['items'][0]) => {
69
+ const addCartItem = (newItem: CartItem) => {
68
70
  const cartObj = shoppingCart.value;
69
71
  const upsertedItem = addItem(cartObj, newItem);
70
72
  if (upsertedItem) {
@@ -97,3 +99,34 @@ export {
97
99
  parseProduct,
98
100
  addProductToCart,
99
101
  };
102
+
103
+ type CartEvent = {
104
+ addCartItem: CartItem,
105
+ removeCartItem: CartItem,
106
+ };
107
+ const cartEmitter = mitt<CartEvent>();
108
+ const cloneItems = () => shoppingCart.value.items.map((item) => ({ ...item }));
109
+ let oldItems = cloneItems();
110
+ watch(shoppingCart, ({ items }) => {
111
+ ['addCartItem' as const, 'removeCartItem' as const].forEach((evName) => {
112
+ const isAdd = evName === 'addCartItem';
113
+ const baseItems = isAdd ? items : oldItems;
114
+ const compareItems = isAdd ? oldItems : items;
115
+ baseItems.forEach((baseItem) => {
116
+ if (!baseItem.quantity) return;
117
+ const compareItem = compareItems.find(({ _id }) => _id === baseItem._id);
118
+ if (compareItem && baseItem.quantity > compareItem.quantity) {
119
+ cartEmitter.emit(evName, {
120
+ ...baseItem,
121
+ quantity: baseItem.quantity - (compareItem?.quantity || 0),
122
+ });
123
+ }
124
+ });
125
+ });
126
+ oldItems = cloneItems();
127
+ });
128
+
129
+ export const cartEvents = {
130
+ on: cartEmitter.on,
131
+ off: cartEmitter.off,
132
+ };
@@ -1,6 +1,8 @@
1
1
  import type { Products, Carts, SearchItem } from '@cloudcommerce/types';
2
+ import { watchOnce } from '@vueuse/core';
2
3
  import { price as getPrice, name as getName } from '@ecomplus/utils';
3
- import { customer } from '@@sf/state/customer-session';
4
+ import { customer, isLogged } from '@@sf/state/customer-session';
5
+ import { cartEvents } from '@@sf/state/shopping-cart';
4
6
  import utm from '@@sf/scripts/session-utm';
5
7
 
6
8
  export const trackingIds: {
@@ -13,93 +15,53 @@ export const trackingIds: {
13
15
  session_id?: string,
14
16
  } = {};
15
17
 
16
- export const getAnalyticsContext = () => {
17
- return {
18
- ...trackingIds,
19
- user_id: customer.value._id,
20
- utm,
21
- };
18
+ // `page_view` params not typed
19
+ // https://developers.google.com/tag-platform/gtagjs/reference/events#page_view
20
+ type PageViewParams = {
21
+ page_location: string,
22
+ client_id?: string,
23
+ language: string,
24
+ page_encoding?: string,
25
+ page_title: string,
26
+ user_agent: string,
22
27
  };
23
-
24
28
  const pageViewState = {} as {
25
29
  resolve: ((...args: any[]) => void) | null | undefined,
26
30
  waiting: Promise<unknown> | undefined,
31
+ params: PageViewParams | undefined,
32
+ };
33
+ const resetPageViewPromise = () => {
34
+ if (pageViewState.resolve) return;
35
+ pageViewState.waiting = new Promise((resolve) => {
36
+ pageViewState.resolve = resolve;
37
+ });
27
38
  };
28
39
  if (!import.meta.env.SSR) {
29
- const resetPageViewPromise = () => {
30
- if (pageViewState.resolve) return;
31
- pageViewState.waiting = new Promise((resolve) => {
32
- pageViewState.resolve = resolve;
33
- });
34
- };
35
40
  resetPageViewPromise();
36
- document.addEventListener('astro:beforeload', resetPageViewPromise);
37
- const {
38
- gtag,
39
- GTAG_TAG_ID,
40
- GA_TRACKING_ID,
41
- } = window as { [k:string]: any, gtag?: Gtag.Gtag };
42
- const tagId = GTAG_TAG_ID || GA_TRACKING_ID;
43
- if (tagId && typeof gtag === 'function') {
44
- ['client_id', 'session_id', 'gclid'].forEach((key) => {
45
- gtag('get', tagId, key, (id) => {
46
- trackingIds[key === 'gclid' ? key : `g_${key}`] = id;
47
- });
48
- });
49
- }
50
- const url = new URL(window.location.toString());
51
- ['gclid', 'fbclid'].forEach((key) => {
52
- const id = trackingIds[key] || url.searchParams.get(key);
53
- if (id) {
54
- trackingIds[key] = id;
55
- sessionStorage.setItem(`analytics_${key}`, id);
56
- } else {
57
- trackingIds[key] = sessionStorage.getItem(`analytics_${key}`) || undefined;
58
- }
59
- });
60
- const cookieNames = ['_fbp'];
61
- if (!trackingIds.fbclid) cookieNames.push('_fbc');
62
- if (!trackingIds.g_client_id) cookieNames.push('_ga');
63
- cookieNames.forEach((cookieName) => {
64
- document.cookie.split(';').forEach((cookie) => {
65
- const [key, value] = cookie.split('=');
66
- if (key.trim() === cookieName && value) {
67
- switch (cookieName) {
68
- case '_fbp': trackingIds.fbp = value; break;
69
- case '_fbc': trackingIds.fbclid = value; break;
70
- case '_ga': trackingIds.g_client_id = value.substring(6); break;
71
- default:
72
- }
73
- }
74
- });
75
- });
76
- ['client_id', 'session_id'].forEach((key) => {
77
- const storage = key === 'client_id' ? localStorage : sessionStorage;
78
- const storedValue = storage.getItem(`analytics_${key}`);
79
- if (storedValue) {
80
- trackingIds[key] = storedValue;
81
- } else {
82
- trackingIds[key] = trackingIds[`g_${key}`]
83
- || `${Math.ceil(Math.random() * 1000000)}.${Math.ceil(Math.random() * 1000000)}`;
84
- }
85
- storage.setItem(`analytics_${key}`, trackingIds[key]);
86
- });
87
41
  }
88
42
 
89
- // `page_view` params not typed
90
- // https://developers.google.com/tag-platform/gtagjs/reference/events#page_view
91
- type PageViewParams = {
92
- page_location?: string,
93
- client_id?: string,
94
- language?: string,
95
- page_encoding?: string,
96
- page_title?: string,
97
- user_agent?: string,
43
+ export const getPageViewParams = () => {
44
+ return {
45
+ page_location: window.location.toString(),
46
+ language: globalThis.$storefront.settings.lang || 'pt_br',
47
+ page_title: document.title,
48
+ user_agent: navigator.userAgent,
49
+ };
50
+ };
51
+
52
+ export const getAnalyticsContext = () => {
53
+ return {
54
+ ...(pageViewState.params || getPageViewParams()),
55
+ ...trackingIds,
56
+ user_id: customer.value._id,
57
+ utm,
58
+ };
98
59
  };
99
60
 
100
61
  export const GTAG_EVENT_TYPE = 'GtagEvent';
101
62
 
102
63
  export type GtagEventMessage = typeof trackingIds &
64
+ PageViewParams &
103
65
  { utm: typeof utm } &
104
66
  {
105
67
  type: typeof GTAG_EVENT_TYPE,
@@ -118,7 +80,7 @@ let defaultItemsList = '';
118
80
 
119
81
  export const emitGtagEvent = async <N extends Gtag.EventNames = 'view_item'>(
120
82
  name: N,
121
- _params: N extends 'page_view' ? PageViewParams : Gtag.EventParams = {},
83
+ _params: N extends 'page_view' ? PageViewParams : Gtag.EventParams,
122
84
  ) => {
123
85
  if (import.meta.env.SSR) return;
124
86
  const params = _params as Gtag.EventParams;
@@ -148,6 +110,7 @@ export const emitGtagEvent = async <N extends Gtag.EventNames = 'view_item'>(
148
110
  }
149
111
  }
150
112
  if (pageViewState.resolve) {
113
+ pageViewState.params = (_params as PageViewParams);
151
114
  pageViewState.resolve();
152
115
  pageViewState.resolve = null;
153
116
  }
@@ -156,6 +119,14 @@ export const emitGtagEvent = async <N extends Gtag.EventNames = 'view_item'>(
156
119
  if (name === 'view_item') {
157
120
  params.items?.forEach((item) => {
158
121
  if (item.index !== undefined) return;
122
+ if (!item.item_list_id && !item.item_list_name) {
123
+ const { apiContext } = window.$storefront;
124
+ // @ts-ignore
125
+ if (apiContext?.doc._id === item.object_id) {
126
+ item.item_list_id = 'product-page';
127
+ item.item_list_name = 'Product page';
128
+ }
129
+ }
159
130
  const listKey = item.item_list_id
160
131
  || item.item_list_name
161
132
  || defaultItemsList;
@@ -207,6 +178,9 @@ export const getGtagItem = (product: Partial<Products> | SearchItem | CartItem)
207
178
  item_name: name,
208
179
  item_id: product.sku,
209
180
  price: Math.round(getPrice(product) * 100) / 100,
181
+ // https://developers.google.com/analytics/devguides/collection/ga4/item-scoped-ecommerce#add_an_item-scoped_custom_parameter
182
+ // @ts-ignore
183
+ object_id: product._id,
210
184
  };
211
185
  if (variants && variants.length) {
212
186
  item.item_variant = variants.join(' / ');
@@ -240,3 +214,70 @@ export const getGtagItem = (product: Partial<Products> | SearchItem | CartItem)
240
214
  }
241
215
  return item;
242
216
  };
217
+
218
+ export const useAnalytics = () => {
219
+ document.addEventListener('astro:beforeload', resetPageViewPromise);
220
+ if (isLogged.value) {
221
+ emitGtagEvent('login', {});
222
+ } else {
223
+ watchOnce(isLogged, () => emitGtagEvent('login', {}));
224
+ }
225
+ cartEvents.on('*', (evName, cartItem) => {
226
+ emitGtagEvent(
227
+ evName === 'addCartItem' ? 'add_to_cart' : 'remove_from_cart',
228
+ { items: [getGtagItem(cartItem)] },
229
+ );
230
+ });
231
+ const {
232
+ gtag,
233
+ GTAG_TAG_ID,
234
+ GA_TRACKING_ID,
235
+ } = window as { [k:string]: any, gtag?: Gtag.Gtag };
236
+ const tagId = GTAG_TAG_ID || GA_TRACKING_ID;
237
+ if (tagId && typeof gtag === 'function') {
238
+ ['client_id', 'session_id', 'gclid'].forEach((key) => {
239
+ gtag('get', tagId, key, (id) => {
240
+ trackingIds[key === 'gclid' ? key : `g_${key}`] = id;
241
+ });
242
+ });
243
+ }
244
+ const url = new URL(window.location.toString());
245
+ ['gclid', 'fbclid'].forEach((key) => {
246
+ const id = trackingIds[key] || url.searchParams.get(key);
247
+ if (id) {
248
+ trackingIds[key] = id;
249
+ sessionStorage.setItem(`analytics_${key}`, id);
250
+ } else {
251
+ trackingIds[key] = sessionStorage.getItem(`analytics_${key}`) || undefined;
252
+ }
253
+ });
254
+ const cookieNames = ['_fbp'];
255
+ if (!trackingIds.fbclid) cookieNames.push('_fbc');
256
+ if (!trackingIds.g_client_id) cookieNames.push('_ga');
257
+ cookieNames.forEach((cookieName) => {
258
+ document.cookie.split(';').forEach((cookie) => {
259
+ const [key, value] = cookie.split('=');
260
+ if (key.trim() === cookieName && value) {
261
+ switch (cookieName) {
262
+ case '_fbp': trackingIds.fbp = value; break;
263
+ case '_fbc': trackingIds.fbclid = value; break;
264
+ case '_ga': trackingIds.g_client_id = value.substring(6); break;
265
+ default:
266
+ }
267
+ }
268
+ });
269
+ });
270
+ ['client_id', 'session_id'].forEach((key) => {
271
+ const storage = key === 'client_id' ? localStorage : sessionStorage;
272
+ const storedValue = storage.getItem(`analytics_${key}`);
273
+ if (storedValue) {
274
+ trackingIds[key] = storedValue;
275
+ } else {
276
+ trackingIds[key] = trackingIds[`g_${key}`]
277
+ || `${Math.ceil(Math.random() * 1000000)}.${Math.ceil(Math.random() * 1000000)}`;
278
+ }
279
+ storage.setItem(`analytics_${key}`, trackingIds[key]);
280
+ });
281
+ };
282
+
283
+ export default useAnalytics;
@@ -2,7 +2,7 @@
2
2
  "name": "@cloudcommerce/test-base",
3
3
  "private": true,
4
4
  "type": "module",
5
- "version": "0.33.1",
5
+ "version": "0.33.3",
6
6
  "description": "E-Com Plus Cloud Commerce basic setup for testing",
7
7
  "main": "lib/index.js",
8
8
  "repository": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/types",
3
3
  "type": "module",
4
- "version": "0.33.1",
4
+ "version": "0.33.3",
5
5
  "description": "E-Com Plus Cloud Commerce reusable type definitions",
6
6
  "main": "index.ts",
7
7
  "repository": {