@cimplify/sdk 0.21.0 → 0.22.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/dist/react.mjs CHANGED
@@ -3,7 +3,7 @@ import { getVariantDisplayName, createCimplifyClient } from './chunk-W3DMONZR.mj
3
3
  export { getVariantDisplayName } from './chunk-W3DMONZR.mjs';
4
4
  import { ELEMENT_TYPES, EVENT_TYPES } from './chunk-R4EYMPSN.mjs';
5
5
  import { isSupportedCurrency, formatPrice, parsePrice, isOnSale, getDiscountPercentage, getBasePrice } from './chunk-JFT63H34.mjs';
6
- import React3, { createContext, useContext, useState, useEffect, useRef, useMemo, useSyncExternalStore, useCallback, useId } from 'react';
6
+ import React3, { createContext, useRef, useState, useCallback, useEffect, useContext, useMemo, useSyncExternalStore, useId } from 'react';
7
7
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
8
  import { NumberField } from '@base-ui/react/number-field';
9
9
  import { clsx } from 'clsx';
@@ -439,6 +439,9 @@ function buildLineKey(productId, options) {
439
439
  if (options.staffId) {
440
440
  parts.push(`st:${options.staffId}`);
441
441
  }
442
+ if (options.resourceId) {
443
+ parts.push(`r:${options.resourceId}`);
444
+ }
442
445
  return parts.join("|");
443
446
  }
444
447
  function calculateLineSubtotal(item) {
@@ -756,7 +759,8 @@ function createCartStore(params) {
756
759
  composite_selections: normalizedOptions.compositeSelections,
757
760
  scheduled_start: normalizedOptions.scheduledStart,
758
761
  scheduled_end: normalizedOptions.scheduledEnd,
759
- staff_id: normalizedOptions.staffId
762
+ staff_id: normalizedOptions.staffId,
763
+ resource_id: normalizedOptions.resourceId
760
764
  });
761
765
  if (!result.ok) {
762
766
  throw result.error;
@@ -1675,41 +1679,130 @@ function Price({ amount, className, prefix }) {
1675
1679
  formatPrice(convertPrice(amount), displayCurrency)
1676
1680
  ] });
1677
1681
  }
