@aranova/tracking-react 0.11.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.js CHANGED
@@ -22,7 +22,10 @@ var src_exports = {};
22
22
  __export(src_exports, {
23
23
  AranovaApiError: () => AranovaApiError,
24
24
  ConsentBanner: () => ConsentBanner,
25
+ DEFAULT_PHONE_COUNTRY: () => DEFAULT_PHONE_COUNTRY,
25
26
  GoogleAdsTracking: () => GoogleAdsTracking,
27
+ PhoneConfigContext: () => PhoneConfigContext,
28
+ PhoneField: () => PhoneField,
26
29
  TRACKING_PARAM_KEYS: () => TRACKING_PARAM_KEYS,
27
30
  captureTrackingParamsFromLocation: () => captureTrackingParamsFromLocation,
28
31
  createSalesClient: () => createSalesClient,
@@ -31,15 +34,23 @@ __export(src_exports, {
31
34
  createTrackingEventCreatePayload: () => createTrackingEventCreatePayload,
32
35
  createTrackingSessionUpsertPayload: () => createTrackingSessionUpsertPayload,
33
36
  fetchServices: () => fetchServices,
37
+ formatDateInTz: () => formatDateInTz,
34
38
  formatMoney: () => formatMoney,
39
+ formatPhone: () => formatPhone,
40
+ formatPhoneAsTyped: () => formatPhoneAsTyped,
35
41
  fromMinor: () => fromMinor,
36
42
  getConsentState: () => getConsentState,
43
+ parsePhone: () => parsePhone,
44
+ phoneField: () => phoneField,
37
45
  resetConsent: () => resetConsent,
38
46
  setConsentState: () => setConsentState,
47
+ toE164: () => toE164,
39
48
  toMinor: () => toMinor,
40
49
  useConsent: () => useConsent,
41
50
  useConsentState: () => useConsentState,
42
51
  useGclid: () => useGclid,
52
+ usePhoneConfig: () => usePhoneConfig,
53
+ usePhoneField: () => usePhoneField,
43
54
  useTrackingParams: () => useTrackingParams
44
55
  });
45
56
  module.exports = __toCommonJS(src_exports);
@@ -1379,21 +1390,40 @@ function createSalesClient(config) {
1379
1390
  return salesRequest(config, "POST", "/sales", body);
1380
1391
  },
1381
1392
  async list(query) {
1382
- const { cursor, limit, ...filters } = query ?? {};
1393
+ const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
1383
1394
  return salesRequest(config, "POST", "/sales/query", {
1384
1395
  filters,
1385
1396
  ...limit !== void 0 ? { limit } : {},
1386
- ...cursor !== void 0 ? { cursor } : {}
1397
+ ...cursor !== void 0 ? { cursor } : {},
1398
+ ...sort !== void 0 ? { sort } : {},
1399
+ ...order !== void 0 ? { order } : {},
1400
+ ...want_total !== void 0 ? { want_total } : {}
1387
1401
  });
1388
1402
  },
1389
1403
  async summary(query) {
1390
- const { range, include_categories, include_deleted_services, top_n, ...filters } = query;
1404
+ const {
1405
+ range,
1406
+ include_categories,
1407
+ include_deleted_services,
1408
+ top_n,
1409
+ since,
1410
+ until,
1411
+ timezone,
1412
+ granularity,
1413
+ compare_to,
1414
+ ...filters
1415
+ } = query;
1391
1416
  return salesRequest(config, "POST", "/sales/summary", {
1392
1417
  filters,
1393
- range,
1418
+ ...range !== void 0 ? { range } : {},
1394
1419
  ...include_categories !== void 0 ? { include_categories } : {},
1395
1420
  ...include_deleted_services !== void 0 ? { include_deleted_services } : {},
1396
- ...top_n !== void 0 ? { top_n } : {}
1421
+ ...top_n !== void 0 ? { top_n } : {},
1422
+ ...since !== void 0 ? { since } : {},
1423
+ ...until !== void 0 ? { until } : {},
1424
+ ...timezone !== void 0 ? { timezone } : {},
1425
+ ...granularity !== void 0 ? { granularity } : {},
1426
+ ...compare_to !== void 0 ? { compare_to } : {}
1397
1427
  });
1398
1428
  },
1399
1429
  async get(id) {
@@ -1404,6 +1434,49 @@ function createSalesClient(config) {
1404
1434
  },
1405
1435
  async delete(id) {
1406
1436
  await salesRequest(config, "DELETE", `/sales/${id}`);
1437
+ },
1438
+ customers: {
1439
+ async list(query) {
1440
+ const { segment, sort, order, cursor, limit, want_total, ...filters } = query ?? {};
1441
+ return salesRequest(config, "POST", "/customers/query", {
1442
+ filters,
1443
+ ...segment !== void 0 ? { segment } : {},
1444
+ ...sort !== void 0 ? { sort } : {},
1445
+ ...order !== void 0 ? { order } : {},
1446
+ ...cursor !== void 0 ? { cursor } : {},
1447
+ ...limit !== void 0 ? { limit } : {},
1448
+ ...want_total !== void 0 ? { want_total } : {}
1449
+ });
1450
+ },
1451
+ async get(id, options) {
1452
+ const params = new URLSearchParams();
1453
+ if (options?.include_sales !== void 0)
1454
+ params.set("include_sales", String(options.include_sales));
1455
+ if (options?.limit !== void 0) params.set("limit", String(options.limit));
1456
+ if (options?.cursor != null) params.set("cursor", options.cursor);
1457
+ const qs = params.toString();
1458
+ return salesRequest(
1459
+ config,
1460
+ "GET",
1461
+ `/customers/${encodeURIComponent(id)}${qs ? `?${qs}` : ""}`
1462
+ );
1463
+ },
1464
+ async summary(query) {
1465
+ const { range, since, until, timezone, compare_to, ...filters } = query ?? {};
1466
+ return salesRequest(config, "POST", "/customers/summary", {
1467
+ filters,
1468
+ ...range !== void 0 ? { range } : {},
1469
+ ...since !== void 0 ? { since } : {},
1470
+ ...until !== void 0 ? { until } : {},
1471
+ ...timezone !== void 0 ? { timezone } : {},
1472
+ ...compare_to !== void 0 ? { compare_to } : {}
1473
+ });
1474
+ }
1475
+ },
1476
+ business: {
1477
+ async config() {
1478
+ return salesRequest(config, "GET", "/business/config");
1479
+ }
1407
1480
  }
1408
1481
  };
1409
1482
  }
@@ -1432,6 +1505,64 @@ function formatMoney(cents, currency, locale) {
1432
1505
  fromMinor(cents, currency)
1433
1506
  );
1434
1507
  }
