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
289 lines (288 loc) • 23.4 kB
JavaScript
// components/products/ProductDetail.tsx
'use client';
"use strict";
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 tfi_1 = require("react-icons/tfi");
var si_1 = require("react-icons/si");
var Modal_1 = __importDefault(require("../modal/Modal"));
var Div_1 = __importDefault(require("../div/Div"));
var Text_1 = __importDefault(require("../text/Text"));
var Flex_1 = __importDefault(require("../flex/Flex"));
var Button_1 = __importDefault(require("../button/Button"));
var RowFlex_1 = __importDefault(require("../specials/RowFlex"));
var Carousel_1 = __importDefault(require("../carousel/Carousel"));
var Select_1 = __importDefault(require("../select/Select"));
var io5_1 = require("react-icons/io5");
var ImageScaler_1 = __importDefault(require("../components/ImageScaler"));
var ProductDetail = function (_a) {
var product = _a.product, open = _a.open, setOpen = _a.setOpen, _b = _a.currency, currency = _b === void 0 ? '$' : _b, onAddToCart = _a.onAddToCart;
var _c = (0, react_1.useState)(0), selectedImageIndex = _c[0], setSelectedImageIndex = _c[1];
var _d = (0, react_1.useState)(''), selectedColor = _d[0], setSelectedColor = _d[1];
var _e = (0, react_1.useState)(''), selectedSize = _e[0], setSelectedSize = _e[1];
var _f = (0, react_1.useState)(false), showFullDescription = _f[0], setShowFullDescription = _f[1];
var _g = (0, react_1.useState)(false), canAddToCart = _g[0], setCanAddToCart = _g[1];
var hasDiscount = product.comparePrice && product.comparePrice > product.price;
var discountPercent = hasDiscount
? Math.round(((product.comparePrice - product.price) / product.comparePrice) * 100)
: 0;
// Stock information logic
var stockAvailable = product.stock === undefined || product.stock > 0;
var lowStock = product.stock !== undefined && product.stock > 0 && product.stock < 10;
var getDisplayPrice = function () {
var price = product.price || 0;
var productCurrency = product.currency || currency;
return "".concat(productCurrency).concat(price.toFixed(2));
};
var handleAddToCart = function () {
// Always add with quantity 1
onAddToCart === null || onAddToCart === void 0 ? void 0 : onAddToCart(product, 1, {
color: selectedColor,
size: selectedSize,
});
setOpen(false);
};
// Validate if all required selections are made
(0, react_1.useEffect)(function () {
var isValid = true;
// Check if color selection is required and selected
if (product.colors && product.colors.length > 0) {
isValid = isValid && selectedColor !== '';
}
// Check if size selection is required and selected
if (product.sizes && product.sizes.length > 0) {
isValid = isValid && selectedSize !== '';
}
// Also check stock availability
isValid = isValid && stockAvailable;
setCanAddToCart(isValid);
}, [selectedColor, selectedSize, stockAvailable, product.colors, product.sizes]);
// Function to safely process description
var processDescription = function (description) {
if (!description)
return '';
// Remove h1, h2, h3 tags but keep their content
var processed = description
.replace(/<h[1-3][^>]*>/gi, '<p>')
.replace(/<\/h[1-3]>/gi, '</p>');
return processed;
};
// Function to truncate HTML while preserving tags
var truncateHTML = function (html, maxLength) {
if (html.length <= maxLength)
return html;
var truncated = '';
var length = 0;
var inTag = false;
var tagBuffer = '';
for (var i = 0; i < html.length && length < maxLength; i++) {
var char = html[i];
if (char === '<') {
inTag = true;
tagBuffer = char;
}
else if (char === '>') {
inTag = false;
tagBuffer += char;
truncated += tagBuffer;
tagBuffer = '';
}
else if (inTag) {
tagBuffer += char;
}
else {
truncated += char;
length++;
}
}
// Close any open tags
var tempDiv = document.createElement('div');
tempDiv.innerHTML = truncated + '...';
// Get the innerHTML to ensure tags are properly closed
return tempDiv.innerHTML;
};
// Process the description
var processedDescription = processDescription(product.description || '');
var maxDescriptionLength = 200;
var shouldTruncate = processedDescription.length > maxDescriptionLength;
var displayDescription = showFullDescription
? processedDescription
: (shouldTruncate ? truncateHTML(processedDescription, maxDescriptionLength) : processedDescription);
// Check if description has HTML tags
var hasHTML = /<[a-z][\s\S]*>/i.test(processedDescription);
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 },
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" },
react_1.default.createElement(Div_1.default, { funcss: "funui_products_main_image_container mb-3" },
react_1.default.createElement(ImageScaler_1.default, { src: product.images[selectedImageIndex], size: "400px", funcss: 'round-edge' })),
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); } },
react_1.default.createElement(ImageScaler_1.default, { src: image, size: "100px", funcss: 'round-edge' }))); })))))),
react_1.default.createElement("div", { className: "col" },
react_1.default.createElement(Flex_1.default, { direction: 'column', gap: 2, alignItems: 'flex-start', justify: 'flex-start', width: '100%' },
react_1.default.createElement("div", { className: "w-full" },
react_1.default.createElement(Flex_1.default, { justify: "space-between", alignItems: "center", width: '100%' },
react_1.default.createElement(Flex_1.default, { gap: 1, alignItems: "center" },
product.category && (react_1.default.createElement(Text_1.default, { size: 'xs', opacity: 4, uppercase: true, weight: 500, color: "text-muted" }, product.category)),
product.isNew && (react_1.default.createElement(Div_1.default, { funcss: "badge-new" },
react_1.default.createElement(Text_1.default, { size: 'xs', color: 'white', weight: 600 }, "NEW"))),
product.isSale && (react_1.default.createElement(Div_1.default, { funcss: "badge-sale" },
react_1.default.createElement(Text_1.default, { size: 'xs', color: 'white', weight: 600 }, "SALE")))),
!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 },
"Only ",
product.stock,
" left")) : (react_1.default.createElement(Text_1.default, { size: 'xs', color: 'success', weight: 600 }, "In Stock"))),
react_1.default.createElement(Text_1.default, { text: product.name, size: "2xl", block: true, weight: 600, funcss: "mb-3" }),
react_1.default.createElement(Flex_1.default, { justify: "space-between", alignItems: "center", width: '100%', funcss: "mb-3" },
react_1.default.createElement(Flex_1.default, { gap: 1, alignItems: "baseline" },
react_1.default.createElement(Text_1.default, { text: getDisplayPrice(), size: "xl", weight: 700, color: "primary" }),
hasDiscount && (react_1.default.createElement(Div_1.default, { funcss: "discount-percent" },
react_1.default.createElement(Text_1.default, { size: 'sm', color: 'white', weight: 600 },
"-",
discountPercent,
"%")))),
(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" })))),
product.stock !== undefined && (react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { size: 'xs', opacity: 4 },
product.stock,
" units available"))),
((product.colors && product.colors.length > 0) || (product.sizes && product.sizes.length > 0)) && (react_1.default.createElement(Flex_1.default, { width: '100%', gap: 1 },
product.colors && product.colors.length > 0 && (react_1.default.createElement("div", { className: "col" },
react_1.default.createElement(Text_1.default, { size: "sm", weight: 500, funcss: "mb-1" },
"Color",
react_1.default.createElement(Text_1.default, { size: "xs", color: "error", funcss: "margin-left-1" }, "*")),
react_1.default.createElement(Select_1.default, { fullWidth: true, options: __spreadArray([
{ text: 'Select Color', value: '' }
], product.colors.map(function (color) { return ({
text: color.name,
value: color.name,
prefix: (react_1.default.createElement(Div_1.default, { funcss: "color-preview", customStyle: {
width: "20px",
height: "20px",
borderRadius: "50%",
backgroundColor: color.code,
border: selectedColor === color.name ? '2px solid var(--primary)' : '1px solid var(--border)'
} }))
}); }), true), value: selectedColor, onChange: function (e) { return setSelectedColor(e); }, bordered: true }),
selectedColor === '' && (react_1.default.createElement(Text_1.default, { size: "xs", color: "error", funcss: "margin-top-1" }, "Please select a color")))),
product.sizes && product.sizes.length > 0 && (react_1.default.createElement("div", { className: "col" },
react_1.default.createElement(Text_1.default, { size: "sm", weight: 500, funcss: "mb-1" },
"Size",
react_1.default.createElement(Text_1.default, { size: "xs", color: "error", funcss: "margin-left-1" }, "*")),
react_1.default.createElement(Select_1.default, { fullWidth: true, options: __spreadArray([
{ text: 'Select Size', value: '' }
], product.sizes.map(function (size) { return ({ text: size, value: size }); }), true), value: selectedSize, onChange: function (e) { return setSelectedSize(e); }, bordered: true }),
selectedSize === '' && (react_1.default.createElement(Text_1.default, { size: "xs", color: "error", funcss: "margin-top-1" }, "Please select a size")))))),
processedDescription && (react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Description", size: "lg", weight: 600, funcss: "margin-bottom-1" }),
react_1.default.createElement("div", { className: "article text-sm ".concat(hasHTML ? '' : 'whitespace-pre-wrap'), dangerouslySetInnerHTML: { __html: displayDescription } }),
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" })))),
react_1.default.createElement(Div_1.default, { funcss: "product-details-grid" },
react_1.default.createElement(Flex_1.default, { gap: 3, width: '100%' },
product.brand && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(si_1.SiBlackmagicdesign, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Brand", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: product.brand, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.sku && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(pi_1.PiTag, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "SKU", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: product.sku, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.rating && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(tfi_1.TfiComments, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Rating", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: product.rating.toString(), size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.weight && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(pi_1.PiScales, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Weight", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: "".concat(product.weight, " ").concat(product.weightUnit || ''), size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.manufacturer && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(pi_1.PiUser, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Manufacturer", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: product.manufacturer, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.countryOfOrigin && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(pi_1.PiGlobe, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Country of Origin", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: product.countryOfOrigin, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.warranty && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(pi_1.PiShieldCheck, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Warranty", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: product.warranty, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.category && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(io5_1.IoLayersOutline, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Category", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: product.category, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.brand && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(pi_1.PiStorefront, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Store", size: "xs", opacity: 4, block: true }),
react_1.default.createElement(Text_1.default, { text: product.brand, size: "sm", block: true, lineHeight: '1', weight: 500 })))),
product.tags && product.tags.length > 0 && (react_1.default.createElement(Flex_1.default, { gap: 0.3, funcss: "detail-item" },
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(pi_1.PiTag, { className: 'text-primary' })),
react_1.default.createElement(Div_1.default, null,
react_1.default.createElement(Text_1.default, { text: "Tags", size: "xs", opacity: 4, block: true }),
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' },
react_1.default.createElement(Button_1.default, { text: "Add to Cart", startIcon: react_1.default.createElement(pi_1.PiBag, null), bg: "primary", raised: true, onClick: handleAddToCart, disabled: !canAddToCart, funcss: !canAddToCart ? "opacity-6" : "" })) }));
};
exports.default = ProductDetail;