From c3e2b4de814b74be2d121b6b07521e3feb284180 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Mon, 17 Jun 2024 16:34:28 -0700 Subject: [PATCH] auto cookie domain detect and update error message --- .../src/analytics-manager/CartAnalytics.tsx | 2 +- .../analytics-manager/ShopifyAnalytics.tsx | 63 +++++++++---------- .../ShopifyCustomerPrivacy.tsx | 40 +++++++++--- 3 files changed, 60 insertions(+), 45 deletions(-) diff --git a/packages/hydrogen/src/analytics-manager/CartAnalytics.tsx b/packages/hydrogen/src/analytics-manager/CartAnalytics.tsx index 6e713f5bb5..fad620b48f 100644 --- a/packages/hydrogen/src/analytics-manager/CartAnalytics.tsx +++ b/packages/hydrogen/src/analytics-manager/CartAnalytics.tsx @@ -10,7 +10,7 @@ import {flattenConnection} from '@shopify/hydrogen-react'; function logMissingField(fieldName: string) { // eslint-disable-next-line no-console console.error( - `[h2:error:CartAnalytics] Can't set up cart analytics events because the \`cart.${fieldName}\` value is missing from your GraphQL cart query. In standard Hydrogen projects, the cart query is contained in \`app/lib/fragments.js\`. Make sure it includes \`cart.${fieldName}\`. Check the Hydrogen Skeleton template for reference: https://github.com/Shopify/hydrogen/blob/main/templates/skeleton/app/lib/fragments.ts#L59.`, + `[h2:error:CartAnalytics] Can't set up cart analytics events because the \`cart.${fieldName}\` value is missing from your GraphQL cart query. In your project, search for where \`fragment CartApiQuery on Cart\` is defined and make sure \`${fieldName}\` is part of your cart query. Check the Hydrogen Skeleton template for reference: https://github.com/Shopify/hydrogen/blob/main/templates/skeleton/app/lib/fragments.ts#L59.`, ); } diff --git a/packages/hydrogen/src/analytics-manager/ShopifyAnalytics.tsx b/packages/hydrogen/src/analytics-manager/ShopifyAnalytics.tsx index 86298d5f20..d7b363b2c6 100644 --- a/packages/hydrogen/src/analytics-manager/ShopifyAnalytics.tsx +++ b/packages/hydrogen/src/analytics-manager/ShopifyAnalytics.tsx @@ -208,10 +208,7 @@ function productViewHandler(payload: ProductViewPayload) { if ( eventPayload && validateProducts({ - eventName: PRODUCT_VIEWED, - productField: 'products', - variantField: 'product.', - fromSource: 'product_viewed products array', + type: 'product', products: payload.products, }) ) { @@ -318,10 +315,7 @@ function sendCartAnalytics({ }; if ( validateProducts({ - eventName: ADD_TO_CART, - productField: 'merchandise.product', - variantField: 'merchandise', - fromSource: 'cart query', + type: 'cart', products: [product], }) ) { @@ -335,17 +329,23 @@ function sendCartAnalytics({ } } -const PRODUCT_VIEWED = 'Product viewed'; -const ADD_TO_CART = 'Add to cart'; function missingErrorMessage( - eventName: string, - missingFieldName: string, - fromSource: string, + type: 'cart' | 'product', + fieldName: string, + isVariantField: boolean, + viewKeyName?: string, ) { - // eslint-disable-next-line no-console - console.error( - `[h2:error:ShopifyAnalytics] ${eventName}: ${missingFieldName} is required from the ${fromSource}.`, - ); + if(type === 'cart') { + // eslint-disable-next-line no-console + console.error( + `[h2:error:ShopifyAnalytics] Can't set up cart analytics events because the \`cart.lines[].${isVariantField ? 'merchandise' : 'merchandise.product'}.${fieldName}\` value is missing from your GraphQL cart query. In your project, search for where \`fragment CartLine on CartLine\` is defined and make sure \`${isVariantField ? 'merchandise' : 'merchandise.product'}.${fieldName}\` is part of your cart query. Check the Hydrogen Skeleton template for reference: https://github.com/Shopify/hydrogen/blob/main/templates/skeleton/app/lib/fragments.ts#L25-L56.`, + ); + } else { + // eslint-disable-next-line no-console + console.error( + `[h2:error:ShopifyAnalytics] Can't set up product view analytics events because the \`${viewKeyName || fieldName}\` is missing from your \`\`. Make sure \`${viewKeyName || fieldName}\` is part of your products data prop. Check the Hydrogen Skeleton template for reference: https://github.com/Shopify/hydrogen/blob/main/templates/skeleton/app/routes/products.%24handle.tsx#L159-L165.`, + ); + } } // Product expected field and types: @@ -360,50 +360,45 @@ function missingErrorMessage( // category: string, optional // quantity: float function validateProducts({ - eventName, - productField, - variantField, + type, products, - fromSource, }: { - eventName: string; - productField: string; - variantField: string; - fromSource: string; + type: 'cart' | 'product'; products: Array>; }) { if (!products || products.length === 0) { - missingErrorMessage(eventName, `${productField}`, fromSource); + missingErrorMessage(type, '', false, 'data.products'); return false; } products.forEach((product) => { if (!product.id) { - missingErrorMessage(eventName, `${productField}.id`, fromSource); + missingErrorMessage(type, 'id', false); return false; } if (!product.title) { - missingErrorMessage(eventName, `${productField}.title`, fromSource); + missingErrorMessage(type, 'title', false); return false; } if (!product.price) { missingErrorMessage( - eventName, - `${variantField}.price.amount`, - fromSource, + type, + 'price.amount', + true, + 'price', ); return false; } if (!product.vendor) { - missingErrorMessage(eventName, `${productField}.vendor`, fromSource); + missingErrorMessage(type, 'vendor', false); return false; } if (!product.variantId) { - missingErrorMessage(eventName, `${variantField}.id`, fromSource); + missingErrorMessage(type, 'id', true, 'variantId'); return false; } if (!product.variantTitle) { - missingErrorMessage(eventName, `${variantField}.title`, fromSource); + missingErrorMessage(type, 'title', true, 'variantTitle'); return false; } }); diff --git a/packages/hydrogen/src/customer-privacy/ShopifyCustomerPrivacy.tsx b/packages/hydrogen/src/customer-privacy/ShopifyCustomerPrivacy.tsx index a396f2e93e..640ad7111b 100644 --- a/packages/hydrogen/src/customer-privacy/ShopifyCustomerPrivacy.tsx +++ b/packages/hydrogen/src/customer-privacy/ShopifyCustomerPrivacy.tsx @@ -143,14 +143,15 @@ export function useCustomerPrivacy(props: CustomerPrivacyApiProps) { if (scriptStatus !== 'done' || loadedEvent.current) return; loadedEvent.current = true; - if (!consentConfig.checkoutDomain) logMissingConfig('checkoutDomain'); - if (!consentConfig.storefrontAccessToken) + const {checkoutDomain, storefrontAccessToken} = consentConfig; + if (!checkoutDomain) logMissingConfig('checkoutDomain'); + if (!storefrontAccessToken) logMissingConfig('storefrontAccessToken'); // validate that the storefront access token is not a server API token if ( - consentConfig.storefrontAccessToken.startsWith('shpat_') || - consentConfig.storefrontAccessToken.length !== 32 + storefrontAccessToken.startsWith('shpat_') || + storefrontAccessToken.length !== 32 ) { // eslint-disable-next-line no-console console.error( @@ -158,11 +159,31 @@ export function useCustomerPrivacy(props: CustomerPrivacyApiProps) { ); } - if (withPrivacyBanner && window?.privacyBanner) { - window?.privacyBanner?.loadBanner({ - checkoutRootDomain: consentConfig.checkoutDomain, - storefrontAccessToken: consentConfig.storefrontAccessToken, + const config: CustomerPrivacyConsentConfig = { + checkoutRootDomain: checkoutDomain, + storefrontAccessToken, + } + + if (checkoutDomain) { + let storefrontRootDomain = window.document.location.host; + const checkoutDomainParts = checkoutDomain.split('.').reverse(); + const currentDomainParts = storefrontRootDomain.split('.').reverse(); + const sameDomainParts: Array = []; + checkoutDomainParts.forEach((part, index) => { + if (part === currentDomainParts[index]) { + sameDomainParts.push(part); + } }); + + storefrontRootDomain = sameDomainParts.reverse().join('.'); + + if (storefrontRootDomain) { + config.storefrontRootDomain = storefrontRootDomain; + } + } + + if (withPrivacyBanner && window?.privacyBanner) { + window?.privacyBanner?.loadBanner(config); } if (!window.Shopify?.customerPrivacy) return; @@ -179,8 +200,7 @@ export function useCustomerPrivacy(props: CustomerPrivacyApiProps) { { ...consent, headlessStorefront: true, - checkoutRootDomain: consentConfig.checkoutDomain, - storefrontAccessToken: consentConfig.storefrontAccessToken, + ...config, }, callback, );