@aranova/tracking-react 0.10.0 → 0.12.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
@@ -1333,21 +1333,40 @@ function createSalesClient(config) {
1333
1333
  return salesRequest(config, "POST", "/sales", body);
1334
1334
  },
1335
1335
  async list(query) {
1336
- const { cursor, limit, ...filters } = query ?? {};
1336
+ const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
1337
1337
  return salesRequest(config, "POST", "/sales/query", {
1338
1338
  filters,
1339
1339
  ...limit !== void 0 ? { limit } : {},
1340
- ...cursor !== void 0 ? { cursor } : {}
1340
+ ...cursor !== void 0 ? { cursor } : {},
1341
+ ...sort !== void 0 ? { sort } : {},
1342
+ ...order !== void 0 ? { order } : {},
1343
+ ...want_total !== void 0 ? { want_total } : {}
1341
1344
  });
1342
1345
  },
1343
1346
  async summary(query) {
1344
- const { range, include_categories, include_deleted_services, top_n, ...filters } = query;
1347
+ const {
1348
+ range,
1349
+ include_categories,
1350
+ include_deleted_services,
1351
+ top_n,
1352
+ since,
1353
+ until,
1354
+ timezone,
1355
+ granularity,
1356
+ compare_to,
1357
+ ...filters
1358
+ } = query;
1345
1359
  return salesRequest(config, "POST", "/sales/summary", {
1346
1360
  filters,
1347
- range,
1361
+ ...range !== void 0 ? { range } : {},
1348
1362
  ...include_categories !== void 0 ? { include_categories } : {},
1349
1363
  ...include_deleted_services !== void 0 ? { include_deleted_services } : {},
1350
- ...top_n !== void 0 ? { top_n } : {}
1364
+ ...top_n !== void 0 ? { top_n } : {},
1365
+ ...since !== void 0 ? { since } : {},
1366
+ ...until !== void 0 ? { until } : {},
1367
+ ...timezone !== void 0 ? { timezone } : {},
1368
+ ...granularity !== void 0 ? { granularity } : {},
1369
+ ...compare_to !== void 0 ? { compare_to } : {}
1351
1370
  });
1352
1371
  },
1353
1372
  async get(id) {
@@ -1358,6 +1377,49 @@ function createSalesClient(config) {
1358
1377
  },
1359
1378
  async delete(id) {
1360
1379
  await salesRequest(config, "DELETE", `/sales/${id}`);
1380
+ },
1381
+ customers: {
1382
+ async list(query) {
1383
+ const { segment, sort, order, cursor, limit, want_total, ...filters } = query ?? {};
1384
+ return salesRequest(config, "POST", "/customers/query", {
1385
+ filters,
1386
+ ...segment !== void 0 ? { segment } : {},
1387
+ ...sort !== void 0 ? { sort } : {},
1388
+ ...order !== void 0 ? { order } : {},
1389
+ ...cursor !== void 0 ? { cursor } : {},
1390
+ ...limit !== void 0 ? { limit } : {},
1391
+ ...want_total !== void 0 ? { want_total } : {}
1392
+ });
1393
+ },
1394
+ async get(id, options) {
1395
+ const params = new URLSearchParams();
1396
+ if (options?.include_sales !== void 0)
1397
+ params.set("include_sales", String(options.include_sales));
1398
+ if (options?.limit !== void 0) params.set("limit", String(options.limit));
1399
+ if (options?.cursor != null) params.set("cursor", options.cursor);
1400
+ const qs = params.toString();
1401
+ return salesRequest(
1402
+ config,
1403
+ "GET",
1404
+ `/customers/${encodeURIComponent(id)}${qs ? `?${qs}` : ""}`
1405
+ );
1406
+ },
1407
+ async summary(query) {
1408
+ const { range, since, until, timezone, compare_to, ...filters } = query ?? {};
1409
+ return salesRequest(config, "POST", "/customers/summary", {
1410
+ filters,
1411
+ ...range !== void 0 ? { range } : {},
1412
+ ...since !== void 0 ? { since } : {},
1413
+ ...until !== void 0 ? { until } : {},
1414
+ ...timezone !== void 0 ? { timezone } : {},
1415
+ ...compare_to !== void 0 ? { compare_to } : {}
1416
+ });
1417
+ }
1418
+ },
1419
+ business: {
1420
+ async config() {
1421
+ return salesRequest(config, "GET", "/business/config");
1422
+ }
1361
1423
  }
1362
1424
  };
