@cimplify/sdk 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { getVariantDisplayName, createCimplifyClient } from './chunk-G6KNFYJD.mjs';
3
- export { getVariantDisplayName } from './chunk-G6KNFYJD.mjs';
4
- import { ELEMENT_TYPES, EVENT_TYPES } from './chunk-GFKBH7R2.mjs';
5
- import { isSupportedCurrency, formatPrice, parsePrice, isOnSale, getDiscountPercentage, getBasePrice } from './chunk-OVM5YWM5.mjs';
2
+ import { getVariantDisplayName, createCimplifyClient } from './chunk-AFEE43MQ.mjs';
3
+ export { getVariantDisplayName } from './chunk-AFEE43MQ.mjs';
4
+ import { ELEMENT_TYPES, EVENT_TYPES } from './chunk-7ACYNCNM.mjs';
5
+ import { isSupportedCurrency, formatPrice, parsePrice, isOnSale, getDiscountPercentage, getBasePrice } from './chunk-FTL2SXCG.mjs';
6
6
  import React3, { createContext, useContext, useState, useEffect, useRef, useMemo, useSyncExternalStore, useCallback, useId } from 'react';
7
7
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
8
  import { NumberField } from '@base-ui/react/number-field';
@@ -1661,7 +1661,6 @@ function buildProductsCacheKey(client, locationId, options) {
1661
1661
  client.getPublicKey(),
1662
1662
  locationId ?? "",
1663
1663
  options.category ?? "",
1664
- options.collection ?? "",
1665
1664
  options.taxonomy ?? "",
1666
1665
  options.search ?? "",
1667
1666
  options.featured ?? "",
@@ -1672,7 +1671,8 @@ function buildProductsCacheKey(client, locationId, options) {
1672
1671
  options.in_stock ?? "",
1673
1672
  options.min_price ?? "",
1674
1673
  options.max_price ?? "",
1675
- options.page ?? ""
1674
+ options.page ?? "",
1675
+ options.properties ? JSON.stringify(options.properties) : ""
1676
1676
  ].join("|");
1677
1677
  }
1678
1678
  function useProducts(options = {}) {
@@ -1689,7 +1689,6 @@ function useProducts(options = {}) {
1689
1689
  const queryOptions = useMemo(
1690
1690
  () => ({
1691
1691
  category: options.category,
1692
- collection: options.collection,
1693
1692
  taxonomy: options.taxonomy,
1694
1693
  search: options.search,
1695
1694
  featured: options.featured,
@@ -1700,11 +1699,11 @@ function useProducts(options = {}) {
1700
1699
  in_stock: options.in_stock,
1701
1700
  min_price: options.min_price,
1702
1701
  max_price: options.max_price,
1703
- page: options.page
1702
+ page: options.page,
1703
+ properties: options.properties
1704
1704
  }),
1705
1705
  [
1706
1706
  options.category,
1707
- options.collection,
1708
1707
  options.taxonomy,
1709
1708
  options.search,
1710
1709
  options.featured,
@@ -1715,7 +1714,8 @@ function useProducts(options = {}) {
1715
1714
  options.in_stock,
1716
1715
  options.min_price,
1717
1716
  options.max_price,
1718
- options.page
1717
+ options.page,
1718
+ options.properties
1719
1719
  ]
1720
1720
  );
1721
1721
  const cacheKey = useMemo(
@@ -2416,6 +2416,7 @@ function useSearch(options = {}) {
2416
2416
  const [error, setError] = useState(null);
2417
2417
  const requestIdRef = useRef(0);
2418
2418
  const timerRef = useRef(null);
2419
+ const tagsSignature = options.tags?.join(",") ?? "";
2419
2420
  useEffect(() => {
2420
2421
  if (timerRef.current) {
2421
2422
  clearTimeout(timerRef.current);
@@ -2436,7 +2437,15 @@ function useSearch(options = {}) {
2436
2437
  try {
2437
2438
  const result = await client.catalogue.searchProducts(trimmedQuery, {
2438
2439
  limit,
2439
- category: options.category
2440
+ category: options.category,
2441
+ taxonomy: options.taxonomy,
2442
+ tags: options.tags,
2443
+ featured: options.featured,
2444
+ in_stock: options.in_stock,
2445
+ min_price: options.min_price,
2446
+ max_price: options.max_price,
2447
+ sort_by: options.sort_by,
2448
+ sort_order: options.sort_order
2440
2449
  });
2441
2450
  if (!result.ok) {
2442
2451
  throw result.error;
@@ -2462,7 +2471,7 @@ function useSearch(options = {}) {
2462
2471
  timerRef.current = null;
2463
2472
  }
2464
2473
  };
2465
- }, [client, debounceMs, limit, minLength, options.category, query]);
2474
+ }, [client, debounceMs, limit, minLength, options.category, options.taxonomy, tagsSignature, options.featured, options.in_stock, options.min_price, options.max_price, options.sort_by, options.sort_order, query]);
2466
2475
  const setQuery = useCallback((nextQuery) => {
2467
2476
  setQueryState(nextQuery);
2468
2477
  }, []);
@@ -7545,6 +7554,13 @@ function CataloguePage({
7545
7554
  categories: categoriesProp,
7546
7555
  showFilter = true,
7547
7556
  showSearch = true,
7557
+ showSort = false,
7558
+ showInStockFilter = false,
7559
+ showPriceRange = false,
7560
+ showTagsFilter = false,
7561
+ availableTags,
7562
+ defaultSort,
7563
+ pageSize,
7548
7564
  onProductClick,
7549
7565
  onCategoryClick,
7550
7566
  renderCard,
@@ -7555,30 +7571,36 @@ function CataloguePage({
7555
7571
  }) {
7556
7572
  const [selectedCategoryId, setSelectedCategoryId] = useState(null);
7557
7573
  const [searchQuery, setSearchQuery] = useState("");
7558
- const { products: fetchedProducts, isLoading: productsLoading } = useProducts({
7559
- enabled: productsProp === void 0
7574
+ const [sortBy, setSortBy] = useState(defaultSort?.by ?? "name");
7575
+ const [sortOrder, setSortOrder] = useState(defaultSort?.order ?? "asc");
7576
+ const [inStockOnly, setInStockOnly] = useState(false);
7577
+ const [selectedTags, setSelectedTags] = useState([]);
7578
+ const [minPrice, setMinPrice] = useState("");
7579
+ const [maxPrice, setMaxPrice] = useState("");
7580
+ const [page, setPage] = useState(1);
7581
+ const debouncedSearch = useDebounce(searchQuery, 300);
7582
+ const { products: fetchedProducts, isLoading: productsLoading, pagination } = useProducts({
7583
+ enabled: productsProp === void 0,
7584
+ category: selectedCategoryId ?? void 0,
7585
+ search: debouncedSearch.length >= 2 ? debouncedSearch : void 0,
7586
+ sort_by: sortBy,
7587
+ sort_order: sortOrder,
7588
+ in_stock: inStockOnly || void 0,
7589
+ tags: selectedTags.length > 0 ? selectedTags : void 0,
7590
+ min_price: minPrice ? parseFloat(minPrice) : void 0,
7591
+ max_price: maxPrice ? parseFloat(maxPrice) : void 0,
7592
+ page,
7593
+ limit: pageSize
7560
7594
  });
7561
7595
  const { categories: fetchedCategories } = useCategories({
7562
7596
  enabled: showFilter && categoriesProp === void 0
7563
7597
  });
7564
7598
  const allProducts = productsProp ?? fetchedProducts;
7565
7599
  const categories = categoriesProp ?? fetchedCategories;
7566
- const filteredProducts = useMemo(() => {
7567
- let result = allProducts;
7568
- if (selectedCategoryId) {
7569
- result = result.filter((p) => p.category_id === selectedCategoryId);
7570
- }
7571
- if (searchQuery.trim().length >= 2) {
7572
- const q = searchQuery.trim().toLowerCase();
7573
- result = result.filter(
7574
- (p) => p.name.toLowerCase().includes(q) || p.description?.toLowerCase().includes(q)
7575
- );
7576
- }
7577
- return result;
7578
- }, [allProducts, selectedCategoryId, searchQuery]);
7579
7600
  const handleCategorySelect = useCallback(
7580
7601
  (id) => {
7581
7602
  setSelectedCategoryId(id);
7603
+ setPage(1);
7582
7604
  if (id) {
7583
7605
  const cat = categories.find((c) => c.id === id);
7584
7606
  if (cat) onCategoryClick?.(cat);
@@ -7586,6 +7608,13 @@ function CataloguePage({
7586
7608
  },
7587
7609
  [categories, onCategoryClick]
7588
7610
  );
7611
+ const handleTagToggle = useCallback((tag) => {
7612
+ setSelectedTags(
7613
+ (prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]
7614
+ );
7615
+ setPage(1);
7616
+ }, []);
7617
+ const totalPages = pagination?.total_pages ?? 1;
7589
7618
  return /* @__PURE__ */ jsxs("div", { "data-cimplify-catalogue-page": true, className: cn(className, classNames?.root), children: [
7590
7619
  /* @__PURE__ */ jsxs("div", { "data-cimplify-catalogue-header": true, className: classNames?.header, children: [
7591
7620
  /* @__PURE__ */ jsx("h1", { "data-cimplify-catalogue-title": true, className: classNames?.title, children: title }),
@@ -7604,6 +7633,92 @@ function CataloguePage({
7604
7633
  onSelect: handleCategorySelect
7605
7634
  }
7606
7635
  ) }),
7636
+ (showSort || showInStockFilter || showPriceRange || showTagsFilter) && /* @__PURE__ */ jsxs("div", { "data-cimplify-catalogue-toolbar": true, className: cn("flex flex-wrap items-center gap-3", classNames?.toolbar), children: [
7637
+ showSort && /* @__PURE__ */ jsxs(
7638
+ "select",
7639
+ {
7640
+ "data-cimplify-sort": true,
7641
+ value: `${sortBy}:${sortOrder}`,
7642
+ onChange: (e) => {
7643
+ const [by, order] = e.target.value.split(":");
7644
+ setSortBy(by);
7645
+ setSortOrder(order);
7646
+ setPage(1);
7647
+ },
7648
+ className: "rounded-md border border-input bg-background px-3 py-1.5 text-sm",
7649
+ children: [
7650
+ /* @__PURE__ */ jsx("option", { value: "name:asc", children: "Name A\u2013Z" }),
7651
+ /* @__PURE__ */ jsx("option", { value: "name:desc", children: "Name Z\u2013A" }),
7652
+ /* @__PURE__ */ jsx("option", { value: "price:asc", children: "Price: Low to High" }),
7653
+ /* @__PURE__ */ jsx("option", { value: "price:desc", children: "Price: High to Low" }),
7654
+ /* @__PURE__ */ jsx("option", { value: "created_at:desc", children: "Newest" }),
7655
+ /* @__PURE__ */ jsx("option", { value: "created_at:asc", children: "Oldest" }),
7656
+ /* @__PURE__ */ jsx("option", { value: "updated_at:desc", children: "Recently Updated" })
7657
+ ]
7658
+ }
7659
+ ),
7660
+ showInStockFilter && /* @__PURE__ */ jsxs("label", { "data-cimplify-stock-filter": true, className: "flex items-center gap-1.5 text-sm cursor-pointer", children: [
7661
+ /* @__PURE__ */ jsx(
7662
+ "input",
7663
+ {
7664
+ type: "checkbox",
7665
+ checked: inStockOnly,
7666
+ onChange: (e) => {
7667
+ setInStockOnly(e.target.checked);
7668
+ setPage(1);
7669
+ },
7670
+ className: "accent-primary"
7671
+ }
7672
+ ),
7673
+ "In stock only"
7674
+ ] }),
7675
+ showPriceRange && /* @__PURE__ */ jsxs("div", { "data-cimplify-price-range": true, className: "flex items-center gap-1.5", children: [
7676
+ /* @__PURE__ */ jsx(
7677
+ "input",
7678
+ {
7679
+ type: "number",
7680
+ placeholder: "Min",
7681
+ value: minPrice,
7682
+ onChange: (e) => {
7683
+ setMinPrice(e.target.value);
7684
+ setPage(1);
7685
+ },
7686
+ className: "w-20 rounded-md border border-input bg-background px-2 py-1.5 text-sm",
7687
+ min: 0,
7688
+ step: "0.01"
7689
+ }
7690
+ ),
7691
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: "\u2013" }),
7692
+ /* @__PURE__ */ jsx(
7693
+ "input",
7694
+ {
7695
+ type: "number",
7696
+ placeholder: "Max",
7697
+ value: maxPrice,
7698
+ onChange: (e) => {
7699
+ setMaxPrice(e.target.value);
7700
+ setPage(1);
7701
+ },
7702
+ className: "w-20 rounded-md border border-input bg-background px-2 py-1.5 text-sm",
7703
+ min: 0,
7704
+ step: "0.01"
7705
+ }
7706
+ )
7707
+ ] }),
7708
+ showTagsFilter && availableTags && availableTags.length > 0 && /* @__PURE__ */ jsx("div", { "data-cimplify-tags-filter": true, className: "flex flex-wrap gap-1.5", children: availableTags.map((tag) => /* @__PURE__ */ jsx(
7709
+ "button",
7710
+ {
7711
+ type: "button",
7712
+ onClick: () => handleTagToggle(tag),
7713
+ className: cn(
7714
+ "rounded-full px-3 py-1 text-xs font-medium border transition-colors",
7715
+ selectedTags.includes(tag) ? "bg-primary text-primary-foreground border-primary" : "bg-background text-foreground border-input hover:bg-muted"
7716
+ ),
7717
+ children: tag
7718
+ },
7719
+ tag
7720
+ )) })
7721
+ ] }),
7607
7722
  /* @__PURE__ */ jsx("div", { "data-cimplify-catalogue-grid": true, className: classNames?.gridContainer, children: productsLoading && !productsProp ? /* @__PURE__ */ jsx(
7608
7723
  "div",
7609
7724
  {
@@ -7614,15 +7729,50 @@ function CataloguePage({
7614
7729
  ) : /* @__PURE__ */ jsx(
7615
7730
  ProductGrid,
7616
7731
  {
7617
- products: filteredProducts,
7732
+ products: allProducts,
7618
7733
  renderCard,
7619
7734
  renderImage,
7620
7735
  columns,
7621
7736
  emptyMessage: "No products found"
7622
7737
  }
7623
- ) })
7738
+ ) }),
7739
+ totalPages > 1 && /* @__PURE__ */ jsxs("div", { "data-cimplify-catalogue-pagination": true, className: cn("flex items-center justify-center gap-2 pt-6", classNames?.pagination), children: [
7740
+ /* @__PURE__ */ jsx(
7741
+ "button",
7742
+ {
7743
+ type: "button",
7744
+ disabled: page <= 1,
7745
+ onClick: () => setPage((p) => Math.max(1, p - 1)),
7746
+ className: "rounded-md border border-input bg-background px-3 py-1.5 text-sm disabled:opacity-40",
7747
+ children: "Previous"
7748
+ }
7749
+ ),
7750
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground tabular-nums", children: [
7751
+ page,
7752
+ " / ",
7753
+ totalPages
7754
+ ] }),
7755
+ /* @__PURE__ */ jsx(
7756
+ "button",
7757
+ {
7758
+ type: "button",
7759
+ disabled: page >= totalPages,
7760
+ onClick: () => setPage((p) => p + 1),
7761
+ className: "rounded-md border border-input bg-background px-3 py-1.5 text-sm disabled:opacity-40",
7762
+ children: "Next"
7763
+ }
7764
+ )
7765
+ ] })
7624
7766
  ] });
7625
7767
  }
7768
+ function useDebounce(value, delay) {
7769
+ const [debounced, setDebounced] = React3.useState(value);
7770
+ React3.useEffect(() => {
7771
+ const timer = setTimeout(() => setDebounced(value), delay);
7772
+ return () => clearTimeout(timer);
7773
+ }, [value, delay]);
7774
+ return debounced;
7775
+ }
7626
7776
  function ProductPage({
7627
7777
  productId,
7628
7778
  product: productProp,
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
1
  /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.z-50{z-index:50}.container{width:100%}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-\[5\/2\]{aspect-ratio:5/2}.h-fit{height:fit-content}.max-h-\[85vh\]{max-height:85vh}.w-2\/5{width:40%}.w-3\/5{width:60%}.w-4\/5{width:80%}.w-full{width:100%}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.\[appearance\:textfield\]{appearance:textfield}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--color-border,oklch(90% 0 0))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.rounded{border-radius:var(--radius,.5rem)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-none{--tw-border-style:none;border-style:none}.border-border{border-color:var(--color-border,oklch(90% 0 0))}.border-muted-foreground\/30{border-color:#6363634d}@supports (color:color-mix(in lab, red, red)){.border-muted-foreground\/30{border-color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 30%, transparent)}}.border-primary{border-color:var(--color-primary,oklch(50% .1 35))}.bg-background{background-color:var(--color-background,oklch(99% 0 0))}.bg-destructive\/10{background-color:#bb061e1a}@supports (color:color-mix(in lab, red, red)){.bg-destructive\/10{background-color:color-mix(in oklab, var(--color-destructive,oklch(50% .2 25)) 10%, transparent)}}.bg-muted{background-color:var(--color-muted,oklch(95% 0 0))}.bg-primary{background-color:var(--color-primary,oklch(50% .1 35))}.bg-primary\/10{background-color:#934c3a1a}@supports (color:color-mix(in lab, red, red)){.bg-primary\/10{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 10%, transparent)}}.bg-transparent{background-color:#0000}.text-center{text-align:center}.text-left{text-align:left}.font-\[inherit\]{font-family:inherit}.text-\[10px\]{font-size:10px}.text-\[inherit\]{color:inherit}.text-destructive{color:var(--color-destructive,oklch(50% .2 25))}.text-muted-foreground{color:var(--color-muted-foreground,oklch(50% 0 0))}.text-muted-foreground\/60{color:#63636399}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/60{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 60%, transparent)}}.text-primary{color:var(--color-primary,oklch(50% .1 35))}.text-primary-foreground{color:var(--color-primary-foreground,oklch(99% 0 0))}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.outline-none{--tw-outline-style:none;outline-style:none}.\[cimplify\:checkout\]{cimplify:checkout}@media (hover:hover){.hover\:border-primary\/50:hover{border-color:#934c3a80}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 50%, transparent)}}.hover\:bg-muted:hover{background-color:var(--color-muted,oklch(95% 0 0))}.hover\:bg-primary\/90:hover{background-color:#934c3ae6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 90%, transparent)}}.hover\:text-primary:hover{color:var(--color-primary,oklch(50% .1 35))}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}.\[\&\:\:-webkit-inner-spin-button\]\:appearance-none::-webkit-inner-spin-button{appearance:none}.\[\&\:\:-webkit-outer-spin-button\]\:appearance-none::-webkit-outer-spin-button{appearance:none}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.z-50{z-index:50}.container{width:100%}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-\[5\/2\]{aspect-ratio:5/2}.h-fit{height:fit-content}.max-h-\[85vh\]{max-height:85vh}.w-2\/5{width:40%}.w-3\/5{width:60%}.w-4\/5{width:80%}.w-full{width:100%}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.\[appearance\:textfield\]{appearance:textfield}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--color-border,oklch(90% 0 0))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.rounded{border-radius:var(--radius,.5rem)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-none{--tw-border-style:none;border-style:none}.border-border{border-color:var(--color-border,oklch(90% 0 0))}.border-input{border-color:var(--color-input,oklch(90% 0 0))}.border-muted-foreground\/30{border-color:#6363634d}@supports (color:color-mix(in lab, red, red)){.border-muted-foreground\/30{border-color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 30%, transparent)}}.border-primary{border-color:var(--color-primary,oklch(50% .1 35))}.bg-background{background-color:var(--color-background,oklch(99% 0 0))}.bg-destructive\/10{background-color:#bb061e1a}@supports (color:color-mix(in lab, red, red)){.bg-destructive\/10{background-color:color-mix(in oklab, var(--color-destructive,oklch(50% .2 25)) 10%, transparent)}}.bg-muted{background-color:var(--color-muted,oklch(95% 0 0))}.bg-primary{background-color:var(--color-primary,oklch(50% .1 35))}.bg-primary\/10{background-color:#934c3a1a}@supports (color:color-mix(in lab, red, red)){.bg-primary\/10{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 10%, transparent)}}.bg-transparent{background-color:#0000}.text-center{text-align:center}.text-left{text-align:left}.font-\[inherit\]{font-family:inherit}.text-\[10px\]{font-size:10px}.text-\[inherit\]{color:inherit}.text-destructive{color:var(--color-destructive,oklch(50% .2 25))}.text-foreground{color:var(--color-foreground,oklch(15% 0 0))}.text-muted-foreground{color:var(--color-muted-foreground,oklch(50% 0 0))}.text-muted-foreground\/60{color:#63636399}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/60{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 60%, transparent)}}.text-primary{color:var(--color-primary,oklch(50% .1 35))}.text-primary-foreground{color:var(--color-primary-foreground,oklch(99% 0 0))}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.no-underline{text-decoration-line:none}.accent-primary{accent-color:var(--color-primary,oklch(50% .1 35))}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.outline-none{--tw-outline-style:none;outline-style:none}.\[cimplify\:checkout\]{cimplify:checkout}@media (hover:hover){.hover\:border-primary\/50:hover{border-color:#934c3a80}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 50%, transparent)}}.hover\:bg-muted:hover{background-color:var(--color-muted,oklch(95% 0 0))}.hover\:bg-primary\/90:hover{background-color:#934c3ae6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 90%, transparent)}}.hover\:text-primary:hover{color:var(--color-primary,oklch(50% .1 35))}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.\[\&\:\:-webkit-inner-spin-button\]\:appearance-none::-webkit-inner-spin-button{appearance:none}.\[\&\:\:-webkit-outer-spin-button\]\:appearance-none::-webkit-outer-spin-button{appearance:none}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
package/dist/utils.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as Money, C as CurrencyCode, aB as PricePathTaxInfo, bm as PaymentErrorDetails, bk as PaymentResponse, bl as PaymentStatusResponse } from './payment-C6ULw3K-.mjs';
2
- import { P as ProductWithPrice } from './price-CbR6DCt0.mjs';
1
+ import { M as Money, C as CurrencyCode, aL as PricePathTaxInfo, bw as PaymentErrorDetails, bu as PaymentResponse, bv as PaymentStatusResponse } from './payment-CO2tRThu.mjs';
2
+ import { P as ProductWithPrice } from './price-Dg7MJEsL.mjs';
3
3
  import { CountryCode } from 'libphonenumber-js';
4
4
 
5
5
  declare const CURRENCY_SYMBOLS: Record<string, string>;
@@ -40,15 +40,15 @@ declare function normalizeStatusResponse(response: unknown): PaymentStatusRespon
40
40
  declare const MOBILE_MONEY_PROVIDERS: {
41
41
  readonly mtn: {
42
42
  readonly name: "MTN Mobile Money";
43
- readonly prefix: readonly ["024", "054", "055", "059"];
43
+ readonly prefix: readonly ["024", "025", "053", "054", "055", "059"];
44
44
  };
45
45
  readonly vodafone: {
46
- readonly name: "Vodafone Cash";
46
+ readonly name: "Telecel Cash";
47
47
  readonly prefix: readonly ["020", "050"];
48
48
  };
49
49
  readonly airtel: {
50
50
  readonly name: "AirtelTigo Money";
51
- readonly prefix: readonly ["027", "057", "026", "056"];
51
+ readonly prefix: readonly ["026", "027", "056", "057"];
52
52
  };
53
53
  };
54
54
  declare function detectMobileMoneyProvider(phoneNumber: string): "mtn" | "vodafone" | "airtel" | null;
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as Money, C as CurrencyCode, aB as PricePathTaxInfo, bm as PaymentErrorDetails, bk as PaymentResponse, bl as PaymentStatusResponse } from './payment-C6ULw3K-.js';
2
- import { P as ProductWithPrice } from './price-Bd6JBV8W.js';
1
+ import { M as Money, C as CurrencyCode, aL as PricePathTaxInfo, bw as PaymentErrorDetails, bu as PaymentResponse, bv as PaymentStatusResponse } from './payment-CO2tRThu.js';
2
+ import { P as ProductWithPrice } from './price-Bp6JlVzW.js';
3
3
  import { CountryCode } from 'libphonenumber-js';
4
4
 
5
5
  declare const CURRENCY_SYMBOLS: Record<string, string>;
@@ -40,15 +40,15 @@ declare function normalizeStatusResponse(response: unknown): PaymentStatusRespon
40
40
  declare const MOBILE_MONEY_PROVIDERS: {
41
41
  readonly mtn: {
42
42
  readonly name: "MTN Mobile Money";
43
- readonly prefix: readonly ["024", "054", "055", "059"];
43
+ readonly prefix: readonly ["024", "025", "053", "054", "055", "059"];
44
44
  };
45
45
  readonly vodafone: {
46
- readonly name: "Vodafone Cash";
46
+ readonly name: "Telecel Cash";
47
47
  readonly prefix: readonly ["020", "050"];
48
48
  };
49
49
  readonly airtel: {
50
50
  readonly name: "AirtelTigo Money";
51
- readonly prefix: readonly ["027", "057", "026", "056"];
51
+ readonly prefix: readonly ["026", "027", "056", "057"];
52
52
  };
53
53
  };
54
54
  declare function detectMobileMoneyProvider(phoneNumber: string): "mtn" | "vodafone" | "airtel" | null;
package/dist/utils.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkS6TU55KP_js = require('./chunk-S6TU55KP.js');
4
- var chunkJ65U5SED_js = require('./chunk-J65U5SED.js');
4
+ var chunkRYQ5IPWX_js = require('./chunk-RYQ5IPWX.js');
5
5
 
6
6
 
7
7
 
@@ -19,109 +19,109 @@ Object.defineProperty(exports, "resolvePhoneCountryCode", {
19
19
  });
20
20
  Object.defineProperty(exports, "CURRENCY_SYMBOLS", {
21
21
  enumerable: true,
22
- get: function () { return chunkJ65U5SED_js.CURRENCY_SYMBOLS; }
22
+ get: function () { return chunkRYQ5IPWX_js.CURRENCY_SYMBOLS; }
23
23
  });
24
24
  Object.defineProperty(exports, "MOBILE_MONEY_PROVIDERS", {
25
25
  enumerable: true,
26
- get: function () { return chunkJ65U5SED_js.MOBILE_MONEY_PROVIDERS; }
26
+ get: function () { return chunkRYQ5IPWX_js.MOBILE_MONEY_PROVIDERS; }
27
27
  });
28
28
  Object.defineProperty(exports, "categorizePaymentError", {
29
29
  enumerable: true,
30
- get: function () { return chunkJ65U5SED_js.categorizePaymentError; }
30
+ get: function () { return chunkRYQ5IPWX_js.categorizePaymentError; }
31
31
  });
32
32
  Object.defineProperty(exports, "detectMobileMoneyProvider", {
33
33
  enumerable: true,
34
- get: function () { return chunkJ65U5SED_js.detectMobileMoneyProvider; }
34
+ get: function () { return chunkRYQ5IPWX_js.detectMobileMoneyProvider; }
35
35
  });
36
36
  Object.defineProperty(exports, "formatMoney", {
37
37
  enumerable: true,
38
- get: function () { return chunkJ65U5SED_js.formatMoney; }
38
+ get: function () { return chunkRYQ5IPWX_js.formatMoney; }
39
39
  });
40
40
  Object.defineProperty(exports, "formatNumberCompact", {
41
41
  enumerable: true,
42
- get: function () { return chunkJ65U5SED_js.formatNumberCompact; }
42
+ get: function () { return chunkRYQ5IPWX_js.formatNumberCompact; }
43
43
  });
44
44
  Object.defineProperty(exports, "formatPrice", {
45
45
  enumerable: true,
46
- get: function () { return chunkJ65U5SED_js.formatPrice; }
46
+ get: function () { return chunkRYQ5IPWX_js.formatPrice; }
47
47
  });
48
48
  Object.defineProperty(exports, "formatPriceAdjustment", {
49
49
  enumerable: true,
50
- get: function () { return chunkJ65U5SED_js.formatPriceAdjustment; }
50
+ get: function () { return chunkRYQ5IPWX_js.formatPriceAdjustment; }
51
51
  });
52
52
  Object.defineProperty(exports, "formatPriceCompact", {
53
53
  enumerable: true,
54
- get: function () { return chunkJ65U5SED_js.formatPriceCompact; }
54
+ get: function () { return chunkRYQ5IPWX_js.formatPriceCompact; }
55
55
  });
56
56
  Object.defineProperty(exports, "formatPriceWithTax", {
57
57
  enumerable: true,
58
- get: function () { return chunkJ65U5SED_js.formatPriceWithTax; }
58
+ get: function () { return chunkRYQ5IPWX_js.formatPriceWithTax; }
59
59
  });
60
60
  Object.defineProperty(exports, "formatProductPrice", {
61
61
  enumerable: true,
62
- get: function () { return chunkJ65U5SED_js.formatProductPrice; }
62
+ get: function () { return chunkRYQ5IPWX_js.formatProductPrice; }
63
63
  });
64
64
  Object.defineProperty(exports, "getBasePrice", {
65
65
  enumerable: true,
66
- get: function () { return chunkJ65U5SED_js.getBasePrice; }
66
+ get: function () { return chunkRYQ5IPWX_js.getBasePrice; }
67
67
  });
68
68
  Object.defineProperty(exports, "getCurrencySymbol", {
69
69
  enumerable: true,
70
- get: function () { return chunkJ65U5SED_js.getCurrencySymbol; }
70
+ get: function () { return chunkRYQ5IPWX_js.getCurrencySymbol; }
71
71
  });
72
72
  Object.defineProperty(exports, "getDiscountPercentage", {
73
73
  enumerable: true,
74
- get: function () { return chunkJ65U5SED_js.getDiscountPercentage; }
74
+ get: function () { return chunkRYQ5IPWX_js.getDiscountPercentage; }
75
75
  });
76
76
  Object.defineProperty(exports, "getDisplayPrice", {
77
77
  enumerable: true,
78
- get: function () { return chunkJ65U5SED_js.getDisplayPrice; }
78
+ get: function () { return chunkRYQ5IPWX_js.getDisplayPrice; }
79
79
  });
80
80
  Object.defineProperty(exports, "getMarkupPercentage", {
81
81
  enumerable: true,
82
- get: function () { return chunkJ65U5SED_js.getMarkupPercentage; }
82
+ get: function () { return chunkRYQ5IPWX_js.getMarkupPercentage; }
83
83
  });
84
84
  Object.defineProperty(exports, "getProductCurrency", {
85
85
  enumerable: true,
86
- get: function () { return chunkJ65U5SED_js.getProductCurrency; }
86
+ get: function () { return chunkRYQ5IPWX_js.getProductCurrency; }
87
87
  });
88
88
  Object.defineProperty(exports, "getTaxAmount", {
89
89
  enumerable: true,
90
- get: function () { return chunkJ65U5SED_js.getTaxAmount; }
90
+ get: function () { return chunkRYQ5IPWX_js.getTaxAmount; }
91
91
  });
92
92
  Object.defineProperty(exports, "hasTaxInfo", {
93
93
  enumerable: true,
94
- get: function () { return chunkJ65U5SED_js.hasTaxInfo; }
94
+ get: function () { return chunkRYQ5IPWX_js.hasTaxInfo; }
95
95
  });
96
96
  Object.defineProperty(exports, "isOnSale", {
97
97
  enumerable: true,
98
- get: function () { return chunkJ65U5SED_js.isOnSale; }
98
+ get: function () { return chunkRYQ5IPWX_js.isOnSale; }
99
99
  });
100
100
  Object.defineProperty(exports, "isPaymentStatusFailure", {
101
101
  enumerable: true,
102
- get: function () { return chunkJ65U5SED_js.isPaymentStatusFailure; }
102
+ get: function () { return chunkRYQ5IPWX_js.isPaymentStatusFailure; }
103
103
  });
104
104
  Object.defineProperty(exports, "isPaymentStatusRequiresAction", {
105
105
  enumerable: true,
106
- get: function () { return chunkJ65U5SED_js.isPaymentStatusRequiresAction; }
106
+ get: function () { return chunkRYQ5IPWX_js.isPaymentStatusRequiresAction; }
107
107
  });
108
108
  Object.defineProperty(exports, "isPaymentStatusSuccess", {
109
109
  enumerable: true,
110
- get: function () { return chunkJ65U5SED_js.isPaymentStatusSuccess; }
110
+ get: function () { return chunkRYQ5IPWX_js.isPaymentStatusSuccess; }
111
111
  });
112
112
  Object.defineProperty(exports, "isTaxInclusive", {
113
113
  enumerable: true,
114
- get: function () { return chunkJ65U5SED_js.isTaxInclusive; }
114
+ get: function () { return chunkRYQ5IPWX_js.isTaxInclusive; }
115
115
  });
116
116
  Object.defineProperty(exports, "normalizePaymentResponse", {
117
117
  enumerable: true,
118
- get: function () { return chunkJ65U5SED_js.normalizePaymentResponse; }
118
+ get: function () { return chunkRYQ5IPWX_js.normalizePaymentResponse; }
119
119
  });
120
120
  Object.defineProperty(exports, "normalizeStatusResponse", {
121
121
  enumerable: true,
122
- get: function () { return chunkJ65U5SED_js.normalizeStatusResponse; }
122
+ get: function () { return chunkRYQ5IPWX_js.normalizeStatusResponse; }
123
123
  });
124
124
  Object.defineProperty(exports, "parsePrice", {
125
125
  enumerable: true,
126
- get: function () { return chunkJ65U5SED_js.parsePrice; }
126
+ get: function () { return chunkRYQ5IPWX_js.parsePrice; }
127
127
  });
package/dist/utils.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  export { isValidPhoneForContext, normalizePhoneToE164, resolvePhoneCountryCode } from './chunk-WDIMISRC.mjs';
2
- export { CURRENCY_SYMBOLS, MOBILE_MONEY_PROVIDERS, categorizePaymentError, detectMobileMoneyProvider, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatPriceWithTax, formatProductPrice, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getProductCurrency, getTaxAmount, hasTaxInfo, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isTaxInclusive, normalizePaymentResponse, normalizeStatusResponse, parsePrice } from './chunk-OVM5YWM5.mjs';
2
+ export { CURRENCY_SYMBOLS, MOBILE_MONEY_PROVIDERS, categorizePaymentError, detectMobileMoneyProvider, formatMoney, formatNumberCompact, formatPrice, formatPriceAdjustment, formatPriceCompact, formatPriceWithTax, formatProductPrice, getBasePrice, getCurrencySymbol, getDiscountPercentage, getDisplayPrice, getMarkupPercentage, getProductCurrency, getTaxAmount, hasTaxInfo, isOnSale, isPaymentStatusFailure, isPaymentStatusRequiresAction, isPaymentStatusSuccess, isTaxInclusive, normalizePaymentResponse, normalizeStatusResponse, parsePrice } from './chunk-FTL2SXCG.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/sdk",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "Cimplify Commerce SDK for storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -12,7 +12,7 @@
12
12
  "files": [
13
13
  {
14
14
  "path": "catalogue-page.tsx",
15
- "content": "\"use client\";\n\nimport React, { useState, useMemo, useCallback } from \"react\";\nimport type { Product, Category } from \"@cimplify/sdk\";\nimport { useProducts, useCategories } from \"@cimplify/sdk/react\";\nimport { ProductGrid } from \"./product-grid\";\nimport { CategoryFilter } from \"@cimplify/sdk/react\";\nimport { SearchInput } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CataloguePageClassNames {\n root?: string;\n header?: string;\n title?: string;\n searchContainer?: string;\n filterContainer?: string;\n gridContainer?: string;\n empty?: string;\n loading?: string;\n}\n\nexport interface CataloguePageProps {\n /** Page title. */\n title?: string;\n /** Pre-fetched products for SSR. When provided, skips client-side fetch. */\n products?: Product[];\n /** Pre-fetched categories for SSR. */\n categories?: Category[];\n /** Show category filter. Default: true. */\n showFilter?: boolean;\n /** Show search input. Default: true. */\n showSearch?: boolean;\n /** Called when a product is selected (e.g. navigate to product page). */\n onProductClick?: (product: Product) => void;\n /** Called when a category is selected. */\n onCategoryClick?: (category: Category) => void;\n /** Custom card renderer passed to ProductGrid. */\n renderCard?: (product: Product) => React.ReactNode;\n /** Custom image renderer passed to ProductGrid. */\n renderImage?: (props: { src: string; alt: string; className?: string }) => React.ReactNode;\n /** Grid column config. */\n columns?: { sm?: number; md?: number; lg?: number; xl?: number };\n className?: string;\n classNames?: CataloguePageClassNames;\n}\n\n/**\n * CataloguePage — browse all products with category filtering and search.\n *\n * SSR-friendly: pass `products` and `categories` props for server rendering.\n * Omit them for fully client-side data fetching.\n */\nexport function CataloguePage({\n title = \"Products\",\n products: productsProp,\n categories: categoriesProp,\n showFilter = true,\n showSearch = true,\n onProductClick,\n onCategoryClick,\n renderCard,\n renderImage,\n columns,\n className,\n classNames,\n}: CataloguePageProps): React.ReactElement {\n const [selectedCategoryId, setSelectedCategoryId] = useState<string | null>(null);\n const [searchQuery, setSearchQuery] = useState(\"\");\n\n const { products: fetchedProducts, isLoading: productsLoading } = useProducts({\n enabled: productsProp === undefined,\n });\n const { categories: fetchedCategories } = useCategories({\n enabled: showFilter && categoriesProp === undefined,\n });\n\n const allProducts = productsProp ?? fetchedProducts;\n const categories = categoriesProp ?? fetchedCategories;\n\n const filteredProducts = useMemo(() => {\n let result = allProducts;\n if (selectedCategoryId) {\n result = result.filter((p) => p.category_id === selectedCategoryId);\n }\n if (searchQuery.trim().length >= 2) {\n const q = searchQuery.trim().toLowerCase();\n result = result.filter(\n (p) =>\n p.name.toLowerCase().includes(q) ||\n p.description?.toLowerCase().includes(q),\n );\n }\n return result;\n }, [allProducts, selectedCategoryId, searchQuery]);\n\n const handleCategorySelect = useCallback(\n (id: string | null) => {\n setSelectedCategoryId(id);\n if (id) {\n const cat = categories.find((c: Category) => c.id === id);\n if (cat) onCategoryClick?.(cat);\n }\n },\n [categories, onCategoryClick],\n );\n\n return (\n <div data-cimplify-catalogue-page className={cn(className, classNames?.root)}>\n {/* Header */}\n <div data-cimplify-catalogue-header className={classNames?.header}>\n <h1 data-cimplify-catalogue-title className={classNames?.title}>\n {title}\n </h1>\n\n {showSearch && (\n <div data-cimplify-catalogue-search className={classNames?.searchContainer}>\n <SearchInput\n onResultClick={onProductClick}\n searchOptions={{ category: selectedCategoryId ?? undefined }}\n />\n </div>\n )}\n </div>\n\n {/* Category filter */}\n {showFilter && categories.length > 0 && (\n <div data-cimplify-catalogue-filter className={classNames?.filterContainer}>\n <CategoryFilter\n selectedId={selectedCategoryId}\n onSelect={handleCategorySelect}\n />\n </div>\n )}\n\n {/* Product grid */}\n <div data-cimplify-catalogue-grid className={classNames?.gridContainer}>\n {productsLoading && !productsProp ? (\n <div\n data-cimplify-catalogue-loading\n aria-busy=\"true\"\n className={classNames?.loading}\n />\n ) : (\n <ProductGrid\n products={filteredProducts}\n renderCard={renderCard}\n renderImage={renderImage}\n columns={columns}\n emptyMessage=\"No products found\"\n />\n )}\n </div>\n </div>\n );\n}\n"
15
+ "content": "\"use client\";\n\nimport React, { useState, useMemo, useCallback } from \"react\";\nimport type { Product, Category } from \"@cimplify/sdk\";\nimport { useProducts, useCategories } from \"@cimplify/sdk/react\";\nimport { ProductGrid } from \"./product-grid\";\nimport { CategoryFilter } from \"@cimplify/sdk/react\";\nimport { SearchInput } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CataloguePageClassNames {\n root?: string;\n header?: string;\n title?: string;\n searchContainer?: string;\n filterContainer?: string;\n toolbar?: string;\n gridContainer?: string;\n empty?: string;\n loading?: string;\n pagination?: string;\n}\n\nexport interface CataloguePageProps {\n /** Page title. */\n title?: string;\n /** Pre-fetched products for SSR. When provided, skips client-side fetch. */\n products?: Product[];\n /** Pre-fetched categories for SSR. */\n categories?: Category[];\n /** Show category filter. Default: true. */\n showFilter?: boolean;\n /** Show search input. Default: true. */\n showSearch?: boolean;\n /** Show sort controls. Default: false. */\n showSort?: boolean;\n /** Show in-stock toggle. Default: false. */\n showInStockFilter?: boolean;\n /** Show price range filter. Default: false. */\n showPriceRange?: boolean;\n /** Show tags filter. Default: false. */\n showTagsFilter?: boolean;\n /** Available tags for filtering. When provided, renders tag chips. */\n availableTags?: string[];\n /** Default sort. */\n defaultSort?: { by: \"name\" | \"price\" | \"created_at\" | \"updated_at\"; order: \"asc\" | \"desc\" };\n /** Products per page. */\n pageSize?: number;\n /** Called when a product is selected (e.g. navigate to product page). */\n onProductClick?: (product: Product) => void;\n /** Called when a category is selected. */\n onCategoryClick?: (category: Category) => void;\n /** Custom card renderer passed to ProductGrid. */\n renderCard?: (product: Product) => React.ReactNode;\n /** Custom image renderer passed to ProductGrid. */\n renderImage?: (props: { src: string; alt: string; className?: string }) => React.ReactNode;\n /** Grid column config. */\n columns?: { sm?: number; md?: number; lg?: number; xl?: number };\n className?: string;\n classNames?: CataloguePageClassNames;\n}\n\n/**\n * CataloguePage — browse all products with server-side filtering, sorting, and pagination.\n *\n * SSR-friendly: pass `products` and `categories` props for server rendering.\n * Omit them for fully client-side data fetching.\n */\nexport function CataloguePage({\n title = \"Products\",\n products: productsProp,\n categories: categoriesProp,\n showFilter = true,\n showSearch = true,\n showSort = false,\n showInStockFilter = false,\n showPriceRange = false,\n showTagsFilter = false,\n availableTags,\n defaultSort,\n pageSize,\n onProductClick,\n onCategoryClick,\n renderCard,\n renderImage,\n columns,\n className,\n classNames,\n}: CataloguePageProps): React.ReactElement {\n const [selectedCategoryId, setSelectedCategoryId] = useState<string | null>(null);\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [sortBy, setSortBy] = useState<\"name\" | \"price\" | \"created_at\" | \"updated_at\">(defaultSort?.by ?? \"name\");\n const [sortOrder, setSortOrder] = useState<\"asc\" | \"desc\">(defaultSort?.order ?? \"asc\");\n const [inStockOnly, setInStockOnly] = useState(false);\n const [selectedTags, setSelectedTags] = useState<string[]>([]);\n const [minPrice, setMinPrice] = useState<string>(\"\");\n const [maxPrice, setMaxPrice] = useState<string>(\"\");\n const [page, setPage] = useState(1);\n\n const debouncedSearch = useDebounce(searchQuery, 300);\n\n const { products: fetchedProducts, isLoading: productsLoading, pagination } = useProducts({\n enabled: productsProp === undefined,\n category: selectedCategoryId ?? undefined,\n search: debouncedSearch.length >= 2 ? debouncedSearch : undefined,\n sort_by: sortBy,\n sort_order: sortOrder,\n in_stock: inStockOnly || undefined,\n tags: selectedTags.length > 0 ? selectedTags : undefined,\n min_price: minPrice ? parseFloat(minPrice) : undefined,\n max_price: maxPrice ? parseFloat(maxPrice) : undefined,\n page,\n limit: pageSize,\n });\n const { categories: fetchedCategories } = useCategories({\n enabled: showFilter && categoriesProp === undefined,\n });\n\n const allProducts = productsProp ?? fetchedProducts;\n const categories = categoriesProp ?? fetchedCategories;\n\n const handleCategorySelect = useCallback(\n (id: string | null) => {\n setSelectedCategoryId(id);\n setPage(1);\n if (id) {\n const cat = categories.find((c: Category) => c.id === id);\n if (cat) onCategoryClick?.(cat);\n }\n },\n [categories, onCategoryClick],\n );\n\n const handleTagToggle = useCallback((tag: string) => {\n setSelectedTags((prev) =>\n prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag],\n );\n setPage(1);\n }, []);\n\n const totalPages = pagination?.total_pages ?? 1;\n\n return (\n <div data-cimplify-catalogue-page className={cn(className, classNames?.root)}>\n {/* Header */}\n <div data-cimplify-catalogue-header className={classNames?.header}>\n <h1 data-cimplify-catalogue-title className={classNames?.title}>\n {title}\n </h1>\n\n {showSearch && (\n <div data-cimplify-catalogue-search className={classNames?.searchContainer}>\n <SearchInput\n onResultClick={onProductClick}\n searchOptions={{ category: selectedCategoryId ?? undefined }}\n />\n </div>\n )}\n </div>\n\n {/* Category filter */}\n {showFilter && categories.length > 0 && (\n <div data-cimplify-catalogue-filter className={classNames?.filterContainer}>\n <CategoryFilter\n selectedId={selectedCategoryId}\n onSelect={handleCategorySelect}\n />\n </div>\n )}\n\n {/* Toolbar: sort, in-stock, price range, tags */}\n {(showSort || showInStockFilter || showPriceRange || showTagsFilter) && (\n <div data-cimplify-catalogue-toolbar className={cn(\"flex flex-wrap items-center gap-3\", classNames?.toolbar)}>\n {showSort && (\n <select\n data-cimplify-sort\n value={`${sortBy}:${sortOrder}`}\n onChange={(e) => {\n const [by, order] = e.target.value.split(\":\") as [typeof sortBy, typeof sortOrder];\n setSortBy(by);\n setSortOrder(order);\n setPage(1);\n }}\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n >\n <option value=\"name:asc\">Name A–Z</option>\n <option value=\"name:desc\">Name Z–A</option>\n <option value=\"price:asc\">Price: Low to High</option>\n <option value=\"price:desc\">Price: High to Low</option>\n <option value=\"created_at:desc\">Newest</option>\n <option value=\"created_at:asc\">Oldest</option>\n <option value=\"updated_at:desc\">Recently Updated</option>\n </select>\n )}\n\n {showInStockFilter && (\n <label data-cimplify-stock-filter className=\"flex items-center gap-1.5 text-sm cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={inStockOnly}\n onChange={(e) => { setInStockOnly(e.target.checked); setPage(1); }}\n className=\"accent-primary\"\n />\n In stock only\n </label>\n )}\n\n {showPriceRange && (\n <div data-cimplify-price-range className=\"flex items-center gap-1.5\">\n <input\n type=\"number\"\n placeholder=\"Min\"\n value={minPrice}\n onChange={(e) => { setMinPrice(e.target.value); setPage(1); }}\n className=\"w-20 rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n min={0}\n step=\"0.01\"\n />\n <span className=\"text-muted-foreground text-sm\">–</span>\n <input\n type=\"number\"\n placeholder=\"Max\"\n value={maxPrice}\n onChange={(e) => { setMaxPrice(e.target.value); setPage(1); }}\n className=\"w-20 rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n min={0}\n step=\"0.01\"\n />\n </div>\n )}\n\n {showTagsFilter && availableTags && availableTags.length > 0 && (\n <div data-cimplify-tags-filter className=\"flex flex-wrap gap-1.5\">\n {availableTags.map((tag) => (\n <button\n key={tag}\n type=\"button\"\n onClick={() => handleTagToggle(tag)}\n className={cn(\n \"rounded-full px-3 py-1 text-xs font-medium border transition-colors\",\n selectedTags.includes(tag)\n ? \"bg-primary text-primary-foreground border-primary\"\n : \"bg-background text-foreground border-input hover:bg-muted\",\n )}\n >\n {tag}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n\n {/* Product grid */}\n <div data-cimplify-catalogue-grid className={classNames?.gridContainer}>\n {productsLoading && !productsProp ? (\n <div\n data-cimplify-catalogue-loading\n aria-busy=\"true\"\n className={classNames?.loading}\n />\n ) : (\n <ProductGrid\n products={allProducts}\n renderCard={renderCard}\n renderImage={renderImage}\n columns={columns}\n emptyMessage=\"No products found\"\n />\n )}\n </div>\n\n {/* Pagination */}\n {totalPages > 1 && (\n <div data-cimplify-catalogue-pagination className={cn(\"flex items-center justify-center gap-2 pt-6\", classNames?.pagination)}>\n <button\n type=\"button\"\n disabled={page <= 1}\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm disabled:opacity-40\"\n >\n Previous\n </button>\n <span className=\"text-sm text-muted-foreground tabular-nums\">\n {page} / {totalPages}\n </span>\n <button\n type=\"button\"\n disabled={page >= totalPages}\n onClick={() => setPage((p) => p + 1)}\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm disabled:opacity-40\"\n >\n Next\n </button>\n </div>\n )}\n </div>\n );\n}\n\nfunction useDebounce(value: string, delay: number): string {\n const [debounced, setDebounced] = React.useState(value);\n React.useEffect(() => {\n const timer = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n return debounced;\n}\n"
16
16
  }
17
17
  ]
18
18
  }