@aranova/tracking-next 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.js CHANGED
@@ -44,8 +44,10 @@ __export(src_exports, {
44
44
  formatMoney: () => formatMoney,
45
45
  fromMinor: () => fromMinor,
46
46
  getConsentState: () => getConsentState,
47
+ resetConsent: () => resetConsent,
47
48
  setConsentState: () => setConsentState,
48
49
  toMinor: () => toMinor,
50
+ useConsent: () => useConsent,
49
51
  useConsentState: () => useConsentState,
50
52
  useGclid: () => useGclid,
51
53
  useTrackingParams: () => useTrackingParams
@@ -79,6 +81,12 @@ function setConsentState(state) {
79
81
  if (typeof window.gtag === "function")
80
82
  window.gtag("consent", "update", buildConsentPayload(state));
81
83
  }
84
+ function resetConsent() {
85
+ if (typeof window === "undefined")
86
+ return;
87
+ window.localStorage.removeItem(CONSENT_STATE_KEY);
88
+ window.localStorage.removeItem(CONSENT_TIMESTAMP_KEY);
89
+ }
82
90
 
83
91
  // ../tracking-core/src/payloads.ts
84
92
  function createTrackingClientContext(surface, input = {}) {
@@ -1182,6 +1190,16 @@ function createSalesClient(config) {
1182
1190
  ...cursor !== void 0 ? { cursor } : {}
1183
1191
  });
1184
1192
  },
1193
+ async summary(query) {
1194
+ const { range, include_categories, include_deleted_services, top_n, ...filters } = query;
1195
+ return salesRequest(config, "POST", "/sales/summary", {
1196
+ filters,
1197
+ range,
1198
+ ...include_categories !== void 0 ? { include_categories } : {},
1199
+ ...include_deleted_services !== void 0 ? { include_deleted_services } : {},
1200
+ ...top_n !== void 0 ? { top_n } : {}
1201
+ });
1202
+ },
1185
1203
  async get(id) {
1186
1204
  return salesRequest(config, "GET", `/sales/${id}`);
1187
1205
  },
@@ -1220,81 +1238,244 @@ function formatMoney(cents, currency, locale) {
1220
1238
  }
1221
1239
 
1222
1240
  // src/ConsentBanner.tsx
1223
- var import_react = require("react");
1224
- var import_jsx_runtime = require("react/jsx-runtime");
1225
- function ConsentBanner() {
1226
- const [consentState, setBannerConsentState] = (0, import_react.useState)("pending");
1227
- const [isReady, setIsReady] = (0, import_react.useState)(false);
1228
- (0, import_react.useEffect)(() => {
1229
- setBannerConsentState(getConsentState());
1230
- setIsReady(true);
1231
- }, []);
1232
- if (!isReady || consentState !== "pending")
1233
- return null;
1234
- const handleAccept = () => {
1235
- setConsentState("granted");
1236
- setBannerConsentState("granted");
1237
- };
1238
- const handleDecline = () => {
1239
- setConsentState("denied");
1240
- setBannerConsentState("denied");
1241
- };
1242
- return /* @__PURE__ */ (0, import_jsx_runtime.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__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mx-auto flex max-w-5xl flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
1243
- /* @__PURE__ */ (0, import_jsx_runtime.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." }),
1244
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2 sm:flex-row", children: [
1245
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1246
- "button",
1247
- {
1248
- type: "button",
1249
- onClick: handleDecline,
1250
- className: "rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100",
1251
- children: "Decline"
1252
- }
1253
- ),
1254
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1255
- "button",
1256
- {
1257
- type: "button",
1258
- onClick: handleAccept,
1259
- className: "rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-gray-800",
1260
- children: "Accept"
1261
- }
1262
- )
1263
- ] })
1264
- ] }) });
1265
- }
1241
+ var import_react2 = require("react");
1266
1242
 
1267
1243
  // src/hooks.ts
