@cartbase/storefront 0.13.0 → 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.
Files changed (36) hide show
  1. package/README.md +9 -3
  2. package/package.json +2 -1
  3. package/src/api/customers.ts +4 -7
  4. package/src/api/products.ts +4 -0
  5. package/src/cart-drawer/context.tsx +11 -1
  6. package/src/cart-drawer/item/variant.tsx +8 -3
  7. package/src/checkout/checkout-client.tsx +10 -1
  8. package/src/checkout/line-item-card.tsx +5 -2
  9. package/src/checkout/order-summary.tsx +9 -5
  10. package/src/common/country-select.tsx +11 -65
  11. package/src/common/index.ts +2 -0
  12. package/src/common/market-select.tsx +57 -0
  13. package/src/lib/variant-caption.ts +32 -0
  14. package/src/locales/context.ts +37 -0
  15. package/src/locales/provider.tsx +17 -21
  16. package/src/locales/types.ts +34 -6
  17. package/src/order/order-completed-template.tsx +10 -4
  18. package/src/order/order-item.tsx +4 -4
  19. package/src/products/product-template.tsx +16 -0
  20. package/src/products/related-products.tsx +11 -5
  21. package/src/reviews-ui/photo-upload.tsx +2 -1
  22. package/src/reviews-ui/review-list.tsx +4 -2
  23. package/src/reviews-ui/review-widget.tsx +4 -1
  24. package/src/reviews-ui/review-wizard.tsx +3 -2
  25. package/src/store/category-template.tsx +8 -1
  26. package/src/store/collection-template.tsx +8 -1
  27. package/src/store/search-template.tsx +10 -4
  28. package/src/store/store-template.tsx +10 -4
  29. package/src/tracking/chatgpt-pixel.tsx +17 -10
  30. package/src/tracking/consent-init.tsx +62 -44
  31. package/src/tracking/consent.ts +58 -7
  32. package/src/tracking/index.ts +2 -0
  33. package/src/tracking/meta-pixel.tsx +27 -15
  34. package/src/tracking/storefront-tags.tsx +14 -7
  35. package/src/tracking/tiktok-pixel.tsx +15 -7
  36. package/src/tracking/types.ts +5 -2
@@ -22,10 +22,15 @@ import type { TrackingConfig } from "./types"
22
22
  * it at the next deploy.
23
23
  *
24
24
  * Consent: nothing extra to wire. <ConsentInit> sets the Consent Mode v2
25
- * defaults synchronously ahead of these, the Meta and TikTok snippets read
26
- * the shared consent cookie before they are allowed to write cookies, and
27
- * `applyConsent()` relays a live decision to all three vendors. Mount this
28
- * AFTER <ConsentInit> and the gate holds.
25
+ * defaults synchronously ahead of these, the Meta, TikTok and ChatGPT
26
+ * snippets resolve the same decision before they are allowed to send, and
27
+ * `applyConsent()` relays a live decision to every vendor. Mount this
28
+ * AFTER <ConsentInit> and the gate holds. Whether there IS a gate is the
29
+ * store's own setting, `consent_required` on this same block: a store that
30
+ * collects consent starts every visitor denied until they choose, a store
31
+ * with the banner off starts them granted, and a stored choice wins either
32
+ * way. The block always carries the flag; an absent one is read as
33
+ * required, the closed side.
29
34
  *
30
35
  * Google is deliberately ONE tag with two destinations rather than two
31
36
  * loaders — see the comment block in ./ga4.tsx. It is also why the Ads
@@ -56,6 +61,8 @@ export async function StorefrontTags({
56
61
  }
57
62
  }
58
63
 
64
+ const consentRequired = tracking.consent_required ?? true
65
+
59
66
  return (
60
67
  <>
61
68
  {/* Cartbase's own analytics, always on and never a merchant setting:
@@ -64,9 +71,9 @@ export async function StorefrontTags({
64
71
  only appear when the merchant has entered their ids. */}
65
72
  <PageViews />
66
73
  <LiveHeartbeat />
67
- <MetaPixel pixelId={tracking.facebookPixel?.pixelId} />
68
- <TikTokPixel pixelId={tracking.tiktok?.pixelId} />
69
- <ChatGptPixel pixelId={tracking.chatgptAds?.pixelId} />
74
+ <MetaPixel pixelId={tracking.facebookPixel?.pixelId} consentRequired={consentRequired} />
75
+ <TikTokPixel pixelId={tracking.tiktok?.pixelId} consentRequired={consentRequired} />
76
+ <ChatGptPixel pixelId={tracking.chatgptAds?.pixelId} consentRequired={consentRequired} />
70
77
  <GoogleTag
71
78
  measurementId={tracking.ga4?.measurementId}
72
79
  adsConversionId={tracking.googleAds?.conversionId}
@@ -2,7 +2,7 @@
2
2
 
3
3
  import Script from "next/script"
4
4
 
5
- import { CONSENT_COOKIE } from "./consent"
5
+ import { adsConsentSnippet } from "./consent"
6
6
  import { jsStringLiteral } from "./inline-script"
7
7
 
8
8
  /**
@@ -33,6 +33,11 @@ import { jsStringLiteral } from "./inline-script"
33
33
  *
34
34
  * Live decisions arrive through `applyConsent()` in ./consent.ts, which
35
35
  * relays to gtag, fbq and ttq together — there is no cookie watcher.
36
+ * The first decision is the store's default overridden by the visitor's
37
+ * stored choice (`adsConsentSnippet`): revoked on a store that collects
38
+ * consent, granted on a store with no consent gate, the cookie winning
39
+ * either way. `consentRequired` is `tracking.consent_required`, passed by
40
+ * <StorefrontTags>, and required for the reason <ConsentInit> gives.
36
41
  *
37
42
  * One dependency outside the code: Cookie Consent Mode must be switched
38
43
  * on for this pixel in TikTok Events Manager. With it off, `holdConsent`
@@ -43,7 +48,14 @@ import { jsStringLiteral } from "./inline-script"
43
48
  * Renders nothing when `pixelId` is falsy, so a layout can mount it
44
49
  * unconditionally.
45
50
  */
46
- export function TikTokPixel({ pixelId }: { pixelId?: string }) {
51
+ export function TikTokPixel({
52
+ pixelId,
53
+ consentRequired,
54
+ }: {
55
+ pixelId?: string
56
+ /** `tracking.consent_required`: does this store collect consent before it tracks? */
57
+ consentRequired: boolean
58
+ }) {
47
59
  if (!pixelId) return null
48
60
 
49
61
  const id = jsStringLiteral(pixelId)
@@ -63,11 +75,7 @@ export function TikTokPixel({ pixelId }: { pixelId?: string }) {
63
75
  ttq.page();
64
76
 
65
77
  (function(){
66
- var ads=false;
67
- try{
68
- var m=d.cookie.match(/(?:^|; )${CONSENT_COOKIE}=([^;]*)/);
69
- if(m){ads=!!JSON.parse(decodeURIComponent(m[1])).ads;}
70
- }catch(e){}
78
+ ${adsConsentSnippet(consentRequired, "d")}
71
79
  if(ads){ttq.grantConsent();}else{ttq.revokeConsent();}
72
80
  })();
73
81
  }(window, document, 'ttq');
@@ -48,8 +48,11 @@ export type TrackingConfig = {
48
48
  * token and stays on the platform, read by the order.placed forwarder
49
49
  * (chatgpt-ads-pixel card). */
50
50
  chatgptAds?: { pixelId: string }
51
- /** True when the store's consent CMP is enabled client tags must
52
- * mount through the consent gate (`_1c_consent` / Consent Mode v2). */
51
+ /** Whether this store collects consent before it tracks (the consent
52
+ * CMP's `enabled`). True: every tag starts denied until the visitor
53
+ * chooses on the banner. False: the store has no consent gate and every
54
+ * tag starts granted. A choice stored in `_1c_consent` wins either way.
55
+ * Always served; a reader treats an absent value as true. */
53
56
  consent_required?: boolean
54
57
  }
55
58