@aranova/tracking-react 0.9.0 → 0.10.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.
package/dist/index.mjs CHANGED
@@ -1,5 +1,8 @@
1
1
  // src/ConsentBanner.tsx
2
- import { useEffect, useState } from "react";
2
+ import { useEffect as useEffect2, useState as useState2 } from "react";
3
+
4
+ // src/hooks.ts
5
+ import { useCallback, useEffect, useState } from "react";
3
6
 
4
7
  // ../tracking-core/src/consent.ts
5
8
  var CONSENT_STATE_KEY = "consent_state";
@@ -28,6 +31,12 @@ function setConsentState(state) {
28
31
  if (typeof window.gtag === "function")
29
32
  window.gtag("consent", "update", buildConsentPayload(state));
30
33
  }
34
+ function resetConsent() {
35
+ if (typeof window === "undefined")
36
+ return;
37
+ window.localStorage.removeItem(CONSENT_STATE_KEY);
38
+ window.localStorage.removeItem(CONSENT_TIMESTAMP_KEY);
39
+ }
31
40
  function restoreStoredConsent() {
32
41
  const consentState = getConsentState();
33
42
  if (consentState === "granted" || consentState === "denied")
@@ -1331,6 +1340,16 @@ function createSalesClient(config) {
1331
1340
  ...cursor !== void 0 ? { cursor } : {}
1332
1341
  });
1333
1342
  },
1343
+ async summary(query) {
1344
+ const { range, include_categories, include_deleted_services, top_n, ...filters } = query;
1345
+ return salesRequest(config, "POST", "/sales/summary", {
1346
+ filters,
1347
+ range,
1348
+ ...include_categories !== void 0 ? { include_categories } : {},
1349
+ ...include_deleted_services !== void 0 ? { include_deleted_services } : {},
1350
+ ...top_n !== void 0 ? { top_n } : {}
1351
+ });
1352
+ },
1334
1353
  async get(id) {
1335
1354
  return salesRequest(config, "GET", `/sales/${id}`);
1336
1355
  },
@@ -1368,59 +1387,251 @@ function formatMoney(cents, currency, locale) {
1368
1387
  );
1369
1388
  }
1370
1389
 