1268
- var import_react2 = require("react");
1244
+ var import_react = require("react");
1269
1245
  function useGclid() {
1270
- const [gclid, setGclid] = (0, import_react2.useState)(null);
1271
- (0, import_react2.useEffect)(() => {
1246
+ const [gclid, setGclid] = (0, import_react.useState)(null);
1247
+ (0, import_react.useEffect)(() => {
1272
1248
  setGclid(getCookieValueFromDocument("gclid"));
1273
1249
  }, []);
1274
1250
  return gclid;
1275
1251
  }
1276
1252
  function useTrackingParams() {
1277
- const [trackingParams, setTrackingParams] = (0, import_react2.useState)(createEmptyTrackingParams());
1278
- (0, import_react2.useEffect)(() => {
1253
+ const [trackingParams, setTrackingParams] = (0, import_react.useState)(createEmptyTrackingParams());
1254
+ (0, import_react.useEffect)(() => {
1279
1255
  setTrackingParams(getTrackingParamsFromCookieReader(getCookieValueFromDocument));
1280
1256
  }, []);
1281
1257
  return trackingParams;
1282
1258
  }
1283
1259
  function useConsentState() {
1284
- const [consentState, setConsentStateValue] = (0, import_react2.useState)("pending");
1285
- (0, import_react2.useEffect)(() => {
1286
- setConsentStateValue(getConsentState());
1260
+ return useConsent().state;
1261
+ }
1262
+ function useConsent() {
1263
+ const [state, setState] = (0, import_react.useState)("pending");
1264
+ (0, import_react.useEffect)(() => {
1265
+ setState(getConsentState());
1287
1266
  const handleStorage = (event) => {
1288
- if (event.key === "consent_state")
1289
- setConsentStateValue(getConsentState());
1267
+ if (event.key === CONSENT_STATE_KEY) setState(getConsentState());
1290
1268
  };
1291
1269
  window.addEventListener("storage", handleStorage);
1292
- return () => {
1293
- window.removeEventListener("storage", handleStorage);
1294
- };
1270
+ return () => window.removeEventListener("storage", handleStorage);
1271
+ }, []);
1272
+ const accept = (0, import_react.useCallback)(() => {
1273
+ setConsentState("granted");
1274
+ setState("granted");
1275
+ }, []);
1276
+ const decline = (0, import_react.useCallback)(() => {
1277
+ setConsentState("denied");
1278
+ setState("denied");
1295
1279
  }, []);
1296
- return consentState;
1280
+ const reset = (0, import_react.useCallback)(() => {
1281
+ resetConsent();
1282
+ setState("pending");
1283
+ }, []);
1284
+ return {
1285
+ state,
1286
+ isPending: state === "pending",
1287
+ isGranted: state === "granted",
1288
+ isDenied: state === "denied",
1289
+ accept,
1290
+ decline,
1291
+ reset
1292
+ };
1293
+ }
1294
+
1295
+ // src/ConsentBanner.tsx
1296
+ var import_jsx_runtime = require("react/jsx-runtime");
1297
+ var LIGHT_THEME = {
1298
+ background: "#ffffff",
1299
+ border: "#e5e7eb",
1300
+ text: "#111827",
1301
+ mutedText: "#4b5563",
1302
+ acceptBg: "#111827",
1303
+ acceptText: "#ffffff",
1304
+ declineBg: "transparent",
1305
+ declineText: "#111827",
1306
+ declineBorder: "#d1d5db",
1307
+ shadow: "0 -4px 16px -2px rgba(15, 23, 42, 0.08), 0 -2px 6px -1px rgba(15, 23, 42, 0.04)",
1308
+ linkColor: "#1f2937"
1309
+ };
1310
+ var DARK_THEME = {
1311
+ background: "#0f172a",
1312
+ border: "#1e293b",
1313
+ text: "#f1f5f9",
1314
+ mutedText: "#cbd5e1",
1315
+ acceptBg: "#f1f5f9",
1316
+ acceptText: "#0f172a",
1317
+ declineBg: "transparent",
1318
+ declineText: "#f1f5f9",
1319
+ declineBorder: "#334155",
1320
+ shadow: "0 -4px 16px -2px rgba(0, 0, 0, 0.5), 0 -2px 6px -1px rgba(0, 0, 0, 0.3)",
1321
+ linkColor: "#e2e8f0"
1322
+ };
1323
+ function useResolvedTheme(theme) {
1324
+ const [prefersDark, setPrefersDark] = (0, import_react2.useState)(false);
1325
+ (0, import_react2.useEffect)(() => {
1326
+ if (theme !== "auto" || typeof window === "undefined" || !window.matchMedia) return;
1327
+ const mql = window.matchMedia("(prefers-color-scheme: dark)");
1328
+ setPrefersDark(mql.matches);
1329
+ const onChange = (e) => setPrefersDark(e.matches);
1330
+ mql.addEventListener("change", onChange);
1331
+ return () => mql.removeEventListener("change", onChange);
1332
+ }, [theme]);
1333
+ if (theme === "dark") return DARK_THEME;
1334
+ if (theme === "auto" && prefersDark) return DARK_THEME;
1335
+ return LIGHT_THEME;
1336
+ }
1337
+ 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.";
1338
+ function ConsentBanner({
1339
+ message,
1340
+ title,
1341
+ acceptLabel = "Accept",
1342
+ declineLabel = "Decline",
1343
+ policyHref,
1344
+ policyLabel = "Learn more",
1345
+ onAccept,
1346
+ onDecline,
1347
+ position = "bottom",
1348
+ theme = "light",
1349
+ className,
1350
+ style
1351
+ } = {}) {
1352
+ const { isPending, accept, decline } = useConsent();
1353
+ const tokens = useResolvedTheme(theme);
1354
+ const [hasMounted, setHasMounted] = (0, import_react2.useState)(false);
1355
+ (0, import_react2.useEffect)(() => {
1356
+ setHasMounted(true);
1357
+ }, []);
1358
+ if (!hasMounted || !isPending) return null;
1359
+ const wrapperStyle = {
1360
+ position: "fixed",
1361
+ left: 0,
1362
+ right: 0,
1363
+ [position]: 0,
1364
+ zIndex: 2147483640,
1365
+ background: tokens.background,
1366
+ color: tokens.text,
1367
+ borderTop: position === "bottom" ? `1px solid ${tokens.border}` : "none",
1368
+ borderBottom: position === "top" ? `1px solid ${tokens.border}` : "none",
1369
+ boxShadow: tokens.shadow,
1370
+ padding: "16px 20px",
1371
+ boxSizing: "border-box",
1372
+ animation: `${ANIMATION_NAME}-${position} 200ms ease-out`,
1373
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
1374
+ ...style
1375
+ };
1376
+ const innerStyle = {
1377
+ maxWidth: 1100,
1378
+ margin: "0 auto",
1379
+ display: "flex",
1380
+ flexWrap: "wrap",
1381
+ gap: 16,
1382
+ alignItems: "center",
1383
+ justifyContent: "space-between"
1384
+ };
1385
+ const messageStyle = {
1386
+ flex: "1 1 320px",
1387
+ margin: 0,
1388
+ fontSize: 14,
1389
+ lineHeight: 1.5,
1390
+ color: tokens.mutedText
1391
+ };
1392
+ const titleStyle = {
1393
+ margin: "0 0 4px 0",
1394
+ fontSize: 14,
1395
+ fontWeight: 600,
1396
+ color: tokens.text
1397
+ };
1398
+ const actionsStyle = {
1399
+ display: "flex",
1400
+ gap: 8,
1401
+ flexShrink: 0
1402
+ };
1403
+ const buttonBase = {
1404
+ appearance: "none",
1405
+ fontFamily: "inherit",
1406
+ fontSize: 14,
1407
+ fontWeight: 500,
1408
+ padding: "8px 16px",
1409
+ borderRadius: 6,
1410
+ cursor: "pointer",
1411
+ border: "1px solid transparent",
1412
+ transition: "opacity 120ms ease"
1413
+ };
1414
+ const declineStyle = {
1415
+ ...buttonBase,
1416
+ background: tokens.declineBg,
1417
+ color: tokens.declineText,
1418
+ borderColor: tokens.declineBorder
1419
+ };
1420
+ const acceptStyle = {
1421
+ ...buttonBase,
1422
+ background: tokens.acceptBg,
1423
+ color: tokens.acceptText
1424
+ };
1425
+ const linkStyle = {
1426
+ color: tokens.linkColor,
1427
+ textDecoration: "underline",
1428
+ textUnderlineOffset: 2
1429
+ };
1430
+ const handleAccept = () => {
1431
+ accept();
1432
+ onAccept?.();
1433
+ };
1434
+ const handleDecline = () => {
1435
+ decline();
1436
+ onDecline?.();
1437
+ };
1438
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1439
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: ANIMATION_KEYFRAMES }),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1441
+ "div",
1442
+ {
1443
+ role: "dialog",
1444
+ "aria-live": "polite",
1445
+ "aria-label": "Cookie consent",
1446
+ className,
1447
+ style: wrapperStyle,
1448
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: innerStyle, children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: "1 1 320px" }, children: [
1450
+ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: titleStyle, children: title }) : null,
1451
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: messageStyle, children: [
1452
+ message ?? DEFAULT_MESSAGE,
1453
+ policyHref ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1454
+ " ",
1455
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: policyHref, style: linkStyle, children: policyLabel })
1456
+ ] }) : null
1457
+ ] })
1458
+ ] }),
1459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: actionsStyle, children: [
1460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: handleDecline, style: declineStyle, children: declineLabel }),
1461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: handleAccept, style: acceptStyle, children: acceptLabel })
1462
+ ] })
1463
+ ] })
1464
+ }
1465
+ )
1466
+ ] });
1467
+ }
1468
+ var ANIMATION_NAME = "aranova-consent-banner";
1469
+ var ANIMATION_KEYFRAMES = `
1470
+ @keyframes ${ANIMATION_NAME}-bottom {
1471
+ from { transform: translateY(100%); opacity: 0; }
1472
+ to { transform: translateY(0); opacity: 1; }
1473
+ }
1474
+ @keyframes ${ANIMATION_NAME}-top {
1475
+ from { transform: translateY(-100%); opacity: 0; }
1476
+ to { transform: translateY(0); opacity: 1; }
1297
1477
  }
1478
+ `;
1298
1479
 
1299
1480
  // src/GoogleAdsTracking.tsx
1300
1481
  var import_script = __toESM(require("next/script"));
@@ -1323,7 +1504,7 @@ function GoogleAdsTracking(props) {
1323
1504
  var import_navigation = require("next/navigation");
1324
1505
 
1325
1506
  // package.json
1326
- var version = "0.9.0";
1507
+ var version = "0.10.0";
1327
1508
 
1328
1509
  // src/factory.tsx
1329
1510
  var import_react3 = require("react");
@@ -1543,8 +1724,10 @@ function createTracking(options) {
1543
1724
  formatMoney,
1544
1725
  fromMinor,
1545
1726
  getConsentState,
1727
+ resetConsent,
1546
1728
  setConsentState,
1547
1729
  toMinor,
1730
+ useConsent,
1548
1731
  useConsentState,
1549
1732
  useGclid,
1550
1733
  useTrackingParams