@ecomiq/storefront 2.1.4 → 3.0.1
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.
- package/LICENSE +21 -0
- package/README.md +377 -76
- package/dist/account-Dx4e3syi.js +258 -0
- package/dist/account-forms-AVk71ko7.js +39 -0
- package/dist/account-forms.d.ts +13 -0
- package/dist/account.d.ts +240 -0
- package/dist/address-defaults-BqjRhS82.js +14 -0
- package/dist/address-defaults.d.ts +11 -0
- package/dist/analytics/analytics-CG1Qagwl.js +148 -0
- package/dist/analytics/analytics.d.ts +1 -0
- package/dist/analytics/config-DJr-GRhs.js +62 -0
- package/dist/analytics/config.d.ts +22 -0
- package/dist/analytics/consent-B13rBpBs.js +57 -0
- package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
- package/dist/analytics/consent-banner.d.ts +20 -0
- package/dist/analytics/cookies-pLozllyB.js +100 -0
- package/dist/analytics/cookies.d.ts +6 -0
- package/dist/analytics/destinations-iECrlLIr.js +144 -0
- package/dist/analytics/events-Dt81tIp1.js +102 -0
- package/dist/analytics/events.d.ts +51 -0
- package/dist/analytics/tracker-p4yaoOyk.js +51 -0
- package/dist/api-cache-DsuIn5hX.js +159 -0
- package/dist/bank-accounts-DlEczQQ5.js +81 -0
- package/dist/bank-accounts.d.ts +4 -0
- package/dist/captcha-DAqaIx8m.js +117 -0
- package/dist/captcha.d.ts +34 -0
- package/dist/cart-D7DxZ2lD.js +30 -0
- package/dist/cart-actions-Bsub_FFG.js +116 -0
- package/dist/cart-actions.d.ts +15 -0
- package/dist/cart-drawer-B7PPsR69.js +22 -0
- package/dist/cart-drawer.d.ts +9 -0
- package/dist/cart-form-y9P1r_EX.js +81 -0
- package/dist/cart-form.d.ts +24 -0
- package/dist/cart-summary-ur2VWKr-.js +89 -0
- package/dist/cart-summary.d.ts +31 -0
- package/dist/cart.d.ts +4 -0
- package/dist/catalog-7gJ-l5hW.js +67 -0
- package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
- package/dist/catalog-hooks.d.ts +111 -0
- package/dist/catalog.d.ts +5 -0
- package/dist/checkout-DVWSXANi.js +474 -0
- package/dist/checkout-form-CbOIdENC.js +131 -0
- package/dist/checkout-form.d.ts +20 -0
- package/dist/checkout.d.ts +86 -0
- package/dist/complaint-book-BvwLvfx0.js +117 -0
- package/dist/complaint-book.d.ts +62 -0
- package/dist/culqi-authentication-BGr20vAJ.js +204 -0
- package/dist/culqi-authentication.d.ts +15 -0
- package/dist/culqi-checkout-CTXpKgp_.js +55 -0
- package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
- package/dist/culqi-three-ds.d.ts +29 -0
- package/dist/customer-avatar-CJXSDroK.js +12 -0
- package/dist/customer-avatar.d.ts +5 -0
- package/dist/customer-cookies-DHOgFAMV.js +45 -0
- package/dist/customer-csrf-C1oDX-Py.js +17 -0
- package/dist/customer-csrf.d.ts +3 -0
- package/dist/customer-document-fields-C1K-_DAD.js +70 -0
- package/dist/customer-document-fields.d.ts +9 -0
- package/dist/customer-login-form-C7AC6vXd.js +144 -0
- package/dist/customer-login-form.d.ts +17 -0
- package/dist/customer-registration-form-BEm_NEfP.js +93 -0
- package/dist/customer-registration-form.d.ts +8 -0
- package/dist/document-url-CGsAl5Tf.js +9 -0
- package/dist/documents-T1mzc_j8.js +38 -0
- package/dist/documents.d.ts +16 -0
- package/dist/entries/account.d.ts +11 -0
- package/dist/entries/account.js +10 -0
- package/dist/entries/analytics.d.ts +6 -0
- package/dist/entries/analytics.js +5 -0
- package/dist/entries/cart.d.ts +8 -0
- package/dist/entries/cart.js +7 -0
- package/dist/entries/checkout.d.ts +12 -0
- package/dist/entries/checkout.js +11 -0
- package/dist/entries/order.d.ts +11 -0
- package/dist/entries/order.js +11 -0
- package/dist/favicon-YpyhVJ9H.js +28 -0
- package/dist/favicon.d.ts +4 -0
- package/dist/form-BtQcbXlJ.js +18 -0
- package/dist/form-contracts-BNpHaTGJ.js +166 -0
- package/dist/form-contracts.d.ts +42 -0
- package/dist/form.d.ts +3 -0
- package/dist/hidden-CCWtOWKR.js +3 -0
- package/dist/hidden.d.ts +4 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +50 -0
- package/dist/invoice-fields-BG0HjL6T.js +103 -0
- package/dist/invoice-fields.d.ts +39 -0
- package/dist/links-kFxBZfdb.js +96 -0
- package/dist/links.d.ts +21 -0
- package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
- package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
- package/dist/mercadopago-dialog.d.ts +21 -0
- package/dist/money-DtGOtDri.js +32 -0
- package/dist/money.d.ts +1 -0
- package/dist/optimistic-cart-BveVOHuh.js +53 -0
- package/dist/optimistic-cart.d.ts +16 -0
- package/dist/order-display-DTt5WBbc.js +112 -0
- package/dist/order-display.d.ts +20 -0
- package/dist/order-draft-DxV1Uhm3.js +38 -0
- package/dist/order-draft.d.ts +16 -0
- package/dist/order-pages-Dm6l5m0m.js +38 -0
- package/dist/order-pages.d.ts +12 -0
- package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
- package/dist/order-payment-continuation.d.ts +10 -0
- package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
- package/dist/order-payment-in-review.d.ts +5 -0
- package/dist/order-status-BJXLJwxq.js +162 -0
- package/dist/order-status.d.ts +89 -0
- package/dist/order-view-9IlBg5AF.js +62 -0
- package/dist/order-view.d.ts +12 -0
- package/dist/page-action-Ctkf4xC6.js +7 -0
- package/dist/page-seo-a5FJl8x5.js +262 -0
- package/dist/page-seo.d.ts +34 -0
- package/dist/pages-Ca_Gt6mq.js +150 -0
- package/dist/pages.d.ts +45 -0
- package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
- package/dist/payment-logo-BoPaGJc_.js +46 -0
- package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
- package/dist/payment-logo.d.ts +11 -0
- package/dist/payment-logos-BYvdwYyJ.js +23 -0
- package/dist/payment-methods-B8ocTgFZ.js +73 -0
- package/dist/payment-methods.d.ts +58 -0
- package/dist/payment-return-5cjypwIw.js +7 -0
- package/dist/place-CmW4hBWY.js +13 -0
- package/dist/place.d.ts +4 -0
- package/dist/preview-BiIBif3C.js +427 -0
- package/dist/preview-address-Cf0sJ-9p.js +21 -0
- package/dist/preview-cart-Dg2CVktS.js +376 -0
- package/dist/product-preview-BspMAecJ.js +51 -0
- package/dist/product-preview.d.ts +5 -0
- package/dist/product-variants-BNDHvs1y.js +46 -0
- package/dist/product-variants.d.ts +12 -0
- package/dist/public-pages-XtwlaLwt.js +194 -0
- package/dist/public-pages.d.ts +67 -0
- package/dist/registration-CpDcv8ql.js +91 -0
- package/dist/registration.d.ts +12 -0
- package/dist/request-security-CRsRMgwj.js +76 -0
- package/dist/rich-text-M-Ju8RL4.js +9 -0
- package/dist/rich-text.d.ts +5 -0
- package/dist/route-text-DcJw2SsK.js +9 -0
- package/dist/route-text.d.ts +4 -0
- package/dist/section-preview-CmsMl3yu.js +83 -0
- package/dist/section-preview.d.ts +12 -0
- package/dist/sections/account-addresses.d.ts +18 -0
- package/dist/sections/account-addresses.js +375 -0
- package/dist/sections/account-order.d.ts +12 -0
- package/dist/sections/account-order.js +592 -0
- package/dist/sections/account-orders.d.ts +8 -0
- package/dist/sections/account-orders.js +359 -0
- package/dist/sections/account-profile.d.ts +16 -0
- package/dist/sections/account-profile.js +123 -0
- package/dist/sections/checkout.d.ts +24 -0
- package/dist/sections/checkout.js +963 -0
- package/dist/sections/complaint-book.d.ts +8 -0
- package/dist/sections/complaint-book.js +538 -0
- package/dist/sections/design-system.d.ts +1 -0
- package/dist/sections/design-system.js +310 -0
- package/dist/sections/draft-invoice.d.ts +20 -0
- package/dist/sections/draft-invoice.js +158 -0
- package/dist/sections/order-tracking.d.ts +19 -0
- package/dist/sections/order-tracking.js +141 -0
- package/dist/sections-CNUMWTF_.js +77 -0
- package/dist/sections.d.ts +19 -0
- package/dist/seo-GpDRhAAA.js +78 -0
- package/dist/session-CKvmFJyg.js +379 -0
- package/dist/session.d.ts +24 -0
- package/dist/settings-B0NO2A6Y.js +78 -0
- package/dist/settings.d.ts +51 -0
- package/dist/source-marker-DhqSkW7n.js +4 -0
- package/dist/sources-B-3JFJfb.js +46 -0
- package/dist/sources.d.ts +7 -0
- package/dist/store-8Q-_LFrQ.js +152 -0
- package/dist/store-contact--hY6CoFb.js +19 -0
- package/dist/store-contact.d.ts +6 -0
- package/dist/store.d.ts +17 -0
- package/dist/storefront-language-DrMjVbmk.js +29 -0
- package/dist/storefront-language.d.ts +14 -0
- package/dist/storefront-text-D25ae1qf.js +11 -0
- package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
- package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
- package/dist/storefront-text.d.ts +4 -0
- package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
- package/dist/stripe-dialog.d.ts +30 -0
- package/dist/telemetry-HuZXWBPi.js +114 -0
- package/dist/telemetry.d.ts +10 -0
- package/dist/templates-CAqoNyTW.js +40 -0
- package/dist/templates.d.ts +15 -0
- package/dist/urls-CpGQPxvf.js +71 -0
- package/dist/urls.d.ts +18 -0
- package/dist/use-checkout-CZcxXJXi.js +42 -0
- package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
- package/dist/use-checkout-payment.d.ts +18 -0
- package/dist/use-checkout.d.ts +36 -0
- package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
- package/dist/use-order-payment-BKIz_IO0.js +45 -0
- package/dist/use-order-payment.d.ts +18 -0
- package/dist/use-payment-token-CErx-tN-.js +105 -0
- package/dist/vite-payment-logos-kziu7wKb.js +27 -0
- package/dist/vite-payment-logos.d.ts +24 -0
- package/dist/vite-public-assets-DW2fgtVb.js +209 -0
- package/dist/vite-public-assets.d.ts +29 -0
- package/dist/vite-sections-DTBr0-Kg.js +464 -0
- package/dist/vite-sections.d.ts +21 -0
- package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
- package/dist/vite-theme-ui.d.ts +10 -0
- package/dist/vite.d.ts +36 -0
- package/dist/vite.js +127 -0
- package/dist/voucher-payment-CVFDZ1L8.js +66 -0
- package/dist/worker-CNi-gzCu.js +86 -0
- package/dist/worker.d.ts +27 -0
- package/package.json +100 -76
- package/src/sections/account-addresses.tsx +215 -0
- package/src/sections/account-order.tsx +424 -0
- package/src/sections/account-orders.tsx +259 -0
- package/src/sections/account-profile.tsx +70 -0
- package/src/sections/checkout.tsx +586 -0
- package/src/sections/complaint-book.tsx +325 -0
- package/src/sections/design-system.tsx +146 -0
- package/src/sections/draft-invoice.tsx +105 -0
- package/src/sections/order-tracking.tsx +81 -0
- package/src/vite-client.d.ts +4 -0
- package/bin/ecomiq.mjs +0 -179
- package/public/design-bridge.js +0 -318
- package/src/api-proxy.ts +0 -194
- package/src/auth/create-storefront-auth.ts +0 -94
- package/src/auth/ecomiq-oauth-password-client.ts +0 -26
- package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
- package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
- package/src/auth/ecomiq-oauth-password-types.ts +0 -24
- package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
- package/src/auth/env.ts +0 -7
- package/src/auth/secure-cookies.ts +0 -23
- package/src/auth/storefront-auth-base-url.ts +0 -117
- package/src/auth/storefront-auth.ts +0 -7
- package/src/cart-session.ts +0 -239
- package/src/client.ts +0 -48
- package/src/codegen.mjs +0 -801
- package/src/components/account/customer-addresses-panel.tsx +0 -678
- package/src/components/account/customer-order-detail-panel.tsx +0 -285
- package/src/components/account/customer-orders-panel.tsx +0 -192
- package/src/components/account/customer-profile-panel.tsx +0 -379
- package/src/components/checkout/cart-sync.ts +0 -143
- package/src/components/checkout/checkout-contact-section.tsx +0 -76
- package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
- package/src/components/checkout/checkout-form-controls.tsx +0 -139
- package/src/components/checkout/checkout-line-items.tsx +0 -43
- package/src/components/checkout/checkout-order-summary.tsx +0 -165
- package/src/components/checkout/checkout-payment-section.tsx +0 -212
- package/src/components/checkout/checkout-session-view.tsx +0 -564
- package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
- package/src/components/checkout/checkout-shipping-section.tsx +0 -98
- package/src/components/checkout/checkout-states.tsx +0 -73
- package/src/components/checkout/checkout-step-card.tsx +0 -101
- package/src/components/checkout/checkout-steps.ts +0 -111
- package/src/components/checkout/form-schema.ts +0 -281
- package/src/components/checkout/format.ts +0 -15
- package/src/components/checkout/index.ts +0 -5
- package/src/components/checkout/map-customer-to-checkout.ts +0 -188
- package/src/components/checkout/types.ts +0 -41
- package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
- package/src/components/checkout/use-checkout-geo.ts +0 -50
- package/src/components/checkout/use-checkout-session.ts +0 -640
- package/src/components/hero.tsx +0 -291
- package/src/config.d.ts +0 -94
- package/src/config.mjs +0 -146
- package/src/format-money.ts +0 -31
- package/src/health.ts +0 -90
- package/src/hooks/index.ts +0 -9
- package/src/hooks/recently-viewed.ts +0 -125
- package/src/index.ts +0 -349
- package/src/link.tsx +0 -2
- package/src/pages/account/account-page-skeleton.tsx +0 -69
- package/src/pages/account/account-page-view.tsx +0 -270
- package/src/pages/account/account-shell.tsx +0 -101
- package/src/pages/account/parse-account-route.ts +0 -36
- package/src/pages/account.tsx +0 -16
- package/src/pages/api.$.ts +0 -20
- package/src/pages/api.auth.$.ts +0 -16
- package/src/pages/checkout.$checkoutId.tsx +0 -60
- package/src/pages/checkout.tsx +0 -31
- package/src/pages/pages.$pageSlug.tsx +0 -74
- package/src/pages/thank-you.tsx +0 -53
- package/src/providers/auth.tsx +0 -96
- package/src/providers/cart.tsx +0 -275
- package/src/providers/ecomiq.ts +0 -137
- package/src/providers/index.ts +0 -26
- package/src/proxy-api.ts +0 -20
- package/src/public-config.ts +0 -24
- package/src/review-submission.ts +0 -89
- package/src/runtime/context.ts +0 -2
- package/src/runtime/default-layout.tsx +0 -64
- package/src/runtime/head.ts +0 -149
- package/src/runtime/page.tsx +0 -143
- package/src/runtime/postcss-browser-stub.mjs +0 -5
- package/src/server-config-store.ts +0 -35
- package/src/server.ts +0 -286
- package/src/styles.css +0 -76
- package/src/ui/badge.tsx +0 -32
- package/src/ui/button.tsx +0 -31
- package/src/ui/card.tsx +0 -31
- package/src/ui/carousel.tsx +0 -218
- package/src/ui/cn.ts +0 -6
- package/src/ui/index.ts +0 -8
- package/src/ui/separator.tsx +0 -6
- package/src/ui/skeleton.tsx +0 -6
- package/src/ui/storefront-error.tsx +0 -49
- package/src/utils.ts +0 -36
- package/src/vite-inline.mjs +0 -263
- package/templates/__root.tsx +0 -83
- package/templates/router.tsx +0 -19
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { currentConsent } from "./consent-B13rBpBs.js";
|
|
2
|
+
import { expiredPurchaseCookie, readPurchase } from "./cookies-pLozllyB.js";
|
|
3
|
+
import { itemsValue } from "./events-Dt81tIp1.js";
|
|
4
|
+
import { configureTracker, startTracker, track } from "./tracker-p4yaoOyk.js";
|
|
5
|
+
import { useLocation, useMatches, useRouteLoaderData } from "react-router";
|
|
6
|
+
import { useEffect, useRef } from "react";
|
|
7
|
+
// src/analytics/analytics.tsx
|
|
8
|
+
const ONCE_STORAGE = "ecomiq_analytics_once";
|
|
9
|
+
const sentOnce = /* @__PURE__ */ new Set();
|
|
10
|
+
const purchases = /* @__PURE__ */ new Set();
|
|
11
|
+
function firstTime(key, remember) {
|
|
12
|
+
if (sentOnce.has(key)) return false;
|
|
13
|
+
sentOnce.add(key);
|
|
14
|
+
if (!remember) return true;
|
|
15
|
+
try {
|
|
16
|
+
const stored = (sessionStorage.getItem(ONCE_STORAGE) ?? "").split(" ").filter((value) => value !== "");
|
|
17
|
+
if (stored.includes(key)) return false;
|
|
18
|
+
sessionStorage.setItem(ONCE_STORAGE, [...stored.slice(-19), key].join(" "));
|
|
19
|
+
} catch {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function pageKey(location) {
|
|
25
|
+
const params = new URLSearchParams(location.search);
|
|
26
|
+
params.delete("variant");
|
|
27
|
+
return `${location.pathname}?${params}`;
|
|
28
|
+
}
|
|
29
|
+
function pageAnalytics(matches) {
|
|
30
|
+
return matches.flatMap((match) => {
|
|
31
|
+
if (match.id === "root") return [];
|
|
32
|
+
const analytics = match.loaderData?.analytics;
|
|
33
|
+
if (analytics === void 0 || analytics === null) return [];
|
|
34
|
+
return [analytics];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function trackPurchase(location, paid) {
|
|
38
|
+
const purchase = readPurchase(document.cookie);
|
|
39
|
+
if (purchase === null) return;
|
|
40
|
+
if (!(location.pathname === "/checkout/done" && new URLSearchParams(location.search).get("order") === purchase.orderId) && !paid.includes(purchase.orderId)) return;
|
|
41
|
+
document.cookie = expiredPurchaseCookie();
|
|
42
|
+
if (purchases.has(purchase.orderId)) return;
|
|
43
|
+
purchases.add(purchase.orderId);
|
|
44
|
+
const { orderId, currency, value, tax, shipping, items } = purchase;
|
|
45
|
+
track({
|
|
46
|
+
name: "purchase",
|
|
47
|
+
params: {
|
|
48
|
+
transaction_id: orderId,
|
|
49
|
+
currency,
|
|
50
|
+
value,
|
|
51
|
+
tax,
|
|
52
|
+
shipping,
|
|
53
|
+
items
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function quantities(items) {
|
|
58
|
+
const lines = /* @__PURE__ */ new Map();
|
|
59
|
+
for (const item of items) {
|
|
60
|
+
const key = `${item.item_id}\u0000${item.item_variant ?? ""}`;
|
|
61
|
+
lines.set(key, {
|
|
62
|
+
...item,
|
|
63
|
+
quantity: (lines.get(key)?.quantity ?? 0) + (item.quantity ?? 1)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return lines;
|
|
67
|
+
}
|
|
68
|
+
function changed(from, to) {
|
|
69
|
+
const before = quantities(from);
|
|
70
|
+
const changes = [];
|
|
71
|
+
for (const [key, item] of quantities(to)) {
|
|
72
|
+
const quantity = item.quantity - (before.get(key)?.quantity ?? 0);
|
|
73
|
+
if (quantity > 0) changes.push({
|
|
74
|
+
...item,
|
|
75
|
+
quantity
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return changes;
|
|
79
|
+
}
|
|
80
|
+
function cartEvents(previous, next) {
|
|
81
|
+
const events = [];
|
|
82
|
+
const added = changed(previous, next);
|
|
83
|
+
const removed = changed(next, previous);
|
|
84
|
+
if (added.length > 0) events.push({
|
|
85
|
+
name: "add_to_cart",
|
|
86
|
+
params: {
|
|
87
|
+
value: itemsValue(added),
|
|
88
|
+
items: added
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
if (removed.length > 0) events.push({
|
|
92
|
+
name: "remove_from_cart",
|
|
93
|
+
params: {
|
|
94
|
+
value: itemsValue(removed),
|
|
95
|
+
items: removed
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return events;
|
|
99
|
+
}
|
|
100
|
+
function Analytics() {
|
|
101
|
+
const config = useRouteLoaderData("root")?.analytics ?? null;
|
|
102
|
+
const location = useLocation();
|
|
103
|
+
const matches = useMatches();
|
|
104
|
+
const enabled = config !== null;
|
|
105
|
+
const page = pageKey(location);
|
|
106
|
+
const pages = pageAnalytics(matches);
|
|
107
|
+
const paid = pages.flatMap((analytics) => analytics.paid === void 0 ? [] : [analytics.paid]).join(" ");
|
|
108
|
+
const cart = config?.cart ?? null;
|
|
109
|
+
const previousCart = useRef(void 0);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (config !== null) configureTracker(config);
|
|
112
|
+
}, [config]);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (enabled) return startTracker();
|
|
115
|
+
}, [enabled]);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (!enabled) return;
|
|
118
|
+
const consent = currentConsent();
|
|
119
|
+
if (consent !== null && !consent.analytics && !consent.marketing) return;
|
|
120
|
+
track({
|
|
121
|
+
name: "page_view",
|
|
122
|
+
params: {
|
|
123
|
+
page_location: new URL(`${location.pathname}${location.search}`, window.location.origin).href,
|
|
124
|
+
page_title: document.title
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
for (const analytics of pages) {
|
|
128
|
+
if (analytics.once !== void 0 && !firstTime(analytics.once, consent !== null)) continue;
|
|
129
|
+
for (const event of analytics.events) track(event);
|
|
130
|
+
}
|
|
131
|
+
}, [enabled, page]);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (enabled) trackPurchase(location, paid.split(" "));
|
|
134
|
+
}, [
|
|
135
|
+
enabled,
|
|
136
|
+
page,
|
|
137
|
+
paid
|
|
138
|
+
]);
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!enabled) return;
|
|
141
|
+
const previous = previousCart.current;
|
|
142
|
+
previousCart.current = cart;
|
|
143
|
+
if (previous === void 0 || cart === null) return;
|
|
144
|
+
for (const event of cartEvents(previous ?? [], cart)) track(event);
|
|
145
|
+
}, [enabled, cart]);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
export { Analytics };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function Analytics(): null;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { worker } from "../worker-CNi-gzCu.js";
|
|
2
|
+
import { readConsent } from "./cookies-pLozllyB.js";
|
|
3
|
+
import { cartItem } from "./events-Dt81tIp1.js";
|
|
4
|
+
// src/analytics/config.ts
|
|
5
|
+
const PRIVACY_PAGE = "privacidad";
|
|
6
|
+
const NOTICE_VERSION = 2;
|
|
7
|
+
function filled(value) {
|
|
8
|
+
const trimmed = value?.trim() ?? "";
|
|
9
|
+
if (trimmed === "") return null;
|
|
10
|
+
return trimmed;
|
|
11
|
+
}
|
|
12
|
+
function customScripts(settings) {
|
|
13
|
+
const { enabled, head, bodyEnd } = settings.cms.settings.customScripts;
|
|
14
|
+
const scripts = {
|
|
15
|
+
head: filled(head),
|
|
16
|
+
bodyEnd: filled(bodyEnd)
|
|
17
|
+
};
|
|
18
|
+
if (!enabled || scripts.head === null && scripts.bodyEnd === null) return null;
|
|
19
|
+
return scripts;
|
|
20
|
+
}
|
|
21
|
+
function trackingTools(settings) {
|
|
22
|
+
const { tracking } = settings.cms.settings;
|
|
23
|
+
const tools = {
|
|
24
|
+
googleAnalytics: filled(tracking.googleAnalyticsMeasurementId),
|
|
25
|
+
googleTagManager: filled(tracking.googleTagManagerContainerId),
|
|
26
|
+
metaPixel: filled(tracking.metaPixelId),
|
|
27
|
+
scripts: customScripts(settings)
|
|
28
|
+
};
|
|
29
|
+
if (tools.googleAnalytics === null && tools.googleTagManager === null && tools.metaPixel === null && tools.scripts === null) return null;
|
|
30
|
+
return tools;
|
|
31
|
+
}
|
|
32
|
+
function privacyPage(settings) {
|
|
33
|
+
return settings.cms.pages.find((page) => page.slug === PRIVACY_PAGE);
|
|
34
|
+
}
|
|
35
|
+
function consentVersion(tools, settings) {
|
|
36
|
+
let used = "";
|
|
37
|
+
if (tools.googleAnalytics !== null) used += "g";
|
|
38
|
+
if (tools.googleTagManager !== null) used += "t";
|
|
39
|
+
if (tools.metaPixel !== null) used += "m";
|
|
40
|
+
if (tools.scripts !== null) used += "s";
|
|
41
|
+
return `${NOTICE_VERSION}.${used}.${privacyPage(settings)?.version ?? 0}`;
|
|
42
|
+
}
|
|
43
|
+
function readAnalytics(context, settings, cart) {
|
|
44
|
+
if (context.get(worker).env.ECOMIQ_PREVIEW === "1") return null;
|
|
45
|
+
const tools = trackingTools(settings);
|
|
46
|
+
if (tools === null) return null;
|
|
47
|
+
return {
|
|
48
|
+
...tools,
|
|
49
|
+
currency: settings.store.currency,
|
|
50
|
+
language: settings.store.language,
|
|
51
|
+
privacy: privacyPage(settings) === void 0 ? null : `/pages/${PRIVACY_PAGE}`,
|
|
52
|
+
consentVersion: consentVersion(tools, settings),
|
|
53
|
+
cart: cart === null ? null : cart.items.map(cartItem)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function trackingConsented(request, settings) {
|
|
57
|
+
const tools = trackingTools(settings);
|
|
58
|
+
const consent = readConsent(request.headers.get("Cookie"));
|
|
59
|
+
if (tools === null || consent === null || consent.version !== consentVersion(tools, settings)) return false;
|
|
60
|
+
return consent.analytics || consent.marketing;
|
|
61
|
+
}
|
|
62
|
+
export { readAnalytics, trackingConsented };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AnalyticsItem } from "./events.js";
|
|
2
|
+
import { RouterContextProvider } from "react-router";
|
|
3
|
+
import { StorefrontCart, StorefrontSettings } from "@ecomiq/core";
|
|
4
|
+
type AnalyticsScripts = {
|
|
5
|
+
head: string | null;
|
|
6
|
+
bodyEnd: string | null;
|
|
7
|
+
};
|
|
8
|
+
type TrackingTools = {
|
|
9
|
+
googleAnalytics: string | null;
|
|
10
|
+
googleTagManager: string | null;
|
|
11
|
+
metaPixel: string | null;
|
|
12
|
+
scripts: AnalyticsScripts | null;
|
|
13
|
+
};
|
|
14
|
+
export type AnalyticsConfig = TrackingTools & {
|
|
15
|
+
currency: string;
|
|
16
|
+
language: string;
|
|
17
|
+
privacy: string | null;
|
|
18
|
+
consentVersion: string;
|
|
19
|
+
cart: AnalyticsItem[] | null;
|
|
20
|
+
};
|
|
21
|
+
export declare function readAnalytics(context: Readonly<RouterContextProvider>, settings: StorefrontSettings, cart: StorefrontCart | null): AnalyticsConfig | null;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { consentCookie, cookieDomains, readConsent, revokedCookies } from "./cookies-pLozllyB.js";
|
|
2
|
+
// src/analytics/consent.ts
|
|
3
|
+
const CONSENT_EVENT = "ecomiq:consent";
|
|
4
|
+
const CONSENT_OPEN_EVENT = "ecomiq:consent-open";
|
|
5
|
+
let terms = null;
|
|
6
|
+
function consentPurposes(config) {
|
|
7
|
+
const generic = config.googleTagManager !== null || config.scripts !== null;
|
|
8
|
+
return {
|
|
9
|
+
analytics: config.googleAnalytics !== null || generic,
|
|
10
|
+
marketing: config.metaPixel !== null || generic
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function currentConsent() {
|
|
14
|
+
const stored = readConsent(document.cookie);
|
|
15
|
+
if (terms === null || stored === null || stored.version !== terms.version) return null;
|
|
16
|
+
return stored;
|
|
17
|
+
}
|
|
18
|
+
function setConsent(choice) {
|
|
19
|
+
if (terms === null) return;
|
|
20
|
+
const consent = {
|
|
21
|
+
analytics: terms.analytics && choice.analytics === true,
|
|
22
|
+
marketing: terms.marketing && choice.marketing === true,
|
|
23
|
+
version: terms.version,
|
|
24
|
+
date: (/* @__PURE__ */ new Date()).toISOString()
|
|
25
|
+
};
|
|
26
|
+
document.cookie = consentCookie(consent);
|
|
27
|
+
for (const name of revokedCookies(document.cookie, consent)) for (const domain of cookieDomains(location.hostname)) document.cookie = `${name}=; Max-Age=0; Path=/${domain === null ? "" : `; Domain=${domain}`}`;
|
|
28
|
+
window.dispatchEvent(new CustomEvent(CONSENT_EVENT, { detail: consent }));
|
|
29
|
+
}
|
|
30
|
+
function installConsent(config) {
|
|
31
|
+
terms = {
|
|
32
|
+
...consentPurposes(config),
|
|
33
|
+
version: config.consentVersion
|
|
34
|
+
};
|
|
35
|
+
const api = window.ecomiq?.consent ?? {
|
|
36
|
+
get: currentConsent,
|
|
37
|
+
set: setConsent
|
|
38
|
+
};
|
|
39
|
+
window.ecomiq = {
|
|
40
|
+
...window.ecomiq,
|
|
41
|
+
consent: api
|
|
42
|
+
};
|
|
43
|
+
return api;
|
|
44
|
+
}
|
|
45
|
+
function onConsent(listener) {
|
|
46
|
+
const handle = (event) => listener(event.detail);
|
|
47
|
+
window.addEventListener(CONSENT_EVENT, handle);
|
|
48
|
+
return () => window.removeEventListener(CONSENT_EVENT, handle);
|
|
49
|
+
}
|
|
50
|
+
function openConsent() {
|
|
51
|
+
window.dispatchEvent(new Event(CONSENT_OPEN_EVENT));
|
|
52
|
+
}
|
|
53
|
+
function onConsentOpen(listener) {
|
|
54
|
+
window.addEventListener(CONSENT_OPEN_EVENT, listener);
|
|
55
|
+
return () => window.removeEventListener(CONSENT_OPEN_EVENT, listener);
|
|
56
|
+
}
|
|
57
|
+
export { consentPurposes, currentConsent, installConsent, onConsent, onConsentOpen, openConsent };
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { consentPurposes, installConsent, onConsent, onConsentOpen, openConsent } from "./consent-B13rBpBs.js";
|
|
2
|
+
import { Link, useRouteLoaderData } from "react-router";
|
|
3
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
// src/analytics/consent-banner.tsx
|
|
6
|
+
const PURPOSES = ["analytics", "marketing"];
|
|
7
|
+
const REJECTED = {
|
|
8
|
+
analytics: false,
|
|
9
|
+
marketing: false
|
|
10
|
+
};
|
|
11
|
+
const ACCEPTED = {
|
|
12
|
+
analytics: true,
|
|
13
|
+
marketing: true
|
|
14
|
+
};
|
|
15
|
+
const COPY = {
|
|
16
|
+
es: {
|
|
17
|
+
label: "Cookies",
|
|
18
|
+
privacy: "Privacidad",
|
|
19
|
+
configure: "Configurar",
|
|
20
|
+
choose: "Elige qué aceptas. Las cookies necesarias para el carrito y tu sesión siempre están activas.",
|
|
21
|
+
accept: "Aceptar",
|
|
22
|
+
reject: "Rechazar",
|
|
23
|
+
save: "Guardar",
|
|
24
|
+
and: " y ",
|
|
25
|
+
others: "otros proveedores",
|
|
26
|
+
uses: {
|
|
27
|
+
analytics: "medir visitas",
|
|
28
|
+
marketing: "mostrarte publicidad"
|
|
29
|
+
},
|
|
30
|
+
options: {
|
|
31
|
+
analytics: {
|
|
32
|
+
name: "Medición",
|
|
33
|
+
use: "para contar visitas y compras"
|
|
34
|
+
},
|
|
35
|
+
marketing: {
|
|
36
|
+
name: "Publicidad",
|
|
37
|
+
use: "para medir campañas y mostrarte anuncios"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
notice: (providers, uses) => `Con tu permiso, usamos cookies de ${providers} para ${uses}. Los datos que recogen pueden tratarse fuera del país, por ejemplo en Estados Unidos. Puedes cambiar tu elección en «Cookies», al pie de la página.`
|
|
41
|
+
},
|
|
42
|
+
en: {
|
|
43
|
+
label: "Cookies",
|
|
44
|
+
privacy: "Privacy",
|
|
45
|
+
configure: "Customize",
|
|
46
|
+
choose: "Choose what you accept. Cookies needed for the cart and your session are always on.",
|
|
47
|
+
accept: "Accept",
|
|
48
|
+
reject: "Reject",
|
|
49
|
+
save: "Save",
|
|
50
|
+
and: " and ",
|
|
51
|
+
others: "other providers",
|
|
52
|
+
uses: {
|
|
53
|
+
analytics: "measure visits",
|
|
54
|
+
marketing: "show you ads"
|
|
55
|
+
},
|
|
56
|
+
options: {
|
|
57
|
+
analytics: {
|
|
58
|
+
name: "Measurement",
|
|
59
|
+
use: "to count visits and purchases"
|
|
60
|
+
},
|
|
61
|
+
marketing: {
|
|
62
|
+
name: "Advertising",
|
|
63
|
+
use: "to measure campaigns and show you ads"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
notice: (providers, uses) => `With your permission, we use cookies from ${providers} to ${uses}. The data they collect may be processed outside your country, for example in the United States. You can change your choice under “Cookies” at the bottom of the page.`
|
|
67
|
+
},
|
|
68
|
+
pt: {
|
|
69
|
+
label: "Cookies",
|
|
70
|
+
privacy: "Privacidade",
|
|
71
|
+
configure: "Configurar",
|
|
72
|
+
choose: "Escolha o que aceita. Os cookies necessários para o carrinho e a sua sessão estão sempre ativos.",
|
|
73
|
+
accept: "Aceitar",
|
|
74
|
+
reject: "Recusar",
|
|
75
|
+
save: "Salvar",
|
|
76
|
+
and: " e ",
|
|
77
|
+
others: "outros fornecedores",
|
|
78
|
+
uses: {
|
|
79
|
+
analytics: "medir visitas",
|
|
80
|
+
marketing: "mostrar publicidade"
|
|
81
|
+
},
|
|
82
|
+
options: {
|
|
83
|
+
analytics: {
|
|
84
|
+
name: "Medição",
|
|
85
|
+
use: "para contar visitas e compras"
|
|
86
|
+
},
|
|
87
|
+
marketing: {
|
|
88
|
+
name: "Publicidade",
|
|
89
|
+
use: "para medir campanhas e mostrar anúncios"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
notice: (providers, uses) => `Com a sua permissão, usamos cookies de ${providers} para ${uses}. Os dados que coletam podem ser tratados fora do país, por exemplo nos Estados Unidos. Você pode mudar sua escolha em “Cookies”, no rodapé da página.`
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
function consentCopy(language) {
|
|
96
|
+
return COPY[language.slice(0, 2).toLowerCase()] ?? COPY.es;
|
|
97
|
+
}
|
|
98
|
+
function useAnalyticsConfig() {
|
|
99
|
+
return useRouteLoaderData("root")?.analytics ?? null;
|
|
100
|
+
}
|
|
101
|
+
function providers(config, purpose) {
|
|
102
|
+
const generic = config.googleTagManager !== null || config.scripts !== null;
|
|
103
|
+
return {
|
|
104
|
+
google: purpose === "marketing" ? config.googleTagManager !== null : config.googleAnalytics !== null || config.googleTagManager !== null,
|
|
105
|
+
meta: purpose !== "analytics" && config.metaPixel !== null,
|
|
106
|
+
others: generic
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function providerNames(found, copy) {
|
|
110
|
+
const names = [];
|
|
111
|
+
if (found.google) names.push("Google");
|
|
112
|
+
if (found.meta) names.push("Meta");
|
|
113
|
+
if (found.others) names.push(copy.others);
|
|
114
|
+
const last = names.pop() ?? "";
|
|
115
|
+
if (names.length === 0) return last;
|
|
116
|
+
return `${names.join(", ")}${copy.and}${last}`;
|
|
117
|
+
}
|
|
118
|
+
function ConsentBanner({ className, classNames = {} }) {
|
|
119
|
+
const config = useAnalyticsConfig();
|
|
120
|
+
const version = config?.consentVersion ?? null;
|
|
121
|
+
const id = useId();
|
|
122
|
+
const [open, setOpen] = useState(false);
|
|
123
|
+
const [configuring, setConfiguring] = useState(false);
|
|
124
|
+
const [choice, setChoice] = useState(REJECTED);
|
|
125
|
+
const [focused, setFocused] = useState(0);
|
|
126
|
+
const banner = useRef(null);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (config === null) return;
|
|
129
|
+
setOpen(installConsent(config).get() === null);
|
|
130
|
+
setConfiguring(false);
|
|
131
|
+
const stopChoice = onConsent(() => setOpen(false));
|
|
132
|
+
const stopOpen = onConsentOpen(() => {
|
|
133
|
+
setConfiguring(false);
|
|
134
|
+
setOpen(true);
|
|
135
|
+
setFocused((count) => count + 1);
|
|
136
|
+
});
|
|
137
|
+
return () => {
|
|
138
|
+
stopChoice();
|
|
139
|
+
stopOpen();
|
|
140
|
+
};
|
|
141
|
+
}, [version]);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (focused > 0) banner.current?.focus();
|
|
144
|
+
}, [focused]);
|
|
145
|
+
if (config === null || !open) return null;
|
|
146
|
+
const copy = consentCopy(config.language);
|
|
147
|
+
const offered = consentPurposes(config);
|
|
148
|
+
const purposes = PURPOSES.filter((purpose) => offered[purpose]);
|
|
149
|
+
const choose = (consent) => installConsent(config).set(consent);
|
|
150
|
+
const configure = () => {
|
|
151
|
+
const current = installConsent(config).get();
|
|
152
|
+
setChoice(current === null ? REJECTED : {
|
|
153
|
+
analytics: current.analytics,
|
|
154
|
+
marketing: current.marketing
|
|
155
|
+
});
|
|
156
|
+
setConfiguring(true);
|
|
157
|
+
setFocused((count) => count + 1);
|
|
158
|
+
};
|
|
159
|
+
const privacy = config.privacy === null ? null : /* @__PURE__ */ jsxs(Fragment$1, { children: [" ", /* @__PURE__ */ jsx(Link, {
|
|
160
|
+
to: config.privacy,
|
|
161
|
+
className: classNames.link,
|
|
162
|
+
children: copy.privacy
|
|
163
|
+
})] });
|
|
164
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
165
|
+
ref: banner,
|
|
166
|
+
tabIndex: -1,
|
|
167
|
+
"aria-label": copy.label,
|
|
168
|
+
className,
|
|
169
|
+
children: [configuring ? /* @__PURE__ */ jsxs("div", {
|
|
170
|
+
className: classNames.options,
|
|
171
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
172
|
+
className: classNames.text,
|
|
173
|
+
children: [copy.choose, privacy]
|
|
174
|
+
}), purposes.map((purpose) => /* @__PURE__ */ jsxs("p", {
|
|
175
|
+
className: classNames.option,
|
|
176
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
177
|
+
id: `${id}${purpose}`,
|
|
178
|
+
type: "checkbox",
|
|
179
|
+
checked: choice[purpose],
|
|
180
|
+
onChange: (event) => setChoice({
|
|
181
|
+
...choice,
|
|
182
|
+
[purpose]: event.currentTarget.checked
|
|
183
|
+
})
|
|
184
|
+
}), /* @__PURE__ */ jsxs("label", {
|
|
185
|
+
htmlFor: `${id}${purpose}`,
|
|
186
|
+
children: [
|
|
187
|
+
/* @__PURE__ */ jsx("strong", { children: copy.options[purpose].name }),
|
|
188
|
+
": ",
|
|
189
|
+
copy.options[purpose].use,
|
|
190
|
+
" (",
|
|
191
|
+
providerNames(providers(config, purpose), copy),
|
|
192
|
+
")."
|
|
193
|
+
]
|
|
194
|
+
})]
|
|
195
|
+
}, purpose))]
|
|
196
|
+
}) : /* @__PURE__ */ jsxs("p", {
|
|
197
|
+
className: classNames.text,
|
|
198
|
+
children: [
|
|
199
|
+
copy.notice(providerNames(providers(config, null), copy), purposes.map((purpose) => copy.uses[purpose]).join(copy.and)),
|
|
200
|
+
privacy,
|
|
201
|
+
purposes.length > 1 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [privacy === null ? " " : " · ", /* @__PURE__ */ jsx("button", {
|
|
202
|
+
type: "button",
|
|
203
|
+
className: classNames.configure ?? classNames.link,
|
|
204
|
+
onClick: configure,
|
|
205
|
+
children: copy.configure
|
|
206
|
+
})] }) : null
|
|
207
|
+
]
|
|
208
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
209
|
+
className: classNames.actions,
|
|
210
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
211
|
+
type: "button",
|
|
212
|
+
className: classNames.reject,
|
|
213
|
+
onClick: () => choose(REJECTED),
|
|
214
|
+
children: copy.reject
|
|
215
|
+
}), configuring ? /* @__PURE__ */ jsx("button", {
|
|
216
|
+
type: "button",
|
|
217
|
+
className: classNames.accept,
|
|
218
|
+
onClick: () => choose(choice),
|
|
219
|
+
children: copy.save
|
|
220
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
221
|
+
type: "button",
|
|
222
|
+
className: classNames.accept,
|
|
223
|
+
onClick: () => choose(ACCEPTED),
|
|
224
|
+
children: copy.accept
|
|
225
|
+
})]
|
|
226
|
+
})]
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
function ConsentLink({ className, children }) {
|
|
230
|
+
const config = useAnalyticsConfig();
|
|
231
|
+
if (config === null) return null;
|
|
232
|
+
return /* @__PURE__ */ jsx("button", {
|
|
233
|
+
type: "button",
|
|
234
|
+
className,
|
|
235
|
+
onClick: openConsent,
|
|
236
|
+
children: children ?? consentCopy(config.language).label
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
export { ConsentBanner, ConsentLink };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
export type ConsentBannerProps = {
|
|
3
|
+
className?: string;
|
|
4
|
+
classNames?: {
|
|
5
|
+
text?: string;
|
|
6
|
+
link?: string;
|
|
7
|
+
configure?: string;
|
|
8
|
+
options?: string;
|
|
9
|
+
option?: string;
|
|
10
|
+
actions?: string;
|
|
11
|
+
accept?: string;
|
|
12
|
+
reject?: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export type ConsentLinkProps = {
|
|
16
|
+
className?: string;
|
|
17
|
+
children?: ReactNode;
|
|
18
|
+
};
|
|
19
|
+
export declare function ConsentBanner({ className, classNames }: ConsentBannerProps): import("react").JSX.Element | null;
|
|
20
|
+
export declare function ConsentLink({ className, children }: ConsentLinkProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { cartItem } from "./events-Dt81tIp1.js";
|
|
2
|
+
// src/analytics/cookies.ts
|
|
3
|
+
const CONSENT_COOKIE = "__Host-ecomiq-consent";
|
|
4
|
+
const PURCHASE_COOKIE = "__Host-ecomiq-purchase";
|
|
5
|
+
const CONSENT_MAX_AGE = 31536e3;
|
|
6
|
+
const PURCHASE_MAX_AGE = 600;
|
|
7
|
+
const PURCHASE_ITEMS = 20;
|
|
8
|
+
const PURCHASE_NAME_LENGTH = 60;
|
|
9
|
+
const PURCHASE_COOKIE_BYTES = 3800;
|
|
10
|
+
const CATEGORIES = ["analytics", "marketing"];
|
|
11
|
+
function cookieValue(cookies, name) {
|
|
12
|
+
for (const part of cookies.split(";")) {
|
|
13
|
+
const separator = part.indexOf("=");
|
|
14
|
+
if (separator > 0 && part.slice(0, separator).trim() === name) return part.slice(separator + 1).trim();
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
function grantedChoice(value) {
|
|
19
|
+
if (value === "none") return {
|
|
20
|
+
analytics: false,
|
|
21
|
+
marketing: false
|
|
22
|
+
};
|
|
23
|
+
const granted = value.split(".");
|
|
24
|
+
if (granted.some((category) => !CATEGORIES.some((known) => known === category))) return null;
|
|
25
|
+
return {
|
|
26
|
+
analytics: granted.includes("analytics"),
|
|
27
|
+
marketing: granted.includes("marketing")
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function readConsent(cookies) {
|
|
31
|
+
const value = cookieValue(cookies ?? "", CONSENT_COOKIE);
|
|
32
|
+
if (value === null) return null;
|
|
33
|
+
const [granted = "", version, date, ...rest] = value.split("|");
|
|
34
|
+
const choice = grantedChoice(granted);
|
|
35
|
+
if (choice === null || rest.length > 0 || !version || !date) return null;
|
|
36
|
+
return {
|
|
37
|
+
...choice,
|
|
38
|
+
version,
|
|
39
|
+
date
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function consentCookie(consent) {
|
|
43
|
+
const granted = CATEGORIES.filter((category) => consent[category]);
|
|
44
|
+
const choice = granted.length === 0 ? "none" : granted.join(".");
|
|
45
|
+
return `${CONSENT_COOKIE}=${choice}|${consent.version}|${consent.date}; Max-Age=${CONSENT_MAX_AGE}; Path=/; SameSite=Lax; Secure`;
|
|
46
|
+
}
|
|
47
|
+
function purchaseCookie(result) {
|
|
48
|
+
const { cart } = result;
|
|
49
|
+
const items = cart.items.slice(0, PURCHASE_ITEMS).map((item) => ({
|
|
50
|
+
...cartItem(item),
|
|
51
|
+
item_name: item.title.slice(0, PURCHASE_NAME_LENGTH)
|
|
52
|
+
}));
|
|
53
|
+
const purchase = {
|
|
54
|
+
orderId: result.orderId,
|
|
55
|
+
currency: cart.currencyCode,
|
|
56
|
+
value: cart.totals.total,
|
|
57
|
+
tax: cart.totals.taxTotal,
|
|
58
|
+
shipping: cart.totals.shippingTotal,
|
|
59
|
+
items
|
|
60
|
+
};
|
|
61
|
+
let value = encodeURIComponent(JSON.stringify(purchase));
|
|
62
|
+
while (value.length > PURCHASE_COOKIE_BYTES && purchase.items.length > 0) {
|
|
63
|
+
purchase.items.pop();
|
|
64
|
+
value = encodeURIComponent(JSON.stringify(purchase));
|
|
65
|
+
}
|
|
66
|
+
return `${PURCHASE_COOKIE}=${value}; Max-Age=${PURCHASE_MAX_AGE}; Path=/; SameSite=Lax; Secure`;
|
|
67
|
+
}
|
|
68
|
+
function expiredPurchaseCookie() {
|
|
69
|
+
return `${PURCHASE_COOKIE}=; Max-Age=0; Path=/; SameSite=Lax; Secure`;
|
|
70
|
+
}
|
|
71
|
+
function purchaseItem(value) {
|
|
72
|
+
if (typeof value !== "object" || value === null) return false;
|
|
73
|
+
const item = value;
|
|
74
|
+
return typeof item.item_id === "string" && typeof item.item_name === "string" && typeof item.price === "number";
|
|
75
|
+
}
|
|
76
|
+
function readPurchase(cookies) {
|
|
77
|
+
const value = cookieValue(cookies, PURCHASE_COOKIE);
|
|
78
|
+
if (value === null || value === "") return null;
|
|
79
|
+
try {
|
|
80
|
+
const purchase = JSON.parse(decodeURIComponent(value));
|
|
81
|
+
if (typeof purchase.orderId !== "string" || typeof purchase.currency !== "string") return null;
|
|
82
|
+
if (typeof purchase.value !== "number" || typeof purchase.tax !== "number" || typeof purchase.shipping !== "number") return null;
|
|
83
|
+
if (!Array.isArray(purchase.items) || !purchase.items.every(purchaseItem)) return null;
|
|
84
|
+
return purchase;
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const ANALYTICS_COOKIE = /^(_ga|_ga_[A-Za-z0-9]+|_gid|_gat(_.+)?)$/;
|
|
90
|
+
const MARKETING_COOKIE = /^(_fbp|_fbc|_gcl_[a-z]+)$/;
|
|
91
|
+
function revokedCookies(cookies, consent) {
|
|
92
|
+
return cookies.split(";").map((pair) => pair.split("=")[0]?.trim() ?? "").filter((name) => !consent.analytics && ANALYTICS_COOKIE.test(name) || !consent.marketing && MARKETING_COOKIE.test(name));
|
|
93
|
+
}
|
|
94
|
+
function cookieDomains(hostname) {
|
|
95
|
+
const parts = hostname.split(".");
|
|
96
|
+
const domains = [null];
|
|
97
|
+
for (let index = 0; index < parts.length - 1; index += 1) domains.push(`.${parts.slice(index).join(".")}`);
|
|
98
|
+
return domains;
|
|
99
|
+
}
|
|
100
|
+
export { CONSENT_COOKIE, PURCHASE_COOKIE, consentCookie, cookieDomains, expiredPurchaseCookie, purchaseCookie, readConsent, readPurchase, revokedCookies };
|