1371
- // src/ConsentBanner.tsx
1372
- import { jsx, jsxs } from "react/jsx-runtime";
1373
- function ConsentBanner() {
1374
- const [consentState, setBannerConsentState] = useState("pending");
1375
- const [isReady, setIsReady] = useState(false);
1390
+ // src/hooks.ts
1391
+ function useGclid() {
1392
+ const [gclid, setGclid] = useState(null);
1376
1393
  useEffect(() => {
1377
- setBannerConsentState(getConsentState());
1378
- setIsReady(true);
1394
+ setGclid(getCookieValueFromDocument("gclid"));
1379
1395
  }, []);
1380
- if (!isReady || consentState !== "pending")
1381
- return null;
1382
- const handleAccept = () => {
1396
+ return gclid;
1397
+ }
1398
+ function useTrackingParams() {
1399
+ const [trackingParams, setTrackingParams] = useState(createEmptyTrackingParams());
1400
+ useEffect(() => {
1401
+ setTrackingParams(getTrackingParamsFromCookieReader(getCookieValueFromDocument));
1402
+ }, []);
1403
+ return trackingParams;
1404
+ }
1405
+ function useConsentState() {
1406
+ return useConsent().state;
1407
+ }
1408
+ function useConsent() {
1409
+ const [state, setState] = useState("pending");
1410
+ useEffect(() => {
1411
+ setState(getConsentState());
1412
+ const handleStorage = (event) => {
1413
+ if (event.key === CONSENT_STATE_KEY) setState(getConsentState());
1414
+ };
1415
+ window.addEventListener("storage", handleStorage);
1416
+ return () => window.removeEventListener("storage", handleStorage);
1417
+ }, []);
1418
+ const accept = useCallback(() => {
1383
1419
  setConsentState("granted");
1384
- setBannerConsentState("granted");
1420
+ setState("granted");
1421
+ }, []);
1422
+ const decline = useCallback(() => {
1423
+ setConsentState("denied");
1424
+ setState("denied");
1425
+ }, []);
1426
+ const reset = useCallback(() => {
1427
+ resetConsent();
1428
+ setState("pending");
1429
+ }, []);
1430
+ return {
1431
+ state,
1432
+ isPending: state === "pending",
1433
+ isGranted: state === "granted",
1434
+ isDenied: state === "denied",
1435
+ accept,
1436
+ decline,
1437
+ reset
1438
+ };
1439
+ }
1440
+
1441
+ // src/ConsentBanner.tsx
1442
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1443
+ var LIGHT_THEME = {
1444
+ background: "#ffffff",
1445
+ border: "#e5e7eb",
1446
+ text: "#111827",
1447
+ mutedText: "#4b5563",
1448
+ acceptBg: "#111827",
1449
+ acceptText: "#ffffff",
1450
+ declineBg: "transparent",
1451
+ declineText: "#111827",
1452
+ declineBorder: "#d1d5db",
1453
+ shadow: "0 -4px 16px -2px rgba(15, 23, 42, 0.08), 0 -2px 6px -1px rgba(15, 23, 42, 0.04)",
1454
+ linkColor: "#1f2937"
1455
+ };
1456
+ var DARK_THEME = {
1457
+ background: "#0f172a",
1458
+ border: "#1e293b",
1459
+ text: "#f1f5f9",
1460
+ mutedText: "#cbd5e1",
1461
+ acceptBg: "#f1f5f9",
1462
+ acceptText: "#0f172a",
1463
+ declineBg: "transparent",
1464
+ declineText: "#f1f5f9",
1465
+ declineBorder: "#334155",
1466
+ shadow: "0 -4px 16px -2px rgba(0, 0, 0, 0.5), 0 -2px 6px -1px rgba(0, 0, 0, 0.3)",
1467
+ linkColor: "#e2e8f0"
1468
+ };
1469
+ function useResolvedTheme(theme) {
1470
+ const [prefersDark, setPrefersDark] = useState2(false);
1471
+ useEffect2(() => {
1472
+ if (theme !== "auto" || typeof window === "undefined" || !window.matchMedia) return;
1473
+ const mql = window.matchMedia("(prefers-color-scheme: dark)");
1474
+ setPrefersDark(mql.matches);
1475
+ const onChange = (e) => setPrefersDark(e.matches);
1476
+ mql.addEventListener("change", onChange);
1477
+ return () => mql.removeEventListener("change", onChange);
1478
+ }, [theme]);
1479
+ if (theme === "dark") return DARK_THEME;
1480
+ if (theme === "auto" && prefersDark) return DARK_THEME;
1481
+ return LIGHT_THEME;
1482
+ }
1483
+ var DEFAULT_MESSAGE = "We use cookies to understand ad performance and improve how our marketing works across visits. You can accept or decline this tracking.";
1484
+ function ConsentBanner({
1485
+ message,
1486
+ title,
1487
+ acceptLabel = "Accept",
1488
+ declineLabel = "Decline",
1489
+ policyHref,
1490
+ policyLabel = "Learn more",
1491
+ onAccept,
1492
+ onDecline,
1493
+ position = "bottom",
1494
+ theme = "light",
1495
+ className,
1496
+ style
1497
+ } = {}) {
1498
+ const { isPending, accept, decline } = useConsent();
1499
+ const tokens = useResolvedTheme(theme);
1500
+ const [hasMounted, setHasMounted] = useState2(false);
1501
+ useEffect2(() => {
1502
+ setHasMounted(true);
1503
+ }, []);
1504
+ if (!hasMounted || !isPending) return null;
1505
+ const wrapperStyle = {
1506
+ position: "fixed",
1507
+ left: 0,
1508
+ right: 0,
1509
+ [position]: 0,
1510
+ zIndex: 2147483640,
1511
+ background: tokens.background,
1512
+ color: tokens.text,
1513
+ borderTop: position === "bottom" ? `1px solid ${tokens.border}` : "none",
1514
+ borderBottom: position === "top" ? `1px solid ${tokens.border}` : "none",
1515
+ boxShadow: tokens.shadow,
1516
+ padding: "16px 20px",
1517
+ boxSizing: "border-box",
1518
+ animation: `${ANIMATION_NAME}-${position} 200ms ease-out`,
1519
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
1520
+ ...style
1521
+ };
1522
+ const innerStyle = {
1523
+ maxWidth: 1100,
1524
+ margin: "0 auto",
1525
+ display: "flex",
1526
+ flexWrap: "wrap",
1527
+ gap: 16,
1528
+ alignItems: "center",
1529
+ justifyContent: "space-between"
1530
+ };
1531
+ const messageStyle = {
1532
+ flex: "1 1 320px",
1533
+ margin: 0,
1534
+ fontSize: 14,
1535
+ lineHeight: 1.5,
1536
+ color: tokens.mutedText
1537
+ };
1538
+ const titleStyle = {
1539
+ margin: "0 0 4px 0",
1540
+ fontSize: 14,
1541
+ fontWeight: 600,
1542
+ color: tokens.text
1543
+ };
1544
+ const actionsStyle = {
1545
+ display: "flex",
1546
+ gap: 8,
1547
+ flexShrink: 0
1548
+ };
1549
+ const buttonBase = {
1550
+ appearance: "none",
1551
+ fontFamily: "inherit",
1552
+ fontSize: 14,
1553
+ fontWeight: 500,
1554
+ padding: "8px 16px",
1555
+ borderRadius: 6,
1556
+ cursor: "pointer",
1557
+ border: "1px solid transparent",
1558
+ transition: "opacity 120ms ease"
1559
+ };
1560
+ const declineStyle = {
1561
+ ...buttonBase,
1562
+ background: tokens.declineBg,
1563
+ color: tokens.declineText,
1564
+ borderColor: tokens.declineBorder
1565
+ };
1566
+ const acceptStyle = {
1567
+ ...buttonBase,
1568
+ background: tokens.acceptBg,
1569
+ color: tokens.acceptText
1570
+ };
1571
+ const linkStyle = {
1572
+ color: tokens.linkColor,
1573
+ textDecoration: "underline",
1574
+ textUnderlineOffset: 2
1575
+ };
1576
+ const handleAccept = () => {
1577
+ accept();
1578
+ onAccept?.();
1385
1579
  };
1386
1580
  const handleDecline = () => {
1387
- setConsentState("denied");
1388
- setBannerConsentState("denied");
1581
+ decline();
1582
+ onDecline?.();
1389
1583
  };
1390
- return /* @__PURE__ */ jsx("div", { className: "fixed bottom-0 left-0 right-0 z-50 border-t border-gray-200 bg-white p-4 shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-5xl flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
1391
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-700", children: "We use cookies to understand ad performance and improve how our marketing works across visits. You can accept or decline this tracking." }),
1392
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [
1393
- /* @__PURE__ */ jsx(
1394
- "button",
1395
- {
1396
- type: "button",
1397
- onClick: handleDecline,
1398
- className: "rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100",
1399
- children: "Decline"
1400
- }
1401
- ),
1402
- /* @__PURE__ */ jsx(
1403
- "button",
1404
- {
1405
- type: "button",
1406
- onClick: handleAccept,
1407
- className: "rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-gray-800",
1408
- children: "Accept"
1409
- }
1410
- )
1411
- ] })
1412
- ] }) });
1584
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1585
+ /* @__PURE__ */ jsx("style", { children: ANIMATION_KEYFRAMES }),
1586
+ /* @__PURE__ */ jsx(
1587
+ "div",
1588
+ {
1589
+ role: "dialog",
1590
+ "aria-live": "polite",
1591
+ "aria-label": "Cookie consent",
1592
+ className,
1593
+ style: wrapperStyle,
1594
+ children: /* @__PURE__ */ jsxs("div", { style: innerStyle, children: [
1595
+ /* @__PURE__ */ jsxs("div", { style: { flex: "1 1 320px" }, children: [
1596
+ title ? /* @__PURE__ */ jsx("p", { style: titleStyle, children: title }) : null,
1597
+ /* @__PURE__ */ jsxs("p", { style: messageStyle, children: [
1598
+ message ?? DEFAULT_MESSAGE,
1599
+ policyHref ? /* @__PURE__ */ jsxs(Fragment, { children: [
1600
+ " ",
1601
+ /* @__PURE__ */ jsx("a", { href: policyHref, style: linkStyle, children: policyLabel })
1602
+ ] }) : null
1603
+ ] })
1604
+ ] }),
1605
+ /* @__PURE__ */ jsxs("div", { style: actionsStyle, children: [
1606
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: handleDecline, style: declineStyle, children: declineLabel }),
1607
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: handleAccept, style: acceptStyle, children: acceptLabel })
1608
+ ] })
1609
+ ] })
1610
+ }
1611
+ )
1612
+ ] });
1613
+ }
1614
+ var ANIMATION_NAME = "aranova-consent-banner";
1615
+ var ANIMATION_KEYFRAMES = `
1616
+ @keyframes ${ANIMATION_NAME}-bottom {
1617
+ from { transform: translateY(100%); opacity: 0; }
1618
+ to { transform: translateY(0); opacity: 1; }
1619
+ }
1620
+ @keyframes ${ANIMATION_NAME}-top {
1621
+ from { transform: translateY(-100%); opacity: 0; }
1622
+ to { transform: translateY(0); opacity: 1; }
1413
1623
  }
1624
+ `;
1414
1625
 