1363
1425
  }
@@ -1386,6 +1448,64 @@ function formatMoney(cents, currency, locale) {
1386
1448
  fromMinor(cents, currency)
1387
1449
  );
1388
1450
  }
1451
+ function formatDateInTz(iso, timeZone, opts, locale) {
1452
+ const date = new Date(iso);
1453
+ if (Number.isNaN(date.getTime())) return iso;
1454
+ return new Intl.DateTimeFormat(locale, {
1455
+ year: "numeric",
1456
+ month: "short",
1457
+ day: "2-digit",
1458
+ hour: "2-digit",
1459
+ minute: "2-digit",
1460
+ ...opts,
1461
+ timeZone
1462
+ }).format(date);
1463
+ }
1464
+
1465
+ // ../tracking-core/src/phone.ts
1466
+ import { AsYouType, parsePhoneNumberFromString } from "libphonenumber-js";
1467
+ var DEFAULT_PHONE_COUNTRY = "CA";
1468
+ function parsePhone(raw, country) {
1469
+ const region = country ?? DEFAULT_PHONE_COUNTRY;
1470
+ const parsed = parsePhoneNumberFromString(raw ?? "", region);
1471
+ if (!parsed) {
1472
+ return { e164: null, national: "", international: "", country: region, isValid: false };
1473
+ }
1474
+ const isValid = parsed.isValid();
1475
+ return {
1476
+ // E.164 is only surfaced for a *valid* number — a possible-but-invalid input
1477
+ // (e.g. too few digits) still parses but must not be transmitted.
1478
+ e164: isValid ? parsed.number : null,
1479
+ national: parsed.formatNational(),
1480
+ international: parsed.formatInternational(),
1481
+ country: parsed.country ?? region,
1482
+ isValid
1483
+ };
1484
+ }
1485
+ function toE164(raw, country) {
1486
+ return parsePhone(raw, country).e164;
1487
+ }
1488
+ function formatPhone(value, format = "national", country) {
1489
+ const parsed = parsePhone(value, country);
1490
+ if (typeof format === "function") return format(parsed);
1491
+ switch (format) {
1492
+ case "international":
1493
+ return parsed.international || value;
1494
+ case "e164":
1495
+ return parsed.e164 ?? value;
1496
+ case "national":
1497
+ default:
1498
+ return parsed.national || value;
1499
+ }
1500
+ }
1501
+ function formatPhoneAsTyped(raw, country) {
1502
+ return new AsYouType(country ?? DEFAULT_PHONE_COUNTRY).input(raw ?? "");
1503
+ }
1504
+
1505
+ // ../tracking-core/src/phone-field.ts
1506
+ function phoneField(name, raw, country) {
1507
+ return { name, type: "phone", value: toE164(raw, country) };
1508
+ }
1389
1509
 
1390
1510
  // src/hooks.ts
