import {useCallback, type MouseEvent} from 'react'; import {Link} from 'react-router'; import {CartForm, Image, Money} from '@shopify/hydrogen'; import {motion} from 'motion/react'; import {Heart, ShoppingBag, Star} from 'lucide-react'; import {useAbiirProductWishlist} from '~/hooks/useAbiirProductWishlist'; import {useVariantUrl} from '~/lib/variants'; import type {AbiirProductListItemData} from '~/lib/featuredProducts'; const gold = '#b39763'; const badgeGold = '#b8a042'; const burgundy = '#5d0b24'; const imageBg = '#ede8e0'; const chipBg = '#f5f1e8'; const muted = '#5a5550'; const charcoal = '#1a1a18'; const cream = '#f8f4ef'; const overlayBurgundy = 'rgba(93, 11, 36, 0.95)'; function productBadges( item: AbiirProductListItemData, ): {label: string; variant: 'burgundy' | 'gold'}[] { if (item.hideBadge) return []; const out: {label: string; variant: 'burgundy' | 'gold'}[] = []; const lower = item.tags.map((t) => t.toLowerCase()); if (lower.some((t) => t.includes('bestseller'))) { out.push({label: 'Bestseller', variant: 'burgundy'}); } if ( item.compareAtPrice && parseFloat(item.compareAtPrice.amount) > parseFloat(item.price.amount) ) { out.push({label: 'Sale', variant: 'burgundy'}); } if (lower.some((t) => t === 'new' || t.includes('new'))) { out.push({label: 'New', variant: 'gold'}); } return out; } export type AbiirProductListItemProps = { item: AbiirProductListItemData; index?: number; /** Heart on the image; default `true`. */ showWishlist?: boolean; /** Replaces the default built-in wishlist click handler if set. */ onToggleWishlist?: (e: MouseEvent) => void; /** Image hover strip + Add to Bag */ showQuickAdd?: boolean; onQuickAdd?: (e: MouseEvent) => void; /** * `homepageFeatured` — homepage “Most Loved” strip only: no corner badges, review row, or benefit chips * (CMS can carry demo ratings/tags that do not match the PDP). */ variant?: 'default' | 'homepageFeatured'; }; /** * Pixel-matched product card from `React/src/app/components/FeaturedProductCard.tsx`. * Uses Shopify `Image` + `Money`; links to `/products/:handle`. */ export function AbiirProductListItem({ item, index = 0, showWishlist = true, onToggleWishlist: onToggleWishlistProp, showQuickAdd = true, onQuickAdd, variant = 'default', }: AbiirProductListItemProps) { const {wishlisted, toggleWishlist} = useAbiirProductWishlist( item.handle?.trim() ?? '', ); const onWishlistButtonClick = useCallback( (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (onToggleWishlistProp) { onToggleWishlistProp(e); return; } toggleWishlist(); }, [onToggleWishlistProp, toggleWishlist], ); const variantUrl = useVariantUrl(item.handle); const badges = productBadges(item); const isHomeFeatured = variant === 'homepageFeatured'; const showImageBadges = !isHomeFeatured && badges.length > 0; const hasReviewData = item.rating > 0 && item.reviewCount > 0; const showReviewsRow = !isHomeFeatured && hasReviewData; const showBenefitChips = !isHomeFeatured && item.benefits.length > 0; const starCount = item.rating >= 4.75 ? 5 : Math.max(0, Math.min(5, Math.round(item.rating))); const averageLabel = (() => { if (!Number.isFinite(item.rating) || item.rating <= 0) return ''; const x = Math.round(item.rating * 10) / 10; return Number.isInteger(x) ? String(x) : x.toFixed(1); })(); const countLabel = item.reviewCount === 1 ? '1 review' : `${item.reviewCount} reviews`; const imageData = item.imageForCard; const canQuickAdd = Boolean(item.firstVariantId) && item.availableForSale !== false; const onQuickAddButtonClick = useCallback( (e: MouseEvent) => { // Prevent the surrounding product `Link` from navigating. e.preventDefault(); e.stopPropagation(); onQuickAdd?.(e); }, [onQuickAdd], ); return (
{imageData ? ( ) : null} {showImageBadges ? (
{badges.map((b) => ( {b.label} ))}
) : null} {showWishlist ? ( ) : null} {showQuickAdd ? (
{onQuickAdd ? ( ) : item.firstVariantId ? ( {(fetcher) => ( )} ) : ( )}
) : null}

{item.subtitle}

{item.displayTitle}

{item.compareAtPrice ? (
) : null}
{showReviewsRow ? (
{Array.from({length: 5}).map((_, i) => ( ))}
{averageLabel ? ( {averageLabel} ) : null} ({countLabel})
) : null} {showBenefitChips ? (
{item.benefits.map((benefit) => ( {benefit} ))}
) : null}
); }