funuicss
Version:
React and Next.js component UI Library for creating Easy and good looking websites with fewer lines of code. Elevate your web development experience with our cutting-edge React/Next.js component UI Library. Craft stunning websites effortlessly, boasting b
802 lines • 52.3 kB
JavaScript
'use client';
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importStar(require("react"));
var pi_1 = require("react-icons/pi");
var Button_1 = __importDefault(require("../button/Button"));
var RowFlex_1 = __importDefault(require("../specials/RowFlex"));
var Text_1 = __importDefault(require("../text/Text"));
var Input_1 = __importDefault(require("../input/Input"));
var Div_1 = __importDefault(require("../div/Div"));
var sl_1 = require("react-icons/sl");
var Flex_1 = __importDefault(require("../flex/Flex"));
var ProductCard_1 = __importDefault(require("./ProductCard"));
var CartModal_1 = __importDefault(require("./CartModal"));
var ProductDetail_1 = __importDefault(require("./ProductDetail"));
var theme_1 = require("../theme/theme");
var componentUtils_1 = require("../../utils/componentUtils");
var Empty_1 = __importDefault(require("../empty/Empty"));
var Modal_1 = __importDefault(require("../modal/Modal"));
var Accordion_1 = __importDefault(require("../accordion/Accordion"));
var View_1 = __importDefault(require("../view/View"));
var getCssVariable_1 = require("../../utils/getCssVariable");
var ProductLoader_1 = __importDefault(require("./ProductLoader"));
var Store = function (localProps) {
// Use component configuration with variant
var mergeWithLocal = (0, componentUtils_1.useComponentConfiguration)('Store', localProps.variant).mergeWithLocal;
var final = mergeWithLocal(localProps).props;
// Destructure props from final merged configuration
var _a = final.products, products = _a === void 0 ? [] : _a, bucket = final.bucket, _b = final.bucketPage, bucketPage = _b === void 0 ? 1 : _b, _c = final.bucketSize, bucketSize = _c === void 0 ? 50 : _c, _d = final.title, title = _d === void 0 ? 'Products' : _d, _e = final.heroTitle, heroTitle = _e === void 0 ? 'Our Products' : _e, _f = final.heroDescription, heroDescription = _f === void 0 ? 'Discover our amazing collection of products' : _f, _g = final.heroBackgroundImage, heroBackgroundImage = _g === void 0 ? '' : _g, _h = final.overlayColor, overlayColor = _h === void 0 ? 'primary' : _h, _j = final.overlayOpacity, overlayOpacity = _j === void 0 ? 0.6 : _j, _k = final.overlayGradient, overlayGradient = _k === void 0 ? false : _k, _l = final.gradientDirection, gradientDirection = _l === void 0 ? 'to-bottom' : _l, _m = final.invertGradient, invertGradient = _m === void 0 ? false : _m, _o = final.showHeader, showHeader = _o === void 0 ? true : _o, _p = final.showSearch, showSearch = _p === void 0 ? true : _p, _q = final.showFilters, showFilters = _q === void 0 ? true : _q, _r = final.showCart, showCart = _r === void 0 ? true : _r, _s = final.showHero, showHero = _s === void 0 ? true : _s, _t = final.titleSize, titleSize = _t === void 0 ? 'big' : _t, _u = final.titleColor, titleColor = _u === void 0 ? 'white' : _u, _v = final.descriptionSize, descriptionSize = _v === void 0 ? 'lg' : _v, _w = final.descriptionColor, descriptionColor = _w === void 0 ? 'white' : _w, _x = final.descriptionOpacity, descriptionOpacity = _x === void 0 ? 0.8 : _x, _y = final.cartBadgeColor, cartBadgeColor = _y === void 0 ? 'error' : _y, cartBadgeText = final.cartBadgeText, _z = final.checkoutText, checkoutText = _z === void 0 ? 'Checkout' : _z, checkoutIcon = final.checkoutIcon, _0 = final.currency, currency = _0 === void 0 ? '$' : _0, _1 = final.persistCart, persistCart = _1 === void 0 ? true : _1, _2 = final.storageKey, storageKey = _2 === void 0 ? 'funui_cart' : _2, whatsappOrderNumber = final.whatsappOrderNumber, otherInfo = final.otherInfo, onAddToCart = final.onAddToCart, onRemoveFromCart = final.onRemoveFromCart, onUpdateQuantity = final.onUpdateQuantity, onCheckout = final.onCheckout, onProductClick = final.onProductClick, _3 = final.className, className = _3 === void 0 ? '' : _3, _4 = final.gridClassName, gridClassName = _4 === void 0 ? '' : _4, children = final.children, id = final.id, _5 = final.heroAlign, heroAlign = _5 === void 0 ? 'center' : _5, _6 = final.funcss, funcss = _6 === void 0 ? '' : _6, _7 = final.heroHeight, heroHeight = _7 === void 0 ? '400px' : _7, _8 = final.fullWidth, fullWidth = _8 === void 0 ? false : _8, _9 = final.itemsPerPage, itemsPerPage = _9 === void 0 ? 10 : _9;
// Mobile state
var _10 = (0, react_1.useState)(false), isMobile = _10[0], setIsMobile = _10[1];
var _11 = (0, react_1.useState)(false), showMobileFilters = _11[0], setShowMobileFilters = _11[1];
// Check screen size
(0, react_1.useEffect)(function () {
var checkMobile = function () {
setIsMobile(window.innerWidth < 768);
};
checkMobile();
window.addEventListener('resize', checkMobile);
return function () { return window.removeEventListener('resize', checkMobile); };
}, []);
// Parse otherInfo
var parsedOtherInfo = (0, react_1.useMemo)(function () {
if (!otherInfo)
return [];
try {
if (typeof otherInfo === 'string') {
return JSON.parse(otherInfo);
}
return otherInfo;
}
catch (error) {
console.error('Error parsing otherInfo:', error);
return [];
}
}, [otherInfo]);
// Checkout state
var _12 = (0, react_1.useState)(false), showCheckoutModal = _12[0], setShowCheckoutModal = _12[1];
var _13 = (0, react_1.useState)({}), userInfoData = _13[0], setUserInfoData = _13[1];
var _14 = (0, react_1.useState)(false), checkoutLoading = _14[0], setCheckoutLoading = _14[1];
// Loading state - track bucket loading and initial load
var _15 = (0, react_1.useState)(true), isInitialLoading = _15[0], setIsInitialLoading = _15[1];
// Use bucket data if bucket prop is provided
var _16 = (0, theme_1.usePaginatedRecords)(bucket || '', bucketPage, bucketSize), bucketRecords = _16.records, bucketLoading = _16.loading;
// Function to calculate discount price
var calculateDiscountedPrice = (0, react_1.useCallback)(function (price, discount) {
if (discount && discount > 0 && discount <= 100) {
var discountAmount = (price * discount) / 100;
return {
finalPrice: price - discountAmount,
originalPrice: price
};
}
return {
finalPrice: price,
originalPrice: price
};
}, []);
// Apply discounts to products
var applyDiscounts = (0, react_1.useCallback)(function (products) {
return products.map(function (product) {
var _a, _b;
if (product.variants && product.variants.length > 0) {
var discountedVariants = product.variants.map(function (variant) {
var _a = calculateDiscountedPrice(variant.price, variant.discount), finalPrice = _a.finalPrice, originalPrice = _a.originalPrice;
return __assign(__assign({}, variant), { price: finalPrice, comparePrice: originalPrice });
});
return __assign(__assign({}, product), { variants: discountedVariants, price: ((_a = discountedVariants[0]) === null || _a === void 0 ? void 0 : _a.price) || product.price, comparePrice: ((_b = discountedVariants[0]) === null || _b === void 0 ? void 0 : _b.comparePrice) || product.comparePrice });
}
else {
var _c = calculateDiscountedPrice(product.price, product.discount), finalPrice = _c.finalPrice, originalPrice = _c.originalPrice;
return __assign(__assign({}, product), { price: finalPrice, comparePrice: originalPrice !== finalPrice ? originalPrice : product.comparePrice });
}
});
}, [calculateDiscountedPrice]);
// Convert bucket records to products format with discount handling
var bucketProducts = (0, react_1.useMemo)(function () {
if (!bucket || !bucketRecords)
return null;
var mappedProducts = bucketRecords.map(function (record) {
var values = record.values || record;
var discount = values.discount || values.salePercentage || values.discountPercentage;
return {
id: values.id || record.id || "bucket_".concat(Math.random().toString(36).substr(2, 9)),
name: values.name || values.title || 'Unnamed Product',
price: parseFloat(values.price) || 0,
comparePrice: parseFloat(values.comparePrice) || parseFloat(values.originalPrice) || undefined,
currency: values.currency || currency,
description: values.description || '',
images: Array.isArray(values.images) ? values.images.map(function (image) { return image.url; }) :
values.image ? [values.image] : [],
category: values.category || values.type || '',
brand: values.brand || values.manufacturer || '',
tags: Array.isArray(values.tags) ? values.tags :
values.tag ? [values.tag] : [],
colors: values.colors || [],
sizes: values.sizes || [],
weight: parseFloat(values.weight) || undefined,
weightUnit: values.weightUnit,
sku: values.sku || values.productCode,
stock: parseInt(values.stock) || parseInt(values.quantity) || undefined,
rating: parseFloat(values.rating) || undefined,
isNew: values.isNew || values.newArrival || false,
isSale: values.isSale || values.onSale || false,
variants: values.variants ? values.variants.map(function (variant) { return (__assign(__assign({}, variant), { discount: variant.discount || discount })); }) : [],
manufacturer: values.manufacturer || '',
countryOfOrigin: values.countryOfOrigin || '',
warranty: values.warranty || '',
isFeatured: values.isFeatured || '',
discount: discount ? parseFloat(discount) : undefined
};
});
return applyDiscounts(mappedProducts);
}, [bucketRecords, bucket, currency, applyDiscounts]);
// Parse and process products
var parsedProducts = react_1.default.useMemo(function () {
var productList;
if (bucket && bucketProducts) {
console.log("Using ".concat(bucketProducts.length, " products from bucket: ").concat(bucket));
productList = bucketProducts.map(function (product, index) { return (__assign(__assign({}, product), { id: product.id || "bucket_product_".concat(index, "_").concat(Date.now()) })); });
}
else {
if (typeof products === 'string') {
try {
var parsed = JSON.parse(products);
productList = Array.isArray(parsed) ? parsed : [];
}
catch (error) {
console.error('Error parsing products JSON:', error);
productList = [];
}
}
else {
productList = products || [];
}
productList = applyDiscounts(productList);
}
return productList.map(function (product, index) { return (__assign(__assign({}, product), { id: product.id || "product_".concat(index, "_").concat(Date.now(), "_").concat(Math.random().toString(36).substr(2, 9)) })); });
}, [products, bucket, bucketProducts, applyDiscounts]);
// Handle loading state separately
(0, react_1.useEffect)(function () {
if (bucket) {
if (!bucketLoading && bucketProducts !== undefined) {
var timer_1 = setTimeout(function () {
setIsInitialLoading(false);
}, 300);
return function () { return clearTimeout(timer_1); };
}
}
else {
setIsInitialLoading(false);
}
}, [bucket, bucketLoading, bucketProducts]);
// Combined loading state
var showLoading = isInitialLoading || (bucket && bucketLoading);
// Initialize cart from localStorage with discount handling
var _17 = (0, react_1.useState)(function () {
if (!persistCart)
return [];
try {
var stored = localStorage.getItem(storageKey);
if (stored) {
var parsed = JSON.parse(stored);
if (parsed.items && Array.isArray(parsed.items)) {
return parsed.items.map(function (item) { return (__assign(__assign({}, item), { product: __assign(__assign({}, item.product), { id: item.product.id || "restored_".concat(Date.now()) }), originalPrice: item.originalPrice || item.product.comparePrice || item.product.price })); });
}
}
}
catch (error) {
console.error('Error loading cart from localStorage:', error);
}
return [];
}), cart = _17[0], setCart = _17[1];
// Save cart to localStorage
(0, react_1.useEffect)(function () {
if (persistCart) {
try {
var cartStorage = {
items: cart,
updatedAt: Date.now(),
};
localStorage.setItem(storageKey, JSON.stringify(cartStorage));
}
catch (error) {
console.error('Error saving cart to localStorage:', error);
}
}
}, [cart, persistCart, storageKey]);
var _18 = (0, react_1.useState)(false), isCartOpen = _18[0], setIsCartOpen = _18[1];
var _19 = (0, react_1.useState)(false), isProductModalOpen = _19[0], setIsProductModalOpen = _19[1];
var _20 = (0, react_1.useState)(null), selectedProduct = _20[0], setSelectedProduct = _20[1];
var _21 = (0, react_1.useState)(''), searchQuery = _21[0], setSearchQuery = _21[1];
var _22 = (0, react_1.useState)('all'), selectedCategory = _22[0], setSelectedCategory = _22[1];
var _23 = (0, react_1.useState)('all'), selectedColor = _23[0], setSelectedColor = _23[1];
var _24 = (0, react_1.useState)('all'), selectedBrand = _24[0], setSelectedBrand = _24[1];
var _25 = (0, react_1.useState)(1), currentPage = _25[0], setCurrentPage = _25[1];
// Get unique categories
var categories = (0, react_1.useMemo)(function () {
if (showLoading)
return ['all'];
var allCategories = parsedProducts
.map(function (p) { return p.category; })
.filter(function (cat) { return typeof cat === 'string' && cat.trim() !== ''; });
var uniqueCategories = Array.from(new Set(allCategories));
return __spreadArray(['all'], uniqueCategories, true);
}, [parsedProducts, showLoading]);
// Get brands based on selected category
var brands = (0, react_1.useMemo)(function () {
if (showLoading)
return ['all'];
var filteredProducts = parsedProducts;
// If a category is selected, filter by it
if (selectedCategory !== 'all') {
filteredProducts = parsedProducts.filter(function (p) { return p.category === selectedCategory; });
}
var allBrands = filteredProducts
.map(function (p) { return p.brand; })
.filter(function (brand) { return typeof brand === 'string' && brand.trim() !== ''; });
return __spreadArray(['all'], Array.from(new Set(allBrands)), true);
}, [parsedProducts, selectedCategory, showLoading]);
// Get colors based on selected category and brand
var colors = (0, react_1.useMemo)(function () {
if (showLoading)
return ['all'];
var filteredProducts = parsedProducts;
// If a category is selected, filter by it
if (selectedCategory !== 'all') {
filteredProducts = filteredProducts.filter(function (p) { return p.category === selectedCategory; });
}
// If a brand is selected, filter by it
if (selectedBrand !== 'all') {
filteredProducts = filteredProducts.filter(function (p) { return p.brand === selectedBrand; });
}
var allColors = [];
filteredProducts.forEach(function (product) {
var _a;
(_a = product.colors) === null || _a === void 0 ? void 0 : _a.forEach(function (color) {
if (!allColors.includes(color.name)) {
allColors.push(color.name);
}
});
});
return __spreadArray(['all'], allColors, true);
}, [parsedProducts, selectedCategory, selectedBrand, showLoading]);
// Filter products
var filteredProducts = (0, react_1.useMemo)(function () {
if (showLoading)
return [];
var filtered = __spreadArray([], parsedProducts, true);
if (searchQuery.trim()) {
var query_1 = searchQuery.toLowerCase();
filtered = filtered.filter(function (p) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
var nameMatch = (_b = (_a = p.name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(query_1)) !== null && _b !== void 0 ? _b : false;
var descMatch = (_d = (_c = p.description) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes(query_1)) !== null && _d !== void 0 ? _d : false;
var brandMatch = (_f = (_e = p.brand) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes(query_1)) !== null && _f !== void 0 ? _f : false;
var categoryMatch = (_h = (_g = p.category) === null || _g === void 0 ? void 0 : _g.toLowerCase().includes(query_1)) !== null && _h !== void 0 ? _h : false;
var tagsMatch = (_k = (_j = p.tags) === null || _j === void 0 ? void 0 : _j.some(function (tag) { return tag.toLowerCase().includes(query_1); })) !== null && _k !== void 0 ? _k : false;
return nameMatch || descMatch || brandMatch || categoryMatch || tagsMatch;
});
}
if (selectedCategory !== 'all') {
filtered = filtered.filter(function (p) { return p.category === selectedCategory; });
}
if (selectedBrand !== 'all') {
filtered = filtered.filter(function (p) { return p.brand === selectedBrand; });
}
if (selectedColor !== 'all') {
filtered = filtered.filter(function (p) { var _a; return (_a = p.colors) === null || _a === void 0 ? void 0 : _a.some(function (color) { return color.name === selectedColor; }); });
}
return filtered;
}, [parsedProducts, searchQuery, selectedCategory, selectedBrand, selectedColor, showLoading]);
// Reset dependent filters when parent filter changes
(0, react_1.useEffect)(function () {
if (selectedCategory === 'all') {
setSelectedBrand('all');
setSelectedColor('all');
}
}, [selectedCategory]);
(0, react_1.useEffect)(function () {
if (selectedBrand === 'all') {
setSelectedColor('all');
}
}, [selectedBrand]);
// Pagination
var totalPages = Math.ceil(filteredProducts.length / itemsPerPage);
var startIndex = (currentPage - 1) * itemsPerPage;
var endIndex = startIndex + itemsPerPage;
var currentProducts = filteredProducts.slice(startIndex, endIndex);
(0, react_1.useEffect)(function () {
setCurrentPage(1);
}, [searchQuery, selectedCategory, selectedBrand, selectedColor]);
// Enhanced cart calculations with discount consideration
var totalItems = (0, react_1.useMemo)(function () { return cart.reduce(function (sum, item) { return sum + item.quantity; }, 0); }, [cart]);
var subtotal = (0, react_1.useMemo)(function () { return cart.reduce(function (sum, item) {
var _a;
var price = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
return sum + (price * item.quantity);
}, 0); }, [cart]);
// Calculate total savings from discounts
var totalSavings = (0, react_1.useMemo)(function () { return cart.reduce(function (sum, item) {
var _a;
var originalPrice = item.originalPrice || item.product.comparePrice || item.product.price;
var currentPrice = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
var savings = originalPrice !== currentPrice ? (originalPrice - currentPrice) * item.quantity : 0;
return sum + savings;
}, 0); }, [cart]);
// Generate cart item ID
var generateCartItemId = (0, react_1.useCallback)(function (product, options) {
var _a;
var parts = [
product.id,
((_a = options === null || options === void 0 ? void 0 : options.variant) === null || _a === void 0 ? void 0 : _a.id) || '',
(options === null || options === void 0 ? void 0 : options.color) || '',
(options === null || options === void 0 ? void 0 : options.size) || ''
].filter(Boolean);
return parts.join('_');
}, []);
// Enhanced cart functions with original price preservation
var addToCart = (0, react_1.useCallback)(function (product, selectedOptions) {
var _a;
var cartItemId = generateCartItemId(product, selectedOptions);
var existingItemIndex = cart.findIndex(function (item) {
var itemId = generateCartItemId(item.product, {
color: item.selectedColor,
size: item.selectedSize,
variant: item.variant
});
return itemId === cartItemId;
});
var updatedCart;
// Store original price before discount
var originalPrice = ((_a = selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.variant) === null || _a === void 0 ? void 0 : _a.comparePrice) ||
product.comparePrice ||
product.price;
if (existingItemIndex !== -1) {
updatedCart = __spreadArray([], cart, true);
var existingItem = updatedCart[existingItemIndex];
updatedCart[existingItemIndex] = __assign(__assign({}, existingItem), { quantity: existingItem.quantity + 1 });
}
else {
var newItem = {
product: product,
variant: selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.variant,
quantity: 1,
selectedColor: selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.color,
selectedSize: selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.size,
addedAt: Date.now(),
originalPrice: originalPrice
};
updatedCart = __spreadArray(__spreadArray([], cart, true), [newItem], false);
}
setCart(updatedCart);
if (existingItemIndex !== -1) {
var existingItem = cart[existingItemIndex];
onUpdateQuantity === null || onUpdateQuantity === void 0 ? void 0 : onUpdateQuantity(cartItemId, existingItem.quantity + 1);
}
else {
var newItem = updatedCart[updatedCart.length - 1];
onAddToCart === null || onAddToCart === void 0 ? void 0 : onAddToCart(newItem);
}
}, [cart, generateCartItemId, onUpdateQuantity, onAddToCart]);
var updateQuantity = (0, react_1.useCallback)(function (cartItemId, newQuantity) {
if (newQuantity < 1) {
removeFromCart(cartItemId);
return;
}
var updatedCart = cart.map(function (item) {
var itemId = generateCartItemId(item.product, {
color: item.selectedColor,
size: item.selectedSize,
variant: item.variant
});
if (itemId === cartItemId) {
return __assign(__assign({}, item), { quantity: newQuantity });
}
return item;
});
setCart(updatedCart);
onUpdateQuantity === null || onUpdateQuantity === void 0 ? void 0 : onUpdateQuantity(cartItemId, newQuantity);
}, [cart, generateCartItemId, onUpdateQuantity]);
var removeFromCart = (0, react_1.useCallback)(function (cartItemId) {
var updatedCart = cart.filter(function (item) {
var itemId = generateCartItemId(item.product, {
color: item.selectedColor,
size: item.selectedSize,
variant: item.variant
});
return itemId !== cartItemId;
});
setCart(updatedCart);
onRemoveFromCart === null || onRemoveFromCart === void 0 ? void 0 : onRemoveFromCart(cartItemId);
}, [cart, generateCartItemId, onRemoveFromCart]);
var clearCart = (0, react_1.useCallback)(function () {
setCart([]);
if (persistCart) {
localStorage.removeItem(storageKey);
}
}, [persistCart, storageKey]);
// Create WhatsApp message
var createWhatsAppMessage = (0, react_1.useCallback)(function (cartItems, userInfo) {
if (userInfo === void 0) { userInfo = {}; }
var lines = [];
// Order summary header
lines.push('🛒 *ORDER SUMMARY*');
lines.push('');
// List products
cartItems.forEach(function (item, index) {
var _a;
var productName = item.product.name;
var variantInfo = item.variant ? " (".concat(item.variant.name, ")") : '';
var options = [];
if (item.selectedColor)
options.push("Color: ".concat(item.selectedColor));
if (item.selectedSize)
options.push("Size: ".concat(item.selectedSize));
var optionsText = options.length > 0 ? " [".concat(options.join(', '), "]") : '';
var price = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
var total = price * item.quantity;
lines.push("".concat(index + 1, ". ").concat(productName).concat(variantInfo).concat(optionsText));
lines.push(" Quantity: ".concat(item.quantity));
lines.push(" Price: ".concat(currency).concat(price.toFixed(2), " each"));
lines.push(" Total: ".concat(currency).concat(total.toFixed(2)));
lines.push('');
});
// Cart totals
var subtotal = cartItems.reduce(function (sum, item) {
var _a;
var price = ((_a = item.variant) === null || _a === void 0 ? void 0 : _a.price) || item.product.price;
return sum + (price * item.quantity);
}, 0);
lines.push('---');
lines.push("*Subtotal:* ".concat(currency).concat(subtotal.toFixed(2)));
lines.push("*Total Items:* ".concat(cartItems.reduce(function (sum, item) { return sum + item.quantity; }, 0)));
lines.push('');
// User information
if (Object.keys(userInfo).length > 0) {
lines.push('👤 *CUSTOMER INFORMATION*');
lines.push('');
Object.entries(userInfo).forEach(function (_a) {
var key = _a[0], value = _a[1];
if (value.trim()) {
var label = key.charAt(0).toUpperCase() + key.slice(1);
lines.push("*".concat(label, ":* ").concat(value));
}
});
lines.push('');
}
// Footer
lines.push('Thank you for your order!');
return encodeURIComponent(lines.join('\n'));
}, [currency]);
// Handle checkout
var handleCheckout = (0, react_1.useCallback)(function () {
if (cart.length === 0)
return;
// If there's otherInfo to collect, show modal
if (parsedOtherInfo.length > 0) {
setShowCheckoutModal(true);
}
else {
// No additional info needed, proceed directly
proceedToWhatsAppOrCallback({});
}
}, [cart, parsedOtherInfo]);
// Proceed with checkout (either to WhatsApp or callback)
var proceedToWhatsAppOrCallback = (0, react_1.useCallback)(function (userInfo) {
var checkoutData = {
cartItems: cart,
totalAmount: subtotal,
userInfo: userInfo
};
// Call the onCheckout callback if provided
if (onCheckout) {
onCheckout(checkoutData);
}
// If WhatsApp number is provided, create WhatsApp message
if (whatsappOrderNumber) {
var message = createWhatsAppMessage(cart, userInfo);
var whatsappUrl = "https://wa.me/".concat(whatsappOrderNumber, "?text=").concat(message);
window.open(whatsappUrl, '_blank');
}
// Clear cart and close modals
if (persistCart) {
clearCart();
}
setShowCheckoutModal(false);
setIsCartOpen(false);
setUserInfoData({});
}, [cart, subtotal, onCheckout, whatsappOrderNumber, createWhatsAppMessage, persistCart, clearCart]);
// Handle user info form submission
var handleUserInfoSubmit = (0, react_1.useCallback)(function () {
setCheckoutLoading(true);
// Validate required fields
var missingFields = parsedOtherInfo
.filter(function (field) { var _a; return field.required && !((_a = userInfoData[field.infoName]) === null || _a === void 0 ? void 0 : _a.trim()); })
.map(function (field) { return field.label || field.infoName; });
if (missingFields.length > 0) {
alert("Please fill in the following required fields: ".concat(missingFields.join(', ')));
setCheckoutLoading(false);
return;
}
setTimeout(function () {
proceedToWhatsAppOrCallback(userInfoData);
setCheckoutLoading(false);
}, 500);
}, [parsedOtherInfo, userInfoData, proceedToWhatsAppOrCallback]);
// Update user info
var handleUserInfoChange = (0, react_1.useCallback)(function (fieldName, value) {
setUserInfoData(function (prev) {
var _a;
return (__assign(__assign({}, prev), (_a = {}, _a[fieldName] = value, _a)));
});
}, []);
// Product modal
var openProductModal = (0, react_1.useCallback)(function (product) {
if (onProductClick) {
onProductClick(product);
return;
}
setSelectedProduct(product);
setIsProductModalOpen(true);
}, [onProductClick]);
var handleAddFromModal = (0, react_1.useCallback)(function (product, quantity, options) {
for (var i = 0; i < quantity; i++) {
addToCart(product, options);
}
setIsProductModalOpen(false);
}, [addToCart]);
// Pagination
var goToPage = (0, react_1.useCallback)(function (page) {
if (page >= 1 && page <= totalPages) {
setCurrentPage(page);
}
}, [totalPages]);
// Helper function to get color with opacity
var getColorWithOpacity = (0, react_1.useCallback)(function (color, opacity) {
// Try to get CSS variable value first
var cssVariableValue = (0, getCssVariable_1.getCssVariableValue)(color);
// If getCssVariableValue returns a different value than input,
// it means the color was a CSS variable (like "primary", "dark", etc.)
var colorValue = cssVariableValue && cssVariableValue !== color ? cssVariableValue : color;
// Check if color is already in rgba format
var rgbaMatch = colorValue.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);
if (rgbaMatch) {
var r = rgbaMatch[1], g = rgbaMatch[2], b = rgbaMatch[3];
return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(opacity, ")");
}
// Check if color is in rgb format
var rgbMatch = colorValue.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (rgbMatch) {
var r = rgbMatch[1], g = rgbMatch[2], b = rgbMatch[3];
return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(opacity, ")");
}
// Check if color is hex format
if (colorValue.startsWith('#')) {
var hex = colorValue.replace('#', '');
var r = void 0, g = void 0, b = void 0;
if (hex.length === 3) {
r = parseInt(hex[0] + hex[0], 16);
g = parseInt(hex[1] + hex[1], 16);
b = parseInt(hex[2] + hex[2], 16);
}
else if (hex.length === 6) {
r = parseInt(hex.substring(0, 2), 16);
g = parseInt(hex.substring(2, 4), 16);
b = parseInt(hex.substring(4, 6), 16);
}
if (r !== undefined && g !== undefined && b !== undefined) {
return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(opacity, ")");
}
}
// For named colors that don't match patterns above, return as-is
// CSS will handle the opacity via the opacity property
return colorValue;
}, []);
// Create overlay style based on gradient settings
// Create overlay style based on gradient settings
var getOverlayStyle = (0, react_1.useCallback)(function () {
// Get the actual color value (resolve CSS variables)
var cssVariableValue = (0, getCssVariable_1.getCssVariableValue)(overlayColor);
var resolvedColor = cssVariableValue && cssVariableValue !== overlayColor
? cssVariableValue
: overlayColor;
if (!overlayGradient) {
// Solid overlay
return {
backgroundColor: resolvedColor,
opacity: overlayOpacity,
};
}
// For gradient overlay, create color with opacity
var colorWithOpacity = getColorWithOpacity(resolvedColor, overlayOpacity);
// Build gradient direction
var direction = gradientDirection.replace('to-', 'to ');
if (invertGradient) {
// From transparent to color
return {
background: "linear-gradient(".concat(direction, ", transparent 0%, ").concat(colorWithOpacity, " 100%)"),
};
}
else {
// From color to transparent
return {
background: "linear-gradient(".concat(direction, ", ").concat(colorWithOpacity, " 0%, transparent 100%)"),
};
}
}, [overlayGradient, gradientDirection, invertGradient, overlayColor, overlayOpacity, getColorWithOpacity]);
// Create accordion items for filters
var filterAccordionItems = (0, react_1.useMemo)(function () { return [
{
icon: react_1.default.createElement(pi_1.PiList, { className: 'text-primary', size: 20 }),
title: 'Categories',
content: (react_1.default.createElement(Div_1.default, { funcss: "filter-options" }, categories.map(function (cat) { return (react_1.default.createElement("div", { key: cat, className: "filter-option ".concat(selectedCategory === cat ? 'primary100 text-primary' : ''), onClick: function () {
setSelectedCategory(cat);
if (cat === 'all') {
setSelectedBrand('all');
setSelectedColor('all');
}
} }, cat === 'all' ? 'All Categories' : cat)); }))),
},
{
icon: react_1.default.createElement(pi_1.PiUserCircle, { className: 'text-primary', size: 20 }),
title: 'Brands',
content: (react_1.default.createElement(Div_1.default, { funcss: "filter-options" }, brands.map(function (brand) { return (react_1.default.createElement("div", { key: brand, className: "filter-option ".concat(selectedBrand === brand ? 'primary100 text-primary' : ''), onClick: function () {
setSelectedBrand(brand);
if (brand === 'all') {
setSelectedColor('all');
}
}, style: {
opacity: selectedCategory === 'all' && brand !== 'all' ? 0.5 : 1,
pointerEvents: selectedCategory === 'all' && brand !== 'all' ? 'none' : 'auto'
} },
brand === 'all' ? 'All Brands' : brand,
selectedCategory === 'all' && brand !== 'all' && (react_1.default.createElement("small", { className: "text-muted", style: { fontSize: '0.7rem', display: 'block' } }, "(Select category first)")))); }))),
},
{
icon: react_1.default.createElement(pi_1.PiHandTap, { className: 'text-primary', size: 20 }),
title: 'Colors',
content: (react_1.default.createElement(Div_1.default, { funcss: "filter-options" }, colors.map(function (color) { return (react_1.default.createElement("div", { key: color, className: "filter-option ".concat(selectedColor === color ? 'primary100 text-primary' : ''), onClick: function () { return setSelectedColor(color); }, style: {
opacity: (selectedCategory === 'all' || selectedBrand === 'all') && color !== 'all' ? 0.5 : 1,
pointerEvents: (selectedCategory === 'all' || selectedBrand === 'all') && color !== 'all' ? 'none' : 'auto'
} },
color === 'all' ? 'All Colors' : color,
(selectedCategory === 'all' || selectedBrand === 'all') && color !== 'all' && (react_1.default.createElement("small", { className: "text-muted", style: { fontSize: '0.7rem', display: 'block' } }, "(Select category & brand first)")))); }))),
},
]; }, [categories, brands, colors, selectedCategory, selectedBrand, selectedColor]);
// Clear all filters
var clearFilters = (0, react_1.useCallback)(function () {
setSelectedCategory('all');
setSelectedBrand('all');
setSelectedColor('all');
setSearchQuery('');
}, []);
return (react_1.default.createElement(Div_1.default, { funcss: "".concat(className, " ").concat(funcss), id: id },
showHero && (react_1.default.createElement(Div_1.default, { funcss: "store-hero-section", customStyle: {
backgroundImage: heroBackgroundImage ? "url(".concat(heroBackgroundImage, ")") : 'none',
backgroundColor: heroBackgroundImage ? undefined : 'var(--lighter)',
height: heroHeight,
position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
} },
react_1.default.createElement("div", { className: "hero-overlay fit", style: __assign({ position: 'absolute', top: 0, left: 0 }, getOverlayStyle()) }),
react_1.default.createElement("div", { className: "hero-content text-".concat(heroAlign || 'center', " relative z-10"), style: {
padding: '2rem',
width: '100%',
} },
react_1.default.createElement(Text_1.default, { text: heroTitle || title, size: titleSize, color: titleColor, block: true, bold: true }),
react_1.default.createElement(Text_1.default, { text: heroDescription, size: descriptionSize, color: descriptionColor, opacity: descriptionOpacity })))),
react_1.default.createElement(View_1.default, { funcss: "pt-10 pl-5 pr-5 center", fit: true, style: {
maxWidth: "1500px"
} },
react_1.default.createElement(Flex_1.default, { width: '100%', justify: 'center', gap: 2 },
showFilters && !isMobile && (react_1.default.createElement(View_1.default, { funcss: "w-200" },
react_1.default.createElement(RowFlex_1.default, { justify: "space-between", funcss: 'bb mb', alignItems: "center" },
react_1.default.createElement(Text_1.default, { text: "Filters", size: "h5" }),
react_1.default.createElement(Button_1.default, { text: "Clear", onClick: clearFilters, small: true, bg: "lighter", startIcon: react_1.default.createElement(pi_1.PiX, null) })),
react_1.default.createElement(Accordion_1.default, { border: false, funcss: 'bg borderless', items: filterAccordionItems, allowMultiple: true, titleClass: "text-sm", contentClass: "text-sm" }))),
react_1.default.createElement("div", { className: 'col fit' },
showFilters && isMobile && (react_1.default.createElement(Div_1.default, { funcss: "mobile-filters-button mb-4" },
react_1.default.createElement(Button_1.default, { startIcon: react_1.default.createElement(pi_1.PiFunnel, null), text: "Filters", onClick: function () { return setShowMobileFilters(true); }, bg: "light", color: "text", raised: true, funcss: "w-full" }))),
react_1.default.createElement(Flex_1.default, { gap: 1, width: '100%', funcss: 'mb-4', justify: 'space-between' },
showSearch && (react_1.default.createElement("div", { className: "w-400" },
react_1.default.createElement(Input_1.default, { label: "Search products...", value: searchQuery, onChange: function (e) { return setSearchQuery(e.target.value); }, startIcon: react_1.default.createElement(pi_1.PiMagnifyingGlass, null), borderless: true }))),
showCart && (react_1.default.createElement("button", { onClick: function () { return setIsCartOpen(true); }, className: "cart-icon relative", type: "button", "aria-label": "Shopping cart (".concat(totalItems, " items)"), disabled: showLoading },
react_1.default.createElement(sl_1.SlHandbag, { size: 30 }),
totalItems > 0 && (react_1.default.createElement("div", { className: "cart-badge error", style: { backgroundColor: cartBadgeColor } }, cartBadgeText || (totalItems > 99 ? '99+' : totalItems)))))),
showLoading ? (react_1.default.createElement(Div_1.default, { funcss: "\r\n funui_products_grid grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4\r\n " }, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(function (index) { return (react_1.default.createElement("div", { key: index },
react_1.default.createElement(ProductLoader_1.default, null))); }))) : (react_1.default.createElement(react_1.default.Fragment, null,
currentProducts.length === 0 ? (react_1.default.createElement(Div_1.default, { funcss: "" },
react_1.default.createElement(Empty_1.default, { title: 'No products found', ctaIcon: react_1.default.createElement(pi_1.PiSpinnerGap, null), ctaText: 'Reset Filters', ctaOnClick: clearFilters }))) : (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(Div_1.default, { margin: '2rem 0', funcss: "funui_products_grid grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 ".concat(gridClassName) }, currentProducts.map(function (product) { return (react_1.default.createElement(ProductCard_1.default, { key: product.id, product: product, currency: currency, onClick: function () { return openProductModal(product); }, onAddToCart: function () { return addToCart(product); }, showBadges: true })); })),
totalPages > 1 && (react_1.default.createElement(Flex_1.default, { width: '100%', justify: 'center', gap: 0.5, funcss: "mt-8" },
react_1.default.createElement(Button_1.default, { startIcon: react_1.default.createElement(pi_1.PiCaretLeft, null), onClick: function () { return goToPage(currentPage - 1); }, disabled: currentPage === 1, text: "Prev", small: true }),
react_1.default.createElement(Div_1.default, { funcss: "pagination-numbers" },
Array.from({ length: Math.min(5, totalPages) }, function (_, i) {
var pageNum;
if (totalPages <= 5) {
pageNum = i + 1;
}
else if (currentPage <= 3) {
pageNum = i + 1;
}
else if (currentPage >= totalPages - 2) {
pageNum = totalPages - 4 + i;
}
else {
pageNum = currentPage - 2 + i;
}
return (react_1.default.createElement(Button_1.default, { key: pageNum, text: pageNum.toString(), onClick: function () { return goToPage(pageNum); }, bg: currentPage === pageNum ? 'primary' : undefined, color: currentPage === pageNum ? 'white' : 'text', small: true }));
}),
totalPages > 5 && currentPage < totalPages - 2 && (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(Text_1.default, { text: "...", color: "text-light" }),
react_1.default.createElement(Button_1.default, { text: totalPages.toString(), onClick: function () { return goToPage(totalPages); }, small: true })))),
react_1.default.createElement(Button_1.default, { endIcon: react_1.default.createElement(pi_1.PiCaretRight, null), onClick: function () { return goToPage(currentPage + 1); }, disabled: currentPage === totalPages, text: "Next", small: true }))))),
children))))),
showFilters && isMobile && (react_1.default.createElement(Modal_1.default, { animation: "slideUp", open: showMobileFilters, setOpen: setShowMobileFilters, title: react_1.default.createElement(RowFlex_1.default, { justify: "space-between", alignItems: "center" },
react_1.default.createElement(Text_1.default, { text: "Filters", size: "h5" }),
react_1.default.createElement(Button_1.default, { text: "Clear All", onClick: clearFilters, small: true, bg: "transparent", color: "text-light" })), body: react_1.default.createElement(Div_1.default, { funcss: "p-4" },
react_1.default.createElement(Accordion_1.default, { items: filterAccordionItems, allowMultiple: true, titleClass: "text-sm", contentClass: "text-xs", activeClass: "", funcss: 'card' })), footer: react_1.default.createElement(Div_1.default, { funcss: "p-4" },
react_1.default.createElement(Button_1.default, { text: "Apply Filters", onClick: function () { return setShowMobileFilters(false); }, bg: "primary", color: "white", raised: true, funcss: "w-full" })) })),
showCart && (react_1.default.createElement(CartModal_1.default, { cart: cart, isOpen: isCartOpen, setIsOpen: setIsCartOpen, currency: currency, updateQuantity: updateQuantity, removeFromCart: removeFromCart, clearCart: clearCart, handleCheckout: handleCheckout, cartBadgeColor: cartBadgeColor, checkoutText: checkoutText, checkoutIcon: checkoutIcon, persistCart: persistCart })),
showCheckoutModal && (react_1.default.createElement(Modal_1.default, { animation: "fadeIn", open: showCheckoutModal, setOpen: setShowCheckoutModal, maxWidth: '550px', title: react_1.default.createElement(react_1.default.Fragment, null, whatsappOrderNumber ? (react_1.defaul