aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
514 lines (511 loc) • 21.2 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { useState, useEffect, useCallback } from 'react';
import { GlassCore } from '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import '../../primitives/motion/MotionFramer.js';
import { cn } from '../../lib/utilsComprehensive.js';
import { useEcommerce } from './GlassEcommerceProvider.js';
const ProductCard = ({
recommendation,
variant,
showPrice = true,
showRating = true,
showQuickActions = true,
onProductClick,
onAddToCart,
onAddToWishlist
}) => {
const {
product,
score,
reason,
explanation,
confidence
} = recommendation;
const [imageError, setImageError] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const getReasonIcon = reason => {
switch (reason) {
case "similar":
return "🔄";
case "trending":
return "📈";
case "personalized":
return "🎯";
case "bought-together":
return "🛍️";
case "viewed-together":
return "👀";
default:
return "✨";
}
};
const getReasonColor = reason => {
switch (reason) {
case "similar":
return "bg-blue-100 text-blue-800";
case "trending":
return "bg-red-100 text-red-800";
case "personalized":
return "bg-purple-100 text-purple-800";
case "bought-together":
return "bg-green-100 text-green-800";
case "viewed-together":
return "bg-orange-100 text-orange-800";
default:
return "bg-gray-100 text-gray-800";
}
};
const renderCompactCard = () => jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-3 glass-p-3 hover:glass-surface-subtle glass-radius-lg transition-colors',
children: [jsx("img", {
src: imageError ? "https://via.placeholder.com/60x60?text=No+Image" : product.thumbnail || product.images[0],
alt: product.name,
className: 'w-12 h-12 object-cover glass-radius',
onError: () => setImageError(true)
}), jsxs("div", {
className: "glass-flex-1 glass-min-w-0",
children: [jsx("h3", {
className: 'font-medium glass-text-secondary glass-text-sm truncate',
children: product.name
}), showPrice && jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-2 mt-1',
children: [jsxs("span", {
className: 'glass-text-sm font-medium glass-text-secondary',
children: ["$", product.price.toFixed(2)]
}), product.originalPrice && product.originalPrice > product.price && jsxs("span", {
className: 'glass-text-xs glass-text-secondary line-through',
children: ["$", product.originalPrice.toFixed(2)]
})]
})]
}), showQuickActions && jsxs("div", {
className: "glass-flex glass-items-center glass-gap-1",
children: [jsx("button", {
onClick: e => {
e.stopPropagation();
onAddToWishlist(product.id);
},
className: 'glass-p-1 glass-text-secondary hover:text-primary transition-colors glass-focus glass-touch-target glass-contrast-guard',
title: "Add to wishlist",
children: "\u2661"
}), jsx("button", {
onClick: e => {
e.stopPropagation();
onAddToCart(product.id);
},
className: 'glass-p-1 glass-text-secondary hover:text-primary transition-colors glass-focus glass-touch-target glass-contrast-guard',
title: "Add to cart",
children: "\uD83D\uDED2"
})]
})]
});
const renderListCard = () => jsxs("div", {
className: 'glass-flex glass-gap-4 glass-p-4 glass-border glass-border-subtle glass-radius-lg hover:shadow-md transition-all',
children: [jsx("img", {
src: imageError ? "https://via.placeholder.com/120x120?text=No+Image" : product.thumbnail || product.images[0],
alt: product.name,
className: 'w-20 h-20 object-cover glass-radius',
onError: () => setImageError(true)
}), jsxs("div", {
className: "glass-flex-1",
children: [jsxs("div", {
className: 'glass-flex glass-items-start glass-justify-between mb-2',
children: [jsx("h3", {
className: 'font-medium glass-text-secondary line-clamp-2',
children: product.name
}), jsxs("div", {
className: cn("text-xs px-2 py-1 rounded-full", getReasonColor(reason)),
children: [getReasonIcon(reason), " ", reason.replace("-", " ")]
})]
}), jsx("p", {
className: 'glass-text-sm glass-text-secondary mb-3 line-clamp-2',
children: explanation
}), jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-gap-3",
children: [showPrice && jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsxs("span", {
className: 'glass-text-lg font-semibold glass-text-secondary',
children: ["$", product.price.toFixed(2)]
}), product.originalPrice && product.originalPrice > product.price && jsxs("span", {
className: 'glass-text-sm glass-text-secondary line-through',
children: ["$", product.originalPrice.toFixed(2)]
})]
}), showRating && jsxs("div", {
className: "glass-flex glass-items-center glass-gap-1",
children: [jsx("span", {
className: 'text-primary',
children: "\u2605"
}), jsxs("span", {
className: "glass-text-sm glass-text-secondary",
children: [product.rating.toFixed(1), " (", product.reviewCount, ")"]
})]
})]
}), showQuickActions && jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("button", {
onClick: e => {
e.stopPropagation();
onAddToWishlist(product.id);
},
className: 'glass-px-3 glass-py-1 glass-text-sm glass-text-secondary hover:text-primary transition-colors glass-focus glass-touch-target glass-contrast-guard',
children: "\u2661 Wishlist"
}), jsx("button", {
onClick: e => {
e.stopPropagation();
onAddToCart(product.id);
},
className: 'glass-px-3 glass-py-1 glass-surface-blue text-primary glass-text-sm glass-radius hover:glass-surface-blue transition-colors glass-focus glass-touch-target glass-contrast-guard',
children: "Add to Cart"
})]
})]
}), jsxs("div", {
className: 'mt-2 glass-flex glass-items-center glass-gap-2',
children: [jsx("span", {
className: "glass-text-xs glass-text-secondary",
children: "Match confidence:"
}), jsx("div", {
className: 'glass-flex-1 glass-surface-subtle glass-radius-full h-1',
children: jsx("div", {
className: 'glass-surface-blue h-1 glass-radius-full transition-all',
style: {
width: `${confidence * 100}%`
}
})
}), jsxs("span", {
className: "glass-text-xs glass-text-secondary",
children: [Math.round(confidence * 100), "%"]
})]
})]
})]
});
const renderGridCard = () => jsxs("div", {
className: 'group relative glass-surface-subtle glass-border glass-border-subtle glass-radius-lg overflow-hidden hover:shadow-lg transition-all duration-300',
onMouseEnter: () => setIsHovered(true),
onMouseLeave: () => setIsHovered(false),
children: [jsxs("div", {
className: 'relative aspect-square overflow-hidden',
children: [jsx("img", {
src: imageError ? "https://via.placeholder.com/300x300?text=No+Image" : product.thumbnail || product.images[0],
alt: product.name,
className: 'glass-w-full glass-h-full object-cover group-hover:scale-105 transition-transform duration-300',
onError: () => setImageError(true)
}), jsxs("div", {
className: 'absolute glass-top-2 left-2 glass-flex glass-flex-col glass-gap-1',
children: [product.isOnSale && jsx("span", {
className: 'glass-surface-red text-primary glass-text-xs glass-px-2 glass-py-1 glass-radius',
children: "SALE"
}), product.isNew && jsx("span", {
className: 'glass-surface-green text-primary glass-text-xs glass-px-2 glass-py-1 glass-radius',
children: "NEW"
}), product.isBestseller && jsx("span", {
className: 'glass-surface-yellow text-primary glass-text-xs glass-px-2 glass-py-1 glass-radius',
children: "BESTSELLER"
})]
}), jsx("div", {
className: 'absolute glass-top-2 right-2',
children: jsx("div", {
className: cn("text-xs px-2 py-1 rounded-full", getReasonColor(reason)),
children: getReasonIcon(reason)
})
}), showQuickActions && jsxs("div", {
className: cn("absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center gap-3 transition-opacity duration-300", isHovered ? "opacity-100" : "opacity-0"),
children: [jsx("button", {
onClick: e => {
e.stopPropagation();
onAddToWishlist(product.id);
},
className: 'glass-p-2 glass-surface-subtle glass-radius-full glass-text-secondary hover:text-primary transition-colors glass-focus glass-touch-target glass-contrast-guard',
title: "Add to wishlist",
children: "\u2661"
}), jsx("button", {
onClick: e => {
e.stopPropagation();
onAddToCart(product.id);
},
className: 'glass-p-2 glass-surface-blue text-primary glass-radius-full hover:glass-surface-blue transition-colors glass-focus glass-touch-target glass-contrast-guard',
title: "Add to cart",
children: "\uD83D\uDED2"
})]
})]
}), jsxs("div", {
className: "glass-p-4",
children: [jsx("h3", {
className: 'font-medium glass-text-secondary mb-2 line-clamp-2',
children: product.name
}), product.brand && jsx("p", {
className: 'glass-text-sm glass-text-secondary mb-2',
children: product.brand
}), showRating && jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-1 mb-3',
children: [jsx("div", {
className: 'glass-flex text-primary',
children: Array.from({
length: 5
}, (_, i) => jsx("span", {
className: i < Math.floor(product.rating) ? "text-yellow-400" : "text-gray-300",
children: "\u2605"
}, i))
}), jsxs("span", {
className: "glass-text-sm glass-text-secondary",
children: ["(", product.reviewCount, ")"]
})]
}), showPrice && jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-2 mb-3',
children: [jsxs("span", {
className: 'glass-text-lg font-semibold glass-text-secondary',
children: ["$", product.price.toFixed(2)]
}), product.originalPrice && product.originalPrice > product.price && jsxs("span", {
className: 'glass-text-sm glass-text-secondary line-through',
children: ["$", product.originalPrice.toFixed(2)]
})]
}), jsx("p", {
className: 'glass-text-xs glass-text-secondary mb-3',
children: explanation
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("span", {
className: "glass-text-xs glass-text-secondary",
children: "Match:"
}), jsx("div", {
className: 'glass-flex-1 glass-surface-subtle glass-radius-full h-1',
children: jsx("div", {
className: 'glass-surface-blue h-1 glass-radius-full',
style: {
width: `${confidence * 100}%`
}
})
}), jsxs("span", {
className: "glass-text-xs glass-text-secondary",
children: [Math.round(confidence * 100), "%"]
})]
})]
})]
});
return jsxs("div", {
"data-glass-component": true,
onClick: () => onProductClick?.(product),
className: 'cursor-pointer',
children: [variant === "compact" && renderCompactCard(), variant === "list" && renderListCard(), (variant === "grid" || variant === "carousel") && renderGridCard()]
});
};
const GlassProductRecommendations = ({
productId,
title = "Recommended for You",
subtitle,
maxItems = 8,
variant = "grid",
showPrices = true,
showRatings = true,
showQuickActions = true,
recommendationType = "all",
className,
onProductClick
}) => {
const {
recommendations,
getRecommendations,
generateRecommendations,
addToCart,
addToWishlist
} = useEcommerce();
const [loading, setLoading] = useState(false);
const [currentRecommendations, setCurrentRecommendations] = useState([]);
const [currentIndex, setCurrentIndex] = useState(0);
// Load recommendations
useEffect(() => {
if (!productId) return;
const loadRecommendations = async () => {
setLoading(true);
let recs = getRecommendations(productId);
if (recs.length === 0) {
recs = await generateRecommendations(productId);
}
// Filter by recommendation type
if (recommendationType !== "all") {
recs = recs.filter(rec => rec.reason === recommendationType);
}
setCurrentRecommendations(recs.slice(0, maxItems));
setLoading(false);
};
loadRecommendations();
}, [productId, recommendationType, maxItems, getRecommendations, generateRecommendations]);
const handleAddToCart = useCallback(productId => {
addToCart(productId, 1);
// You could show a toast notification here
}, [addToCart]);
const handleAddToWishlist = useCallback(productId => {
addToWishlist(productId);
// You could show a toast notification here
}, [addToWishlist]);
const nextSlide = useCallback(() => {
setCurrentIndex(prev => prev + 1 >= Math.ceil(currentRecommendations.length / getItemsPerView()) ? 0 : prev + 1);
}, [currentRecommendations.length]);
const prevSlide = useCallback(() => {
setCurrentIndex(prev => prev - 1 < 0 ? Math.ceil(currentRecommendations.length / getItemsPerView()) - 1 : prev - 1);
}, [currentRecommendations.length]);
const getItemsPerView = () => {
switch (variant) {
case "carousel":
return 4;
case "grid":
return 8;
default:
return currentRecommendations.length;
}
};
if (loading) {
return jsx(GlassCore, {
className: cn("p-6", className),
children: jsx("div", {
className: "glass-flex glass-items-center glass-justify-center glass-py-12",
children: jsx("div", {
className: 'animate-spin glass-radius-full h-12 w-12 glass-border-4 glass-border-blue glass-border-t-transparent'
})
})
});
}
if (currentRecommendations.length === 0) {
return jsx(GlassCore, {
className: cn("p-6", className),
children: jsxs("div", {
className: 'text-center glass-py-12',
children: [jsx("div", {
className: 'glass-text-4xl mb-4',
children: "\uD83E\uDD16"
}), jsx("h3", {
className: 'glass-text-lg font-medium glass-text-secondary mb-2',
children: "No recommendations available"
}), jsx("p", {
className: "glass-text-secondary",
children: "We're still learning about your preferences. Check back later!"
})]
})
});
}
return jsxs(GlassCore, {
className: cn("p-6", className),
children: [jsxs("div", {
className: 'glass-flex glass-items-center glass-justify-between mb-6',
children: [jsxs("div", {
children: [jsx("h2", {
className: 'glass-text-xl font-semibold glass-text-secondary',
children: title
}), subtitle && jsx("p", {
className: 'glass-text-secondary mt-1',
children: subtitle
})]
}), variant === "carousel" && jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("button", {
onClick: prevSlide,
className: 'glass-p-2 glass-text-secondary hover:glass-text-secondary transition-colors glass-focus glass-touch-target glass-contrast-guard glass-focus glass-touch-target glass-contrast-guard',
children: "\u2190"
}), jsxs("span", {
className: "glass-text-sm glass-text-secondary",
children: [currentIndex + 1, " /", " ", Math.ceil(currentRecommendations.length / getItemsPerView())]
}), jsx("button", {
onClick: nextSlide,
className: 'glass-p-2 glass-text-secondary hover:glass-text-secondary transition-colors glass-focus glass-touch-target glass-contrast-guard glass-focus glass-touch-target glass-contrast-guard',
children: "\u2192"
})]
})]
}), jsx("div", {
className: cn(variant === "grid" && "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6", variant === "carousel" && "overflow-hidden", variant === "list" && "space-y-4", variant === "compact" && "space-y-2"),
children: variant === "carousel" ? jsx("div", {
className: 'glass-flex transition-transform duration-300',
style: {
transform: `translateX(-${currentIndex * 100}%)`,
width: `${Math.ceil(currentRecommendations.length / getItemsPerView()) * 100}%`
},
children: Array.from({
length: Math.ceil(currentRecommendations.length / getItemsPerView())
}, (_, slideIndex) => jsx("div", {
className: "glass-grid glass-grid-cols-4 glass-gap-6 glass-w-full glass-flex-shrink-0",
children: currentRecommendations.slice(slideIndex * getItemsPerView(), (slideIndex + 1) * getItemsPerView()).map(recommendation => jsx(ProductCard, {
recommendation: recommendation,
variant: variant,
showPrice: showPrices,
showRating: showRatings,
showQuickActions: showQuickActions,
onProductClick: onProductClick,
onAddToCart: handleAddToCart,
onAddToWishlist: handleAddToWishlist
}, recommendation.productId))
}, slideIndex))
}) : currentRecommendations.map(recommendation => jsx(ProductCard, {
recommendation: recommendation,
variant: variant,
showPrice: showPrices,
showRating: showRatings,
showQuickActions: showQuickActions,
onProductClick: onProductClick,
onAddToCart: handleAddToCart,
onAddToWishlist: handleAddToWishlist
}, recommendation.productId))
}), jsx("div", {
className: 'mt-6 glass-p-4 glass-surface-subtle glass-radius-lg',
children: jsxs("div", {
className: "glass-flex glass-items-start glass-gap-3",
children: [jsx("div", {
className: "glass-text-2xl",
children: "\uD83E\uDD16"
}), jsxs("div", {
children: [jsx("h3", {
className: 'font-medium text-primary mb-1',
children: "AI Insights"
}), jsxs("p", {
className: 'glass-text-sm text-primary',
children: ["These recommendations are personalized based on your browsing history, purchase patterns, and preferences similar to users like you. Our AI analyzes ", currentRecommendations.length, " factors to suggest the best products for you."]
}), jsx("div", {
className: 'glass-flex glass-flex-wrap glass-gap-2 mt-3',
children: Array.from(new Set(currentRecommendations.map(r => r.reason))).map(reason => jsxs("span", {
className: cn("text-xs px-2 py-1 rounded-full", getReasonColor(reason)),
children: [getReasonIcon(reason), " ", reason.replace("-", " ")]
}, reason))
})]
})]
})
})]
});
function getReasonColor(reason) {
switch (reason) {
case "similar":
return "bg-blue-100 text-blue-800";
case "trending":
return "bg-red-100 text-red-800";
case "personalized":
return "bg-purple-100 text-purple-800";
case "bought-together":
return "bg-green-100 text-green-800";
case "viewed-together":
return "bg-orange-100 text-orange-800";
default:
return "bg-gray-100 text-gray-800";
}
}
function getReasonIcon(reason) {
switch (reason) {
case "similar":
return "🔄";
case "trending":
return "📈";
case "personalized":
return "🎯";
case "bought-together":
return "🛍️";
case "viewed-together":
return "👀";
default:
return "✨";
}
}
};
export { GlassProductRecommendations };
//# sourceMappingURL=GlassProductRecommendations.js.map