1678
- var productsCache = /* @__PURE__ */ new Map();
1679
- var productsInflight = /* @__PURE__ */ new Map();
1680
- function buildProductsCacheKey(client, locationId, options) {
1681
- return [
1682
- client.getPublicKey(),
1683
- locationId ?? "",
1684
- options.category ?? "",
1685
- options.taxonomy ?? "",
1686
- options.search ?? "",
1687
- options.featured ?? "",
1688
- options.limit ?? "",
1689
- options.sort_by ?? "",
1690
- options.sort_order ?? "",
1691
- options.tags?.join(",") ?? "",
1692
- options.in_stock ?? "",
1693
- options.min_price ?? "",
1694
- options.max_price ?? "",
1695
- options.page ?? "",
1696
- options.properties ? JSON.stringify(options.properties) : ""
1697
- ].join("|");
1698
- }
1699
- function useProducts(options = {}) {
1700
- const context = useOptionalCimplifyClient();
1701
- const client = options.client ?? context?.client;
1702
- if (!client) {
1703
- throw new Error("useProducts must be used within CimplifyProvider or passed { client }.");
1682
+ function createCachedQuery(config) {
1683
+ const cache = /* @__PURE__ */ new Map();
1684
+ const inflight = /* @__PURE__ */ new Map();
1685
+ const gcMaxAge = config.gcMaxAge ?? 3e5;
1686
+ let lastSweep = 0;
1687
+ function sweep() {
1688
+ const now = Date.now();
1689
+ if (now - lastSweep < 6e4) return;
1690
+ lastSweep = now;
1691
+ const cutoff = now - gcMaxAge;
1692
+ for (const [key, entry] of cache) {
1693
+ if (entry.ts < cutoff) cache.delete(key);
1694
+ }
1704
1695
  }
1705
- const enabled = options.enabled ?? true;
1706
- const locationId = client.getLocationId();
1707
- const previousLocationIdRef = useRef(locationId);
1708
- const requestIdRef = useRef(0);
1709
- const tagsSignature = options.tags?.join(",") ?? "";
1710
- const propertiesSignature = options.properties ? JSON.stringify(options.properties) : "";
1711
- const queryOptions = useMemo(
1712
- () => ({
1696
+ function useCachedQuery(options = {}) {
1697
+ const context = useOptionalCimplifyClient();
1698
+ const client = options.client ?? context?.client;
1699
+ if (!client) {
1700
+ throw new Error(
1701
+ `${config.name} must be used within CimplifyProvider or passed { client }.`
1702
+ );
1703
+ }
1704
+ const enabled = options.enabled ?? true;
1705
+ const locationId = client.getLocationId();
1706
+ const previousLocationRef = useRef(locationId);
1707
+ const requestIdRef = useRef(0);
1708
+ const optionsRef = useRef(options);
1709
+ optionsRef.current = options;
1710
+ if (config.locationSensitive && previousLocationRef.current !== locationId) {
1711
+ cache.clear();
1712
+ inflight.clear();
1713
+ previousLocationRef.current = locationId;
1714
+ }
1715
+ sweep();
1716
+ const cacheKey2 = config.key(client, options);
1717
+ const cached = cache.get(cacheKey2);
1718
+ const [data, setData] = useState(cached?.data ?? null);
1719
+ const [isLoading, setIsLoading] = useState(enabled && !cached);
1720
+ const [error, setError] = useState(null);
1721
+ const load = useCallback(
1722
+ async (force = false) => {
1723
+ if (!enabled) {
1724
+ setIsLoading(false);
1725
+ return;
1726
+ }
1727
+ const nextRequestId = ++requestIdRef.current;
1728
+ setError(null);
1729
+ if (!force) {
1730
+ const hit = cache.get(cacheKey2);
1731
+ if (hit) {
1732
+ setData(hit.data);
1733
+ setIsLoading(false);
1734
+ return;
1735
+ }
1736
+ }
1737
+ setIsLoading(true);
1738
+ try {
1739
+ const existing = inflight.get(cacheKey2);
1740
+ const promise = existing ?? (async () => config.fetch(client, optionsRef.current))();
1741
+ if (!existing) {
1742
+ inflight.set(cacheKey2, promise);
1743
+ promise.finally(() => inflight.delete(cacheKey2)).catch(() => {
1744
+ });
1745
+ }
1746
+ const value = await promise;
1747
+ cache.set(cacheKey2, { data: value, ts: Date.now() });
1748
+ if (nextRequestId === requestIdRef.current) {
1749
+ setData(value);
1750
+ setError(null);
1751
+ }
1752
+ } catch (loadError) {
1753
+ if (nextRequestId === requestIdRef.current) {
1754
+ setError(loadError);
1755
+ }
1756
+ } finally {
1757
+ if (nextRequestId === requestIdRef.current) {
1758
+ setIsLoading(false);
1759
+ }
1760
+ }
1761
+ },
1762
+ [cacheKey2, client, enabled]
1763
+ );
1764
+ useEffect(() => {
1765
+ void load(false);
1766
+ }, [load]);
1767
+ const refetch = useCallback(async () => {
1768
+ cache.delete(cacheKey2);
1769
+ await load(true);
1770
+ }, [cacheKey2, load]);
1771
+ return { data, isLoading, error, refetch };
1772
+ }
1773
+ useCachedQuery.clear = () => {
1774
+ cache.clear();
1775
+ inflight.clear();
1776
+ };
1777
+ return useCachedQuery;
1778
+ }
1779
+ function cacheKey(...parts) {
1780
+ return parts.map((p) => String(p ?? "")).join("|");
1781
+ }
1782
+
1783
+ // src/react/hooks/use-products.ts
1784
+ var productsQuery = createCachedQuery({
1785
+ name: "useProducts",
1786
+ locationSensitive: true,
1787
+ key: (client, options) => cacheKey(
1788
+ client.getPublicKey(),
1789
+ client.getLocationId(),
1790
+ options.category,
1791
+ options.taxonomy,
1792
+ options.search,
1793
+ options.featured,
1794
+ options.limit,
1795
+ options.sort_by,
1796
+ options.sort_order,
1797
+ options.tags?.join(","),
1798
+ options.in_stock,
1799
+ options.min_price,
1800
+ options.max_price,
1801
+ options.page,
1802
+ options.properties ? JSON.stringify(options.properties) : void 0
1803
+ ),
1804
+ fetch: async (client, options) => {
1805
+ const result = await client.catalogue.getProducts({
1713
1806
  category: options.category,
1714
1807
  taxonomy: options.taxonomy,
1715
1808
  search: options.search,
@@ -1723,301 +1816,71 @@ function useProducts(options = {}) {
1723
1816
  max_price: options.max_price,
1724
1817
  page: options.page,
1725
1818
  properties: options.properties
1726
- }),
1727
- [
1728
- options.category,
1729
- options.taxonomy,
1730
- options.search,
1731
- options.featured,
1732
- options.limit,
1733
- options.sort_by,
1734
- options.sort_order,
1735
- tagsSignature,
1736
- options.in_stock,
1737
- options.min_price,
1738
- options.max_price,
1739
- options.page,
1740
- propertiesSignature
1741
- ]
1742
- );
1743
- const cacheKey = useMemo(
1744
- () => buildProductsCacheKey(client, locationId, queryOptions),
1745
- [client, locationId, queryOptions]
1746
- );
1747
- const cached = productsCache.get(cacheKey);
1748
- const [products, setProducts] = useState(cached?.products ?? []);
1749
- const [isComplete, setIsComplete] = useState(cached?.is_complete ?? true);
1750
- const [totalAvailable, setTotalAvailable] = useState(cached?.total_available);
1751
- const [pagination, setPagination] = useState(cached?.pagination);
1752
- const [isLoading, setIsLoading] = useState(enabled && !cached);
1753
- const [error, setError] = useState(null);
1754
- useEffect(() => {
1755
- if (previousLocationIdRef.current !== locationId) {
1756
- productsCache.clear();
1757
- productsInflight.clear();
1758
- previousLocationIdRef.current = locationId;
1759
- }
1760
- }, [locationId]);
1761
- const load = useCallback(
1762
- async (force = false) => {
1763
- if (!enabled) {
1764
- setIsLoading(false);
1765
- return;
1766
- }
1767
- const nextRequestId = ++requestIdRef.current;
1768
- setError(null);
1769
- if (!force) {
1770
- const cacheEntry = productsCache.get(cacheKey);
1771
- if (cacheEntry) {
1772
- setProducts(cacheEntry.products);
1773
- setIsComplete(cacheEntry.is_complete);
1774
- setTotalAvailable(cacheEntry.total_available);
1775
- setPagination(cacheEntry.pagination);
1776
- setIsLoading(false);
1777
- return;
1778
- }
1779
- }
1780
- setIsLoading(true);
1781
- try {
1782
- const existing = productsInflight.get(cacheKey);
1783
- const promise = existing ?? (async () => {
1784
- const result = await client.catalogue.getProducts(queryOptions);
1785
- if (!result.ok) {
1786
- throw result.error;
1787
- }
1788
- return result.value;
1789
- })();
1790
- if (!existing) {
1791
- productsInflight.set(
1792
- cacheKey,
1793
- promise.finally(() => {
1794
- productsInflight.delete(cacheKey);
1795
- })
1796
- );
1797
- }
1798
- const value = await promise;
1799
- productsCache.set(cacheKey, {
1800
- products: value.items,
1801
- is_complete: value.is_complete,
1802
- total_available: value.total_available,
1803
- pagination: value.pagination
1804
- });
1805
- if (nextRequestId === requestIdRef.current) {
1806
- setProducts(value.items);
1807
- setIsComplete(value.is_complete);
1808
- setTotalAvailable(value.total_available);
1809
- setPagination(value.pagination);
1810
- setError(null);
1811
- }
1812
- } catch (loadError) {
1813
- if (nextRequestId === requestIdRef.current) {
1814
- setError(loadError);
1815
- }
1816
- } finally {
1817
- if (nextRequestId === requestIdRef.current) {
1818
- setIsLoading(false);
1819
- }
1820
- }
1821
- },
1822
- [cacheKey, client, enabled, queryOptions]
1823
- );
1824
- useEffect(() => {
1825
- void load(false);
1826
- }, [load]);
1827
- const refetch = useCallback(async () => {
1828
- productsCache.delete(cacheKey);
1829
- await load(true);
1830
- }, [cacheKey, load]);
1819
+ });
1820
+ if (!result.ok) throw result.error;
1821
+ return {
1822
+ products: result.value.items,
1823
+ is_complete: result.value.is_complete,
1824
+ total_available: result.value.total_available,
1825
+ pagination: result.value.pagination
1826
+ };
1827
+ }
1828
+ });
1829
+ function useProducts(options = {}) {
1830
+ const { data, isLoading, error, refetch } = productsQuery(options);
1831
1831
  return {
1832
- products,
1833
- is_complete: isComplete,
1834
- total_available: totalAvailable,
1835
- pagination,
1832
+ products: data?.products ?? [],
1833
+ is_complete: data?.is_complete ?? true,
1834
+ total_available: data?.total_available,
1835
+ pagination: data?.pagination,
1836
1836
  isLoading,
1837
1837
  error,
1838
1838
  refetch
1839
1839
  };
1840
1840
  }
1841
- var productCache = /* @__PURE__ */ new Map();
1842
- var productInflight = /* @__PURE__ */ new Map();
1841
+
1842
+ // src/react/hooks/use-product.ts
1843
1843
  function isLikelySlug(value) {
1844
1844
  return /^[a-z0-9-]+$/.test(value);
1845
1845
  }
1846
- function buildProductCacheKey(client, locationId, slugOrId) {
1847
- return JSON.stringify({
1848
- key: client.getPublicKey(),
1849
- location_id: locationId || "__none__",
1850
- slug_or_id: slugOrId
1846
+ var productQuery = createCachedQuery({
1847
+ name: "useProduct",
1848
+ locationSensitive: true,
1849
+ key: (client, options) => cacheKey(client.getPublicKey(), client.getLocationId(), options.slugOrId),
1850
+ fetch: async (client, options) => {
1851
+ const result = isLikelySlug(options.slugOrId) ? await client.catalogue.getProductBySlug(options.slugOrId) : await client.catalogue.getProduct(options.slugOrId);
1852
+ if (!result.ok) throw result.error;
1853
+ client.activity.trackProductView(result.value.id, {
1854
+ productName: result.value.name,
1855
+ categoryId: result.value.category_id,
1856
+ price: String(result.value.default_price)
1857
+ });
1858
+ return result.value;
1859
+ }
1860
+ });
1861
+ function useProduct(slugOrId, options = {}) {
1862
+ const normalized = (slugOrId || "").trim();
1863
+ const { data, isLoading, error, refetch } = productQuery({
1864
+ ...options,
1865
+ slugOrId: normalized,
1866
+ enabled: (options.enabled ?? true) && normalized.length > 0
1851
1867
  });
1868
+ return { product: data, isLoading, error, refetch };
1852
1869
  }
1853
- function useProduct(slugOrId, options = {}) {
1854
- const context = useOptionalCimplifyClient();
1855
- const client = options.client ?? context?.client;
1856
- if (!client) {
1857
- throw new Error("useProduct must be used within CimplifyProvider or passed { client }.");
1870
+
1871
+ // src/react/hooks/use-categories.ts
1872
+ var categoriesQuery = createCachedQuery({
1873
+ name: "useCategories",
1874
+ key: (client) => client.getPublicKey() || "__demo__",
1875
+ fetch: async (client) => {
1876
+ const result = await client.catalogue.getCategories();
1877
+ if (!result.ok) throw result.error;
1878
+ return result.value;
1858
1879
  }
1859
- const enabled = options.enabled ?? true;
1860
- const locationId = client.getLocationId();
1861
- const previousLocationIdRef = useRef(locationId);
1862
- const requestIdRef = useRef(0);
1863
- const trackedProductRef = useRef(null);
1864
- const normalizedSlugOrId = useMemo(() => (slugOrId || "").trim(), [slugOrId]);
1865
- const cacheKey = useMemo(
1866
- () => buildProductCacheKey(client, locationId, normalizedSlugOrId),
1867
- [client, locationId, normalizedSlugOrId]
1868
- );
1869
- const cached = productCache.get(cacheKey);
1870
- const [product, setProduct] = useState(cached?.product ?? null);
1871
- const [isLoading, setIsLoading] = useState(
1872
- enabled && normalizedSlugOrId.length > 0 && !cached
1873
- );
1874
- const [error, setError] = useState(null);
1875
- useEffect(() => {
1876
- if (previousLocationIdRef.current !== locationId) {
1877
- productCache.clear();
1878
- productInflight.clear();
1879
- previousLocationIdRef.current = locationId;
1880
- }
1881
- }, [locationId]);
1882
- const load = useCallback(
1883
- async (force = false) => {
1884
- if (!enabled || normalizedSlugOrId.length === 0) {
1885
- setProduct(null);
1886
- setIsLoading(false);
1887
- return;
1888
- }
1889
- const nextRequestId = ++requestIdRef.current;
1890
- setError(null);
1891
- if (!force) {
1892
- const cacheEntry = productCache.get(cacheKey);
1893
- if (cacheEntry) {
1894
- setProduct(cacheEntry.product);
1895
- setIsLoading(false);
1896
- return;
1897
- }
1898
- }
1899
- setIsLoading(true);
1900
- try {
1901
- const existing = productInflight.get(cacheKey);
1902
- const promise = existing ?? (async () => {
1903
- const result = isLikelySlug(normalizedSlugOrId) ? await client.catalogue.getProductBySlug(normalizedSlugOrId) : await client.catalogue.getProduct(normalizedSlugOrId);
1904
- if (!result.ok) {
1905
- throw result.error;
1906
- }
1907
- return result.value;
1908
- })();
1909
- if (!existing) {
1910
- productInflight.set(
1911
- cacheKey,
1912
- promise.finally(() => {
1913
- productInflight.delete(cacheKey);
1914
- })
1915
- );
1916
- }
1917
- const value = await promise;
1918
- productCache.set(cacheKey, { product: value });
1919
- if (nextRequestId === requestIdRef.current) {
1920
- setProduct(value);
1921
- setError(null);
1922
- if (value && trackedProductRef.current !== value.id) {
1923
- trackedProductRef.current = value.id;
1924
- client.activity.trackProductView(value.id, {
1925
- productName: value.name,
1926
- categoryId: value.category_id,
1927
- price: String(value.default_price)
1928
- });
1929
- }
1930
- }
1931
- } catch (loadError) {
1932
- if (nextRequestId === requestIdRef.current) {
1933
- setError(loadError);
1934
- }
1935
- } finally {
1936
- if (nextRequestId === requestIdRef.current) {
1937
- setIsLoading(false);
1938
- }
1939
- }
1940
- },
1941
- [cacheKey, client, enabled, normalizedSlugOrId]
1942
- );
1943
- useEffect(() => {
1944
- void load(false);
1945
- }, [load]);
1946
- const refetch = useCallback(async () => {
1947
- productCache.delete(cacheKey);
1948
- await load(true);
1949
- }, [cacheKey, load]);
1950
- return { product, isLoading, error, refetch };
1951
- }
1952
- var categoriesCache = /* @__PURE__ */ new Map();
1953
- var categoriesInflight = /* @__PURE__ */ new Map();
1954
- function buildCategoriesCacheKey(client) {
1955
- return client.getPublicKey() || "__demo__";
1956
- }
1880
+ });
1957
1881
  function useCategories(options = {}) {
1958
- const context = useOptionalCimplifyClient();
1959
- const client = options.client ?? context?.client;
1960
- if (!client) {
1961
- throw new Error("useCategories must be used within CimplifyProvider or passed { client }.");
1962
- }
1963
- const enabled = options.enabled ?? true;
1964
- const cacheKey = useMemo(() => buildCategoriesCacheKey(client), [client]);
1965
- const cached = categoriesCache.get(cacheKey);
1966
- const [categories, setCategories] = useState(cached ?? []);
1967
- const [isLoading, setIsLoading] = useState(enabled && !cached);
1968
- const [error, setError] = useState(null);
1969
- const load = useCallback(
1970
- async (force = false) => {
1971
- if (!enabled) {
1972
- setIsLoading(false);
1973
- return;
1974
- }
1975
- setError(null);
1976
- if (!force) {
1977
- const cachedCategories = categoriesCache.get(cacheKey);
1978
- if (cachedCategories) {
1979
- setCategories(cachedCategories);
1980
- setIsLoading(false);
1981
- return;
1982
- }
1983
- }
1984
- setIsLoading(true);
1985
- try {
1986
- const existing = categoriesInflight.get(cacheKey);
1987
- const promise = existing ?? (async () => {
1988
- const result = await client.catalogue.getCategories();
1989
- if (!result.ok) {
1990
- throw result.error;
1991
- }
1992
- return result.value;
1993
- })();
1994
- if (!existing) {
1995
- categoriesInflight.set(
1996
- cacheKey,
1997
- promise.finally(() => {
1998
- categoriesInflight.delete(cacheKey);
1999
- })
2000
- );
2001
- }
2002
- const value = await promise;
2003
- categoriesCache.set(cacheKey, value);
2004
- setCategories(value);
2005
- } catch (loadError) {
2006
- setError(loadError);
2007
- } finally {
2008
- setIsLoading(false);
2009
- }
2010
- },
2011
- [cacheKey, client, enabled]
2012
- );
2013
- useEffect(() => {
2014
- void load(false);
2015
- }, [load]);
2016
- const refetch = useCallback(async () => {
2017
- categoriesCache.delete(cacheKey);
2018
- await load(true);
2019
- }, [cacheKey, load]);
2020
- return { categories, isLoading, error, refetch };
1882
+ const { data, isLoading, error, refetch } = categoriesQuery(options);
1883
+ return { categories: data ?? [], isLoading, error, refetch };
2021
1884
  }
2022
1885
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "cancelled", "refunded"]);
2023
1886
  var orderCache = /* @__PURE__ */ new Map();
@@ -2039,11 +1902,11 @@ function useOrder(orderId, options = {}) {
2039
1902
  const previousStatusRef = useRef(null);
2040
1903
  const onStatusChangeRef = useRef(options.onStatusChange);
2041
1904
  onStatusChangeRef.current = options.onStatusChange;
2042
- const cacheKey = useMemo(
1905
+ const cacheKey2 = useMemo(
2043
1906
  () => buildOrderCacheKey(client, normalizedOrderId),
2044
1907
  [client, normalizedOrderId]
2045
1908
  );
2046
- const cached = orderCache.get(cacheKey);
1909
+ const cached = orderCache.get(cacheKey2);
2047
1910
  const [order, setOrder] = useState(cached?.order ?? null);
2048
1911
  const [isLoading, setIsLoading] = useState(
2049
1912
  enabled && normalizedOrderId.length > 0 && !cached
@@ -2059,7 +1922,7 @@ function useOrder(orderId, options = {}) {
2059
1922
  const nextRequestId = ++requestIdRef.current;
2060
1923
  setError(null);
2061
1924
  if (!force) {
2062
- const cacheEntry = orderCache.get(cacheKey);
1925
+ const cacheEntry = orderCache.get(cacheKey2);
2063
1926
  if (cacheEntry) {
2064
1927
  setOrder(cacheEntry.order);
2065
1928
  setIsLoading(false);
@@ -2068,7 +1931,7 @@ function useOrder(orderId, options = {}) {
2068
1931
  }
2069
1932
  setIsLoading(true);
2070
1933
  try {
2071
- const existing = orderInflight.get(cacheKey);
1934
+ const existing = orderInflight.get(cacheKey2);
2072
1935
  const promise = existing ?? (async () => {
2073
1936
  const result = await client.orders.get(normalizedOrderId);
2074
1937
  if (!result.ok) {
@@ -2078,14 +1941,14 @@ function useOrder(orderId, options = {}) {
2078
1941
  })();
2079
1942
  if (!existing) {
2080
1943
  orderInflight.set(
2081
- cacheKey,
1944
+ cacheKey2,
2082
1945
  promise.finally(() => {
2083
- orderInflight.delete(cacheKey);
1946
+ orderInflight.delete(cacheKey2);
2084
1947
  })
2085
1948
  );
2086
1949
  }
2087
1950
  const value = await promise;
2088
- orderCache.set(cacheKey, { order: value });
1951
+ orderCache.set(cacheKey2, { order: value });
2089
1952
  if (nextRequestId === requestIdRef.current) {
2090
1953
  setOrder(value);
2091
1954
  setError(null);
@@ -2106,7 +1969,7 @@ function useOrder(orderId, options = {}) {
2106
1969
  }
2107
1970
  }
2108
1971
  },
2109
- [cacheKey, client, enabled, normalizedOrderId]
1972
+ [cacheKey2, client, enabled, normalizedOrderId]
2110
1973
  );
2111
1974
  useEffect(() => {
2112
1975
  void load(false);
@@ -2124,9 +1987,9 @@ function useOrder(orderId, options = {}) {
2124
1987
  };
2125
1988
  }, [enabled, isTerminal, load, normalizedOrderId, poll, pollInterval]);
2126
1989
  const refetch = useCallback(async () => {
2127
- orderCache.delete(cacheKey);
1990
+ orderCache.delete(cacheKey2);
2128
1991
  await load(true);
2129
- }, [cacheKey, load]);
1992
+ }, [cacheKey2, load]);
2130
1993
  return { order, isLoading, error, refetch, isTerminal };
2131
1994
  }
2132
1995
  var LOCATION_STORAGE_KEY2 = "cimplify_location_id";
@@ -2229,216 +2092,64 @@ function useLocations(options = {}) {
2229
2092
  isLoading
2230
2093
  };
2231
2094
  }
2232
- var collectionsCache = /* @__PURE__ */ new Map();
2233
- var collectionsInflight = /* @__PURE__ */ new Map();
2234
- function buildCollectionsCacheKey(client, locationId) {
2235
- return JSON.stringify({
2236
- key: client.getPublicKey(),
2237
- location_id: locationId || "__none__"
2095
+
2096
+ // src/react/hooks/use-collections.ts
2097
+ var collectionsQuery = createCachedQuery({
2098
+ name: "useCollections",
2099
+ locationSensitive: true,
2100
+ key: (client) => cacheKey(client.getPublicKey(), client.getLocationId()),
2101
+ fetch: async (client) => {
2102
+ const result = await client.catalogue.getCollections();
2103
+ if (!result.ok) throw result.error;
2104
+ return result.value;
2105
+ }
2106
+ });
2107
+ function useCollections(options = {}) {
2108
+ const { data, isLoading, error, refetch } = collectionsQuery(options);
2109
+ return { collections: data ?? [], isLoading, error, refetch };
2110
+ }
2111
+
2112
+ // src/react/hooks/use-collection.ts
2113
+ function isLikelySlug2(value) {
2114
+ return /^[a-z0-9-]+$/.test(value);
2115
+ }
2116
+ var collectionQuery = createCachedQuery({
2117
+ name: "useCollection",
2118
+ locationSensitive: true,
2119
+ key: (client, options) => cacheKey(client.getPublicKey(), client.getLocationId(), options.idOrSlug),
2120
+ fetch: async (client, options) => {
2121
+ const collectionResult = isLikelySlug2(options.idOrSlug) ? await client.catalogue.getCollectionBySlug(options.idOrSlug) : await client.catalogue.getCollection(options.idOrSlug);
2122
+ if (!collectionResult.ok) throw collectionResult.error;
2123
+ const productsResult = await client.catalogue.getCollectionProducts(
2124
+ collectionResult.value.id
2125
+ );
2126
+ if (!productsResult.ok) throw productsResult.error;
2127
+ return {
2128
+ collection: collectionResult.value,
2129
+ products: productsResult.value
2130
+ };
2131
+ }
2132
+ });
2133
+ function useCollection(idOrSlug, options = {}) {
2134
+ const normalized = (idOrSlug || "").trim();
2135
+ const { data, isLoading, error, refetch } = collectionQuery({
2136
+ ...options,
2137
+ idOrSlug: normalized,
2138
+ enabled: (options.enabled ?? true) && normalized.length > 0
2238
2139
  });
2140
+ return {
2141
+ collection: data?.collection ?? null,
2142
+ products: data?.products ?? [],
2143
+ isLoading,
2144
+ error,
2145
+ refetch
2146
+ };
2239
2147
  }
2240
- function useCollections(options = {}) {
2148
+ function useSearch(options = {}) {
2241
2149
  const context = useOptionalCimplifyClient();
2242
2150
  const client = options.client ?? context?.client;
2243
2151
  if (!client) {
2244
- throw new Error("useCollections must be used within CimplifyProvider or passed { client }.");
2245
- }
2246
- const enabled = options.enabled ?? true;
2247
- const locationId = client.getLocationId();
2248
- const previousLocationIdRef = useRef(locationId);
2249
- const requestIdRef = useRef(0);
2250
- const cacheKey = useMemo(
2251
- () => buildCollectionsCacheKey(client, locationId),
2252
- [client, locationId]
2253
- );
2254
- const cached = collectionsCache.get(cacheKey);
2255
- const [collections, setCollections] = useState(cached?.collections ?? []);
2256
- const [isLoading, setIsLoading] = useState(enabled && !cached);
2257
- const [error, setError] = useState(null);
2258
- useEffect(() => {
2259
- if (previousLocationIdRef.current !== locationId) {
2260
- collectionsCache.clear();
2261
- collectionsInflight.clear();
2262
- previousLocationIdRef.current = locationId;
2263
- }
2264
- }, [locationId]);
2265
- const load = useCallback(
2266
- async (force = false) => {
2267
- if (!enabled) {
2268
- setIsLoading(false);
2269
- return;
2270
- }
2271
- const nextRequestId = ++requestIdRef.current;
2272
- setError(null);
2273
- if (!force) {
2274
- const cacheEntry = collectionsCache.get(cacheKey);
2275
- if (cacheEntry) {
2276
- setCollections(cacheEntry.collections);
2277
- setIsLoading(false);
2278
- return;
2279
- }
2280
- }
2281
- setIsLoading(true);
2282
- try {
2283
- const existing = collectionsInflight.get(cacheKey);
2284
- const promise = existing ?? (async () => {
2285
- const result = await client.catalogue.getCollections();
2286
- if (!result.ok) {
2287
- throw result.error;
2288
- }
2289
- return result.value;
2290
- })();
2291
- if (!existing) {
2292
- collectionsInflight.set(cacheKey, promise);
2293
- promise.finally(() => {
2294
- collectionsInflight.delete(cacheKey);
2295
- }).catch(() => void 0);
2296
- }
2297
- const value = await promise;
2298
- collectionsCache.set(cacheKey, { collections: value });
2299
- if (nextRequestId === requestIdRef.current) {
2300
- setCollections(value);
2301
- setError(null);
2302
- }
2303
- } catch (loadError) {
2304
- if (nextRequestId === requestIdRef.current) {
2305
- setError(loadError);
2306
- }
2307
- } finally {
2308
- if (nextRequestId === requestIdRef.current) {
2309
- setIsLoading(false);
2310
- }
2311
- }
2312
- },
2313
- [cacheKey, client, enabled]
2314
- );
2315
- useEffect(() => {
2316
- void load(false);
2317
- }, [load]);
2318
- const refetch = useCallback(async () => {
2319
- collectionsCache.delete(cacheKey);
2320
- await load(true);
2321
- }, [cacheKey, load]);
2322
- return { collections, isLoading, error, refetch };
2323
- }
2324
- var collectionCache = /* @__PURE__ */ new Map();
2325
- var collectionInflight = /* @__PURE__ */ new Map();
2326
- function isLikelySlug2(value) {
2327
- return /^[a-z0-9-]+$/.test(value);
2328
- }
2329
- function buildCollectionCacheKey(client, locationId, idOrSlug) {
2330
- return JSON.stringify({
2331
- key: client.getPublicKey(),
2332
- location_id: locationId || "__none__",
2333
- collection: idOrSlug
2334
- });
2335
- }
2336
- function useCollection(idOrSlug, options = {}) {
2337
- const context = useOptionalCimplifyClient();
2338
- const client = options.client ?? context?.client;
2339
- if (!client) {
2340
- throw new Error("useCollection must be used within CimplifyProvider or passed { client }.");
2341
- }
2342
- const enabled = options.enabled ?? true;
2343
- const locationId = client.getLocationId();
2344
- const previousLocationIdRef = useRef(locationId);
2345
- const requestIdRef = useRef(0);
2346
- const normalizedIdOrSlug = useMemo(() => (idOrSlug || "").trim(), [idOrSlug]);
2347
- const cacheKey = useMemo(
2348
- () => buildCollectionCacheKey(client, locationId, normalizedIdOrSlug),
2349
- [client, locationId, normalizedIdOrSlug]
2350
- );
2351
- const cached = collectionCache.get(cacheKey);
2352
- const [collection, setCollection] = useState(cached?.collection ?? null);
2353
- const [products, setProducts] = useState(cached?.products ?? []);
2354
- const [isLoading, setIsLoading] = useState(
2355
- enabled && normalizedIdOrSlug.length > 0 && !cached
2356
- );
2357
- const [error, setError] = useState(null);
2358
- useEffect(() => {
2359
- if (previousLocationIdRef.current !== locationId) {
2360
- collectionCache.clear();
2361
- collectionInflight.clear();
2362
- previousLocationIdRef.current = locationId;
2363
- }
2364
- }, [locationId]);
2365
- const load = useCallback(
2366
- async (force = false) => {
2367
- if (!enabled || normalizedIdOrSlug.length === 0) {
2368
- setCollection(null);
2369
- setProducts([]);
2370
- setIsLoading(false);
2371
- return;
2372
- }
2373
- const nextRequestId = ++requestIdRef.current;
2374
- setError(null);
2375
- if (!force) {
2376
- const cacheEntry = collectionCache.get(cacheKey);
2377
- if (cacheEntry) {
2378
- setCollection(cacheEntry.collection);
2379
- setProducts(cacheEntry.products);
2380
- setIsLoading(false);
2381
- return;
2382
- }
2383
- }
2384
- setIsLoading(true);
2385
- try {
2386
- const existing = collectionInflight.get(cacheKey);
2387
- const promise = existing ?? (async () => {
2388
- const collectionResult = isLikelySlug2(normalizedIdOrSlug) ? await client.catalogue.getCollectionBySlug(normalizedIdOrSlug) : await client.catalogue.getCollection(normalizedIdOrSlug);
2389
- if (!collectionResult.ok) {
2390
- throw collectionResult.error;
2391
- }
2392
- const productsResult = await client.catalogue.getCollectionProducts(
2393
- collectionResult.value.id
2394
- );
2395
- if (!productsResult.ok) {
2396
- throw productsResult.error;
2397
- }
2398
- return {
2399
- collection: collectionResult.value,
2400
- products: productsResult.value
2401
- };
2402
- })();
2403
- if (!existing) {
2404
- collectionInflight.set(cacheKey, promise);
2405
- promise.finally(() => {
2406
- collectionInflight.delete(cacheKey);
2407
- }).catch(() => void 0);
2408
- }
2409
- const value = await promise;
2410
- collectionCache.set(cacheKey, value);
2411
- if (nextRequestId === requestIdRef.current) {
2412
- setCollection(value.collection);
2413
- setProducts(value.products);
2414
- setError(null);
2415
- }
2416
- } catch (loadError) {
2417
- if (nextRequestId === requestIdRef.current) {
2418
- setError(loadError);
2419
- }
2420
- } finally {
2421
- if (nextRequestId === requestIdRef.current) {
2422
- setIsLoading(false);
2423
- }
2424
- }
2425
- },
2426
- [cacheKey, client, enabled, normalizedIdOrSlug]
2427
- );
2428
- useEffect(() => {
2429
- void load(false);
2430
- }, [load]);
2431
- const refetch = useCallback(async () => {
2432
- collectionCache.delete(cacheKey);
2433
- await load(true);
2434
- }, [cacheKey, load]);
2435
- return { collection, products, isLoading, error, refetch };
2436
- }
2437
- function useSearch(options = {}) {
2438
- const context = useOptionalCimplifyClient();
2439
- const client = options.client ?? context?.client;
2440
- if (!client) {
2441
- throw new Error("useSearch must be used within CimplifyProvider or passed { client }.");
2152
+ throw new Error("useSearch must be used within CimplifyProvider or passed { client }.");
2442
2153
  }
2443
2154
  const minLength = Math.max(0, options.minLength ?? 2);
2444
2155
  const debounceMs = Math.max(0, options.debounceMs ?? 300);
@@ -2609,11 +2320,11 @@ function useQuote(input, options = {}) {
2609
2320
  const normalized = normalizeInput(input, locationId);
2610
2321
  return normalized.product_id.length > 0 ? normalized : null;
2611
2322
  }, [inputSignature, locationId]);
2612
- const cacheKey = useMemo(
2323
+ const cacheKey2 = useMemo(
2613
2324
  () => buildQuoteCacheKey(client, locationId, inputSignature),
2614
2325
  [client, inputSignature, locationId]
2615
2326
  );
2616
- const cached = quoteCache.get(cacheKey);
2327
+ const cached = quoteCache.get(cacheKey2);
2617
2328
  const [quote, setQuote] = useState(cached?.quote ?? null);
2618
2329
  const [isLoading, setIsLoading] = useState(enabled && normalizedInput !== null && !cached);
2619
2330
  const [error, setError] = useState(null);
@@ -2639,7 +2350,7 @@ function useQuote(input, options = {}) {
2639
2350
  const nextRequestId = ++requestIdRef.current;
2640
2351
  setError(null);
2641
2352
  if (!force) {
2642
- const cacheEntry = quoteCache.get(cacheKey);
2353
+ const cacheEntry = quoteCache.get(cacheKey2);
2643
2354
  if (cacheEntry) {
2644
2355
  setQuote(cacheEntry.quote);
2645
2356
  setMessages(cacheEntry.quote?.ui_messages ?? []);
@@ -2652,7 +2363,7 @@ function useQuote(input, options = {}) {
2652
2363
  setQuote(null);
2653
2364
  setMessages([]);
2654
2365
  try {
2655
- const existing = quoteInflight.get(cacheKey);
2366
+ const existing = quoteInflight.get(cacheKey2);
2656
2367
  const promise = existing ?? (async () => {
2657
2368
  const result = await client.catalogue.fetchQuote(normalizedInput);
2658
2369
  if (!result.ok) {
@@ -2661,13 +2372,13 @@ function useQuote(input, options = {}) {
2661
2372
  return result.value;
2662
2373
  })();
2663
2374
  if (!existing) {
2664
- quoteInflight.set(cacheKey, promise);
2375
+ quoteInflight.set(cacheKey2, promise);
2665
2376
  promise.finally(() => {
2666
- quoteInflight.delete(cacheKey);
2377
+ quoteInflight.delete(cacheKey2);
2667
2378
  }).catch(() => void 0);
2668
2379
  }
2669
2380
  const value = await promise;
2670
- quoteCache.set(cacheKey, { quote: value });
2381
+ quoteCache.set(cacheKey2, { quote: value });
2671
2382
  if (nextRequestId === requestIdRef.current) {
2672
2383
  setQuote(value);
2673
2384
  setMessages(value.ui_messages ?? []);
@@ -2684,13 +2395,13 @@ function useQuote(input, options = {}) {
2684
2395
  }
2685
2396
  }
2686
2397
  },
2687
- [cacheKey, client, enabled, normalizedInput]
2398
+ [cacheKey2, client, enabled, normalizedInput]
2688
2399
  );
2689
2400
  useEffect(() => {
2690
2401
  if (debounceTimerRef.current) {
2691
2402
  clearTimeout(debounceTimerRef.current);
2692
2403
  }
2693
- const hasCached = quoteCache.has(cacheKey);
2404
+ const hasCached = quoteCache.has(cacheKey2);
2694
2405
  if (hasCached || !enabled || !normalizedInput) {
2695
2406
  void load(false);
2696
2407
  } else {
@@ -2704,7 +2415,7 @@ function useQuote(input, options = {}) {
2704
2415
  debounceTimerRef.current = null;
2705
2416
  }
2706
2417
  };
2707
- }, [load, cacheKey, enabled, normalizedInput]);
2418
+ }, [load, cacheKey2, enabled, normalizedInput]);
2708
2419
  const refresh = useCallback(async () => {
2709
2420
  if (!enabled || !normalizedInput) {
2710
2421
  return;
@@ -2725,7 +2436,7 @@ function useQuote(input, options = {}) {
2725
2436
  throw result.error;
2726
2437
  }
2727
2438
  const refreshed = result.value.quote;
2728
- quoteCache.set(cacheKey, { quote: refreshed });
2439
+ quoteCache.set(cacheKey2, { quote: refreshed });
2729
2440
  if (nextRequestId === requestIdRef.current) {
2730
2441
  setQuote(refreshed);
2731
2442
  setMessages(refreshed.ui_messages ?? []);
@@ -2741,7 +2452,7 @@ function useQuote(input, options = {}) {
2741
2452
  setIsLoading(false);
2742
2453
  }
2743
2454
  }
2744
- }, [cacheKey, client, enabled, load, normalizedInput, quote]);
2455
+ }, [cacheKey2, client, enabled, load, normalizedInput, quote]);
2745
2456
  useEffect(() => {
2746
2457
  if (!Number.isFinite(quoteExpiryTimestamp)) {
2747
2458
  setIsExpired(false);
@@ -2773,1930 +2484,628 @@ function useQuote(input, options = {}) {
2773
2484
  }, [autoRefresh, enabled, quoteExpiryTimestamp, refresh, refreshBeforeExpiryMs]);
2774
2485
  return { quote, isLoading, error, refresh, isExpired, messages };
2775
2486
  }
2776
- var taxonomiesCache = /* @__PURE__ */ new Map();
2777
- var taxonomiesInflight = /* @__PURE__ */ new Map();
2778
- function buildTaxonomiesCacheKey(client, parentId) {
2779
- return JSON.stringify({
2780
- key: client.getPublicKey(),
2781
- parent_id: parentId || "__root__"
2782
- });
2783
- }
2487
+
2488
+ // src/react/hooks/use-taxonomies.ts
2489
+ var taxonomiesQuery = createCachedQuery({
2490
+ name: "useTaxonomies",
2491
+ key: (client, options) => cacheKey(client.getPublicKey(), options.parentId ?? "__root__"),
2492
+ fetch: async (client, options) => {
2493
+ const result = await client.catalogue.getTaxonomies(options.parentId);
2494
+ if (!result.ok) throw result.error;
2495
+ return result.value;
2496
+ }
2497
+ });
2784
2498
  function useTaxonomies(options = {}) {
2499
+ const { data, isLoading, error, refetch } = taxonomiesQuery(options);
2500
+ return { taxonomies: data ?? [], isLoading, error, refetch };
2501
+ }
2502
+
2503
+ // src/react/hooks/use-taxonomy.ts
2504
+ var taxonomyQuery = createCachedQuery({
2505
+ name: "useTaxonomy",
2506
+ key: (client, options) => cacheKey(client.getPublicKey(), options.id),
2507
+ fetch: async (client, options) => {
2508
+ const result = await client.catalogue.getTaxonomy(options.id);
2509
+ if (!result.ok) throw result.error;
2510
+ client.activity.trackCategoryView(result.value.id, {
2511
+ categoryName: result.value.name
2512
+ });
2513
+ return result.value;
2514
+ }
2515
+ });
2516
+ function useTaxonomy(id, options = {}) {
2517
+ const { data, isLoading, error, refetch } = taxonomyQuery({ ...options, id });
2518
+ return { taxonomy: data, isLoading, error, refetch };
2519
+ }
2520
+
2521
+ // src/react/hooks/use-taxonomy-path.ts
2522
+ var taxonomyPathQuery = createCachedQuery({
2523
+ name: "useTaxonomyPath",
2524
+ key: (client, options) => cacheKey(client.getPublicKey(), options.id),
2525
+ fetch: async (client, options) => {
2526
+ const result = await client.catalogue.getTaxonomyPath(options.id);
2527
+ if (!result.ok) throw result.error;
2528
+ return result.value;
2529
+ }
2530
+ });
2531
+ function useTaxonomyPath(id, options = {}) {
2532
+ const { data, isLoading, error, refetch } = taxonomyPathQuery({ ...options, id });
2533
+ return { path: data ?? [], isLoading, error, refetch };
2534
+ }
2535
+
2536
+ // src/react/hooks/use-product-schedules.ts
2537
+ var productSchedulesQuery = createCachedQuery({
2538
+ name: "useProductSchedules",
2539
+ key: (client, options) => cacheKey(client.getPublicKey(), options.productId),
2540
+ fetch: async (client, options) => {
2541
+ const result = await client.catalogue.getProductSchedules(options.productId);
2542
+ if (!result.ok) throw result.error;
2543
+ return result.value;
2544
+ }
2545
+ });
2546
+ function useProductSchedules(productId, options = {}) {
2547
+ const { data, isLoading, error, refetch } = productSchedulesQuery({ ...options, productId });
2548
+ return { schedules: data ?? [], isLoading, error, refetch };
2549
+ }
2550
+
2551
+ // src/react/hooks/use-product-availability.ts
2552
+ var productAvailabilityQuery = createCachedQuery({
2553
+ name: "useProductAvailability",
2554
+ locationSensitive: true,
2555
+ key: (client, options) => {
2556
+ const locationId = options.locationId ?? client.getLocationId();
2557
+ return cacheKey(client.getPublicKey(), options.productId, locationId);
2558
+ },
2559
+ fetch: async (client, options) => {
2560
+ const locationId = options.locationId ?? client.getLocationId();
2561
+ if (!locationId) return null;
2562
+ const result = await client.catalogue.checkProductAvailableNow(options.productId, locationId);
2563
+ if (!result.ok) throw result.error;
2564
+ return result.value;
2565
+ }
2566
+ });
2567
+ function useProductAvailability(productId, options = {}) {
2568
+ const { data, isLoading, error, refetch } = productAvailabilityQuery({ ...options, productId });
2569
+ return {
2570
+ isAvailable: data?.is_available ?? null,
2571
+ schedulesToday: data?.schedules_today ?? [],
2572
+ isLoading,
2573
+ error,
2574
+ refetch
2575
+ };
2576
+ }
2577
+
2578
+ // src/react/hooks/use-deals.ts
2579
+ function resolveLocationId(client, options) {
2580
+ return options.locationId ?? client.getLocationId() ?? void 0;
2581
+ }
2582
+ var dealsQuery = createCachedQuery({
2583
+ name: "useDeals",
2584
+ locationSensitive: true,
2585
+ key: (client, options) => cacheKey(client.getPublicKey(), resolveLocationId(client, options)),
2586
+ fetch: async (client, options) => {
2587
+ const result = await client.catalogue.getDeals(resolveLocationId(client, options));
2588
+ if (!result.ok) throw result.error;
2589
+ return result.value;
2590
+ }
2591
+ });
2592
+ function useDeals(options = {}) {
2593
+ const { data, isLoading, error, refetch } = dealsQuery(options);
2594
+ return { deals: data ?? [], isLoading, error, refetch };
2595
+ }
2596
+
2597
+ // src/react/hooks/use-products-on-sale.ts
2598
+ var productsOnSaleQuery = createCachedQuery({
2599
+ name: "useProductsOnSale",
2600
+ key: (client) => client.getPublicKey() || "__demo__",
2601
+ fetch: async (client) => {
2602
+ const result = await client.catalogue.getProductsOnSale();
2603
+ if (!result.ok) throw result.error;
2604
+ return result.value;
2605
+ }
2606
+ });
2607
+ function useProductsOnSale(options = {}) {
2608
+ const { data, isLoading, error, refetch } = productsOnSaleQuery(options);
2609
+ return { products: data ?? [], isLoading, error, refetch };
2610
+ }
2611
+
2612
+ // src/react/hooks/use-product-deals.ts
2613
+ var productDealsQuery = createCachedQuery({
2614
+ name: "useProductDeals",
2615
+ key: (client, options) => cacheKey(client.getPublicKey(), options.productId),
2616
+ fetch: async (client, options) => {
2617
+ const result = await client.catalogue.getProductDeals(options.productId);
2618
+ if (!result.ok) throw result.error;
2619
+ return result.value;
2620
+ }
2621
+ });
2622
+ function useProductDeals(productId, options = {}) {
2623
+ const { data, isLoading, error, refetch } = productDealsQuery({ ...options, productId });
2624
+ return { deals: data ?? [], isLoading, error, refetch };
2625
+ }
2626
+ function useValidateDiscount(options = {}) {
2785
2627
  const context = useOptionalCimplifyClient();
2786
2628
  const client = options.client ?? context?.client;
2787
2629
  if (!client) {
2788
- throw new Error("useTaxonomies must be used within CimplifyProvider or passed { client }.");
2630
+ throw new Error("useValidateDiscount must be used within CimplifyProvider or passed { client }.");
2789
2631
  }
2790
- const enabled = options.enabled ?? true;
2791
- const cacheKey = useMemo(
2792
- () => buildTaxonomiesCacheKey(client, options.parentId),
2793
- [client, options.parentId]
2794
- );
2795
- const cached = taxonomiesCache.get(cacheKey);
2796
- const [taxonomies, setTaxonomies] = useState(cached ?? []);
2797
- const [isLoading, setIsLoading] = useState(enabled && !cached);
2632
+ const [isValidating, setIsValidating] = useState(false);
2798
2633
  const [error, setError] = useState(null);
2799
- const parentId = options.parentId;
2800
- const load = useCallback(
2801
- async (force = false) => {
2802
- if (!enabled) {
2803
- setIsLoading(false);
2804
- return;
2805
- }
2634
+ const validate = useCallback(
2635
+ async (code, orderSubtotal, locationId) => {
2806
2636
  setError(null);
2807
- if (!force) {
2808
- const cachedTaxonomies = taxonomiesCache.get(cacheKey);
2809
- if (cachedTaxonomies) {
2810
- setTaxonomies(cachedTaxonomies);
2811
- setIsLoading(false);
2812
- return;
2813
- }
2814
- }
2815
- setIsLoading(true);
2637
+ setIsValidating(true);
2816
2638
  try {
2817
- const existing = taxonomiesInflight.get(cacheKey);
2818
- const promise = existing ?? (async () => {
2819
- const result = await client.catalogue.getTaxonomies(parentId);
2820
- if (!result.ok) {
2821
- throw result.error;
2822
- }
2823
- return result.value;
2824
- })();
2825
- if (!existing) {
2826
- taxonomiesInflight.set(
2827
- cacheKey,
2828
- promise.finally(() => {
2829
- taxonomiesInflight.delete(cacheKey);
2830
- })
2831
- );
2639
+ const result = await client.catalogue.validateDiscountCode(code, orderSubtotal, locationId);
2640
+ if (!result.ok) {
2641
+ setError(result.error);
2642
+ return null;
2832
2643
  }
2833
- const value = await promise;
2834
- taxonomiesCache.set(cacheKey, value);
2835
- setTaxonomies(value);
2836
- } catch (loadError) {
2837
- setError(loadError);
2644
+ return result.value;
2645
+ } catch (validateError) {
2646
+ setError(validateError);
2647
+ return null;
2838
2648
  } finally {
2839
- setIsLoading(false);
2649
+ setIsValidating(false);
2840
2650
  }
2841
2651
  },
2842
- [cacheKey, client, enabled, parentId]
2652
+ [client]
2843
2653
  );
2844
- useEffect(() => {
2845
- void load(false);
2846
- }, [load]);
2847
- const refetch = useCallback(async () => {
2848
- taxonomiesCache.delete(cacheKey);
2849
- await load(true);
2850
- }, [cacheKey, load]);
2851
- return { taxonomies, isLoading, error, refetch };
2852
- }
2853
- var taxonomyCache = /* @__PURE__ */ new Map();
2854
- var taxonomyInflight = /* @__PURE__ */ new Map();
2855
- function buildTaxonomyCacheKey(client, id) {
2856
- return JSON.stringify({
2857
- key: client.getPublicKey(),
2858
- id
2859
- });
2654
+ return { validate, isValidating, error };
2860
2655
  }
2861
- function useTaxonomy(id, options = {}) {
2862
- const context = useOptionalCimplifyClient();
2863
- const client = options.client ?? context?.client;
2864
- if (!client) {
2865
- throw new Error("useTaxonomy must be used within CimplifyProvider or passed { client }.");
2656
+
2657
+ // src/react/hooks/use-bundles.ts
2658
+ var bundlesQuery = createCachedQuery({
2659
+ name: "useBundles",
2660
+ locationSensitive: true,
2661
+ key: (client) => cacheKey(client.getPublicKey(), client.getLocationId()),
2662
+ fetch: async (client) => {
2663
+ const result = await client.catalogue.getBundles();
2664
+ if (!result.ok) throw result.error;
2665
+ return result.value;
2866
2666
  }
2867
- const enabled = options.enabled ?? true;
2868
- const cacheKey = useMemo(() => buildTaxonomyCacheKey(client, id), [client, id]);
2869
- const cached = taxonomyCache.get(cacheKey);
2870
- const [taxonomy, setTaxonomy] = useState(cached ?? null);
2871
- const [isLoading, setIsLoading] = useState(enabled && !cached);
2872
- const [error, setError] = useState(null);
2873
- const trackedTaxonomyRef = useRef(null);
2874
- const load = useCallback(
2875
- async (force = false) => {
2876
- if (!enabled) {
2877
- setIsLoading(false);
2878
- return;
2879
- }
2880
- setError(null);
2881
- if (!force) {
2882
- const cachedTaxonomy = taxonomyCache.get(cacheKey);
2883
- if (cachedTaxonomy) {
2884
- setTaxonomy(cachedTaxonomy);
2885
- setIsLoading(false);
2886
- return;
2887
- }
2888
- }
2889
- setIsLoading(true);
2890
- try {
2891
- const existing = taxonomyInflight.get(cacheKey);
2892
- const promise = existing ?? (async () => {
2893
- const result = await client.catalogue.getTaxonomy(id);
2894
- if (!result.ok) {
2895
- throw result.error;
2896
- }
2897
- return result.value;
2898
- })();
2899
- if (!existing) {
2900
- taxonomyInflight.set(
2901
- cacheKey,
2902
- promise.finally(() => {
2903
- taxonomyInflight.delete(cacheKey);
2904
- })
2905
- );
2906
- }
2907
- const value = await promise;
2908
- taxonomyCache.set(cacheKey, value);
2909
- setTaxonomy(value);
2910
- if (value && trackedTaxonomyRef.current !== value.id) {
2911
- trackedTaxonomyRef.current = value.id;
2912
- client.activity.trackCategoryView(value.id, {
2913
- categoryName: value.name
2914
- });
2915
- }
2916
- } catch (loadError) {
2917
- setError(loadError);
2918
- } finally {
2919
- setIsLoading(false);
2920
- }
2921
- },
2922
- [cacheKey, client, enabled, id]
2923
- );
2924
- useEffect(() => {
2925
- void load(false);
2926
- }, [load]);
2927
- const refetch = useCallback(async () => {
2928
- taxonomyCache.delete(cacheKey);
2929
- await load(true);
2930
- }, [cacheKey, load]);
2931
- return { taxonomy, isLoading, error, refetch };
2932
- }
2933
- var taxonomyPathCache = /* @__PURE__ */ new Map();
2934
- var taxonomyPathInflight = /* @__PURE__ */ new Map();
2935
- function buildTaxonomyPathCacheKey(client, id) {
2936
- return JSON.stringify({
2937
- key: client.getPublicKey(),
2938
- id
2939
- });
2940
- }
2941
- function useTaxonomyPath(id, options = {}) {
2942
- const context = useOptionalCimplifyClient();
2943
- const client = options.client ?? context?.client;
2944
- if (!client) {
2945
- throw new Error("useTaxonomyPath must be used within CimplifyProvider or passed { client }.");
2946
- }
2947
- const enabled = options.enabled ?? true;
2948
- const cacheKey = useMemo(() => buildTaxonomyPathCacheKey(client, id), [client, id]);
2949
- const cached = taxonomyPathCache.get(cacheKey);
2950
- const [path, setPath] = useState(cached ?? []);
2951
- const [isLoading, setIsLoading] = useState(enabled && !cached);
2952
- const [error, setError] = useState(null);
2953
- const load = useCallback(
2954
- async (force = false) => {
2955
- if (!enabled) {
2956
- setIsLoading(false);
2957
- return;
2958
- }
2959
- setError(null);
2960
- if (!force) {
2961
- const cachedPath = taxonomyPathCache.get(cacheKey);
2962
- if (cachedPath) {
2963
- setPath(cachedPath);
2964
- setIsLoading(false);
2965
- return;
2966
- }
2967
- }
2968
- setIsLoading(true);
2969
- try {
2970
- const existing = taxonomyPathInflight.get(cacheKey);
2971
- const promise = existing ?? (async () => {
2972
- const result = await client.catalogue.getTaxonomyPath(id);
2973
- if (!result.ok) {
2974
- throw result.error;
2975
- }
2976
- return result.value;
2977
- })();
2978
- if (!existing) {
2979
- taxonomyPathInflight.set(
2980
- cacheKey,
2981
- promise.finally(() => {
2982
- taxonomyPathInflight.delete(cacheKey);
2983
- })
2984
- );
2985
- }
2986
- const value = await promise;
2987
- taxonomyPathCache.set(cacheKey, value);
2988
- setPath(value);
2989
- } catch (loadError) {
2990
- setError(loadError);
2991
- } finally {
2992
- setIsLoading(false);
2993
- }
2994
- },
2995
- [cacheKey, client, enabled, id]
2996
- );
2997
- useEffect(() => {
2998
- void load(false);
2999
- }, [load]);
3000
- const refetch = useCallback(async () => {
3001
- taxonomyPathCache.delete(cacheKey);
3002
- await load(true);
3003
- }, [cacheKey, load]);
3004
- return { path, isLoading, error, refetch };
3005
- }
3006
- var schedulesCache = /* @__PURE__ */ new Map();
3007
- var schedulesInflight = /* @__PURE__ */ new Map();
3008
- function buildSchedulesCacheKey(client, productId) {
3009
- return JSON.stringify({
3010
- key: client.getPublicKey(),
3011
- product_id: productId
3012
- });
3013
- }
3014
- function useProductSchedules(productId, options = {}) {
3015
- const context = useOptionalCimplifyClient();
3016
- const client = options.client ?? context?.client;
3017
- if (!client) {
3018
- throw new Error("useProductSchedules must be used within CimplifyProvider or passed { client }.");
3019
- }
3020
- const enabled = options.enabled ?? true;
3021
- const cacheKey = useMemo(() => buildSchedulesCacheKey(client, productId), [client, productId]);
3022
- const cached = schedulesCache.get(cacheKey);
3023
- const [schedules, setSchedules] = useState(cached ?? []);
3024
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3025
- const [error, setError] = useState(null);
3026
- const load = useCallback(
3027
- async (force = false) => {
3028
- if (!enabled) {
3029
- setIsLoading(false);
3030
- return;
3031
- }
3032
- setError(null);
3033
- if (!force) {
3034
- const cachedSchedules = schedulesCache.get(cacheKey);
3035
- if (cachedSchedules) {
3036
- setSchedules(cachedSchedules);
3037
- setIsLoading(false);
3038
- return;
3039
- }
3040
- }
3041
- setIsLoading(true);
3042
- try {
3043
- const existing = schedulesInflight.get(cacheKey);
3044
- const promise = existing ?? (async () => {
3045
- const result = await client.catalogue.getProductSchedules(productId);
3046
- if (!result.ok) {
3047
- throw result.error;
3048
- }
3049
- return result.value;
3050
- })();
3051
- if (!existing) {
3052
- schedulesInflight.set(
3053
- cacheKey,
3054
- promise.finally(() => {
3055
- schedulesInflight.delete(cacheKey);
3056
- })
3057
- );
3058
- }
3059
- const value = await promise;
3060
- schedulesCache.set(cacheKey, value);
3061
- setSchedules(value);
3062
- } catch (loadError) {
3063
- setError(loadError);
3064
- } finally {
3065
- setIsLoading(false);
3066
- }
3067
- },
3068
- [cacheKey, client, enabled, productId]
3069
- );
3070
- useEffect(() => {
3071
- void load(false);
3072
- }, [load]);
3073
- const refetch = useCallback(async () => {
3074
- schedulesCache.delete(cacheKey);
3075
- await load(true);
3076
- }, [cacheKey, load]);
3077
- return { schedules, isLoading, error, refetch };
3078
- }
3079
- var availabilityCache = /* @__PURE__ */ new Map();
3080
- var availabilityInflight = /* @__PURE__ */ new Map();
3081
- function buildAvailabilityCacheKey(client, productId, locationId) {
3082
- return JSON.stringify({
3083
- key: client.getPublicKey(),
3084
- product_id: productId,
3085
- location_id: locationId
3086
- });
3087
- }
3088
- function useProductAvailability(productId, options = {}) {
3089
- const context = useOptionalCimplifyClient();
3090
- const client = options.client ?? context?.client;
3091
- if (!client) {
3092
- throw new Error("useProductAvailability must be used within CimplifyProvider or passed { client }.");
3093
- }
3094
- const enabled = options.enabled ?? true;
3095
- const locationId = options.locationId ?? client.getLocationId();
3096
- const previousLocationIdRef = useRef(locationId);
3097
- const cacheKey = useMemo(
3098
- () => locationId ? buildAvailabilityCacheKey(client, productId, locationId) : null,
3099
- [client, productId, locationId]
3100
- );
3101
- const cached = cacheKey ? availabilityCache.get(cacheKey) : void 0;
3102
- const [isAvailable, setIsAvailable] = useState(cached?.is_available ?? null);
3103
- const [schedulesToday, setSchedulesToday] = useState(
3104
- cached?.schedules_today ?? []
3105
- );
3106
- const [isLoading, setIsLoading] = useState(enabled && !cached && !!locationId);
3107
- const [error, setError] = useState(null);
3108
- useEffect(() => {
3109
- if (previousLocationIdRef.current !== locationId) {
3110
- availabilityCache.clear();
3111
- availabilityInflight.clear();
3112
- previousLocationIdRef.current = locationId;
3113
- }
3114
- }, [locationId]);
3115
- const load = useCallback(
3116
- async (force = false) => {
3117
- if (!enabled || !locationId || !cacheKey) {
3118
- setIsLoading(false);
3119
- return;
3120
- }
3121
- setError(null);
3122
- if (!force) {
3123
- const cachedAvailability = availabilityCache.get(cacheKey);
3124
- if (cachedAvailability) {
3125
- setIsAvailable(cachedAvailability.is_available);
3126
- setSchedulesToday(cachedAvailability.schedules_today);
3127
- setIsLoading(false);
3128
- return;
3129
- }
3130
- }
3131
- setIsLoading(true);
3132
- try {
3133
- const existing = availabilityInflight.get(cacheKey);
3134
- const promise = existing ?? (async () => {
3135
- const result = await client.catalogue.checkProductAvailableNow(productId, locationId);
3136
- if (!result.ok) {
3137
- throw result.error;
3138
- }
3139
- return result.value;
3140
- })();
3141
- if (!existing) {
3142
- availabilityInflight.set(
3143
- cacheKey,
3144
- promise.finally(() => {
3145
- availabilityInflight.delete(cacheKey);
3146
- })
3147
- );
3148
- }
3149
- const value = await promise;
3150
- availabilityCache.set(cacheKey, value);
3151
- setIsAvailable(value.is_available);
3152
- setSchedulesToday(value.schedules_today);
3153
- } catch (loadError) {
3154
- setError(loadError);
3155
- } finally {
3156
- setIsLoading(false);
3157
- }
3158
- },
3159
- [cacheKey, client, enabled, locationId, productId]
3160
- );
3161
- useEffect(() => {
3162
- void load(false);
3163
- }, [load]);
3164
- const refetch = useCallback(async () => {
3165
- if (cacheKey) {
3166
- availabilityCache.delete(cacheKey);
3167
- }
3168
- await load(true);
3169
- }, [cacheKey, load]);
3170
- return { isAvailable, schedulesToday, isLoading, error, refetch };
3171
- }
3172
- var dealsCache = /* @__PURE__ */ new Map();
3173
- var dealsInflight = /* @__PURE__ */ new Map();
3174
- function buildDealsCacheKey(client, locationId) {
3175
- return JSON.stringify({ key: client.getPublicKey(), location_id: locationId });
3176
- }
3177
- function useDeals(options = {}) {
3178
- const context = useOptionalCimplifyClient();
3179
- const client = options.client ?? context?.client;
3180
- if (!client) {
3181
- throw new Error("useDeals must be used within CimplifyProvider or passed { client }.");
3182
- }
3183
- const enabled = options.enabled ?? true;
3184
- const locationId = options.locationId ?? client.getLocationId() ?? void 0;
3185
- const previousLocationIdRef = useRef(locationId);
3186
- const cacheKey = useMemo(() => buildDealsCacheKey(client, locationId), [client, locationId]);
3187
- const cached = dealsCache.get(cacheKey);
3188
- const [deals, setDeals] = useState(cached ?? []);
3189
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3190
- const [error, setError] = useState(null);
3191
- useEffect(() => {
3192
- if (previousLocationIdRef.current !== locationId) {
3193
- dealsCache.clear();
3194
- dealsInflight.clear();
3195
- previousLocationIdRef.current = locationId;
3196
- }
3197
- }, [locationId]);
3198
- const load = useCallback(
3199
- async (force = false) => {
3200
- if (!enabled) {
3201
- setIsLoading(false);
3202
- return;
3203
- }
3204
- setError(null);
3205
- if (!force) {
3206
- const cachedDeals = dealsCache.get(cacheKey);
3207
- if (cachedDeals) {
3208
- setDeals(cachedDeals);
3209
- setIsLoading(false);
3210
- return;
3211
- }
3212
- }
3213
- setIsLoading(true);
3214
- try {
3215
- const existing = dealsInflight.get(cacheKey);
3216
- const promise = existing ?? (async () => {
3217
- const result = await client.catalogue.getDeals(locationId);
3218
- if (!result.ok) {
3219
- throw result.error;
3220
- }
3221
- return result.value;
3222
- })();
3223
- if (!existing) {
3224
- dealsInflight.set(
3225
- cacheKey,
3226
- promise.finally(() => {
3227
- dealsInflight.delete(cacheKey);
3228
- })
3229
- );
3230
- }
3231
- const value = await promise;
3232
- dealsCache.set(cacheKey, value);
3233
- setDeals(value);
3234
- } catch (loadError) {
3235
- setError(loadError);
3236
- } finally {
3237
- setIsLoading(false);
3238
- }
3239
- },
3240
- [cacheKey, client, enabled, locationId]
3241
- );
3242
- useEffect(() => {
3243
- void load(false);
3244
- }, [load]);
3245
- const refetch = useCallback(async () => {
3246
- dealsCache.delete(cacheKey);
3247
- await load(true);
3248
- }, [cacheKey, load]);
3249
- return { deals, isLoading, error, refetch };
3250
- }
3251
- var onSaleCache = /* @__PURE__ */ new Map();
3252
- var onSaleInflight = /* @__PURE__ */ new Map();
3253
- function buildOnSaleCacheKey(client) {
3254
- return client.getPublicKey() || "__demo__";
3255
- }
3256
- function useProductsOnSale(options = {}) {
3257
- const context = useOptionalCimplifyClient();
3258
- const client = options.client ?? context?.client;
3259
- if (!client) {
3260
- throw new Error("useProductsOnSale must be used within CimplifyProvider or passed { client }.");
3261
- }
3262
- const enabled = options.enabled ?? true;
3263
- const cacheKey = useMemo(() => buildOnSaleCacheKey(client), [client]);
3264
- const cached = onSaleCache.get(cacheKey);
3265
- const [products, setProducts] = useState(cached ?? []);
3266
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3267
- const [error, setError] = useState(null);
3268
- const load = useCallback(
3269
- async (force = false) => {
3270
- if (!enabled) {
3271
- setIsLoading(false);
3272
- return;
3273
- }
3274
- setError(null);
3275
- if (!force) {
3276
- const cachedProducts = onSaleCache.get(cacheKey);
3277
- if (cachedProducts) {
3278
- setProducts(cachedProducts);
3279
- setIsLoading(false);
3280
- return;
3281
- }
3282
- }
3283
- setIsLoading(true);
3284
- try {
3285
- const existing = onSaleInflight.get(cacheKey);
3286
- const promise = existing ?? (async () => {
3287
- const result = await client.catalogue.getProductsOnSale();
3288
- if (!result.ok) {
3289
- throw result.error;
3290
- }
3291
- return result.value;
3292
- })();
3293
- if (!existing) {
3294
- onSaleInflight.set(
3295
- cacheKey,
3296
- promise.finally(() => {
3297
- onSaleInflight.delete(cacheKey);
3298
- })
3299
- );
3300
- }
3301
- const value = await promise;
3302
- onSaleCache.set(cacheKey, value);
3303
- setProducts(value);
3304
- } catch (loadError) {
3305
- setError(loadError);
3306
- } finally {
3307
- setIsLoading(false);
3308
- }
3309
- },
3310
- [cacheKey, client, enabled]
3311
- );
3312
- useEffect(() => {
3313
- void load(false);
3314
- }, [load]);
3315
- const refetch = useCallback(async () => {
3316
- onSaleCache.delete(cacheKey);
3317
- await load(true);
3318
- }, [cacheKey, load]);
3319
- return { products, isLoading, error, refetch };
3320
- }
3321
- var productDealsCache = /* @__PURE__ */ new Map();
3322
- var productDealsInflight = /* @__PURE__ */ new Map();
3323
- function buildProductDealsCacheKey(client, productId) {
3324
- return JSON.stringify({ key: client.getPublicKey(), product_id: productId });
3325
- }
3326
- function useProductDeals(productId, options = {}) {
3327
- const context = useOptionalCimplifyClient();
3328
- const client = options.client ?? context?.client;
3329
- if (!client) {
3330
- throw new Error("useProductDeals must be used within CimplifyProvider or passed { client }.");
3331
- }
3332
- const enabled = options.enabled ?? true;
3333
- const cacheKey = useMemo(
3334
- () => buildProductDealsCacheKey(client, productId),
3335
- [client, productId]
3336
- );
3337
- const cached = productDealsCache.get(cacheKey);
3338
- const [deals, setDeals] = useState(cached ?? []);
3339
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3340
- const [error, setError] = useState(null);
3341
- const load = useCallback(
3342
- async (force = false) => {
3343
- if (!enabled) {
3344
- setIsLoading(false);
3345
- return;
3346
- }
3347
- setError(null);
3348
- if (!force) {
3349
- const cachedDeals = productDealsCache.get(cacheKey);
3350
- if (cachedDeals) {
3351
- setDeals(cachedDeals);
3352
- setIsLoading(false);
3353
- return;
3354
- }
3355
- }
3356
- setIsLoading(true);
3357
- try {
3358
- const existing = productDealsInflight.get(cacheKey);
3359
- const promise = existing ?? (async () => {
3360
- const result = await client.catalogue.getProductDeals(productId);
3361
- if (!result.ok) {
3362
- throw result.error;
3363
- }
3364
- return result.value;
3365
- })();
3366
- if (!existing) {
3367
- productDealsInflight.set(
3368
- cacheKey,
3369
- promise.finally(() => {
3370
- productDealsInflight.delete(cacheKey);
3371
- })
3372
- );
3373
- }
3374
- const value = await promise;
3375
- productDealsCache.set(cacheKey, value);
3376
- setDeals(value);
3377
- } catch (loadError) {
3378
- setError(loadError);
3379
- } finally {
3380
- setIsLoading(false);
3381
- }
3382
- },
3383
- [cacheKey, client, enabled, productId]
3384
- );
3385
- useEffect(() => {
3386
- void load(false);
3387
- }, [load]);
3388
- const refetch = useCallback(async () => {
3389
- productDealsCache.delete(cacheKey);
3390
- await load(true);
3391
- }, [cacheKey, load]);
3392
- return { deals, isLoading, error, refetch };
3393
- }
3394
- function useValidateDiscount(options = {}) {
3395
- const context = useOptionalCimplifyClient();
3396
- const client = options.client ?? context?.client;
3397
- if (!client) {
3398
- throw new Error("useValidateDiscount must be used within CimplifyProvider or passed { client }.");
3399
- }
3400
- const [isValidating, setIsValidating] = useState(false);
3401
- const [error, setError] = useState(null);
3402
- const validate = useCallback(
3403
- async (code, orderSubtotal, locationId) => {
3404
- setError(null);
3405
- setIsValidating(true);
3406
- try {
3407
- const result = await client.catalogue.validateDiscountCode(code, orderSubtotal, locationId);
3408
- if (!result.ok) {
3409
- setError(result.error);
3410
- return null;
3411
- }
3412
- return result.value;
3413
- } catch (validateError) {
3414
- setError(validateError);
3415
- return null;
3416
- } finally {
3417
- setIsValidating(false);
3418
- }
3419
- },
3420
- [client]
3421
- );
3422
- return { validate, isValidating, error };
3423
- }
3424
- var bundlesCache = /* @__PURE__ */ new Map();
3425
- var bundlesInflight = /* @__PURE__ */ new Map();
3426
- function buildBundlesCacheKey(client, locationId) {
3427
- return JSON.stringify({
3428
- key: client.getPublicKey(),
3429
- location_id: locationId || "__none__"
3430
- });
3431
- }
3432
- function useBundles(options = {}) {
3433
- const context = useOptionalCimplifyClient();
3434
- const client = options.client ?? context?.client;
3435
- if (!client) {
3436
- throw new Error("useBundles must be used within CimplifyProvider or passed { client }.");
3437
- }
3438
- const enabled = options.enabled ?? true;
3439
- const locationId = client.getLocationId();
3440
- const previousLocationIdRef = useRef(locationId);
3441
- const requestIdRef = useRef(0);
3442
- const cacheKey = useMemo(
3443
- () => buildBundlesCacheKey(client, locationId),
3444
- [client, locationId]
3445
- );
3446
- const cached = bundlesCache.get(cacheKey);
3447
- const [bundles, setBundles] = useState(cached?.bundles ?? []);
3448
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3449
- const [error, setError] = useState(null);
3450
- useEffect(() => {
3451
- if (previousLocationIdRef.current !== locationId) {
3452
- bundlesCache.clear();
3453
- bundlesInflight.clear();
3454
- previousLocationIdRef.current = locationId;
3455
- }
3456
- }, [locationId]);
3457
- const load = useCallback(
3458
- async (force = false) => {
3459
- if (!enabled) {
3460
- setIsLoading(false);
3461
- return;
3462
- }
3463
- const nextRequestId = ++requestIdRef.current;
3464
- setError(null);
3465
- if (!force) {
3466
- const cacheEntry = bundlesCache.get(cacheKey);
3467
- if (cacheEntry) {
3468
- setBundles(cacheEntry.bundles);
3469
- setIsLoading(false);
3470
- return;
3471
- }
3472
- }
3473
- setIsLoading(true);
3474
- try {
3475
- const existing = bundlesInflight.get(cacheKey);
3476
- const promise = existing ?? (async () => {
3477
- const result = await client.catalogue.getBundles();
3478
- if (!result.ok) {
3479
- throw result.error;
3480
- }
3481
- return result.value;
3482
- })();
3483
- if (!existing) {
3484
- bundlesInflight.set(cacheKey, promise);
3485
- promise.finally(() => {
3486
- bundlesInflight.delete(cacheKey);
3487
- }).catch(() => void 0);
3488
- }
3489
- const value = await promise;
3490
- bundlesCache.set(cacheKey, { bundles: value });
3491
- if (nextRequestId === requestIdRef.current) {
3492
- setBundles(value);
3493
- setError(null);
3494
- }
3495
- } catch (loadError) {
3496
- if (nextRequestId === requestIdRef.current) {
3497
- setError(loadError);
3498
- }
3499
- } finally {
3500
- if (nextRequestId === requestIdRef.current) {
3501
- setIsLoading(false);
3502
- }
3503
- }
3504
- },
3505
- [cacheKey, client, enabled]
3506
- );
3507
- useEffect(() => {
3508
- void load(false);
3509
- }, [load]);
3510
- const refetch = useCallback(async () => {
3511
- bundlesCache.delete(cacheKey);
3512
- await load(true);
3513
- }, [cacheKey, load]);
3514
- return { bundles, isLoading, error, refetch };
3515
- }
3516
- function useProductPrice(input) {
3517
- const {
3518
- product,
3519
- quantity = 1,
3520
- variant,
3521
- addOnOptions,
3522
- compositePrice,
3523
- bundleTotalPrice
3524
- } = input;
3525
- return useMemo(() => {
3526
- let unitPrice;
3527
- if (bundleTotalPrice != null) {
3528
- unitPrice = bundleTotalPrice;
3529
- } else if (compositePrice) {
3530
- unitPrice = parsePrice(compositePrice.final_price);
3531
- } else {
3532
- unitPrice = parsePrice(product.default_price);
3533
- if (variant?.price_adjustment) {
3534
- unitPrice += parsePrice(variant.price_adjustment);
3535
- }
3536
- if (addOnOptions) {
3537
- for (const option of addOnOptions) {
3538
- if (option.default_price) {
3539
- unitPrice += parsePrice(option.default_price);
3540
- }
3541
- }
3542
- }
3543
- }
3544
- unitPrice = Math.round(unitPrice * 100) / 100;
3545
- const totalPrice = Math.round(unitPrice * quantity * 100) / 100;
3546
- return {
3547
- unitPrice,
3548
- totalPrice,
3549
- displayTotalPrice: totalPrice
3550
- };
3551
- }, [product.default_price, quantity, variant?.price_adjustment, addOnOptions, compositePrice, bundleTotalPrice]);
3552
- }
3553
- var ordersCache = /* @__PURE__ */ new Map();
3554
- var ordersInflight = /* @__PURE__ */ new Map();
3555
- function buildOrdersCacheKey(client, status, limit) {
3556
- return JSON.stringify({ key: client.getPublicKey(), status, limit });
3557
- }
3558
- function useOrders(options = {}) {
3559
- const context = useOptionalCimplifyClient();
3560
- const client = options.client ?? context?.client;
3561
- if (!client) {
3562
- throw new Error("useOrders must be used within CimplifyProvider or passed { client }.");
3563
- }
3564
- const enabled = options.enabled ?? true;
3565
- const requestIdRef = useRef(0);
3566
- const cacheKey = useMemo(
3567
- () => buildOrdersCacheKey(client, options.status, options.limit),
3568
- [client, options.status, options.limit]
3569
- );
3570
- const cached = ordersCache.get(cacheKey);
3571
- const [orders, setOrders] = useState(cached ?? []);
3572
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3573
- const [error, setError] = useState(null);
3574
- const load = useCallback(
3575
- async (force = false) => {
3576
- if (!enabled) {
3577
- setIsLoading(false);
3578
- return;
3579
- }
3580
- const nextRequestId = ++requestIdRef.current;
3581
- setError(null);
3582
- if (!force) {
3583
- const cachedOrders = ordersCache.get(cacheKey);
3584
- if (cachedOrders) {
3585
- setOrders(cachedOrders);
3586
- setIsLoading(false);
3587
- return;
3588
- }
3589
- }
3590
- setIsLoading(true);
3591
- try {
3592
- const existing = ordersInflight.get(cacheKey);
3593
- const promise = existing ?? (async () => {
3594
- const result = await client.orders.list({
3595
- status: options.status,
3596
- limit: options.limit
3597
- });
3598
- if (!result.ok) {
3599
- throw result.error;
3600
- }
3601
- return result.value;
3602
- })();
3603
- if (!existing) {
3604
- ordersInflight.set(
3605
- cacheKey,
3606
- promise.finally(() => {
3607
- ordersInflight.delete(cacheKey);
3608
- })
3609
- );
3610
- }
3611
- const value = await promise;
3612
- ordersCache.set(cacheKey, value);
3613
- if (nextRequestId === requestIdRef.current) {
3614
- setOrders(value);
3615
- setError(null);
3616
- }
3617
- } catch (loadError) {
3618
- if (nextRequestId === requestIdRef.current) {
3619
- setError(loadError);
3620
- }
3621
- } finally {
3622
- if (nextRequestId === requestIdRef.current) {
3623
- setIsLoading(false);
3624
- }
3625
- }
3626
- },
3627
- [cacheKey, client, enabled, options.status, options.limit]
3628
- );
3629
- useEffect(() => {
3630
- void load(false);
3631
- }, [load]);
3632
- const refetch = useCallback(async () => {
3633
- ordersCache.delete(cacheKey);
3634
- await load(true);
3635
- }, [cacheKey, load]);
3636
- return { orders, isLoading, error, refetch };
3637
- }
3638
- var servicesCache = /* @__PURE__ */ new Map();
3639
- var servicesInflight = /* @__PURE__ */ new Map();
3640
- function buildServicesCacheKey(client) {
3641
- return JSON.stringify({ key: client.getPublicKey() });
3642
- }
3643
- function useServices(options = {}) {
3644
- const context = useOptionalCimplifyClient();
3645
- const client = options.client ?? context?.client;
3646
- if (!client) {
3647
- throw new Error("useServices must be used within CimplifyProvider or passed { client }.");
3648
- }
3649
- const enabled = options.enabled ?? true;
3650
- const requestIdRef = useRef(0);
3651
- const cacheKey = useMemo(() => buildServicesCacheKey(client), [client]);
3652
- const cached = servicesCache.get(cacheKey);
3653
- const [services, setServices] = useState(cached ?? []);
3654
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3655
- const [error, setError] = useState(null);
3656
- const load = useCallback(
3657
- async (force = false) => {
3658
- if (!enabled) {
3659
- setIsLoading(false);
3660
- return;
3661
- }
3662
- const nextRequestId = ++requestIdRef.current;
3663
- setError(null);
3664
- if (!force) {
3665
- const cachedServices = servicesCache.get(cacheKey);
3666
- if (cachedServices) {
3667
- setServices(cachedServices);
3668
- setIsLoading(false);
3669
- return;
3670
- }
3671
- }
3672
- setIsLoading(true);
3673
- try {
3674
- const existing = servicesInflight.get(cacheKey);
3675
- const promise = existing ?? (async () => {
3676
- const result = await client.scheduling.getServices();
3677
- if (!result.ok) {
3678
- throw result.error;
3679
- }
3680
- return result.value;
3681
- })();
3682
- if (!existing) {
3683
- servicesInflight.set(
3684
- cacheKey,
3685
- promise.finally(() => {
3686
- servicesInflight.delete(cacheKey);
3687
- })
3688
- );
3689
- }
3690
- const value = await promise;
3691
- servicesCache.set(cacheKey, value);
3692
- if (nextRequestId === requestIdRef.current) {
3693
- setServices(value);
3694
- setError(null);
3695
- }
3696
- } catch (loadError) {
3697
- if (nextRequestId === requestIdRef.current) {
3698
- setError(loadError);
3699
- }
3700
- } finally {
3701
- if (nextRequestId === requestIdRef.current) {
3702
- setIsLoading(false);
3703
- }
3704
- }
3705
- },
3706
- [cacheKey, client, enabled]
3707
- );
3708
- useEffect(() => {
3709
- void load(false);
3710
- }, [load]);
3711
- const refetch = useCallback(async () => {
3712
- servicesCache.delete(cacheKey);
3713
- await load(true);
3714
- }, [cacheKey, load]);
3715
- return { services, isLoading, error, refetch };
3716
- }
3717
- var slotsCache = /* @__PURE__ */ new Map();
3718
- var slotsInflight = /* @__PURE__ */ new Map();
3719
- function buildSlotsCacheKey(client, serviceId, date, participantCount) {
3720
- return JSON.stringify({
3721
- key: client.getPublicKey(),
3722
- serviceId,
3723
- date,
3724
- participantCount
3725
- });
3726
- }
3727
- function useAvailableSlots(serviceId, date, options = {}) {
3728
- const context = useOptionalCimplifyClient();
3729
- const client = options.client ?? context?.client;
3730
- if (!client) {
3731
- throw new Error("useAvailableSlots must be used within CimplifyProvider or passed { client }.");
3732
- }
3733
- const enabled = (options.enabled ?? true) && !!serviceId && !!date;
3734
- const requestIdRef = useRef(0);
3735
- const cacheKey = useMemo(
3736
- () => serviceId && date ? buildSlotsCacheKey(client, serviceId, date, options.participantCount) : null,
3737
- [client, serviceId, date, options.participantCount]
3738
- );
3739
- const cached = cacheKey ? slotsCache.get(cacheKey) : void 0;
3740
- const [slots, setSlots] = useState(cached ?? []);
3741
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3742
- const [error, setError] = useState(null);
3743
- const load = useCallback(
3744
- async (force = false) => {
3745
- if (!enabled || !cacheKey || !serviceId || !date) {
3746
- setIsLoading(false);
3747
- return;
3748
- }
3749
- const nextRequestId = ++requestIdRef.current;
3750
- setError(null);
3751
- if (!force) {
3752
- const cachedSlots = slotsCache.get(cacheKey);
3753
- if (cachedSlots) {
3754
- setSlots(cachedSlots);
3755
- setIsLoading(false);
3756
- return;
3757
- }
3758
- }
3759
- setIsLoading(true);
3760
- try {
3761
- const existing = slotsInflight.get(cacheKey);
3762
- const promise = existing ?? (async () => {
3763
- const result = await client.scheduling.getAvailableSlots({
3764
- service_id: serviceId,
3765
- date,
3766
- participant_count: options.participantCount
3767
- });
3768
- if (!result.ok) {
3769
- throw result.error;
3770
- }
3771
- return result.value;
3772
- })();
3773
- if (!existing) {
3774
- slotsInflight.set(
3775
- cacheKey,
3776
- promise.finally(() => {
3777
- slotsInflight.delete(cacheKey);
3778
- })
3779
- );
3780
- }
3781
- const value = await promise;
3782
- slotsCache.set(cacheKey, value);
3783
- if (nextRequestId === requestIdRef.current) {
3784
- setSlots(value);
3785
- setError(null);
3786
- }
3787
- } catch (loadError) {
3788
- if (nextRequestId === requestIdRef.current) {
3789
- setError(loadError);
3790
- }
3791
- } finally {
3792
- if (nextRequestId === requestIdRef.current) {
3793
- setIsLoading(false);
3794
- }
3795
- }
3796
- },
3797
- [cacheKey, client, enabled, serviceId, date, options.participantCount]
3798
- );
3799
- useEffect(() => {
3800
- void load(false);
3801
- }, [load]);
3802
- const refetch = useCallback(async () => {
3803
- if (cacheKey) {
3804
- slotsCache.delete(cacheKey);
3805
- }
3806
- await load(true);
3807
- }, [cacheKey, load]);
3808
- return { slots, isLoading, error, refetch };
3809
- }
3810
- var availabilityCache2 = /* @__PURE__ */ new Map();
3811
- var availabilityInflight2 = /* @__PURE__ */ new Map();
3812
- function buildAvailabilityCacheKey2(client, serviceId, startDate, endDate, participantCount) {
3813
- return JSON.stringify({
3814
- key: client.getPublicKey(),
3815
- serviceId,
3816
- startDate,
3817
- endDate,
3818
- participantCount
3819
- });
3820
- }
3821
- function useServiceAvailability(serviceId, startDate, endDate, options = {}) {
3822
- const context = useOptionalCimplifyClient();
3823
- const client = options.client ?? context?.client;
3824
- if (!client) {
3825
- throw new Error(
3826
- "useServiceAvailability must be used within CimplifyProvider or passed { client }."
3827
- );
3828
- }
3829
- const enabled = (options.enabled ?? true) && !!serviceId && !!startDate && !!endDate;
3830
- const requestIdRef = useRef(0);
3831
- const cacheKey = useMemo(
3832
- () => serviceId && startDate && endDate ? buildAvailabilityCacheKey2(
3833
- client,
3834
- serviceId,
3835
- startDate,
3836
- endDate,
3837
- options.participantCount
3838
- ) : null,
3839
- [client, serviceId, startDate, endDate, options.participantCount]
3840
- );
3841
- const cached = cacheKey ? availabilityCache2.get(cacheKey) : void 0;
3842
- const [days, setDays] = useState(cached ?? []);
3843
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3844
- const [error, setError] = useState(null);
3845
- const load = useCallback(
3846
- async (force = false) => {
3847
- if (!enabled || !cacheKey || !serviceId || !startDate || !endDate) {
3848
- setIsLoading(false);
3849
- return;
3850
- }
3851
- const nextRequestId = ++requestIdRef.current;
3852
- setError(null);
3853
- if (!force) {
3854
- const cachedDays = availabilityCache2.get(cacheKey);
3855
- if (cachedDays) {
3856
- setDays(cachedDays);
3857
- setIsLoading(false);
3858
- return;
3859
- }
3860
- }
3861
- setIsLoading(true);
3862
- try {
3863
- const existing = availabilityInflight2.get(cacheKey);
3864
- const promise = existing ?? (async () => {
3865
- const result = await client.scheduling.getServiceAvailability({
3866
- service_id: serviceId,
3867
- start_date: startDate,
3868
- end_date: endDate,
3869
- location_id: options.locationId,
3870
- participant_count: options.participantCount
3871
- });
3872
- if (!result.ok) {
3873
- throw result.error;
3874
- }
3875
- return result.value.availability;
3876
- })();
3877
- if (!existing) {
3878
- availabilityInflight2.set(
3879
- cacheKey,
3880
- promise.finally(() => {
3881
- availabilityInflight2.delete(cacheKey);
3882
- })
3883
- );
3884
- }
3885
- const value = await promise;
3886
- availabilityCache2.set(cacheKey, value);
3887
- if (nextRequestId === requestIdRef.current) {
3888
- setDays(value);
3889
- setError(null);
3890
- }
3891
- } catch (loadError) {
3892
- if (nextRequestId === requestIdRef.current) {
3893
- setError(loadError);
3894
- }
3895
- } finally {
3896
- if (nextRequestId === requestIdRef.current) {
3897
- setIsLoading(false);
3898
- }
3899
- }
3900
- },
3901
- [cacheKey, client, enabled, serviceId, startDate, endDate, options.locationId, options.participantCount]
3902
- );
3903
- useEffect(() => {
3904
- void load(false);
3905
- }, [load]);
3906
- const refetch = useCallback(async () => {
3907
- if (cacheKey) {
3908
- availabilityCache2.delete(cacheKey);
3909
- }
3910
- await load(true);
3911
- }, [cacheKey, load]);
3912
- return { days, isLoading, error, refetch };
3913
- }
3914
- var bookingsCache = /* @__PURE__ */ new Map();
3915
- var bookingsInflight = /* @__PURE__ */ new Map();
3916
- function buildBookingsCacheKey(client, filter) {
3917
- return JSON.stringify({ key: client.getPublicKey(), filter });
3918
- }
3919
- function useBookings(options = {}) {
3920
- const context = useOptionalCimplifyClient();
3921
- const client = options.client ?? context?.client;
3922
- if (!client) {
3923
- throw new Error("useBookings must be used within CimplifyProvider or passed { client }.");
3924
- }
3925
- const filter = options.filter ?? "all";
3926
- const enabled = options.enabled ?? true;
3927
- const requestIdRef = useRef(0);
3928
- const cacheKey = useMemo(
3929
- () => buildBookingsCacheKey(client, filter),
3930
- [client, filter]
3931
- );
3932
- const cached = bookingsCache.get(cacheKey);
3933
- const [bookings, setBookings] = useState(cached ?? []);
3934
- const [isLoading, setIsLoading] = useState(enabled && !cached);
3935
- const [error, setError] = useState(null);
3936
- const load = useCallback(
3937
- async (force = false) => {
3938
- if (!enabled) {
3939
- setIsLoading(false);
3940
- return;
3941
- }
3942
- const nextRequestId = ++requestIdRef.current;
3943
- setError(null);
3944
- if (!force) {
3945
- const cachedBookings = bookingsCache.get(cacheKey);
3946
- if (cachedBookings) {
3947
- setBookings(cachedBookings);
3948
- setIsLoading(false);
3949
- return;
3950
- }
3951
- }
3952
- setIsLoading(true);
3953
- try {
3954
- const existing = bookingsInflight.get(cacheKey);
3955
- const promise = existing ?? (async () => {
3956
- let result;
3957
- switch (filter) {
3958
- case "upcoming":
3959
- result = await client.scheduling.getUpcomingBookings();
3960
- break;
3961
- case "past":
3962
- result = await client.scheduling.getPastBookings();
3963
- break;
3964
- default:
3965
- result = await client.scheduling.getCustomerBookings();
3966
- break;
3967
- }
3968
- if (!result.ok) {
3969
- throw result.error;
3970
- }
3971
- return result.value;
3972
- })();
3973
- if (!existing) {
3974
- bookingsInflight.set(
3975
- cacheKey,
3976
- promise.finally(() => {
3977
- bookingsInflight.delete(cacheKey);
3978
- })
3979
- );
3980
- }
3981
- const value = await promise;
3982
- bookingsCache.set(cacheKey, value);
3983
- if (nextRequestId === requestIdRef.current) {
3984
- setBookings(value);
3985
- setError(null);
3986
- }
3987
- } catch (loadError) {
3988
- if (nextRequestId === requestIdRef.current) {
3989
- setError(loadError);
3990
- }
3991
- } finally {
3992
- if (nextRequestId === requestIdRef.current) {
3993
- setIsLoading(false);
3994
- }
3995
- }
3996
- },
3997
- [cacheKey, client, enabled, filter]
3998
- );
3999
- useEffect(() => {
4000
- void load(false);
4001
- }, [load]);
4002
- const refetch = useCallback(async () => {
4003
- bookingsCache.delete(cacheKey);
4004
- await load(true);
4005
- }, [cacheKey, load]);
4006
- const cancelBooking = useCallback(
4007
- async (bookingId, reason) => {
4008
- const result = await client.scheduling.cancelBooking({
4009
- booking_id: bookingId,
4010
- reason
4011
- });
4012
- if (!result.ok) {
4013
- throw result.error;
4014
- }
4015
- for (const key of bookingsCache.keys()) {
4016
- bookingsCache.delete(key);
4017
- }
4018
- await load(true);
4019
- return result.value;
4020
- },
4021
- [client, load]
4022
- );
4023
- return { bookings, isLoading, error, refetch, cancelBooking };
4024
- }
4025
- var definitionsCache = /* @__PURE__ */ new Map();
4026
- var definitionsInflight = /* @__PURE__ */ new Map();
4027
- function buildCacheKey(client, namespace) {
4028
- return `attr_defs_${client.getPublicKey() || "__demo__"}${namespace ? `_${namespace}` : ""}`;
4029
- }
4030
- function useAttributeDefinitions(options = {}) {
4031
- const context = useOptionalCimplifyClient();
4032
- const client = options.client ?? context?.client;
4033
- if (!client) {
4034
- throw new Error(
4035
- "useAttributeDefinitions must be used within CimplifyProvider or passed { client }."
4036
- );
4037
- }
4038
- const enabled = options.enabled ?? true;
4039
- const namespace = options.namespace;
4040
- const cacheKey = useMemo(() => buildCacheKey(client, namespace), [client, namespace]);
4041
- const cached = definitionsCache.get(cacheKey);
4042
- const [definitions, setDefinitions] = useState(cached ?? []);
4043
- const [isLoading, setIsLoading] = useState(enabled && !cached);
4044
- const [error, setError] = useState(null);
4045
- const load = useCallback(
4046
- async (force = false) => {
4047
- if (!enabled) {
4048
- setIsLoading(false);
4049
- return;
4050
- }
4051
- setError(null);
4052
- if (!force) {
4053
- const cachedDefs = definitionsCache.get(cacheKey);
4054
- if (cachedDefs) {
4055
- setDefinitions(cachedDefs);
4056
- setIsLoading(false);
4057
- return;
4058
- }
4059
- }
4060
- setIsLoading(true);
4061
- try {
4062
- const existing = definitionsInflight.get(cacheKey);
4063
- const promise = existing ?? (async () => {
4064
- const result = await client.catalogue.getAttributeDefinitions(namespace);
4065
- if (!result.ok) {
4066
- throw result.error;
4067
- }
4068
- return result.value;
4069
- })();
4070
- if (!existing) {
4071
- definitionsInflight.set(
4072
- cacheKey,
4073
- promise.finally(() => {
4074
- definitionsInflight.delete(cacheKey);
4075
- })
4076
- );
4077
- }
4078
- const value = await promise;
4079
- definitionsCache.set(cacheKey, value);
4080
- setDefinitions(value);
4081
- } catch (loadError) {
4082
- setError(loadError);
4083
- } finally {
4084
- setIsLoading(false);
4085
- }
4086
- },
4087
- [cacheKey, client, enabled]
4088
- );
4089
- useEffect(() => {
4090
- void load(false);
4091
- }, [load]);
4092
- const refetch = useCallback(async () => {
4093
- definitionsCache.delete(cacheKey);
4094
- await load(true);
4095
- }, [cacheKey, load]);
4096
- return { definitions, isLoading, error, refetch };
4097
- }
4098
- var facetsCache = /* @__PURE__ */ new Map();
4099
- var facetsInflight = /* @__PURE__ */ new Map();
4100
- function usePropertyFacets(options = {}) {
4101
- const context = useOptionalCimplifyClient();
4102
- const client = options.client ?? context?.client;
4103
- if (!client) {
4104
- throw new Error(
4105
- "usePropertyFacets must be used within CimplifyProvider or passed { client }."
4106
- );
4107
- }
4108
- const enabled = options.enabled ?? true;
4109
- const cacheKey = useMemo(
4110
- () => `facets_${client.getPublicKey() || "__demo__"}_${options.category_id ?? ""}_${options.product_type ?? ""}`,
4111
- [client, options.category_id, options.product_type]
4112
- );
4113
- const cached = facetsCache.get(cacheKey);
4114
- const [facets, setFacets] = useState(cached ?? []);
4115
- const [isLoading, setIsLoading] = useState(enabled && !cached);
4116
- const [error, setError] = useState(null);
4117
- const load = useCallback(
4118
- async (force = false) => {
4119
- if (!enabled) {
4120
- setIsLoading(false);
4121
- return;
4122
- }
4123
- setError(null);
4124
- if (!force) {
4125
- const cachedFacets = facetsCache.get(cacheKey);
4126
- if (cachedFacets) {
4127
- setFacets(cachedFacets);
4128
- setIsLoading(false);
4129
- return;
4130
- }
4131
- }
4132
- setIsLoading(true);
4133
- try {
4134
- const existing = facetsInflight.get(cacheKey);
4135
- const promise = existing ?? (async () => {
4136
- const result = await client.catalogue.getPropertyFacets({
4137
- category_id: options.category_id,
4138
- product_type: options.product_type
4139
- });
4140
- if (!result.ok) {
4141
- throw result.error;
4142
- }
4143
- return result.value;
4144
- })();
4145
- if (!existing) {
4146
- facetsInflight.set(
4147
- cacheKey,
4148
- promise.finally(() => {
4149
- facetsInflight.delete(cacheKey);
4150
- })
4151
- );
4152
- }
4153
- const value = await promise;
4154
- facetsCache.set(cacheKey, value);
4155
- setFacets(value);
4156
- } catch (loadError) {
4157
- setError(loadError);
4158
- } finally {
4159
- setIsLoading(false);
4160
- }
4161
- },
4162
- [cacheKey, client, enabled, options.category_id, options.product_type]
4163
- );
4164
- useEffect(() => {
4165
- void load(false);
4166
- }, [load]);
4167
- const refetch = useCallback(async () => {
4168
- facetsCache.delete(cacheKey);
4169
- await load(true);
4170
- }, [cacheKey, load]);
4171
- return { facets, isLoading, error, refetch };
4172
- }
4173
- function useBillingPlans(productId, options = {}) {
4174
- const context = useOptionalCimplifyClient();
4175
- const client = options.client ?? context?.client;
4176
- if (!client) {
4177
- throw new Error("useBillingPlans must be used within CimplifyProvider or passed { client }.");
4178
- }
4179
- const enabled = options.enabled ?? true;
4180
- const requestIdRef = useRef(0);
4181
- const [plans, setPlans] = useState([]);
4182
- const [isLoading, setIsLoading] = useState(enabled && !!productId);
4183
- const [error, setError] = useState(null);
4184
- const customerId = options.customerId;
4185
- const quantity = options.quantity;
4186
- const orderValue = options.orderValue;
4187
- const load = useCallback(
4188
- async (force = false) => {
4189
- if (!enabled || !productId) {
4190
- setPlans([]);
4191
- setIsLoading(false);
4192
- return;
4193
- }
4194
- const nextRequestId = ++requestIdRef.current;
4195
- setError(null);
4196
- setIsLoading(true);
4197
- try {
4198
- const result = await client.catalogue.getEligibleBillingPlans(productId, {
4199
- customer_id: customerId,
4200
- quantity,
4201
- order_value: orderValue
4202
- });
4203
- if (!result.ok) {
4204
- throw result.error;
4205
- }
4206
- if (nextRequestId === requestIdRef.current) {
4207
- setPlans(result.value);
4208
- setError(null);
4209
- }
4210
- } catch (loadError) {
4211
- if (nextRequestId === requestIdRef.current) {
4212
- setError(loadError);
4213
- }
4214
- } finally {
4215
- if (nextRequestId === requestIdRef.current) {
4216
- setIsLoading(false);
4217
- }
4218
- }
4219
- },
4220
- [client, enabled, productId, customerId, quantity, orderValue]
4221
- );
4222
- useEffect(() => {
4223
- void load(false);
4224
- }, [load]);
4225
- const refetch = useCallback(async () => {
4226
- await load(true);
4227
- }, [load]);
4228
- const subscriptionPlans = useMemo(
4229
- () => plans.filter((p) => p.plan_type === "subscription"),
4230
- [plans]
4231
- );
4232
- const installmentPlans = useMemo(
4233
- () => plans.filter((p) => p.plan_type === "installment"),
4234
- [plans]
4235
- );
4236
- return { plans, subscriptionPlans, installmentPlans, isLoading, error, refetch };
4237
- }
4238
- function findMatchingVariant(variants, selections) {
4239
- const axisCount = Object.keys(selections).length;
4240
- if (axisCount === 0) return null;
4241
- return variants.find((v) => {
4242
- const sel = v.axis_selections;
4243
- if (!sel) return false;
4244
- return Object.entries(selections).every(([axisId, valueId]) => sel[axisId] === valueId);
4245
- }) ?? null;
4246
- }
4247
- function computeAvailableValues(variants, axes, selections) {
4248
- const result = {};
4249
- for (const axis of axes) {
4250
- const otherSelections = Object.entries(selections).filter(([id]) => id !== axis.id);
4251
- const candidates = variants.filter((v) => {
4252
- const sel = v.axis_selections;
4253
- if (!sel) return false;
4254
- return otherSelections.every(([axisId, valueId]) => sel[axisId] === valueId);
4255
- });
4256
- const available = /* @__PURE__ */ new Set();
4257
- for (const v of candidates) {
4258
- const val = v.axis_selections?.[axis.id];
4259
- if (val) available.add(val);
4260
- }
4261
- result[axis.id] = available;
4262
- }
4263
- return result;
4264
- }
4265
- function useVariantSelector(options) {
4266
- const { variants, axes } = options;
4267
- const [selectedValues, setSelectedValues] = useState({});
4268
- const selectValue = useCallback(
4269
- (axisId, valueId) => {
4270
- setSelectedValues((prev) => {
4271
- if (prev[axisId] === valueId) {
4272
- const next = { ...prev };
4273
- delete next[axisId];
4274
- return next;
4275
- }
4276
- return { ...prev, [axisId]: valueId };
4277
- });
4278
- },
4279
- []
4280
- );
4281
- const reset = useCallback(() => setSelectedValues({}), []);
4282
- const selectedVariant = useMemo(
4283
- () => findMatchingVariant(variants, selectedValues),
4284
- [variants, selectedValues]
4285
- );
4286
- const variantImage = useMemo(
4287
- () => selectedVariant?.images?.[0] ?? null,
4288
- [selectedVariant]
4289
- );
4290
- const availableValues = useMemo(
4291
- () => computeAvailableValues(variants, axes, selectedValues),
4292
- [variants, axes, selectedValues]
4293
- );
4294
- return {
4295
- selectedValues,
4296
- selectedVariant,
4297
- variantImage,
4298
- availableValues,
4299
- selectValue,
4300
- reset
4301
- };
4302
- }
4303
- var activityStateCache = /* @__PURE__ */ new Map();
4304
- var activityStateInflight = /* @__PURE__ */ new Map();
4305
- function buildActivityStateCacheKey(client) {
4306
- return JSON.stringify({ key: client.getPublicKey(), type: "activity_state" });
2667
+ });
2668
+ function useBundles(options = {}) {
2669
+ const { data, isLoading, error, refetch } = bundlesQuery(options);
2670
+ return { bundles: data ?? [], isLoading, error, refetch };
4307
2671
  }
4308
- function useActivityState(options = {}) {
4309
- const context = useOptionalCimplifyClient();
4310
- const client = options.client ?? context?.client;
4311
- if (!client) {
4312
- throw new Error("useActivityState must be used within CimplifyProvider or passed { client }.");
4313
- }
4314
- const enabled = options.enabled ?? true;
4315
- const cacheKey = useMemo(() => buildActivityStateCacheKey(client), [client]);
4316
- const cached = activityStateCache.get(cacheKey);
4317
- const [state, setState] = useState(cached ?? null);
4318
- const [isLoading, setIsLoading] = useState(enabled && !cached);
4319
- const [error, setError] = useState(null);
4320
- const load = useCallback(
4321
- async (force = false) => {
4322
- if (!enabled) {
4323
- setIsLoading(false);
4324
- return;
4325
- }
4326
- setError(null);
4327
- if (!force) {
4328
- const cachedState = activityStateCache.get(cacheKey);
4329
- if (cachedState) {
4330
- setState(cachedState);
4331
- setIsLoading(false);
4332
- return;
4333
- }
2672
+ function useProductPrice(input) {
2673
+ const {
2674
+ product,
2675
+ quantity = 1,
2676
+ variant,
2677
+ addOnOptions,
2678
+ compositePrice,
2679
+ bundleTotalPrice
2680
+ } = input;
2681
+ return useMemo(() => {
2682
+ let unitPrice;
2683
+ if (bundleTotalPrice != null) {
2684
+ unitPrice = bundleTotalPrice;
2685
+ } else if (compositePrice) {
2686
+ unitPrice = parsePrice(compositePrice.final_price);
2687
+ } else {
2688
+ unitPrice = parsePrice(product.default_price);
2689
+ if (variant?.price_adjustment) {
2690
+ unitPrice += parsePrice(variant.price_adjustment);
4334
2691
  }
4335
- setIsLoading(true);
4336
- try {
4337
- const existing = activityStateInflight.get(cacheKey);
4338
- const promise = existing ?? (async () => {
4339
- const result = await client.activity.getState();
4340
- if (!result.ok) {
4341
- throw result.error;
2692
+ if (addOnOptions) {
2693
+ for (const option of addOnOptions) {
2694
+ if (option.default_price) {
2695
+ unitPrice += parsePrice(option.default_price);
4342
2696
  }
4343
- return result.value;
4344
- })();
4345
- if (!existing) {
4346
- activityStateInflight.set(
4347
- cacheKey,
4348
- promise.finally(() => {
4349
- activityStateInflight.delete(cacheKey);
4350
- })
4351
- );
4352
2697
  }
4353
- const value = await promise;
4354
- activityStateCache.set(cacheKey, value);
4355
- setState(value);
4356
- } catch (loadError) {
4357
- setError(loadError);
4358
- } finally {
4359
- setIsLoading(false);
4360
- }
4361
- },
4362
- [cacheKey, client, enabled]
4363
- );
4364
- useEffect(() => {
4365
- void load(false);
4366
- }, [load]);
4367
- const refetch = useCallback(async () => {
4368
- activityStateCache.delete(cacheKey);
4369
- await load(true);
4370
- }, [cacheKey, load]);
4371
- const dismissMessage = useCallback(
4372
- async (code) => {
4373
- const result = await client.activity.dismissMessage(code);
4374
- if (!result.ok) {
4375
- setError(result.error);
4376
- return;
4377
2698
  }
4378
- setState((prev) => {
4379
- if (!prev) return prev;
4380
- return { ...prev, messages: result.value.messages };
4381
- });
4382
- activityStateCache.delete(cacheKey);
4383
- await load(true);
4384
- },
4385
- [cacheKey, client, load]
4386
- );
4387
- const messages = state?.messages ?? [];
4388
- const intent = state?.intent ?? "";
4389
- return { state, messages, intent, isLoading, error, refetch, dismissMessage };
4390
- }
4391
- var recommendationsCache = /* @__PURE__ */ new Map();
4392
- var recommendationsInflight = /* @__PURE__ */ new Map();
4393
- function buildRecommendationsCacheKey(client, locationId, limit) {
4394
- return JSON.stringify({
4395
- key: client.getPublicKey(),
4396
- type: "recommendations",
4397
- location_id: locationId,
4398
- limit
4399
- });
2699
+ }
2700
+ unitPrice = Math.round(unitPrice * 100) / 100;
2701
+ const totalPrice = Math.round(unitPrice * quantity * 100) / 100;
2702
+ return {
2703
+ unitPrice,
2704
+ totalPrice,
2705
+ displayTotalPrice: totalPrice
2706
+ };
2707
+ }, [product.default_price, quantity, variant?.price_adjustment, addOnOptions, compositePrice, bundleTotalPrice]);
4400
2708
  }
4401
- function useRecommendations(options = {}) {
4402
- const context = useOptionalCimplifyClient();
4403
- const client = options.client ?? context?.client;
4404
- if (!client) {
4405
- throw new Error(
4406
- "useRecommendations must be used within CimplifyProvider or passed { client }."
4407
- );
2709
+
2710
+ // src/react/hooks/use-orders.ts
2711
+ var ordersQuery = createCachedQuery({
2712
+ name: "useOrders",
2713
+ key: (client, options) => cacheKey(client.getPublicKey(), options.status, options.limit),
2714
+ fetch: async (client, options) => {
2715
+ const result = await client.orders.list({
2716
+ status: options.status,
2717
+ limit: options.limit
2718
+ });
2719
+ if (!result.ok) throw result.error;
2720
+ return result.value;
4408
2721
  }
4409
- const enabled = options.enabled ?? true;
4410
- const locationId = options.locationId ?? client.getLocationId() ?? void 0;
4411
- const limit = options.limit;
4412
- const previousLocationIdRef = useRef(locationId);
4413
- const cacheKey = useMemo(
4414
- () => buildRecommendationsCacheKey(client, locationId, limit),
4415
- [client, locationId, limit]
4416
- );
4417
- const cached = recommendationsCache.get(cacheKey);
4418
- const [response, setResponse] = useState(cached ?? null);
4419
- const [isLoading, setIsLoading] = useState(enabled && !cached);
4420
- const [error, setError] = useState(null);
4421
- useEffect(() => {
4422
- if (previousLocationIdRef.current !== locationId) {
4423
- recommendationsCache.clear();
4424
- recommendationsInflight.clear();
4425
- previousLocationIdRef.current = locationId;
4426
- }
4427
- }, [locationId]);
4428
- const load = useCallback(
4429
- async (force = false) => {
4430
- if (!enabled) {
4431
- setIsLoading(false);
4432
- return;
4433
- }
4434
- setError(null);
4435
- if (!force) {
4436
- const cachedResponse = recommendationsCache.get(cacheKey);
4437
- if (cachedResponse) {
4438
- setResponse(cachedResponse);
4439
- setIsLoading(false);
4440
- return;
4441
- }
4442
- }
4443
- setIsLoading(true);
4444
- try {
4445
- const existing = recommendationsInflight.get(cacheKey);
4446
- const promise = existing ?? (async () => {
4447
- const result = await client.activity.getRecommendations({
4448
- location_id: locationId,
4449
- limit
4450
- });
4451
- if (!result.ok) {
4452
- throw result.error;
4453
- }
4454
- return result.value;
4455
- })();
4456
- if (!existing) {
4457
- recommendationsInflight.set(
4458
- cacheKey,
4459
- promise.finally(() => {
4460
- recommendationsInflight.delete(cacheKey);
4461
- })
4462
- );
4463
- }
4464
- const value = await promise;
4465
- recommendationsCache.set(cacheKey, value);
4466
- setResponse(value);
4467
- } catch (loadError) {
4468
- setError(loadError);
4469
- } finally {
4470
- setIsLoading(false);
4471
- }
4472
- },
4473
- [cacheKey, client, enabled, locationId, limit]
4474
- );
4475
- useEffect(() => {
4476
- void load(false);
4477
- }, [load]);
4478
- const refetch = useCallback(async () => {
4479
- recommendationsCache.delete(cacheKey);
4480
- await load(true);
4481
- }, [cacheKey, load]);
4482
- const recommendations = response?.recommendations ?? [];
4483
- const intent = response?.intent ?? "";
4484
- return { recommendations, intent, isLoading, error, refetch };
4485
- }
4486
- var deliveryFeeCache = /* @__PURE__ */ new Map();
4487
- function buildDeliveryFeeCacheKey(client, latitude, longitude, country) {
4488
- return JSON.stringify({
4489
- key: client.getPublicKey(),
4490
- lat: latitude,
4491
- lng: longitude,
4492
- country
4493
- });
2722
+ });
2723
+ function useOrders(options = {}) {
2724
+ const { data, isLoading, error, refetch } = ordersQuery(options);
2725
+ return { orders: data ?? [], isLoading, error, refetch };
4494
2726
  }
4495
- function useDeliveryFee(options) {
4496
- const context = useOptionalCimplifyClient();
4497
- const client = options.client ?? context?.client;
4498
- if (!client) {
4499
- throw new Error("useDeliveryFee must be used within CimplifyProvider or passed { client }.");
2727
+
2728
+ // src/react/hooks/use-services.ts
2729
+ var servicesQuery = createCachedQuery({
2730
+ name: "useServices",
2731
+ key: (client) => cacheKey(client.getPublicKey()),
2732
+ fetch: async (client) => {
2733
+ const result = await client.scheduling.getServices();
2734
+ if (!result.ok) throw result.error;
2735
+ return result.value;
4500
2736
  }
4501
- const enabled = options.enabled ?? true;
4502
- const { latitude, longitude, country } = options;
4503
- const cacheKey = useMemo(
4504
- () => buildDeliveryFeeCacheKey(client, latitude, longitude, country),
4505
- [client, latitude, longitude, country]
4506
- );
4507
- const cached = deliveryFeeCache.get(cacheKey);
4508
- const [fee, setFee] = useState(cached ?? null);
4509
- const [isLoading, setIsLoading] = useState(enabled && !cached);
4510
- const [error, setError] = useState(null);
4511
- const requestIdRef = useRef(0);
4512
- const load = useCallback(
4513
- async (force = false) => {
4514
- if (!enabled) {
4515
- setIsLoading(false);
4516
- return;
4517
- }
4518
- setError(null);
4519
- if (!force) {
4520
- const cachedFee = deliveryFeeCache.get(cacheKey);
4521
- if (cachedFee) {
4522
- setFee(cachedFee);
4523
- setIsLoading(false);
4524
- return;
4525
- }
4526
- }
4527
- setIsLoading(true);
4528
- const currentRequestId = ++requestIdRef.current;
4529
- try {
4530
- const result = await client.delivery.getFee(latitude, longitude, country);
4531
- if (currentRequestId !== requestIdRef.current) return;
4532
- if (!result.ok) {
4533
- throw result.error;
4534
- }
4535
- deliveryFeeCache.set(cacheKey, result.value);
4536
- setFee(result.value);
4537
- } catch (loadError) {
4538
- if (currentRequestId !== requestIdRef.current) return;
4539
- setError(loadError);
4540
- } finally {
4541
- if (currentRequestId === requestIdRef.current) {
4542
- setIsLoading(false);
4543
- }
4544
- }
4545
- },
4546
- [cacheKey, client, country, enabled, latitude, longitude]
4547
- );
4548
- useEffect(() => {
4549
- void load(false);
4550
- }, [load]);
4551
- const refetch = useCallback(async () => {
4552
- deliveryFeeCache.delete(cacheKey);
4553
- await load(true);
4554
- }, [cacheKey, load]);
4555
- return { fee, isLoading, error, refetch };
2737
+ });
2738
+ function useServices(options = {}) {
2739
+ const { data, isLoading, error, refetch } = servicesQuery(options);
2740
+ return { services: data ?? [], isLoading, error, refetch };
4556
2741
  }
4557
- var fxRateCache = /* @__PURE__ */ new Map();
4558
- var fxRateInflight = /* @__PURE__ */ new Map();
4559
- function buildFxRateCacheKey(client, from, to) {
4560
- return JSON.stringify({ key: client.getPublicKey(), from, to });
2742
+
2743
+ // src/react/hooks/use-available-slots.ts
2744
+ var slotsQuery = createCachedQuery({
2745
+ name: "useAvailableSlots",
2746
+ key: (client, options) => cacheKey(client.getPublicKey(), options.serviceId, options.date, options.participantCount),
2747
+ fetch: async (client, options) => {
2748
+ const result = await client.scheduling.getAvailableSlots({
2749
+ service_id: options.serviceId,
2750
+ date: options.date,
2751
+ participant_count: options.participantCount
2752
+ });
2753
+ if (!result.ok) throw result.error;
2754
+ return result.value;
2755
+ }
2756
+ });
2757
+ function useAvailableSlots(serviceId, date, options = {}) {
2758
+ const { data, isLoading, error, refetch } = slotsQuery({
2759
+ ...options,
2760
+ serviceId: serviceId || "",
2761
+ date: date || "",
2762
+ enabled: (options.enabled ?? true) && !!serviceId && !!date
2763
+ });
2764
+ return { slots: data ?? [], isLoading, error, refetch };
4561
2765
  }
4562
- function useFxRate(options) {
2766
+
2767
+ // src/react/hooks/use-service-availability.ts
2768
+ var serviceAvailabilityQuery = createCachedQuery({
2769
+ name: "useServiceAvailability",
2770
+ key: (client, options) => cacheKey(
2771
+ client.getPublicKey(),
2772
+ options.serviceId,
2773
+ options.startDate,
2774
+ options.endDate,
2775
+ options.participantCount
2776
+ ),
2777
+ fetch: async (client, options) => {
2778
+ const result = await client.scheduling.getServiceAvailability({
2779
+ service_id: options.serviceId,
2780
+ start_date: options.startDate,
2781
+ end_date: options.endDate,
2782
+ location_id: options.locationId,
2783
+ participant_count: options.participantCount
2784
+ });
2785
+ if (!result.ok) throw result.error;
2786
+ return result.value.availability;
2787
+ }
2788
+ });
2789
+ function useServiceAvailability(serviceId, startDate, endDate, options = {}) {
2790
+ const { data, isLoading, error, refetch } = serviceAvailabilityQuery({
2791
+ ...options,
2792
+ serviceId: serviceId || "",
2793
+ startDate: startDate || "",
2794
+ endDate: endDate || "",
2795
+ enabled: (options.enabled ?? true) && !!serviceId && !!startDate && !!endDate
2796
+ });
2797
+ return { days: data ?? [], isLoading, error, refetch };
2798
+ }
2799
+ var bookingsQuery = createCachedQuery({
2800
+ name: "useBookings",
2801
+ key: (client, options) => cacheKey(client.getPublicKey(), options.filter ?? "all"),
2802
+ fetch: async (client, options) => {
2803
+ const filter = options.filter ?? "all";
2804
+ let result;
2805
+ switch (filter) {
2806
+ case "upcoming":
2807
+ result = await client.scheduling.getUpcomingBookings();
2808
+ break;
2809
+ case "past":
2810
+ result = await client.scheduling.getPastBookings();
2811
+ break;
2812
+ default:
2813
+ result = await client.scheduling.getCustomerBookings();
2814
+ break;
2815
+ }
2816
+ if (!result.ok) throw result.error;
2817
+ return result.value;
2818
+ }
2819
+ });
2820
+ function useBookings(options = {}) {
4563
2821
  const context = useOptionalCimplifyClient();
4564
2822
  const client = options.client ?? context?.client;
4565
- if (!client) {
4566
- throw new Error("useFxRate must be used within CimplifyProvider or passed { client }.");
4567
- }
4568
- const enabled = options.enabled ?? true;
4569
- const { from, to } = options;
4570
- const cacheKey = useMemo(
4571
- () => buildFxRateCacheKey(client, from, to),
4572
- [client, from, to]
4573
- );
4574
- const cached = fxRateCache.get(cacheKey);
4575
- const [rate, setRate] = useState(cached ?? null);
4576
- const [isLoading, setIsLoading] = useState(enabled && !cached);
4577
- const [error, setError] = useState(null);
4578
- const load = useCallback(
4579
- async (force = false) => {
4580
- if (!enabled) {
4581
- setIsLoading(false);
4582
- return;
4583
- }
4584
- setError(null);
4585
- if (!force) {
4586
- const cachedRate = fxRateCache.get(cacheKey);
4587
- if (cachedRate) {
4588
- setRate(cachedRate);
4589
- setIsLoading(false);
4590
- return;
4591
- }
4592
- }
4593
- setIsLoading(true);
4594
- try {
4595
- const existing = fxRateInflight.get(cacheKey);
4596
- const promise = existing ?? (async () => {
4597
- const result = await client.fx.getRate(from, to);
4598
- if (!result.ok) {
4599
- throw result.error;
4600
- }
4601
- return result.value;
4602
- })();
4603
- if (!existing) {
4604
- fxRateInflight.set(
4605
- cacheKey,
4606
- promise.finally(() => {
4607
- fxRateInflight.delete(cacheKey);
4608
- })
4609
- );
4610
- }
4611
- const value = await promise;
4612
- fxRateCache.set(cacheKey, value);
4613
- setRate(value);
4614
- } catch (loadError) {
4615
- setError(loadError);
4616
- } finally {
4617
- setIsLoading(false);
4618
- }
2823
+ const { data, isLoading, error, refetch } = bookingsQuery(options);
2824
+ const cancelBooking = useCallback(
2825
+ async (bookingId, reason) => {
2826
+ if (!client) throw new Error("useBookings must be used within CimplifyProvider or passed { client }.");
2827
+ const result = await client.scheduling.cancelBooking({
2828
+ booking_id: bookingId,
2829
+ reason
2830
+ });
2831
+ if (!result.ok) throw result.error;
2832
+ bookingsQuery.clear();
2833
+ await refetch();
2834
+ return result.value;
4619
2835
  },
4620
- [cacheKey, client, enabled, from, to]
2836
+ [client, refetch]
4621
2837
  );
4622
- useEffect(() => {
4623
- void load(false);
4624
- }, [load]);
4625
- const refetch = useCallback(async () => {
4626
- fxRateCache.delete(cacheKey);
4627
- await load(true);
4628
- }, [cacheKey, load]);
4629
- return { rate, isLoading, error, refetch };
2838
+ return { bookings: data ?? [], isLoading, error, refetch, cancelBooking };
2839
+ }
2840
+
2841
+ // src/react/hooks/use-attribute-definitions.ts
2842
+ var attributeDefinitionsQuery = createCachedQuery({
2843
+ name: "useAttributeDefinitions",
2844
+ key: (client, options) => cacheKey(client.getPublicKey() || "__demo__", options.namespace),
2845
+ fetch: async (client, options) => {
2846
+ const result = await client.catalogue.getAttributeDefinitions(options.namespace);
2847
+ if (!result.ok) throw result.error;
2848
+ return result.value;
2849
+ }
2850
+ });
2851
+ function useAttributeDefinitions(options = {}) {
2852
+ const { data, isLoading, error, refetch } = attributeDefinitionsQuery(options);
2853
+ return { definitions: data ?? [], isLoading, error, refetch };
4630
2854
  }
4631
- var bootstrapCache = /* @__PURE__ */ new Map();
4632
- var bootstrapInflight = /* @__PURE__ */ new Map();
4633
- function buildBootstrapCacheKey(client) {
4634
- return JSON.stringify({ key: client.getPublicKey(), type: "bootstrap" });
2855
+
2856
+ // src/react/hooks/use-property-facets.ts
2857
+ var propertyFacetsQuery = createCachedQuery({
2858
+ name: "usePropertyFacets",
2859
+ key: (client, options) => cacheKey(client.getPublicKey() || "__demo__", options.category_id, options.product_type),
2860
+ fetch: async (client, options) => {
2861
+ const result = await client.catalogue.getPropertyFacets({
2862
+ category_id: options.category_id,
2863
+ product_type: options.product_type
2864
+ });
2865
+ if (!result.ok) throw result.error;
2866
+ return result.value;
2867
+ }
2868
+ });
2869
+ function usePropertyFacets(options = {}) {
2870
+ const { data, isLoading, error, refetch } = propertyFacetsQuery(options);
2871
+ return { facets: data ?? [], isLoading, error, refetch };
4635
2872
  }
4636
- function useBootstrap(options = {}) {
2873
+ function useBillingPlans(productId, options = {}) {
4637
2874
  const context = useOptionalCimplifyClient();
4638
2875
  const client = options.client ?? context?.client;
4639
2876
  if (!client) {
4640
- throw new Error("useBootstrap must be used within CimplifyProvider or passed { client }.");
2877
+ throw new Error("useBillingPlans must be used within CimplifyProvider or passed { client }.");
4641
2878
  }
4642
2879
  const enabled = options.enabled ?? true;
4643
- const cacheKey = useMemo(() => buildBootstrapCacheKey(client), [client]);
4644
- const cached = bootstrapCache.get(cacheKey);
4645
- const [bootstrap, setBootstrap] = useState(cached ?? null);
4646
- const [isLoading, setIsLoading] = useState(enabled && !cached);
2880
+ const requestIdRef = useRef(0);
2881
+ const [plans, setPlans] = useState([]);
2882
+ const [isLoading, setIsLoading] = useState(enabled && !!productId);
4647
2883
  const [error, setError] = useState(null);
2884
+ const customerId = options.customerId;
2885
+ const quantity = options.quantity;
2886
+ const orderValue = options.orderValue;
4648
2887
  const load = useCallback(
4649
2888
  async (force = false) => {
4650
- if (!enabled) {
2889
+ if (!enabled || !productId) {
2890
+ setPlans([]);
4651
2891
  setIsLoading(false);
4652
2892
  return;
4653
2893
  }
2894
+ const nextRequestId = ++requestIdRef.current;
4654
2895
  setError(null);
4655
- if (!force) {
4656
- const cachedBootstrap = bootstrapCache.get(cacheKey);
4657
- if (cachedBootstrap) {
4658
- setBootstrap(cachedBootstrap);
4659
- setIsLoading(false);
4660
- return;
4661
- }
4662
- }
4663
2896
  setIsLoading(true);
4664
2897
  try {
4665
- const existing = bootstrapInflight.get(cacheKey);
4666
- const promise = existing ?? (async () => {
4667
- const result = await client.business.getBootstrap();
4668
- if (!result.ok) {
4669
- throw result.error;
4670
- }
4671
- return result.value;
4672
- })();
4673
- if (!existing) {
4674
- bootstrapInflight.set(
4675
- cacheKey,
4676
- promise.finally(() => {
4677
- bootstrapInflight.delete(cacheKey);
4678
- })
4679
- );
2898
+ const result = await client.catalogue.getEligibleBillingPlans(productId, {
2899
+ customer_id: customerId,
2900
+ quantity,
2901
+ order_value: orderValue
2902
+ });
2903
+ if (!result.ok) {
2904
+ throw result.error;
2905
+ }
2906
+ if (nextRequestId === requestIdRef.current) {
2907
+ setPlans(result.value);
2908
+ setError(null);
4680
2909
  }
4681
- const value = await promise;
4682
- bootstrapCache.set(cacheKey, value);
4683
- setBootstrap(value);
4684
2910
  } catch (loadError) {
4685
- setError(loadError);
2911
+ if (nextRequestId === requestIdRef.current) {
2912
+ setError(loadError);
2913
+ }
4686
2914
  } finally {
4687
- setIsLoading(false);
2915
+ if (nextRequestId === requestIdRef.current) {
2916
+ setIsLoading(false);
2917
+ }
4688
2918
  }
4689
2919
  },
4690
- [cacheKey, client, enabled]
2920
+ [client, enabled, productId, customerId, quantity, orderValue]
4691
2921
  );
4692
2922
  useEffect(() => {
4693
2923
  void load(false);
4694
2924
  }, [load]);
4695
2925
  const refetch = useCallback(async () => {
4696
- bootstrapCache.delete(cacheKey);
4697
2926
  await load(true);
4698
- }, [cacheKey, load]);
4699
- return { bootstrap, isLoading, error, refetch };
2927
+ }, [load]);
2928
+ const subscriptionPlans = useMemo(
2929
+ () => plans.filter((p) => p.plan_type === "subscription"),
2930
+ [plans]
2931
+ );
2932
+ const installmentPlans = useMemo(
2933
+ () => plans.filter((p) => p.plan_type === "installment"),
2934
+ [plans]
2935
+ );
2936
+ return { plans, subscriptionPlans, installmentPlans, isLoading, error, refetch };
2937
+ }
2938
+ function findMatchingVariant(variants, selections) {
2939
+ const axisCount = Object.keys(selections).length;
2940
+ if (axisCount === 0) return null;
2941
+ return variants.find((v) => {
2942
+ const sel = v.axis_selections;
2943
+ if (!sel) return false;
2944
+ return Object.entries(selections).every(([axisId, valueId]) => sel[axisId] === valueId);
2945
+ }) ?? null;
2946
+ }
2947
+ function computeAvailableValues(variants, axes, selections) {
2948
+ const result = {};
2949
+ for (const axis of axes) {
2950
+ const otherSelections = Object.entries(selections).filter(([id]) => id !== axis.id);
2951
+ const candidates = variants.filter((v) => {
2952
+ const sel = v.axis_selections;
2953
+ if (!sel) return false;
2954
+ return otherSelections.every(([axisId, valueId]) => sel[axisId] === valueId);
2955
+ });
2956
+ const available = /* @__PURE__ */ new Set();
2957
+ for (const v of candidates) {
2958
+ const val = v.axis_selections?.[axis.id];
2959
+ if (val) available.add(val);
2960
+ }
2961
+ result[axis.id] = available;
2962
+ }
2963
+ return result;
2964
+ }
2965
+ function useVariantSelector(options) {
2966
+ const { variants, axes } = options;
2967
+ const [selectedValues, setSelectedValues] = useState({});
2968
+ const selectValue = useCallback(
2969
+ (axisId, valueId) => {
2970
+ setSelectedValues((prev) => {
2971
+ if (prev[axisId] === valueId) {
2972
+ const next = { ...prev };
2973
+ delete next[axisId];
2974
+ return next;
2975
+ }
2976
+ return { ...prev, [axisId]: valueId };
2977
+ });
2978
+ },
2979
+ []
2980
+ );
2981
+ const reset = useCallback(() => setSelectedValues({}), []);
2982
+ const selectedVariant = useMemo(
2983
+ () => findMatchingVariant(variants, selectedValues),
2984
+ [variants, selectedValues]
2985
+ );
2986
+ const variantImage = useMemo(
2987
+ () => selectedVariant?.images?.[0] ?? null,
2988
+ [selectedVariant]
2989
+ );
2990
+ const availableValues = useMemo(
2991
+ () => computeAvailableValues(variants, axes, selectedValues),
2992
+ [variants, axes, selectedValues]
2993
+ );
2994
+ return {
2995
+ selectedValues,
2996
+ selectedVariant,
2997
+ variantImage,
2998
+ availableValues,
2999
+ selectValue,
3000
+ reset
3001
+ };
3002
+ }
3003
+ var activityStateQuery = createCachedQuery({
3004
+ name: "useActivityState",
3005
+ key: (client) => cacheKey(client.getPublicKey()),
3006
+ fetch: async (client) => {
3007
+ const result = await client.activity.getState();
3008
+ if (!result.ok) throw result.error;
3009
+ return result.value;
3010
+ }
3011
+ });
3012
+ function useActivityState(options = {}) {
3013
+ const context = useOptionalCimplifyClient();
3014
+ const client = options.client ?? context?.client;
3015
+ const { data, isLoading, error, refetch } = activityStateQuery(options);
3016
+ const dismissMessage = useCallback(
3017
+ async (code) => {
3018
+ if (!client) return;
3019
+ const result = await client.activity.dismissMessage(code);
3020
+ if (!result.ok) return;
3021
+ activityStateQuery.clear();
3022
+ await refetch();
3023
+ },
3024
+ [client, refetch]
3025
+ );
3026
+ return {
3027
+ state: data,
3028
+ messages: data?.messages ?? [],
3029
+ intent: data?.intent ?? "",
3030
+ isLoading,
3031
+ error,
3032
+ refetch,
3033
+ dismissMessage
3034
+ };
3035
+ }
3036
+
3037
+ // src/react/hooks/use-recommendations.ts
3038
+ var recommendationsQuery = createCachedQuery({
3039
+ name: "useRecommendations",
3040
+ locationSensitive: true,
3041
+ key: (client, options) => {
3042
+ const locationId = options.locationId ?? client.getLocationId();
3043
+ return cacheKey(client.getPublicKey(), locationId, options.limit);
3044
+ },
3045
+ fetch: async (client, options) => {
3046
+ const locationId = options.locationId ?? client.getLocationId() ?? void 0;
3047
+ const result = await client.activity.getRecommendations({
3048
+ location_id: locationId,
3049
+ limit: options.limit
3050
+ });
3051
+ if (!result.ok) throw result.error;
3052
+ return result.value;
3053
+ }
3054
+ });
3055
+ function useRecommendations(options = {}) {
3056
+ const { data, isLoading, error, refetch } = recommendationsQuery(options);
3057
+ return {
3058
+ recommendations: data?.recommendations ?? [],
3059
+ intent: data?.intent ?? "",
3060
+ isLoading,
3061
+ error,
3062
+ refetch
3063
+ };
3064
+ }
3065
+
3066
+ // src/react/hooks/use-delivery-fee.ts
3067
+ var deliveryFeeQuery = createCachedQuery({
3068
+ name: "useDeliveryFee",
3069
+ key: (client, options) => cacheKey(client.getPublicKey(), options.latitude, options.longitude, options.country),
3070
+ fetch: async (client, options) => {
3071
+ const result = await client.delivery.getFee(options.latitude, options.longitude, options.country);
3072
+ if (!result.ok) throw result.error;
3073
+ return result.value;
3074
+ }
3075
+ });
3076
+ function useDeliveryFee(options) {
3077
+ const { data, isLoading, error, refetch } = deliveryFeeQuery(options);
3078
+ return { fee: data, isLoading, error, refetch };
3079
+ }
3080
+
3081
+ // src/react/hooks/use-fx-rate.ts
3082
+ var fxRateQuery = createCachedQuery({
3083
+ name: "useFxRate",
3084
+ key: (client, options) => cacheKey(client.getPublicKey(), options.from, options.to),
3085
+ fetch: async (client, options) => {
3086
+ const result = await client.fx.getRate(options.from, options.to);
3087
+ if (!result.ok) throw result.error;
3088
+ return result.value;
3089
+ }
3090
+ });
3091
+ function useFxRate(options) {
3092
+ const { data, isLoading, error, refetch } = fxRateQuery(options);
3093
+ return { rate: data, isLoading, error, refetch };
3094
+ }
3095
+
3096
+ // src/react/hooks/use-bootstrap.ts
3097
+ var bootstrapQuery = createCachedQuery({
3098
+ name: "useBootstrap",
3099
+ key: (client) => cacheKey(client.getPublicKey()),
3100
+ fetch: async (client) => {
3101
+ const result = await client.business.getBootstrap();
3102
+ if (!result.ok) throw result.error;
3103
+ return result.value;
3104
+ }
3105
+ });
3106
+ function useBootstrap(options = {}) {
3107
+ const { data, isLoading, error, refetch } = bootstrapQuery(options);
3108
+ return { bootstrap: data, isLoading, error, refetch };
4700
3109
  }
4701
3110
  var ElementsContext = createContext({
4702
3111
  elements: null,
@@ -6877,11 +5286,8 @@ function ProductCard({
6877
5286
  "div",
6878
5287
  {
6879
5288
  "data-cimplify-product-card-image-container": true,
6880
- className: classNames?.imageContainer,
6881
- style: {
6882
- overflow: "hidden",
6883
- ...ASPECT_STYLES2[aspectRatio]
6884
- },
5289
+ className: cn("overflow-hidden rounded-t-xl", classNames?.imageContainer),
5290
+ style: ASPECT_STYLES2[aspectRatio],
6885
5291
  children: renderImage ? renderImage({
6886
5292
  src: imageUrl,
6887
5293
  alt: product.name,
@@ -6891,8 +5297,7 @@ function ProductCard({
6891
5297
  {
6892
5298
  src: imageUrl,
6893
5299
  alt: product.name,
6894
- className: classNames?.image,
6895
- style: { width: "100%", height: "100%", objectFit: "cover" },
5300
+ className: cn("w-full h-full object-cover transition-transform duration-300 group-hover:scale-105", classNames?.image),
6896
5301
  "data-cimplify-product-card-image": true
6897
5302
  }
6898
5303
  )
@@ -6902,13 +5307,13 @@ function ProductCard({
6902
5307
  "div",
6903
5308
  {
6904
5309
  "data-cimplify-product-card-body": true,
6905
- className: classNames?.body,
5310
+ className: cn("p-4 flex flex-col gap-1", classNames?.body),
6906
5311
  children: [
6907
5312
  /* @__PURE__ */ jsx(
6908
5313
  "span",
6909
5314
  {
6910
5315
  "data-cimplify-product-card-name": true,
6911
- className: classNames?.name,
5316
+ className: cn("font-semibold text-foreground leading-tight", classNames?.name),
6912
5317
  children: product.name
6913
5318
  }
6914
5319
  ),
@@ -6916,13 +5321,7 @@ function ProductCard({
6916
5321
  "span",
6917
5322
  {
6918
5323
  "data-cimplify-product-card-description": true,
6919
- className: classNames?.description,
6920
- style: {
6921
- display: "-webkit-box",
6922
- WebkitLineClamp: 2,
6923
- WebkitBoxOrient: "vertical",
6924
- overflow: "hidden"
6925
- },
5324
+ className: cn("text-sm text-muted-foreground line-clamp-2", classNames?.description),
6926
5325
  children: product.description
6927
5326
  }
6928
5327
  ),
@@ -6930,7 +5329,7 @@ function ProductCard({
6930
5329
  Price,
6931
5330
  {
6932
5331
  amount: product.default_price,
6933
- className: classNames?.price
5332
+ className: cn("text-sm font-medium text-foreground mt-1", classNames?.price)
6934
5333
  }
6935
5334
  )
6936
5335
  ]
@@ -6939,7 +5338,7 @@ function ProductCard({
6939
5338
  ] });
6940
5339
  if (mode === "page") {
6941
5340
  const linkHref = href ?? `/menu/${product.slug}`;
6942
- const linkClassName = cn("block no-underline text-[inherit]", className, classNames?.root);
5341
+ const linkClassName = cn("group block no-underline text-[inherit] rounded-xl border border-border bg-background transition-shadow duration-200 hover:shadow-md", className, classNames?.root);
6943
5342
  if (renderLink) {
6944
5343
  return renderLink({ href: linkHref, className: linkClassName, children: cardBody });
6945
5344
  }
@@ -6962,7 +5361,7 @@ function ProductCard({
6962
5361
  "data-cimplify-product-card": true,
6963
5362
  "data-display-mode": "card",
6964
5363
  className: cn(
6965
- "block w-full text-left bg-transparent border-none p-0 cursor-pointer font-[inherit] text-[inherit]",
5364
+ "group block w-full text-left p-0 cursor-pointer font-[inherit] text-[inherit] rounded-xl border border-border bg-background transition-shadow duration-200 hover:shadow-md",
6966
5365
  className,
6967
5366
  classNames?.root
6968
5367
  ),
@@ -8296,10 +6695,14 @@ function BookingCard({
8296
6695
  {
8297
6696
  "data-cimplify-booking-card": true,
8298
6697
  "data-status": booking.status,
8299
- className: cn(className, classNames?.root),
6698
+ className: cn(
6699
+ "rounded-xl border border-border bg-background p-4 flex flex-col gap-3",
6700
+ className,
6701
+ classNames?.root
6702
+ ),
8300
6703
  children: [
8301
- /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-main": true, children: [
8302
- /* @__PURE__ */ jsxs("span", { "data-cimplify-booking-service": true, className: classNames?.service, children: [
6704
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-main": true, className: "flex items-center justify-between gap-2", children: [
6705
+ /* @__PURE__ */ jsxs("span", { "data-cimplify-booking-service": true, className: cn("font-semibold text-foreground", classNames?.service), children: [
8303
6706
  "Booking #",
8304
6707
  booking.order_id.slice(0, 8)
8305
6708
  ] }),
@@ -8308,22 +6711,37 @@ function BookingCard({
8308
6711
  {
8309
6712
  "data-cimplify-booking-status": true,
8310
6713
  "data-status": booking.status,
8311
- className: classNames?.status,
6714
+ className: cn(
6715
+ "text-xs font-medium px-2 py-0.5 rounded-full",
6716
+ active ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground",
6717
+ classNames?.status
6718
+ ),
8312
6719
  children: STATUS_LABELS3[booking.status] ?? booking.status
8313
6720
  }
8314
6721
  )
8315
6722
  ] }),
8316
- firstItem?.confirmation_code && /* @__PURE__ */ jsx("span", { "data-cimplify-booking-code": true, className: classNames?.confirmationCode, children: firstItem.confirmation_code }),
8317
- scheduledTime && /* @__PURE__ */ jsx("time", { "data-cimplify-booking-time": true, className: classNames?.time, children: scheduledTime }),
8318
- /* @__PURE__ */ jsx("span", { "data-cimplify-booking-total": true, className: classNames?.total, children: /* @__PURE__ */ jsx(Price, { amount: booking.total_price }) }),
8319
- active && (onCancel || onReschedule) && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-actions": true, className: classNames?.actions, children: [
6723
+ firstItem?.confirmation_code && /* @__PURE__ */ jsx("span", { "data-cimplify-booking-code": true, className: cn("text-xs font-mono text-muted-foreground tracking-wide", classNames?.confirmationCode), children: firstItem.confirmation_code }),
6724
+ scheduledTime && /* @__PURE__ */ jsx(
6725
+ "time",
6726
+ {
6727
+ "data-cimplify-booking-time": true,
6728
+ dateTime: firstItem?.scheduled_start ?? void 0,
6729
+ className: cn("text-sm text-foreground", classNames?.time),
6730
+ children: scheduledTime
6731
+ }
6732
+ ),
6733
+ /* @__PURE__ */ jsx("span", { "data-cimplify-booking-total": true, className: cn("text-sm font-medium text-foreground", classNames?.total), children: /* @__PURE__ */ jsx(Price, { amount: booking.total_price }) }),
6734
+ active && (onCancel || onReschedule) && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-actions": true, className: cn("flex gap-2 pt-1 border-t border-border mt-1", classNames?.actions), children: [
8320
6735
  onReschedule && /* @__PURE__ */ jsx(
8321
6736
  "button",
8322
6737
  {
8323
6738
  type: "button",
8324
6739
  onClick: () => onReschedule(booking),
8325
6740
  "data-cimplify-booking-reschedule": true,
8326
- className: classNames?.rescheduleButton,
6741
+ className: cn(
6742
+ "flex-1 text-sm font-medium py-2 rounded-lg border border-border bg-background text-foreground transition-colors hover:bg-muted cursor-pointer",
6743
+ classNames?.rescheduleButton
6744
+ ),
8327
6745
  children: "Reschedule"
8328
6746
  }
8329
6747
  ),
@@ -8333,7 +6751,10 @@ function BookingCard({
8333
6751
  type: "button",
8334
6752
  onClick: () => onCancel(booking),
8335
6753
  "data-cimplify-booking-cancel": true,
8336
- className: classNames?.cancelButton,
6754
+ className: cn(
6755
+ "flex-1 text-sm font-medium py-2 rounded-lg border border-border bg-background text-destructive transition-colors hover:bg-destructive/10 cursor-pointer",
6756
+ classNames?.cancelButton
6757
+ ),
8337
6758
  children: "Cancel"
8338
6759
  }
8339
6760
  )
@@ -8446,11 +6867,8 @@ function ServiceCard({
8446
6867
  "div",
8447
6868
  {
8448
6869
  "data-cimplify-service-card-image-container": true,
8449
- className: classNames?.imageContainer,
8450
- style: {
8451
- overflow: "hidden",
8452
- ...ASPECT_STYLES3[aspectRatio]
8453
- },
6870
+ className: cn("overflow-hidden rounded-t-xl", classNames?.imageContainer),
6871
+ style: ASPECT_STYLES3[aspectRatio],
8454
6872
  children: renderImage ? renderImage({
8455
6873
  src: imageUrl,
8456
6874
  alt: service.name,
@@ -8460,36 +6878,35 @@ function ServiceCard({
8460
6878
  {
8461
6879
  src: imageUrl,
8462
6880
  alt: service.name,
8463
- className: classNames?.image,
8464
- style: { width: "100%", height: "100%", objectFit: "cover" },
6881
+ className: cn("w-full h-full object-cover transition-transform duration-300 group-hover:scale-105", classNames?.image),
8465
6882
  "data-cimplify-service-card-image": true
8466
6883
  }
8467
6884
  )
8468
6885
  }
8469
6886
  ),
8470
- /* @__PURE__ */ jsxs("div", { "data-cimplify-service-card-body": true, className: classNames?.body, children: [
8471
- /* @__PURE__ */ jsx("span", { "data-cimplify-service-card-name": true, className: classNames?.name, children: service.name }),
6887
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-service-card-body": true, className: cn("p-4 flex flex-col gap-1.5", classNames?.body), children: [
6888
+ /* @__PURE__ */ jsx("span", { "data-cimplify-service-card-name": true, className: cn("font-semibold text-foreground leading-tight", classNames?.name), children: service.name }),
8472
6889
  service.description && /* @__PURE__ */ jsx(
8473
6890
  "span",
8474
6891
  {
8475
6892
  "data-cimplify-service-card-description": true,
8476
- className: classNames?.description,
8477
- style: {
8478
- display: "-webkit-box",
8479
- WebkitLineClamp: 2,
8480
- WebkitBoxOrient: "vertical",
8481
- overflow: "hidden"
8482
- },
6893
+ className: cn("text-sm text-muted-foreground line-clamp-2", classNames?.description),
8483
6894
  children: service.description
8484
6895
  }
8485
6896
  ),
8486
- /* @__PURE__ */ jsxs("div", { "data-cimplify-service-card-meta": true, className: classNames?.meta, children: [
8487
- /* @__PURE__ */ jsx("span", { "data-cimplify-service-card-duration": true, className: classNames?.duration, children: formatDuration2(service.duration_minutes, service.duration_unit) }),
8488
- service.price && /* @__PURE__ */ jsx("span", { "data-cimplify-service-card-price": true, className: classNames?.price, children: /* @__PURE__ */ jsx(Price, { amount: service.price }) })
6897
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-service-card-meta": true, className: cn("flex items-center gap-2 mt-1 text-sm", classNames?.meta), children: [
6898
+ /* @__PURE__ */ jsx("span", { "data-cimplify-service-card-duration": true, className: cn("text-muted-foreground", classNames?.duration), children: formatDuration2(service.duration_minutes, service.duration_unit) }),
6899
+ service.price && /* @__PURE__ */ jsx("span", { "data-cimplify-service-card-price": true, className: cn("font-medium text-foreground", classNames?.price), children: /* @__PURE__ */ jsx(Price, { amount: service.price }) })
8489
6900
  ] })
8490
6901
  ] }),
8491
- !href && onBook && /* @__PURE__ */ jsx("span", { "data-cimplify-service-card-action": true, className: classNames?.action, children: actionLabel })
6902
+ !href && onBook && /* @__PURE__ */ jsx("span", { "data-cimplify-service-card-action": true, className: cn("px-4 pb-4 pt-1 text-sm font-medium text-primary", classNames?.action), children: actionLabel })
8492
6903
  ] });
6904
+ const cardClasses = cn(
6905
+ "group block w-full rounded-xl border border-border bg-background text-left transition-shadow duration-200 hover:shadow-md",
6906
+ !service.is_available && "opacity-60",
6907
+ className,
6908
+ classNames?.root
6909
+ );
8493
6910
  if (href) {
8494
6911
  return /* @__PURE__ */ jsx(
8495
6912
  "a",
@@ -8497,8 +6914,7 @@ function ServiceCard({
8497
6914
  href,
8498
6915
  "data-cimplify-service-card": true,
8499
6916
  "data-available": service.is_available || void 0,
8500
- className: cn(className, classNames?.root),
8501
- style: { display: "block", textDecoration: "none", color: "inherit" },
6917
+ className: cn(cardClasses, "no-underline text-[inherit]"),
8502
6918
  children: cardBody
8503
6919
  }
8504
6920
  );
@@ -8511,18 +6927,7 @@ function ServiceCard({
8511
6927
  disabled: !service.is_available,
8512
6928
  "data-cimplify-service-card": true,
8513
6929
  "data-available": service.is_available || void 0,
8514
- className: cn(className, classNames?.root),
8515
- style: {
8516
- display: "block",
8517
- width: "100%",
8518
- textAlign: "inherit",
8519
- background: "none",
8520
- border: "none",
8521
- padding: 0,
8522
- cursor: service.is_available ? "pointer" : "default",
8523
- font: "inherit",
8524
- color: "inherit"
8525
- },
6930
+ className: cn(cardClasses, "cursor-pointer font-[inherit] p-0 border-border"),
8526
6931
  children: cardBody
8527
6932
  }
8528
6933
  );
@@ -9258,10 +7663,114 @@ function DealsPage({
9258
7663
  isEmpty && /* @__PURE__ */ jsx("div", { "data-cimplify-deals-empty": true, className: classNames?.empty, children: /* @__PURE__ */ jsx("p", { children: "No deals available right now. Check back soon!" }) })
9259
7664
  ] });
9260
7665
  }
7666
+ var ANY_VALUE2 = "__any__";
7667
+ function roomToResource(room) {
7668
+ return {
7669
+ id: room.id,
7670
+ name: room.name,
7671
+ capacity: room.capacity,
7672
+ floor: room.floor,
7673
+ is_available: room.status === "available"
7674
+ };
7675
+ }
7676
+ function ResourcePicker({
7677
+ resources,
7678
+ selectedResourceId,
7679
+ onResourceSelect,
7680
+ showAnyOption = true,
7681
+ anyLabel = "Any available",
7682
+ renderImage,
7683
+ className,
7684
+ classNames
7685
+ }) {
7686
+ const groupValue = selectedResourceId === null ? ANY_VALUE2 : selectedResourceId ?? "";
7687
+ return /* @__PURE__ */ jsxs(
7688
+ RadioGroup,
7689
+ {
7690
+ "data-cimplify-resource-picker": true,
7691
+ className: cn("flex flex-col gap-2", className, classNames?.root),
7692
+ value: groupValue,
7693
+ onValueChange: (value) => {
7694
+ onResourceSelect?.(value === ANY_VALUE2 ? null : value);
7695
+ },
7696
+ children: [
7697
+ showAnyOption && /* @__PURE__ */ jsx(
7698
+ Radio.Root,
7699
+ {
7700
+ value: ANY_VALUE2,
7701
+ "data-cimplify-resource-option": true,
7702
+ "data-selected": selectedResourceId === null || void 0,
7703
+ "data-any": true,
7704
+ className: cn(
7705
+ "flex items-center gap-3 rounded-lg border border-border p-3 transition-colors cursor-pointer",
7706
+ "hover:bg-muted data-[checked]:border-primary data-[checked]:bg-primary/5",
7707
+ classNames?.option
7708
+ ),
7709
+ children: /* @__PURE__ */ jsx("span", { "data-cimplify-resource-name": true, className: cn("font-medium text-foreground", classNames?.name), children: anyLabel })
7710
+ }
7711
+ ),
7712
+ resources.map((resource) => {
7713
+ const unavailable = resource.is_available === false;
7714
+ return /* @__PURE__ */ jsxs(
7715
+ Radio.Root,
7716
+ {
7717
+ value: resource.id,
7718
+ disabled: unavailable,
7719
+ "data-cimplify-resource-option": true,
7720
+ "data-selected": selectedResourceId === resource.id || void 0,
7721
+ "data-unavailable": unavailable || void 0,
7722
+ className: cn(
7723
+ "flex items-center gap-3 rounded-lg border border-border p-3 transition-colors cursor-pointer",
7724
+ "hover:bg-muted data-[checked]:border-primary data-[checked]:bg-primary/5",
7725
+ unavailable && "opacity-50 cursor-not-allowed",
7726
+ classNames?.option,
7727
+ unavailable ? classNames?.unavailable : void 0
7728
+ ),
7729
+ children: [
7730
+ resource.image_url && (renderImage ? renderImage({
7731
+ src: resource.image_url,
7732
+ alt: resource.name,
7733
+ className: classNames?.image
7734
+ }) : /* @__PURE__ */ jsx(
7735
+ "img",
7736
+ {
7737
+ src: resource.image_url,
7738
+ alt: resource.name,
7739
+ "data-cimplify-resource-image": true,
7740
+ className: cn("w-10 h-10 rounded-lg object-cover", classNames?.image)
7741
+ }
7742
+ )),
7743
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 flex-1 min-w-0", children: [
7744
+ /* @__PURE__ */ jsx("span", { "data-cimplify-resource-name": true, className: cn("font-medium text-foreground", classNames?.name), children: resource.name }),
7745
+ resource.description && /* @__PURE__ */ jsx("span", { "data-cimplify-resource-description": true, className: cn("text-sm text-muted-foreground truncate", classNames?.description), children: resource.description }),
7746
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-resource-meta": true, className: cn("flex items-center gap-2 text-xs text-muted-foreground", classNames?.meta), children: [
7747
+ resource.capacity !== void 0 && /* @__PURE__ */ jsxs("span", { "data-cimplify-resource-capacity": true, className: classNames?.capacity, children: [
7748
+ "Up to ",
7749
+ resource.capacity
7750
+ ] }),
7751
+ resource.floor && /* @__PURE__ */ jsx("span", { "data-cimplify-resource-floor": true, className: classNames?.floor, children: resource.floor })
7752
+ ] })
7753
+ ] })
7754
+ ]
7755
+ },
7756
+ resource.id
7757
+ );
7758
+ })
7759
+ ]
7760
+ }
7761
+ );
7762
+ }
7763
+ var STEP = {
7764
+ SELECT_SLOT: "select-slot",
7765
+ SELECT_RESOURCE: "select-resource",
7766
+ SELECT_STAFF: "select-staff",
7767
+ CONFIRM: "confirm"
7768
+ };
9261
7769
  function BookingPage({
9262
7770
  service,
9263
7771
  product,
9264
7772
  staff,
7773
+ resources,
9265
7774
  participantCount,
9266
7775
  title,
9267
7776
  onBooked,
@@ -9275,12 +7784,14 @@ function BookingPage({
9275
7784
  const { addItem } = useCart();
9276
7785
  const { client } = useCimplifyClient();
9277
7786
  const isReschedule = existingBooking !== void 0;
9278
- const [step, setStep] = useState("select-slot");
7787
+ const [step, setStep] = useState(STEP.SELECT_SLOT);
9279
7788
  const [selectedSlot, setSelectedSlot] = useState(null);
9280
7789
  const [selectedDate, setSelectedDate] = useState(null);
9281
7790
  const [selectedStaffId, setSelectedStaffId] = useState(null);
7791
+ const [selectedResourceId, setSelectedResourceId] = useState(null);
9282
7792
  const [isSubmitting, setIsSubmitting] = useState(false);
9283
7793
  const [error, setError] = useState(null);
7794
+ const hasResourceStep = resources && resources.length > 0;
9284
7795
  const hasStaffStep = staff && staff.length > 0;
9285
7796
  const depositType = product?.deposit_type;
9286
7797
  const depositAmount = product?.deposit_amount;
@@ -9292,28 +7803,45 @@ function BookingPage({
9292
7803
  setSelectedSlot(slot);
9293
7804
  setSelectedDate(date);
9294
7805
  setError(null);
7806
+ if (hasResourceStep) {
7807
+ setStep(STEP.SELECT_RESOURCE);
7808
+ } else if (hasStaffStep) {
7809
+ setStep(STEP.SELECT_STAFF);
7810
+ } else {
7811
+ setStep(STEP.CONFIRM);
7812
+ }
7813
+ },
7814
+ [hasResourceStep, hasStaffStep]
7815
+ );
7816
+ const handleResourceSelect = useCallback(
7817
+ (resourceId) => {
7818
+ setSelectedResourceId(resourceId);
9295
7819
  if (hasStaffStep) {
9296
- setStep("select-staff");
7820
+ setStep(STEP.SELECT_STAFF);
9297
7821
  } else {
9298
- setStep("confirm");
7822
+ setStep(STEP.CONFIRM);
9299
7823
  }
9300
7824
  },
9301
7825
  [hasStaffStep]
9302
7826
  );
9303
7827
  const handleStaffSelect = useCallback((staffId) => {
9304
7828
  setSelectedStaffId(staffId);
9305
- setStep("confirm");
7829
+ setStep(STEP.CONFIRM);
9306
7830
  }, []);
9307
7831
  const handleBack = useCallback(() => {
9308
7832
  setError(null);
9309
- if (step === "confirm" && hasStaffStep) {
9310
- setStep("select-staff");
9311
- } else if (step === "confirm" || step === "select-staff") {
9312
- setStep("select-slot");
7833
+ if (step === STEP.CONFIRM && hasStaffStep) {
7834
+ setStep(STEP.SELECT_STAFF);
7835
+ } else if (step === STEP.CONFIRM && hasResourceStep) {
7836
+ setStep(STEP.SELECT_RESOURCE);
7837
+ } else if (step === STEP.SELECT_STAFF && hasResourceStep) {
7838
+ setStep(STEP.SELECT_RESOURCE);
7839
+ } else if (step === STEP.CONFIRM || step === STEP.SELECT_STAFF || step === STEP.SELECT_RESOURCE) {
7840
+ setStep(STEP.SELECT_SLOT);
9313
7841
  } else {
9314
7842
  onBack?.();
9315
7843
  }
9316
- }, [step, hasStaffStep, onBack]);
7844
+ }, [step, hasStaffStep, hasResourceStep, onBack]);
9317
7845
  const handleConfirm = useCallback(async () => {
9318
7846
  if (!selectedSlot || !selectedDate) return;
9319
7847
  setIsSubmitting(true);
@@ -9353,7 +7881,8 @@ function BookingPage({
9353
7881
  await addItem(serviceProduct, 1, {
9354
7882
  scheduledStart: selectedSlot.start_time,
9355
7883
  scheduledEnd: selectedSlot.end_time,
9356
- staffId: selectedStaffId || void 0
7884
+ staffId: selectedStaffId || void 0,
7885
+ resourceId: selectedResourceId || void 0
9357
7886
  });
9358
7887
  onBooked?.(selectedSlot, selectedStaffId);
9359
7888
  }
@@ -9366,7 +7895,7 @@ function BookingPage({
9366
7895
  }, [selectedSlot, selectedDate, selectedStaffId, service, addItem, onBooked, isReschedule, existingBooking, client, onRescheduled]);
9367
7896
  return /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-page": true, className: cn(className, classNames?.root), children: [
9368
7897
  /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-page-header": true, className: classNames?.header, children: [
9369
- onBack && step === "select-slot" && /* @__PURE__ */ jsx(
7898
+ onBack && step === STEP.SELECT_SLOT && /* @__PURE__ */ jsx(
9370
7899
  "button",
9371
7900
  {
9372
7901
  type: "button",
@@ -9376,7 +7905,7 @@ function BookingPage({
9376
7905
  children: "Back"
9377
7906
  }
9378
7907
  ),
9379
- step !== "select-slot" && /* @__PURE__ */ jsx(
7908
+ step !== STEP.SELECT_SLOT && /* @__PURE__ */ jsx(
9380
7909
  "button",
9381
7910
  {
9382
7911
  type: "button",
@@ -9411,7 +7940,7 @@ function BookingPage({
9411
7940
  ] })
9412
7941
  ] }),
9413
7942
  error && /* @__PURE__ */ jsx("div", { "data-cimplify-booking-error": true, className: classNames?.error, children: error }),
9414
- step === "select-slot" && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-step": "select-slot", className: classNames?.step, children: [
7943
+ step === STEP.SELECT_SLOT && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-step": "select-slot", className: classNames?.step, children: [
9415
7944
  /* @__PURE__ */ jsx("h2", { "data-cimplify-booking-step-title": true, className: classNames?.stepTitle, children: "Select a date & time" }),
9416
7945
  /* @__PURE__ */ jsx(
9417
7946
  DateSlotPicker,
@@ -9424,7 +7953,18 @@ function BookingPage({
9424
7953
  }
9425
7954
  )
9426
7955
  ] }),
9427
- step === "select-staff" && staff && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-step": "select-staff", className: classNames?.step, children: [
7956
+ step === STEP.SELECT_RESOURCE && resources && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-step": "select-resource", className: cn(classNames?.step, classNames?.resourceStep), children: [
7957
+ /* @__PURE__ */ jsx("h2", { "data-cimplify-booking-step-title": true, className: classNames?.stepTitle, children: "Choose a room" }),
7958
+ /* @__PURE__ */ jsx(
7959
+ ResourcePicker,
7960
+ {
7961
+ resources,
7962
+ selectedResourceId,
7963
+ onResourceSelect: handleResourceSelect
7964
+ }
7965
+ )
7966
+ ] }),
7967
+ step === STEP.SELECT_STAFF && staff && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-step": "select-staff", className: classNames?.step, children: [
9428
7968
  /* @__PURE__ */ jsx("h2", { "data-cimplify-booking-step-title": true, className: classNames?.stepTitle, children: "Choose a provider" }),
9429
7969
  /* @__PURE__ */ jsx(
9430
7970
  StaffPicker,
@@ -9435,7 +7975,7 @@ function BookingPage({
9435
7975
  }
9436
7976
  )
9437
7977
  ] }),
9438
- step === "confirm" && selectedSlot && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-step": "confirm", className: classNames?.step, children: [
7978
+ step === STEP.CONFIRM && selectedSlot && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-step": "confirm", className: classNames?.step, children: [
9439
7979
  /* @__PURE__ */ jsx("h2", { "data-cimplify-booking-step-title": true, className: classNames?.stepTitle, children: "Confirm your booking" }),
9440
7980
  /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-summary": true, className: classNames?.summary, children: [
9441
7981
  /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-summary-row": true, className: classNames?.summaryRow, children: [
@@ -9457,6 +7997,10 @@ function BookingPage({
9457
7997
  /* @__PURE__ */ jsx("span", { children: "Duration" }),
9458
7998
  /* @__PURE__ */ jsx("span", { children: formatDuration2(service.duration_minutes, service.duration_unit) })
9459
7999
  ] }),
8000
+ selectedResourceId && resources && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-summary-row": true, className: classNames?.summaryRow, children: [
8001
+ /* @__PURE__ */ jsx("span", { children: "Room" }),
8002
+ /* @__PURE__ */ jsx("span", { children: resources.find((r) => r.id === selectedResourceId)?.name ?? "Selected" })
8003
+ ] }),
9460
8004
  selectedStaffId && staff && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-summary-row": true, className: classNames?.summaryRow, children: [
9461
8005
  /* @__PURE__ */ jsx("span", { children: "Provider" }),
9462
8006
  /* @__PURE__ */ jsx("span", { children: staff.find((s) => s.id === selectedStaffId)?.name ?? "Selected" })
@@ -10025,4 +8569,4 @@ function CurrencySelector({
10025
8569
  }) });
10026
8570
  }
10027
8571
 
10028
- export { Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingsPage, BundleSelector, CartPage, CartSummary, CataloguePage, CategoryFilter, CategoryGrid, CheckoutPage, CimplifyCheckout, CimplifyProvider, CollectionPage, CompositeSelector, CurrencySelector, DateSlotPicker, DealBanner, DealsPage, DeliveryEstimate, DiscountInput, ElementsProvider, LocationPicker, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderSummary, PaymentElement, Price, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductPage, ProductSheet, QuantitySelector, RecentlyViewed, RecommendationCarousel, SaleBadge, SearchInput, SearchPage, ServiceCard, ServiceGrid, SessionMessageBanner, SlotPicker, StaffPicker, StoreNav, VariantSelector, cn, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCategories, useCheckout, useCimplify, useCimplifyClient, useCollection, useCollections, useDeals, useDeliveryFee, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
8572
+ export { Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingsPage, BundleSelector, CartPage, CartSummary, CataloguePage, CategoryFilter, CategoryGrid, CheckoutPage, CimplifyCheckout, CimplifyProvider, CollectionPage, CompositeSelector, CurrencySelector, DateSlotPicker, DealBanner, DealsPage, DeliveryEstimate, DiscountInput, ElementsProvider, LocationPicker, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderSummary, PaymentElement, Price, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductPage, ProductSheet, QuantitySelector, RecentlyViewed, RecommendationCarousel, ResourcePicker, SaleBadge, SearchInput, SearchPage, ServiceCard, ServiceGrid, SessionMessageBanner, SlotPicker, StaffPicker, StoreNav, VariantSelector, cn, roomToResource, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCategories, useCheckout, useCimplify, useCimplifyClient, useCollection, useCollections, useDeals, useDeliveryFee, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };