funuicss 3.8.7 → 3.8.8

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.
@@ -12,8 +12,6 @@ interface CartModalProps {
12
12
  cartBadgeColor?: string;
13
13
  checkoutText?: string;
14
14
  checkoutIcon?: React.ReactNode;
15
- small?: boolean;
16
- big?: boolean;
17
15
  persistCart?: boolean;
18
16
  }
19
17
  declare const CartModal: React.FC<CartModalProps>;
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // components/products/CartModal.tsx
3
2
  'use client';
4
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
5
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -15,71 +14,92 @@ var Div_1 = __importDefault(require("../div/Div"));
15
14
  var Button_1 = __importDefault(require("../button/Button"));
16
15
  var Circle_1 = __importDefault(require("../specials/Circle"));
17
16
  var Flex_1 = __importDefault(require("../flex/Flex"));
18
- var generateCartItemId = function (product, options) {
17
+ var ImageScaler_1 = __importDefault(require("../components/ImageScaler"));
18
+ var tb_1 = require("react-icons/tb");
19
+ var Empty_1 = __importDefault(require("../empty/Empty"));
20
+ var generateCartItemId = function (item) {
21
+ var _a;
19
22
  var parts = [
20
- product.id,
21
- (options === null || options === void 0 ? void 0 : options.color) || '',
22
- (options === null || options === void 0 ? void 0 : options.size) || ''
23
+ item.product.id,
24
+ ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.id) || '',
25
+ item.selectedColor || '',
26
+ item.selectedSize || ''
23
27
  ].filter(Boolean);
24
28
  return parts.join('_');
25
29
  };
26
30
  var CartModal = function (_a) {
27
- var cart = _a.cart, isOpen = _a.isOpen, setIsOpen = _a.setIsOpen, _b = _a.currency, currency = _b === void 0 ? '$' : _b, updateQuantity = _a.updateQuantity, removeFromCart = _a.removeFromCart, clearCart = _a.clearCart, handleCheckout = _a.handleCheckout, _c = _a.cartBadgeColor, cartBadgeColor = _c === void 0 ? 'error' : _c, _d = _a.checkoutText, checkoutText = _d === void 0 ? 'Checkout' : _d, checkoutIcon = _a.checkoutIcon, _e = _a.small, small = _e === void 0 ? false : _e, _f = _a.big, big = _f === void 0 ? false : _f, _g = _a.persistCart, persistCart = _g === void 0 ? true : _g;
31
+ var cart = _a.cart, isOpen = _a.isOpen, setIsOpen = _a.setIsOpen, _b = _a.currency, currency = _b === void 0 ? '$' : _b, updateQuantity = _a.updateQuantity, removeFromCart = _a.removeFromCart, clearCart = _a.clearCart, handleCheckout = _a.handleCheckout, _c = _a.cartBadgeColor, cartBadgeColor = _c === void 0 ? 'error' : _c, _d = _a.checkoutText, checkoutText = _d === void 0 ? 'Checkout' : _d, checkoutIcon = _a.checkoutIcon, _e = _a.persistCart, persistCart = _e === void 0 ? true : _e;
28
32
  var totalItems = cart.reduce(function (sum, item) { return sum + item.quantity; }, 0);
29
- var subtotal = cart.reduce(function (sum, item) { return sum + (item.product.price * item.quantity); }, 0);
33
+ var subtotal = cart.reduce(function (sum, item) {
34
+ var _a;
35
+ var price = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
36
+ return sum + (price * item.quantity);
37
+ }, 0);
38
+ // Calculate total savings from discounts
39
+ var totalSavings = cart.reduce(function (sum, item) {
40
+ var _a;
41
+ var originalPrice = item.originalPrice || item.product.comparePrice || item.product.price;
42
+ var currentPrice = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
43
+ var savings = originalPrice !== currentPrice ? (originalPrice - currentPrice) * item.quantity : 0;
44
+ return sum + savings;
45
+ }, 0);
30
46
  var handleRemoveItem = function (item) {
31
- var cartItemId = generateCartItemId(item.product, {
32
- color: item.selectedColor,
33
- size: item.selectedSize
34
- });
47
+ var cartItemId = generateCartItemId(item);
35
48
  removeFromCart(cartItemId);
36
49
  };
37
50
  var handleUpdateQuantity = function (item, newQuantity) {
38
- var cartItemId = generateCartItemId(item.product, {
39
- color: item.selectedColor,
40
- size: item.selectedSize
41
- });
51
+ var cartItemId = generateCartItemId(item);
42
52
  updateQuantity(cartItemId, newQuantity);
43
53
  };
44
- return (react_1.default.createElement(Modal_1.default, { animation: "SlideLeft", position: "right", duration: 0.3, open: isOpen, setOpen: setIsOpen, funcss: "funui_products_cart_modal width-400 sm:width-500", title: react_1.default.createElement(Text_1.default, { text: "Your Cart", size: "h3" }), body: react_1.default.createElement(Div_1.default, { funcss: "funui_products_cart_body max-height-70vh overflow-y-auto" }, cart.length === 0 ? (react_1.default.createElement(Div_1.default, { funcss: "flex-center padding-40" },
45
- react_1.default.createElement(Text_1.default, { text: "Your cart is empty", color: "text-light", size: "large" }))) : (react_1.default.createElement(react_1.default.Fragment, null,
54
+ return (react_1.default.createElement(Modal_1.default, { animation: "SlideDown", duration: 0.3, open: isOpen, setOpen: setIsOpen, maxWidth: '600px', title: react_1.default.createElement(Text_1.default, { text: "Your Cart", size: "h3" }), body: react_1.default.createElement(Div_1.default, { funcss: "pt pb" }, cart.length === 0 ? (react_1.default.createElement(Div_1.default, null,
55
+ react_1.default.createElement(Empty_1.default, { title: "Your cart is empty", showCta: true, description: "Add items to your cart to checkout", ctaText: "Continue Shopping", ctaOnClick: function () { return setIsOpen(false); }, ctaIcon: react_1.default.createElement(pi_1.PiBag, null) }))) : (react_1.default.createElement(react_1.default.Fragment, null,
46
56
  cart.map(function (item, index) {
47
- var _a;
48
- var cartItemId = generateCartItemId(item.product, {
49
- color: item.selectedColor,
50
- size: item.selectedSize
51
- });
52
- return (react_1.default.createElement(RowFlex_1.default, { key: "".concat(cartItemId, "-").concat(index), funcss: "lighter p round-edge section", alignItems: "center", justify: 'space-between', gap: 1 },
53
- react_1.default.createElement(Div_1.default, { funcss: " width-80 height-80" }, ((_a = item.product.images) === null || _a === void 0 ? void 0 : _a[0]) ? (react_1.default.createElement("img", { src: item.product.images[0], alt: item.product.name, loading: "lazy", className: "fit round-edge" })) : (react_1.default.createElement(Div_1.default, { funcss: "w-80 h-80 flex central" },
57
+ var _a, _b, _c;
58
+ var cartItemId = generateCartItemId(item);
59
+ var currentPrice = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
60
+ var originalPrice = item.originalPrice || item.product.comparePrice || currentPrice;
61
+ var hasDiscount = originalPrice > currentPrice;
62
+ var isFirst = index === 0;
63
+ var isLast = index === cart.length - 1;
64
+ return (react_1.default.createElement(Flex_1.default, { key: "".concat(cartItemId, "-").concat(index), funcss: "bt pt pb ".concat(isLast ? "bb" : ""), alignItems: "center", justify: 'space-between', gap: 1, width: '100%' },
65
+ react_1.default.createElement(Div_1.default, { funcss: " width-80 height-80" }, ((_b = item.product.images) === null || _b === void 0 ? void 0 : _b[0]) ? (react_1.default.createElement(ImageScaler_1.default, { src: item.product.images[0], size: '80px' })) : (react_1.default.createElement(Div_1.default, { funcss: "w-80 h-80 flex central" },
54
66
  react_1.default.createElement(Text_1.default, { text: "No Image", color: "text-light", size: "sm" })))),
55
- react_1.default.createElement(Div_1.default, { funcss: "funui_products_cart_details flex-1 min-width-0" },
56
- react_1.default.createElement(Text_1.default, { text: item.product.name, block: true }),
57
- (item.selectedColor || item.selectedSize) && (react_1.default.createElement(Text_1.default, { text: "".concat(item.selectedColor ? "Color: ".concat(item.selectedColor) : '', " ").concat(item.selectedSize ? "Size: ".concat(item.selectedSize) : ''), size: 'sm' })),
58
- react_1.default.createElement(Text_1.default, { text: "".concat(item.product.currency || currency).concat((item.product.price * item.quantity).toFixed(2)), block: true, size: 'lg' })),
67
+ react_1.default.createElement(Div_1.default, { funcss: 'w-200' },
68
+ react_1.default.createElement(Text_1.default, { text: item.product.name, block: true, weight: 600 }),
69
+ ((_c = item.variant) === null || _c === void 0 ? void 0 : _c.name) && (react_1.default.createElement(Text_1.default, { text: item.variant.name, size: 'sm', color: "text-light" })),
70
+ (item.selectedColor || item.selectedSize) && (react_1.default.createElement(Text_1.default, { text: "".concat(item.selectedColor ? "Color: ".concat(item.selectedColor) : '', " ").concat(item.selectedSize ? "Size: ".concat(item.selectedSize) : ''), size: 'sm', color: "text-light" })),
71
+ react_1.default.createElement(Flex_1.default, { gap: 0.2 },
72
+ hasDiscount && (react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(originalPrice.toFixed(2)), size: 'sm', color: "text-light", style: { textDecoration: 'line-through' } })),
73
+ react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(currentPrice.toFixed(2)).concat(hasDiscount ? " (Save ".concat(currency).concat((originalPrice - currentPrice).toFixed(2), ")") : ''), color: hasDiscount ? 'success' : 'text', size: 'xs' }))),
59
74
  react_1.default.createElement("div", { className: "col" },
60
75
  react_1.default.createElement(Flex_1.default, { width: '100%', gap: 0.5, alignItems: 'center' },
61
- react_1.default.createElement(Circle_1.default, { size: 2.5, bg: 'bg', body: react_1.default.createElement(pi_1.PiMinus, null), onClick: function () { return handleUpdateQuantity(item, item.quantity - 1); } }),
62
- react_1.default.createElement("div", { className: "w-90" },
76
+ react_1.default.createElement(Circle_1.default, { size: 1.5, bg: 'lighter', body: react_1.default.createElement(pi_1.PiMinus, { size: 12 }), onClick: function () { return handleUpdateQuantity(item, item.quantity - 1); }, "aria-label": "Decrease quantity of ".concat(item.product.name) }),
77
+ react_1.default.createElement("div", { className: "w-70" },
63
78
  react_1.default.createElement(Input_1.default, { type: "number", value: item.quantity, onChange: function (e) {
64
79
  var value = parseInt(e.target.value);
65
80
  if (!isNaN(value)) {
66
81
  handleUpdateQuantity(item, Math.max(1, value));
67
82
  }
68
- }, fullWidth: true, bordered: true })),
69
- react_1.default.createElement(Circle_1.default, { bg: 'bg', size: 2.5, body: react_1.default.createElement(pi_1.PiPlus, null), onClick: function () { return handleUpdateQuantity(item, item.quantity + 1); } }))),
70
- react_1.default.createElement(Circle_1.default, { size: 2.5, body: react_1.default.createElement(pi_1.PiTrash, null), onClick: function () { return handleRemoveItem(item); }, bg: 'error' })));
83
+ }, fullWidth: true, bordered: true, "aria-label": "Quantity for ".concat(item.product.name) })),
84
+ react_1.default.createElement(Circle_1.default, { bg: 'lighter', size: 1.5, body: react_1.default.createElement(pi_1.PiPlus, { size: 12 }), onClick: function () { return handleUpdateQuantity(item, item.quantity + 1); }, "aria-label": "Increase quantity of ".concat(item.product.name) }),
85
+ react_1.default.createElement("div", null),
86
+ react_1.default.createElement(Circle_1.default, { size: 1.5, body: react_1.default.createElement(pi_1.PiTrash, { size: 12 }), onClick: function () { return handleRemoveItem(item); }, bg: 'error', "aria-label": "Remove ".concat(item.product.name, " from cart") })))));
71
87
  }),
72
- react_1.default.createElement(Div_1.default, { funcss: "funui_products_cart_summary padding-20 space-y-3" },
73
- react_1.default.createElement(RowFlex_1.default, { justify: "space-between" },
74
- react_1.default.createElement(Text_1.default, { text: "Subtotal", color: "text-light" }),
75
- react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(subtotal.toFixed(2)) })),
88
+ react_1.default.createElement("div", { className: "section" }, persistCart && (react_1.default.createElement(Button_1.default, { text: "Clear Cart", onClick: clearCart, startIcon: react_1.default.createElement(tb_1.TbShoppingBagX, null), bg: "error-light", color: "error", funcss: "full-width" }))),
89
+ react_1.default.createElement(Div_1.default, { funcss: 'mt' },
76
90
  react_1.default.createElement(RowFlex_1.default, { justify: "space-between" },
77
91
  react_1.default.createElement(Text_1.default, { text: "Items", color: "text-light" }),
78
- react_1.default.createElement(Text_1.default, { text: totalItems.toString() })))))), footer: cart.length > 0 ? (react_1.default.createElement(Div_1.default, { funcss: "funui_products_cart_footer padding-20 border-top border-light" },
92
+ react_1.default.createElement(Text_1.default, { text: totalItems.toString() })),
93
+ totalSavings > 0 && (react_1.default.createElement(RowFlex_1.default, { justify: "space-between" },
94
+ react_1.default.createElement(Text_1.default, { text: "Total Savings", color: "text-light" }),
95
+ react_1.default.createElement(Text_1.default, { text: "-".concat(currency).concat(totalSavings.toFixed(2)), color: "success" }))),
96
+ react_1.default.createElement(RowFlex_1.default, { justify: "space-between" },
97
+ react_1.default.createElement(Text_1.default, { text: "Subtotal", color: "text-light" }),
98
+ react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(subtotal.toFixed(2)) })))))), footer: cart.length > 0 ? (react_1.default.createElement(Div_1.default, { funcss: "pt pb" },
79
99
  react_1.default.createElement(RowFlex_1.default, { justify: "space-between", alignItems: "center" },
80
100
  react_1.default.createElement(Div_1.default, null,
81
101
  react_1.default.createElement(Text_1.default, { text: "Total", color: "text-light", size: "sm", block: true }),
82
102
  react_1.default.createElement(Text_1.default, { text: "".concat(currency).concat(subtotal.toFixed(2)), size: "h4" })),
83
- react_1.default.createElement(Button_1.default, { text: checkoutText, bg: "primary", raised: true, onClick: handleCheckout, funcss: "padding-x-30", startIcon: checkoutIcon, small: small, big: big })))) : react_1.default.createElement(react_1.default.Fragment, null) }));
103
+ react_1.default.createElement(Button_1.default, { text: checkoutText, bg: "primary", raised: true, onClick: handleCheckout, funcss: "padding-x-30", startIcon: checkoutIcon })))) : react_1.default.createElement(react_1.default.Fragment, null) }));
84
104
  };
85
105
  exports.default = CartModal;
@@ -15,42 +15,29 @@ var ProductCard = function (_a) {
15
15
  var discountPercent = hasDiscount
16
16
  ? Math.round(((product.comparePrice - product.price) / product.comparePrice) * 100)
17
17
  : 0;
18
- var stockAvailable = product.stock === undefined || product.stock > 0;
19
18
  var handleClick = function () {
20
19
  onClick === null || onClick === void 0 ? void 0 : onClick(product);
21
20
  };
22
- var handleAddToCart = function (e) {
23
- e.stopPropagation();
24
- onAddToCart === null || onAddToCart === void 0 ? void 0 : onAddToCart(product);
25
- };
26
21
  var getDisplayPrice = function () {
27
22
  var price = product.price || 0;
28
23
  var productCurrency = product.currency || currency;
29
24
  return "".concat(productCurrency).concat(price.toFixed(2));
30
25
  };
31
26
  return (react_1.default.createElement(Div_1.default, { funcss: "funui_store_product-card ".concat(className, " ").concat(funcss), onClick: handleClick, customStyle: { cursor: 'pointer' } },
32
- react_1.default.createElement(Div_1.default, { funcss: "funui_store_image-container" },
33
- ((_b = product.images) === null || _b === void 0 ? void 0 : _b[0]) ? (react_1.default.createElement("img", { src: product.images[0], alt: product.name, loading: "lazy", className: "funui_store_product-image" })) : (react_1.default.createElement(Div_1.default, { funcss: "funui_store_no-image" },
34
- react_1.default.createElement(Text_1.default, { text: "No Image", color: "text-muted", size: "sm" }))),
35
- showBadges && (react_1.default.createElement(Div_1.default, { funcss: "funui_store_product-badges" }, product.isSale && (react_1.default.createElement("span", { className: "funui_store_badge sale" }, "Sale"))))),
36
- react_1.default.createElement(Div_1.default, { funcss: "funui_store_product-info" },
37
- react_1.default.createElement(Flex_1.default, { fit: true, justify: 'space-between', alignItems: 'center', gap: 1 },
38
- product.category && (react_1.default.createElement("span", { className: "funui_store_product-category" }, product.category)),
39
- product.isNew && (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'success', weight: 600 }, "New"))),
40
- react_1.default.createElement(Flex_1.default, { gap: 0.5, direction: 'column', width: '100%' },
41
- react_1.default.createElement(Text_1.default, { block: true, size: 'lg', truncate: 2 }, product.name),
42
- react_1.default.createElement(Flex_1.default, { width: '100%', gap: 1, justify: 'space-between' },
27
+ react_1.default.createElement(Div_1.default, { funcss: "funui_store_image-container round-edge" }, ((_b = product.images) === null || _b === void 0 ? void 0 : _b[0]) ? (react_1.default.createElement("img", { src: product.images[0], alt: product.name, loading: "lazy", className: "funui_store_product-image" })) : (react_1.default.createElement(Div_1.default, { funcss: "funui_store_no-image" },
28
+ react_1.default.createElement(Text_1.default, { text: "No Image", color: "text-muted", size: "sm" })))),
29
+ react_1.default.createElement(Div_1.default, { funcss: "funui_store_product-info " },
30
+ product.category && (react_1.default.createElement(Text_1.default, { size: 'xs', opacity: 4, uppercase: true }, product.category)),
31
+ react_1.default.createElement(Flex_1.default, { width: '100%', gap: 0.5, direction: 'column' },
32
+ react_1.default.createElement(Text_1.default, { block: true, weight: 500, truncate: 2 }, product.name),
33
+ react_1.default.createElement(Flex_1.default, { width: '100%', gap: 1, justify: 'space-between', alignItems: 'center' },
43
34
  react_1.default.createElement(Flex_1.default, { gap: 0.5, alignItems: 'center' },
44
- react_1.default.createElement("span", { className: "text-lg block" }, getDisplayPrice()),
35
+ react_1.default.createElement("span", { className: "block" }, getDisplayPrice()),
45
36
  hasDiscount && (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'info', weight: 600 },
46
37
  discountPercent,
47
38
  "% Off"))),
48
39
  hasDiscount && (react_1.default.createElement(Text_1.default, { size: "sm", opacity: 4, textDecoration: 'line-through' },
49
40
  product.currency || currency,
50
- product.comparePrice.toFixed(2)))),
51
- !stockAvailable ? (react_1.default.createElement("span", { className: "funui_store_stock-info out text-xs" }, "Out of Stock")) : product.stock !== undefined && product.stock > 0 && product.stock < 10 ? (react_1.default.createElement("span", { className: "funui_store_stock-info low text-xs" },
52
- "Only ",
53
- product.stock,
54
- " left")) : stockAvailable && (react_1.default.createElement("span", { className: "funui_store_stock-info in text-xs" }, "In Stock"))))));
41
+ product.comparePrice.toFixed(2))))))));
55
42
  };
56
43
  exports.default = ProductCard;
@@ -74,6 +74,9 @@ var ProductDetail = function (_a) {
74
74
  var discountPercent = hasDiscount
75
75
  ? Math.round(((product.comparePrice - product.price) / product.comparePrice) * 100)
76
76
  : 0;
77
+ // Stock information logic
78
+ var stockAvailable = product.stock === undefined || product.stock > 0;
79
+ var lowStock = product.stock !== undefined && product.stock > 0 && product.stock < 10;
77
80
  var getDisplayPrice = function () {
78
81
  var price = product.price || 0;
79
82
  var productCurrency = product.currency || currency;
@@ -143,96 +146,124 @@ var ProductDetail = function (_a) {
143
146
  return (react_1.default.createElement(Modal_1.default, { animation: "SlideDown", open: open, setOpen: setOpen, maxWidth: '1000px', title: react_1.default.createElement(react_1.default.Fragment, null), body: react_1.default.createElement(Flex_1.default, { width: '100%', gap: 2 },
144
147
  react_1.default.createElement("div", { className: "w-400" }, product.images && product.images.length > 0 && (react_1.default.createElement(Div_1.default, { funcss: "margin-bottom-20" },
145
148
  react_1.default.createElement(Div_1.default, { funcss: "funui_products_main_image_container mb-3" },
146
- react_1.default.createElement(ImageScaler_1.default, { src: product.images[selectedImageIndex], size: "100%" })),
149
+ react_1.default.createElement(ImageScaler_1.default, { src: product.images[selectedImageIndex], size: "400px", funcss: 'round-edge' })),
147
150
  product.images.length > 1 && (react_1.default.createElement(Carousel_1.default, { gap: 1 }, product.images.map(function (image, index) { return (react_1.default.createElement(Div_1.default, { key: index, funcss: "funui_products_thumbnail rounde-edge ".concat(selectedImageIndex === index ? 'funui_products_thumbnail-active' : ''), onClick: function () { return setSelectedImageIndex(index); } },
148
- react_1.default.createElement(ImageScaler_1.default, { src: image, size: "100px" }))); })))))),
151
+ react_1.default.createElement(ImageScaler_1.default, { src: image, size: "100px", funcss: 'round-edge' }))); })))))),
149
152
  react_1.default.createElement("div", { className: "col" },
150
- react_1.default.createElement(Flex_1.default, { direction: 'column', gap: 2, alignItems: 'flex-start', justify: 'flex-start' },
151
- react_1.default.createElement("div", null,
152
- react_1.default.createElement(Text_1.default, { text: product.name, size: "2xl", block: true }),
153
+ react_1.default.createElement(Flex_1.default, { direction: 'column', gap: 2, alignItems: 'flex-start', justify: 'flex-start', width: '100%' },
154
+ react_1.default.createElement("div", { className: "w-full" },
153
155
  react_1.default.createElement(Flex_1.default, { justify: "space-between", alignItems: "center", width: '100%' },
154
- react_1.default.createElement(Text_1.default, { text: getDisplayPrice(), size: "xl" }),
155
- (hasDiscount || product.comparePrice) && (react_1.default.createElement(Text_1.default, { text: "".concat(product.currency || currency).concat(product.comparePrice.toFixed(2)), textDecoration: 'line-through' })))),
156
- ((product.colors && product.colors.length > 0) || (product.sizes && product.sizes.length > 0)) &&
157
- react_1.default.createElement(Flex_1.default, { width: '100%', gap: 1 },
158
- product.colors && product.colors.length > 0 && (react_1.default.createElement("div", { className: "col" },
159
- react_1.default.createElement(Select_1.default, { fullWidth: true, options: __spreadArray([
160
- { text: 'Select Color', value: '' }
161
- ], product.colors.map(function (color) { return ({
162
- text: color.name,
163
- value: color.name,
164
- prefix: react_1.default.createElement("div", { className: 'circle', style: { width: "20px", height: '20px', backgroundColor: color.code } })
165
- }); }), true), value: selectedColor, onChange: function (e) { return setSelectedColor(e); }, bordered: true }))),
166
- product.sizes && product.sizes.length > 0 && (react_1.default.createElement("div", { className: "col" },
167
- react_1.default.createElement(Select_1.default, { fullWidth: true, options: __spreadArray([
168
- { text: 'Select Size', value: '' }
169
- ], product.sizes.map(function (size) { return ({ text: size, value: size }); }), true), value: selectedSize, onChange: function (e) { return setSelectedSize(e); }, bordered: true })))),
170
- processedDescription && (react_1.default.createElement("div", null,
171
- react_1.default.createElement(Text_1.default, { text: "Description", size: "lg", funcss: "margin-bottom-1" }),
156
+ react_1.default.createElement(Flex_1.default, { gap: 1, alignItems: "center" },
157
+ product.category && (react_1.default.createElement(Text_1.default, { size: 'xs', opacity: 4, uppercase: true, weight: 500, color: "text-muted" }, product.category)),
158
+ product.isNew && (react_1.default.createElement(Div_1.default, { funcss: "badge-new" },
159
+ react_1.default.createElement(Text_1.default, { size: 'xs', color: 'white', weight: 600 }, "NEW"))),
160
+ product.isSale && (react_1.default.createElement(Div_1.default, { funcss: "badge-sale" },
161
+ react_1.default.createElement(Text_1.default, { size: 'xs', color: 'white', weight: 600 }, "SALE")))),
162
+ !stockAvailable ? (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'error', weight: 600 }, "Out of Stock")) : lowStock ? (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'warning', weight: 600 },
163
+ "Only ",
164
+ product.stock,
165
+ " left")) : (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'success', weight: 600 }, "In Stock"))),
166
+ react_1.default.createElement(Text_1.default, { text: product.name, size: "2xl", block: true, weight: 600, funcss: "mb-3" }),
167
+ react_1.default.createElement(Flex_1.default, { justify: "space-between", alignItems: "center", width: '100%', funcss: "mb-3" },
168
+ react_1.default.createElement(Flex_1.default, { gap: 1, alignItems: "baseline" },
169
+ react_1.default.createElement(Text_1.default, { text: getDisplayPrice(), size: "xl", weight: 700, color: "primary" }),
170
+ hasDiscount && (react_1.default.createElement(Div_1.default, { funcss: "discount-percent" },
171
+ react_1.default.createElement(Text_1.default, { size: 'sm', color: 'white', weight: 600 },
172
+ "-",
173
+ discountPercent,
174
+ "%")))),
175
+ (hasDiscount || product.comparePrice) && (react_1.default.createElement(Text_1.default, { text: "".concat(product.currency || currency).concat(product.comparePrice.toFixed(2)), textDecoration: 'line-through', size: "sm", color: "text-muted" })))),
176
+ product.stock !== undefined && (react_1.default.createElement(Div_1.default, null,
177
+ react_1.default.createElement(Text_1.default, { size: 'xs', opacity: 4 },
178
+ product.stock,
179
+ " units available"))),
180
+ ((product.colors && product.colors.length > 0) || (product.sizes && product.sizes.length > 0)) && (react_1.default.createElement(Flex_1.default, { width: '100%', gap: 1 },
181
+ product.colors && product.colors.length > 0 && (react_1.default.createElement("div", { className: "col" },
182
+ react_1.default.createElement(Text_1.default, { size: "sm", weight: 500, funcss: "mb-1" }, "Color"),
183
+ react_1.default.createElement(Select_1.default, { fullWidth: true, options: __spreadArray([
184
+ { text: 'Select Color', value: '' }
185
+ ], product.colors.map(function (color) { return ({
186
+ text: color.name,
187
+ value: color.name,
188
+ prefix: (react_1.default.createElement(Div_1.default, { funcss: "color-preview", customStyle: {
189
+ width: "20px",
190
+ height: "20px",
191
+ borderRadius: "50%",
192
+ backgroundColor: color.code,
193
+ border: selectedColor === color.name ? '2px solid var(--primary)' : '1px solid var(--border)'
194
+ } }))
195
+ }); }), true), value: selectedColor, onChange: function (e) { return setSelectedColor(e); }, bordered: true }))),
196
+ product.sizes && product.sizes.length > 0 && (react_1.default.createElement("div", { className: "col" },
197
+ react_1.default.createElement(Text_1.default, { size: "sm", weight: 500, funcss: "mb-1" }, "Size"),
198
+ react_1.default.createElement(Select_1.default, { fullWidth: true, options: __spreadArray([
199
+ { text: 'Select Size', value: '' }
200
+ ], product.sizes.map(function (size) { return ({ text: size, value: size }); }), true), value: selectedSize, onChange: function (e) { return setSelectedSize(e); }, bordered: true }))))),
201
+ processedDescription && (react_1.default.createElement(Div_1.default, null,
202
+ react_1.default.createElement(Text_1.default, { text: "Description", size: "lg", weight: 600, funcss: "margin-bottom-1" }),
172
203
  react_1.default.createElement("div", { className: "article text-sm ".concat(hasHTML ? '' : 'whitespace-pre-wrap'), dangerouslySetInnerHTML: { __html: displayDescription } }),
173
- shouldTruncate && (react_1.default.createElement(Button_1.default, { text: showFullDescription ? 'Show Less' : 'Read More', onClick: function () { return setShowFullDescription(!showFullDescription); }, small: true, bg: 'lighter', startIcon: showFullDescription ? react_1.default.createElement(pi_1.PiCaretUp, null) : react_1.default.createElement(pi_1.PiCaretDown, null) })))),
174
- react_1.default.createElement("div", null,
204
+ shouldTruncate && (react_1.default.createElement(Button_1.default, { text: showFullDescription ? 'Show Less' : 'Read More', onClick: function () { return setShowFullDescription(!showFullDescription); }, small: true, bg: 'lighter', startIcon: showFullDescription ? react_1.default.createElement(pi_1.PiCaretUp, null) : react_1.default.createElement(pi_1.PiCaretDown, null), funcss: "mt-2" })))),
205
+ react_1.default.createElement(Div_1.default, { funcss: "product-details-grid" },
175
206
  react_1.default.createElement(Flex_1.default, { gap: 3, width: '100%' },
176
- product.warranty && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
177
- react_1.default.createElement("div", null,
178
- react_1.default.createElement(pi_1.PiShieldCheck, { className: 'text-primary' })),
179
- react_1.default.createElement("div", null,
180
- react_1.default.createElement(Text_1.default, { text: "Warranty", size: "xs", opacity: 4, block: true }),
181
- react_1.default.createElement(Text_1.default, { text: product.warranty, size: "sm", block: true, lineHeight: '1' })))),
182
- product.manufacturer && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
183
- react_1.default.createElement("div", null,
207
+ product.brand && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
208
+ react_1.default.createElement(Div_1.default, null,
209
+ react_1.default.createElement(si_1.SiBlackmagicdesign, { className: 'text-primary' })),
210
+ react_1.default.createElement(Div_1.default, null,
211
+ react_1.default.createElement(Text_1.default, { text: "Brand", size: "xs", opacity: 4, block: true }),
212
+ react_1.default.createElement(Text_1.default, { text: product.brand, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
213
+ product.sku && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
214
+ react_1.default.createElement(Div_1.default, null,
215
+ react_1.default.createElement(pi_1.PiTag, { className: 'text-primary' })),
216
+ react_1.default.createElement(Div_1.default, null,
217
+ react_1.default.createElement(Text_1.default, { text: "SKU", size: "xs", opacity: 4, block: true }),
218
+ react_1.default.createElement(Text_1.default, { text: product.sku, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
219
+ product.rating && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
220
+ react_1.default.createElement(Div_1.default, null,
221
+ react_1.default.createElement(tfi_1.TfiComments, { className: 'text-primary' })),
222
+ react_1.default.createElement(Div_1.default, null,
223
+ react_1.default.createElement(Text_1.default, { text: "Rating", size: "xs", opacity: 4, block: true }),
224
+ react_1.default.createElement(Text_1.default, { text: product.rating.toString(), size: "sm", block: true, lineHeight: '1', weight: 500 })))),
225
+ product.weight && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
226
+ react_1.default.createElement(Div_1.default, null,
227
+ react_1.default.createElement(pi_1.PiScales, { className: 'text-primary' })),
228
+ react_1.default.createElement(Div_1.default, null,
229
+ react_1.default.createElement(Text_1.default, { text: "Weight", size: "xs", opacity: 4, block: true }),
230
+ react_1.default.createElement(Text_1.default, { text: "".concat(product.weight, " ").concat(product.weightUnit || ''), size: "sm", block: true, lineHeight: '1', weight: 500 })))),
231
+ product.manufacturer && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
232
+ react_1.default.createElement(Div_1.default, null,
184
233
  react_1.default.createElement(pi_1.PiUser, { className: 'text-primary' })),
185
- react_1.default.createElement("div", null,
234
+ react_1.default.createElement(Div_1.default, null,
186
235
  react_1.default.createElement(Text_1.default, { text: "Manufacturer", size: "xs", opacity: 4, block: true }),
187
- react_1.default.createElement(Text_1.default, { text: product.manufacturer, size: "sm", block: true, lineHeight: '1' })))),
188
- product.countryOfOrigin && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
189
- react_1.default.createElement("div", null,
236
+ react_1.default.createElement(Text_1.default, { text: product.manufacturer, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
237
+ product.countryOfOrigin && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
238
+ react_1.default.createElement(Div_1.default, null,
190
239
  react_1.default.createElement(pi_1.PiGlobe, { className: 'text-primary' })),
191
- react_1.default.createElement("div", null,
240
+ react_1.default.createElement(Div_1.default, null,
192
241
  react_1.default.createElement(Text_1.default, { text: "Country of Origin", size: "xs", opacity: 4, block: true }),
193
- react_1.default.createElement(Text_1.default, { text: product.countryOfOrigin, size: "sm", block: true, lineHeight: '1' })))),
194
- product.isFeatured && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
195
- react_1.default.createElement("div", null,
196
- react_1.default.createElement(pi_1.PiUsers, { className: 'text-primary' })),
197
- react_1.default.createElement("div", null,
198
- react_1.default.createElement(Text_1.default, { text: "Featured", size: "xs", opacity: 4, block: true }),
199
- react_1.default.createElement(Text_1.default, { text: "Yes", size: "sm", block: true, lineHeight: '1' })))),
200
- product.rating && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
201
- react_1.default.createElement("div", null,
202
- react_1.default.createElement(tfi_1.TfiComments, { className: 'text-primary' })),
203
- react_1.default.createElement("div", null,
204
- react_1.default.createElement(Text_1.default, { text: "Rating", size: "xs", opacity: 4, block: true }),
205
- react_1.default.createElement(Text_1.default, { text: product.rating, size: "sm", block: true, lineHeight: '1' })))),
206
- product.brand && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
207
- react_1.default.createElement("div", null,
208
- react_1.default.createElement(si_1.SiBlackmagicdesign, { className: 'text-primary' })),
209
- react_1.default.createElement("div", null,
210
- react_1.default.createElement(Text_1.default, { text: "Brand", size: "xs", opacity: 4, block: true }),
211
- react_1.default.createElement(Text_1.default, { text: product.brand, size: "sm", block: true, lineHeight: '1' })))),
212
- product.category && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
213
- react_1.default.createElement("div", null,
242
+ react_1.default.createElement(Text_1.default, { text: product.countryOfOrigin, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
243
+ product.warranty && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
244
+ react_1.default.createElement(Div_1.default, null,
245
+ react_1.default.createElement(pi_1.PiShieldCheck, { className: 'text-primary' })),
246
+ react_1.default.createElement(Div_1.default, null,
247
+ react_1.default.createElement(Text_1.default, { text: "Warranty", size: "xs", opacity: 4, block: true }),
248
+ react_1.default.createElement(Text_1.default, { text: product.warranty, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
249
+ product.category && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
250
+ react_1.default.createElement(Div_1.default, null,
214
251
  react_1.default.createElement(io5_1.IoLayersOutline, { className: 'text-primary' })),
215
- react_1.default.createElement("div", null,
252
+ react_1.default.createElement(Div_1.default, null,
216
253
  react_1.default.createElement(Text_1.default, { text: "Category", size: "xs", opacity: 4, block: true }),
217
- react_1.default.createElement(Text_1.default, { text: product.category, size: "sm", block: true, lineHeight: '1' })))),
218
- product.isNew && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
219
- react_1.default.createElement("div", null,
220
- react_1.default.createElement(pi_1.PiChecks, { className: 'text-primary' })),
221
- react_1.default.createElement("div", null,
222
- react_1.default.createElement(Text_1.default, { text: "New", size: "xs", opacity: 4, block: true }),
223
- react_1.default.createElement(Text_1.default, { text: "Yes", size: "sm", block: true, lineHeight: '1' })))),
224
- product.isSale && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
225
- react_1.default.createElement("div", null,
226
- react_1.default.createElement(pi_1.PiChecks, { className: 'text-primary' })),
227
- react_1.default.createElement("div", null,
228
- react_1.default.createElement(Text_1.default, { text: "On Sale", size: "xs", opacity: 4, block: true }),
229
- react_1.default.createElement(Text_1.default, { text: "Yes", size: "sm", block: true, lineHeight: '1' })))),
230
- product.weight && (react_1.default.createElement(Flex_1.default, { gap: 0.3 },
231
- react_1.default.createElement("div", null,
232
- react_1.default.createElement(pi_1.PiScales, { className: 'text-primary' })),
233
- react_1.default.createElement("div", null,
234
- react_1.default.createElement(Text_1.default, { text: "Weight", size: "xs", opacity: 4, block: true }),
235
- react_1.default.createElement(Text_1.default, { text: product.weight + " " + product.weightUnit, size: "sm", block: true, lineHeight: '1' }))))))))), footer: react_1.default.createElement(RowFlex_1.default, { gap: 1, justify: 'flex-end', funcss: 'pt' },
254
+ react_1.default.createElement(Text_1.default, { text: product.category, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
255
+ product.brand && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
256
+ react_1.default.createElement(Div_1.default, null,
257
+ react_1.default.createElement(pi_1.PiStorefront, { className: 'text-primary' })),
258
+ react_1.default.createElement(Div_1.default, null,
259
+ react_1.default.createElement(Text_1.default, { text: "Store", size: "xs", opacity: 4, block: true }),
260
+ react_1.default.createElement(Text_1.default, { text: product.brand, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
261
+ product.tags && product.tags.length > 0 && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
262
+ react_1.default.createElement(Div_1.default, null,
263
+ react_1.default.createElement(pi_1.PiTag, { className: 'text-primary' })),
264
+ react_1.default.createElement(Div_1.default, null,
265
+ react_1.default.createElement(Text_1.default, { text: "Tags", size: "xs", opacity: 4, block: true }),
266
+ react_1.default.createElement(Text_1.default, { text: product.tags.join(', '), size: "sm", block: true, lineHeight: '1', truncate: 1 }))))))))), footer: react_1.default.createElement(RowFlex_1.default, { gap: 1, justify: 'flex-end', funcss: 'pt' },
236
267
  react_1.default.createElement(RowFlex_1.default, { gap: 0.5, alignItems: "center" },
237
268
  react_1.default.createElement(Circle_1.default, { body: react_1.default.createElement(pi_1.PiMinus, null), onClick: function () { return setQuantity(Math.max(1, quantity - 1)); }, funcss: quantity <= 1 ? "disabled" : "" }),
238
269
  react_1.default.createElement("div", { className: "w-90" },
@@ -244,6 +275,6 @@ var ProductDetail = function (_a) {
244
275
  }, funcss: "text-center", bordered: true })),
245
276
  react_1.default.createElement(Circle_1.default, { onClick: function () { return setQuantity(quantity + 1); }, bg: 'lighter' },
246
277
  react_1.default.createElement(pi_1.PiPlus, null))),
247
- react_1.default.createElement(Button_1.default, { text: "Add to Cart - ".concat(currency).concat(totalPrice), bg: "primary", raised: true, onClick: handleAddToCart })) }));
278
+ react_1.default.createElement(Button_1.default, { text: "Add to Cart - ".concat(currency).concat(totalPrice), bg: "primary", raised: true, onClick: handleAddToCart, disabled: !stockAvailable })) }));
248
279
  };
249
280
  exports.default = ProductDetail;
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const ProductLoader: () => React.JSX.Element;
3
+ export default ProductLoader;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ 'use client';
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ var react_1 = __importDefault(require("react"));
8
+ var Div_1 = __importDefault(require("../div/Div"));
9
+ var View_1 = __importDefault(require("../view/View"));
10
+ var ProductLoader = function () {
11
+ return (react_1.default.createElement(Div_1.default, { funcss: "funui_store_product-card " },
12
+ react_1.default.createElement(Div_1.default, { funcss: "funui_store_image-container round-edge skeleton" }),
13
+ react_1.default.createElement(View_1.default, { funcss: 'skeleton round-edge', style: {
14
+ height: "1rem",
15
+ marginTop: "0.5rem"
16
+ } }),
17
+ react_1.default.createElement(View_1.default, { funcss: 'skeleton round-edge', style: {
18
+ height: "3rem",
19
+ marginTop: "0.5rem"
20
+ } })));
21
+ };
22
+ exports.default = ProductLoader;
@@ -59,16 +59,43 @@ export type CartStorage = {
59
59
  items: CartItem[];
60
60
  updatedAt: number;
61
61
  };
62
+ export type UserInfoField = {
63
+ infoName: string;
64
+ type: 'text' | 'tel' | 'email' | 'number' | 'textarea';
65
+ required: boolean;
66
+ label?: string;
67
+ placeholder?: string;
68
+ };
69
+ export type OtherInfo = UserInfoField[] | string;
70
+ export type CheckoutData = {
71
+ cartItems: CartItem[];
72
+ totalAmount: number;
73
+ userInfo: Record<string, string>;
74
+ };
62
75
  type ProductsPageProps = {
63
76
  products?: Product[] | string;
64
77
  bucket?: string;
65
78
  bucketPage?: number;
66
79
  bucketSize?: number;
67
80
  title?: string;
68
- showHeader?: boolean;
81
+ heroAlign?: 'left' | 'center' | 'right' | 'justify';
82
+ heroTitle?: string;
83
+ heroDescription?: string;
84
+ heroBackgroundImage?: string;
85
+ heroHeight?: string;
86
+ overlayColor?: string;
87
+ overlayOpacity?: number;
88
+ overlayGradient?: boolean;
89
+ gradientDirection?: 'to-bottom' | 'to-top' | 'to-left' | 'to-right' | 'to-bottom-right' | 'to-bottom-left' | 'to-top-right' | 'to-top-left';
90
+ invertGradient?: boolean;
69
91
  showSearch?: boolean;
70
92
  showFilters?: boolean;
71
- showCart?: boolean;
93
+ showHero?: boolean;
94
+ titleSize?: string;
95
+ titleColor?: string;
96
+ descriptionSize?: string;
97
+ descriptionColor?: string;
98
+ descriptionOpacity?: number;
72
99
  cartIcon?: string | React.ReactNode;
73
100
  cartBadgeColor?: string;
74
101
  cartBadgeText?: string;
@@ -77,21 +104,19 @@ type ProductsPageProps = {
77
104
  currency?: string;
78
105
  persistCart?: boolean;
79
106
  storageKey?: string;
107
+ whatsappOrderNumber?: string;
108
+ otherInfo?: OtherInfo;
80
109
  onAddToCart?: (item: CartItem) => void;
81
110
  onRemoveFromCart?: (itemId: string) => void;
82
111
  onUpdateQuantity?: (itemId: string, quantity: number) => void;
83
- onCheckout?: (cartItems: CartItem[], totalAmount: number) => void;
112
+ onCheckout?: (checkoutData: CheckoutData) => void;
84
113
  onProductClick?: (product: Product) => void;
85
114
  className?: string;
86
115
  gridClassName?: string;
87
116
  children?: React.ReactNode;
88
117
  id?: string;
89
118
  funcss?: string;
90
- bg?: string;
91
- color?: string;
92
119
  fullWidth?: boolean;
93
- small?: boolean;
94
- big?: boolean;
95
120
  itemsPerPage?: number;
96
121
  variant?: string;
97
122
  };