1508
+ function formatDateInTz(iso, timeZone, opts, locale) {
1509
+ const date = new Date(iso);
1510
+ if (Number.isNaN(date.getTime())) return iso;
1511
+ return new Intl.DateTimeFormat(locale, {
1512
+ year: "numeric",
1513
+ month: "short",
1514
+ day: "2-digit",
1515
+ hour: "2-digit",
1516
+ minute: "2-digit",
1517
+ ...opts,
1518
+ timeZone
1519
+ }).format(date);
1520
+ }
1521
+
1522
+ // ../tracking-core/src/phone.ts
1523
+ var import_libphonenumber_js = require("libphonenumber-js");
1524
+ var DEFAULT_PHONE_COUNTRY = "CA";
1525
+ function parsePhone(raw, country) {
1526
+ const region = country ?? DEFAULT_PHONE_COUNTRY;
1527
+ const parsed = (0, import_libphonenumber_js.parsePhoneNumberFromString)(raw ?? "", region);
1528
+ if (!parsed) {
1529
+ return { e164: null, national: "", international: "", country: region, isValid: false };
1530
+ }
1531
+ const isValid = parsed.isValid();
1532
+ return {
1533
+ // E.164 is only surfaced for a *valid* number — a possible-but-invalid input
1534
+ // (e.g. too few digits) still parses but must not be transmitted.
1535
+ e164: isValid ? parsed.number : null,
1536
+ national: parsed.formatNational(),
1537
+ international: parsed.formatInternational(),
1538
+ country: parsed.country ?? region,
1539
+ isValid
1540
+ };
1541
+ }
1542
+ function toE164(raw, country) {
1543
+ return parsePhone(raw, country).e164;
1544
+ }
1545
+ function formatPhone(value, format = "national", country) {
1546
+ const parsed = parsePhone(value, country);
1547
+ if (typeof format === "function") return format(parsed);
1548
+ switch (format) {
1549
+ case "international":
1550
+ return parsed.international || value;
1551
+ case "e164":
1552
+ return parsed.e164 ?? value;
1553
+ case "national":
1554
+ default:
1555
+ return parsed.national || value;
1556
+ }
1557
+ }
1558
+ function formatPhoneAsTyped(raw, country) {
1559
+ return new import_libphonenumber_js.AsYouType(country ?? DEFAULT_PHONE_COUNTRY).input(raw ?? "");
1560
+ }
1561
+
1562
+ // ../tracking-core/src/phone-field.ts
1563
+ function phoneField(name, raw, country) {
1564
+ return { name, type: "phone", value: toE164(raw, country) };
1565
+ }
1435
1566
 