1415
1626
  // src/GoogleAdsTracking.tsx
1416
- import { useEffect as useEffect2, useMemo } from "react";
1627
+ import { useEffect as useEffect3, useMemo } from "react";
1417
1628
  function GoogleAdsTracking(props) {
1418
1629
  const { gtagId, gtagIds } = props;
1419
1630
  const gtagIdsKey = useMemo(
1420
1631
  () => gtagIds ? JSON.stringify(gtagIds) : "",
1421
1632
  [gtagIds]
1422
1633
  );
1423
- useEffect2(() => {
1634
+ useEffect3(() => {
1424
1635
  if (gtagIds && Object.keys(gtagIds).length > 0) {
1425
1636
  bootstrapMultipleGtags(gtagIds);
1426
1637
  } else if (gtagId) {
@@ -1430,38 +1641,6 @@ function GoogleAdsTracking(props) {
1430
1641
  return null;
1431
1642
  }
1432
1643
 
1433
- // src/hooks.ts
1434
- import { useEffect as useEffect3, useState as useState2 } from "react";
1435
- function useGclid() {
1436
- const [gclid, setGclid] = useState2(null);
1437
- useEffect3(() => {
1438
- setGclid(getCookieValueFromDocument("gclid"));
1439
- }, []);
1440
- return gclid;
1441
- }
1442
- function useTrackingParams() {
1443
- const [trackingParams, setTrackingParams] = useState2(createEmptyTrackingParams());
1444
- useEffect3(() => {
1445
- setTrackingParams(getTrackingParamsFromCookieReader(getCookieValueFromDocument));
1446
- }, []);
1447
- return trackingParams;
1448
- }
1449
- function useConsentState() {
1450
- const [consentState, setConsentStateValue] = useState2("pending");
1451
- useEffect3(() => {
1452
- setConsentStateValue(getConsentState());
1453
- const handleStorage = (event) => {
1454
- if (event.key === "consent_state")
1455
- setConsentStateValue(getConsentState());
1456
- };
1457
- window.addEventListener("storage", handleStorage);
1458
- return () => {
1459
- window.removeEventListener("storage", handleStorage);
1460
- };
1461
- }, []);
1462
- return consentState;
1463
- }
1464
-
1465
1644
  // src/factory.tsx
1466
1645
  import {
1467
1646
  createContext,
@@ -1471,7 +1650,7 @@ import {
1471
1650
  } from "react";
1472
1651
 
1473
1652
  // package.json
1474
- var version = "0.9.0";
1653
+ var version = "0.10.0";
1475
1654
 
1476
1655
  // src/factory.tsx
1477
1656
  import { jsx as jsx2 } from "react/jsx-runtime";
@@ -1594,8 +1773,10 @@ export {
1594
1773
  formatMoney,
1595
1774
  fromMinor,
1596
1775
  getConsentState,
1776
+ resetConsent,
1597
1777
  setConsentState,
1598
1778
  toMinor,
1779
+ useConsent,
1599
1780
  useConsentState,
1600
1781
  useGclid,
1601
1782
  useTrackingParams