1391
1511
  function useGclid() {
@@ -1643,17 +1763,100 @@ function GoogleAdsTracking(props) {
1643
1763
 
1644
1764
  // src/factory.tsx
1645
1765
  import {
1646
- createContext,
1647
- useContext,
1766
+ createContext as createContext2,
1767
+ useContext as useContext2,
1648
1768
  useEffect as useEffect4,
1649
- useMemo as useMemo2
1769
+ useMemo as useMemo3
1650
1770
  } from "react";
1651
1771
 
1652
1772
  // package.json
1653
- var version = "0.10.0";
1773
+ var version = "0.12.0";
1654
1774
 
1655
- // src/factory.tsx
1775
+ // ../tracking-core/src/phone-react.tsx
1776
+ import {
1777
+ createContext,
1778
+ forwardRef,
1779
+ useCallback as useCallback2,
1780
+ useContext,
1781
+ useMemo as useMemo2,
1782
+ useState as useState3
1783
+ } from "react";
1656
1784
  import { jsx as jsx2 } from "react/jsx-runtime";
1785
+ var PhoneConfigContext = createContext(null);
1786
+ function usePhoneConfig() {
1787
+ const ctx = useContext(PhoneConfigContext);
1788
+ return {
1789
+ defaultCountry: ctx?.defaultCountry ?? DEFAULT_PHONE_COUNTRY,
1790
+ display: ctx?.display ?? "national"
1791
+ };
1792
+ }
1793
+ function usePhoneField(opts = {}) {
1794
+ const cfg = usePhoneConfig();
1795
+ const country = opts.country ?? cfg.defaultCountry;
1796
+ const display = opts.display ?? cfg.display;
1797
+ const { onValueChange } = opts;
1798
+ const [value, setValue] = useState3(() => formatPhoneAsTyped(opts.defaultValue ?? "", country));
1799
+ const [touched, setTouched] = useState3(false);
1800
+ const parsed = useMemo2(() => parsePhone(value, country), [value, country]);
1801
+ const onChange = useCallback2(
1802
+ (event) => {
1803
+ const next = formatPhoneAsTyped(event.target.value, country);
1804
+ setValue(next);
1805
+ onValueChange?.(parsePhone(next, country).e164);
1806
+ },
1807
+ [country, onValueChange]
1808
+ );
1809
+ const onBlur = useCallback2(
1810
+ (_event) => {
1811
+ setTouched(true);
1812
+ setValue((current) => {
1813
+ const p = parsePhone(current, country);
1814
+ return p.isValid ? formatPhone(current, display, country) : current;
1815
+ });
1816
+ },
1817
+ [country, display]
1818
+ );
1819
+ const error = touched && value.length > 0 && !parsed.isValid ? "Enter a valid phone number" : null;
1820
+ return {
1821
+ value,
1822
+ e164: parsed.e164,
1823
+ isValid: parsed.isValid,
1824
+ error,
1825
+ parsed,
1826
+ inputProps: { value, onChange, onBlur, type: "tel", inputMode: "tel", autoComplete: "tel" }
1827
+ };
1828
+ }
1829
+ var PhoneField = forwardRef(function PhoneField2({ country, value, defaultValue, onChange, onE164Change, ...rest }, ref) {
1830
+ const cfg = usePhoneConfig();
1831
+ const resolvedCountry = country ?? cfg.defaultCountry;
1832
+ const isControlled = value !== void 0;
1833
+ const [internal, setInternal] = useState3(
1834
+ () => formatPhoneAsTyped(defaultValue ?? "", resolvedCountry)
1835
+ );
1836
+ const handleChange = (event) => {
1837
+ const formatted = formatPhoneAsTyped(event.target.value, resolvedCountry);
1838
+ event.target.value = formatted;
1839
+ onE164Change?.(parsePhone(formatted, resolvedCountry).e164);
1840
+ if (!isControlled) setInternal(formatted);
1841
+ onChange?.(event);
1842
+ };
1843
+ const shown = isControlled ? formatPhoneAsTyped(value, resolvedCountry) : internal;
1844
+ return /* @__PURE__ */ jsx2(
1845
+ "input",
1846
+ {
1847
+ ...rest,
1848
+ ref,
1849
+ type: "tel",
1850
+ inputMode: "tel",
1851
+ autoComplete: "tel",
1852
+ value: shown,
1853
+ onChange: handleChange
1854
+ }
1855
+ );
1856
+ });
1857
+
1858
+ // src/factory.tsx
1859
+ import { jsx as jsx3 } from "react/jsx-runtime";
1657
1860
  var NOOP_CLIENT = {
1658
1861
  trackEvent: () => {
1659
1862
  },
@@ -1663,7 +1866,7 @@ var NOOP_CLIENT = {
1663
1866
  getVisitorId: () => ""
1664
1867
  };
1665
1868
  function createTracking(options) {
1666
- const { apiKey, endpoint, triggers, environment, debug } = options;
1869
+ const { apiKey, endpoint, triggers, environment, debug, phone } = options;
1667
1870
  if (!apiKey || !endpoint) {
1668
1871
  if (apiKey || endpoint) {
1669
1872
  console.warn(
@@ -1672,14 +1875,16 @@ function createTracking(options) {
1672
1875
  }
1673
1876
  const noopTyped = NOOP_CLIENT;
1674
1877
  return {
1675
- TrackingProvider: ({ children }) => children,
1878
+ // Still publish phone config so usePhoneField/<PhoneField> work even when
1879
+ // tracking is disabled (missing apiKey/endpoint).
1880
+ TrackingProvider: ({ children }) => /* @__PURE__ */ jsx3(PhoneConfigContext.Provider, { value: phone ?? null, children }),
1676
1881
  useTracking: () => noopTyped
1677
1882
  };
1678
1883
  }
1679
- const TrackingContext = createContext(null);
1884
+ const TrackingContext = createContext2(null);
1680
1885
  function TrackingProvider({ gtagId, gtagIds, children }) {
1681
1886
  const resolvedGtagIds = gtagIds ?? (gtagId ? { default: gtagId } : void 0);
1682
- const client = useMemo2(
1887
+ const client = useMemo3(
1683
1888
  () => createTypedClient(
1684
1889
  getOrCreateTrackingClient({
1685
1890
  apiKey,
@@ -1697,7 +1902,7 @@ function createTracking(options) {
1697
1902
  ),
1698
1903
  []
1699
1904
  );
1700
- const gtagIdsKey = useMemo2(() => gtagIds ? JSON.stringify(gtagIds) : "", [gtagIds]);
1905
+ const gtagIdsKey = useMemo3(() => gtagIds ? JSON.stringify(gtagIds) : "", [gtagIds]);
1701
1906
  useEffect4(() => {
1702
1907
  if (gtagIds && Object.keys(gtagIds).length > 0) {
1703
1908
  bootstrapMultipleGtags(gtagIds);
@@ -1745,10 +1950,10 @@ function createTracking(options) {
1745
1950
  }
1746
1951
  };
1747
1952
  }, []);
1748
- return /* @__PURE__ */ jsx2(TrackingContext.Provider, { value: client, children });
1953
+ return /* @__PURE__ */ jsx3(TrackingContext.Provider, { value: client, children: /* @__PURE__ */ jsx3(PhoneConfigContext.Provider, { value: phone ?? null, children }) });
1749
1954
  }
1750
1955
  function useTracking() {
1751
- const client = useContext(TrackingContext);
1956
+ const client = useContext2(TrackingContext);
1752
1957
  if (client === null) {
1753
1958
  throw new Error(
1754
1959
  "useTracking must be called inside a <TrackingProvider> returned by createTracking()"
@@ -1761,7 +1966,10 @@ function createTracking(options) {
1761
1966
  export {
1762
1967
  AranovaApiError,
1763
1968
  ConsentBanner,
1969
+ DEFAULT_PHONE_COUNTRY,
1764
1970
  GoogleAdsTracking,
1971
+ PhoneConfigContext,
1972
+ PhoneField,
1765
1973
  TRACKING_PARAM_KEYS,
1766
1974
  captureTrackingParamsFromLocation,
1767
1975
  createSalesClient,
@@ -1770,15 +1978,23 @@ export {
1770
1978
  createTrackingEventCreatePayload,
1771
1979
  createTrackingSessionUpsertPayload,
1772
1980
  fetchServices,
1981
+ formatDateInTz,
1773
1982
  formatMoney,
1983
+ formatPhone,
1984
+ formatPhoneAsTyped,
1774
1985
  fromMinor,
1775
1986
  getConsentState,
1987
+ parsePhone,
1988
+ phoneField,
1776
1989
  resetConsent,
1777
1990
  setConsentState,
1991
+ toE164,
1778
1992
  toMinor,
1779
1993
  useConsent,
1780
1994
  useConsentState,
1781
1995
  useGclid,
1996
+ usePhoneConfig,
1997
+ usePhoneField,
1782
1998
  useTrackingParams
1783
1999
  };
1784
2000
  //# sourceMappingURL=index.mjs.map