1436
1567
  // src/hooks.ts
1437
1568
  function useGclid() {
@@ -1688,13 +1819,89 @@ function GoogleAdsTracking(props) {
1688
1819
  }
1689
1820
 
1690
1821
  // src/factory.tsx
1691
- var import_react4 = require("react");
1822
+ var import_react5 = require("react");
1692
1823
 
1693
1824
  // package.json
1694
- var version = "0.11.0";
1825
+ var version = "0.12.0";
1695
1826
 
1696
- // src/factory.tsx
1827
+ // ../tracking-core/src/phone-react.tsx
1828
+ var import_react4 = require("react");
1697
1829
  var import_jsx_runtime2 = require("react/jsx-runtime");
1830
+ var PhoneConfigContext = (0, import_react4.createContext)(null);
1831
+ function usePhoneConfig() {
1832
+ const ctx = (0, import_react4.useContext)(PhoneConfigContext);
1833
+ return {
1834
+ defaultCountry: ctx?.defaultCountry ?? DEFAULT_PHONE_COUNTRY,
1835
+ display: ctx?.display ?? "national"
1836
+ };
1837
+ }
1838
+ function usePhoneField(opts = {}) {
1839
+ const cfg = usePhoneConfig();
1840
+ const country = opts.country ?? cfg.defaultCountry;
1841
+ const display = opts.display ?? cfg.display;
1842
+ const { onValueChange } = opts;
1843
+ const [value, setValue] = (0, import_react4.useState)(() => formatPhoneAsTyped(opts.defaultValue ?? "", country));
1844
+ const [touched, setTouched] = (0, import_react4.useState)(false);
1845
+ const parsed = (0, import_react4.useMemo)(() => parsePhone(value, country), [value, country]);
1846
+ const onChange = (0, import_react4.useCallback)(
1847
+ (event) => {
1848
+ const next = formatPhoneAsTyped(event.target.value, country);
1849
+ setValue(next);
1850
+ onValueChange?.(parsePhone(next, country).e164);
1851
+ },
1852
+ [country, onValueChange]
1853
+ );
1854
+ const onBlur = (0, import_react4.useCallback)(
1855
+ (_event) => {
1856
+ setTouched(true);
1857
+ setValue((current) => {
1858
+ const p = parsePhone(current, country);
1859
+ return p.isValid ? formatPhone(current, display, country) : current;
1860
+ });
1861
+ },
1862
+ [country, display]
1863
+ );
1864
+ const error = touched && value.length > 0 && !parsed.isValid ? "Enter a valid phone number" : null;
1865
+ return {
1866
+ value,
1867
+ e164: parsed.e164,
1868
+ isValid: parsed.isValid,
1869
+ error,
1870
+ parsed,
1871
+ inputProps: { value, onChange, onBlur, type: "tel", inputMode: "tel", autoComplete: "tel" }
1872
+ };
1873
+ }
1874
+ var PhoneField = (0, import_react4.forwardRef)(function PhoneField2({ country, value, defaultValue, onChange, onE164Change, ...rest }, ref) {
1875
+ const cfg = usePhoneConfig();
1876
+ const resolvedCountry = country ?? cfg.defaultCountry;
1877
+ const isControlled = value !== void 0;
1878
+ const [internal, setInternal] = (0, import_react4.useState)(
1879
+ () => formatPhoneAsTyped(defaultValue ?? "", resolvedCountry)
1880
+ );
1881
+ const handleChange = (event) => {
1882
+ const formatted = formatPhoneAsTyped(event.target.value, resolvedCountry);
1883
+ event.target.value = formatted;
1884
+ onE164Change?.(parsePhone(formatted, resolvedCountry).e164);
1885
+ if (!isControlled) setInternal(formatted);
1886
+ onChange?.(event);
1887
+ };
1888
+ const shown = isControlled ? formatPhoneAsTyped(value, resolvedCountry) : internal;
1889
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1890
+ "input",
1891
+ {
1892
+ ...rest,
1893
+ ref,
1894
+ type: "tel",
1895
+ inputMode: "tel",
1896
+ autoComplete: "tel",
1897
+ value: shown,
1898
+ onChange: handleChange
1899
+ }
1900
+ );
1901
+ });
1902
+
1903
+ // src/factory.tsx
1904
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1698
1905
  var NOOP_CLIENT = {
1699
1906
  trackEvent: () => {
1700
1907
  },
@@ -1704,7 +1911,7 @@ var NOOP_CLIENT = {
1704
1911
  getVisitorId: () => ""
1705
1912
  };
1706
1913
  function createTracking(options) {
1707
- const { apiKey, endpoint, triggers, environment, debug } = options;
1914
+ const { apiKey, endpoint, triggers, environment, debug, phone } = options;
1708
1915
  if (!apiKey || !endpoint) {
1709
1916
  if (apiKey || endpoint) {
1710
1917
  console.warn(
@@ -1713,14 +1920,16 @@ function createTracking(options) {
1713
1920
  }
1714
1921
  const noopTyped = NOOP_CLIENT;
1715
1922
  return {
1716
- TrackingProvider: ({ children }) => children,
1923
+ // Still publish phone config so usePhoneField/<PhoneField> work even when
1924
+ // tracking is disabled (missing apiKey/endpoint).
1925
+ TrackingProvider: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PhoneConfigContext.Provider, { value: phone ?? null, children }),
1717
1926
  useTracking: () => noopTyped
1718
1927
  };
1719
1928
  }
1720
- const TrackingContext = (0, import_react4.createContext)(null);
1929
+ const TrackingContext = (0, import_react5.createContext)(null);
1721
1930
  function TrackingProvider({ gtagId, gtagIds, children }) {
1722
1931
  const resolvedGtagIds = gtagIds ?? (gtagId ? { default: gtagId } : void 0);
1723
- const client = (0, import_react4.useMemo)(
1932
+ const client = (0, import_react5.useMemo)(
1724
1933
  () => createTypedClient(
1725
1934
  getOrCreateTrackingClient({
1726
1935
  apiKey,
@@ -1738,15 +1947,15 @@ function createTracking(options) {
1738
1947
  ),
1739
1948
  []
1740
1949
  );
1741
- const gtagIdsKey = (0, import_react4.useMemo)(() => gtagIds ? JSON.stringify(gtagIds) : "", [gtagIds]);
1742
- (0, import_react4.useEffect)(() => {
1950
+ const gtagIdsKey = (0, import_react5.useMemo)(() => gtagIds ? JSON.stringify(gtagIds) : "", [gtagIds]);
1951
+ (0, import_react5.useEffect)(() => {
1743
1952
  if (gtagIds && Object.keys(gtagIds).length > 0) {
1744
1953
  bootstrapMultipleGtags(gtagIds);
1745
1954
  } else if (gtagId) {
1746
1955
  bootstrapGoogleAdsTracking(gtagId);
1747
1956
  }
1748
1957
  }, [gtagId, gtagIdsKey]);
1749
- (0, import_react4.useEffect)(() => {
1958
+ (0, import_react5.useEffect)(() => {
1750
1959
  const detachers = [];
1751
1960
  const rawClient = getOrCreateTrackingClient({
1752
1961
  apiKey,
@@ -1786,10 +1995,10 @@ function createTracking(options) {
1786
1995
  }
1787
1996
  };
1788
1997
  }, []);
1789
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TrackingContext.Provider, { value: client, children });
1998
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TrackingContext.Provider, { value: client, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PhoneConfigContext.Provider, { value: phone ?? null, children }) });
1790
1999
  }
1791
2000
  function useTracking() {
1792
- const client = (0, import_react4.useContext)(TrackingContext);
2001
+ const client = (0, import_react5.useContext)(TrackingContext);
1793
2002
  if (client === null) {
1794
2003
  throw new Error(
1795
2004
  "useTracking must be called inside a <TrackingProvider> returned by createTracking()"
@@ -1803,7 +2012,10 @@ function createTracking(options) {
1803
2012
  0 && (module.exports = {
1804
2013
  AranovaApiError,
1805
2014
  ConsentBanner,
2015
+ DEFAULT_PHONE_COUNTRY,
1806
2016
  GoogleAdsTracking,
2017
+ PhoneConfigContext,
2018
+ PhoneField,
1807
2019
  TRACKING_PARAM_KEYS,
1808
2020
  captureTrackingParamsFromLocation,
1809
2021
  createSalesClient,
@@ -1812,15 +2024,23 @@ function createTracking(options) {
1812
2024
  createTrackingEventCreatePayload,
1813
2025
  createTrackingSessionUpsertPayload,
1814
2026
  fetchServices,
2027
+ formatDateInTz,
1815
2028
  formatMoney,
2029
+ formatPhone,
2030
+ formatPhoneAsTyped,
1816
2031
  fromMinor,
1817
2032
  getConsentState,
2033
+ parsePhone,
2034
+ phoneField,
1818
2035
  resetConsent,
1819
2036
  setConsentState,
2037
+ toE164,
1820
2038
  toMinor,
1821
2039
  useConsent,
1822
2040
  useConsentState,
1823
2041
  useGclid,
2042
+ usePhoneConfig,
2043
+ usePhoneField,
1824
2044
  useTrackingParams
1825
2045
  });
1826
2046
  //# sourceMappingURL=index.js.map