import {useCallback, useEffect, useMemo, useState} from 'react'; import {Link} from 'react-router'; import {motion} from 'motion/react'; import {AbiirProductListItem} from '~/components/AbiirProductListItem'; import type { AbiirProductListItemData, FeaturedProductsCms, } from '~/lib/featuredProducts'; import { MOCK_FEATURED_PRODUCT_ROWS, MOCK_FEATURED_PRODUCTS_CMS, } from '~/lib/featuredProducts'; import {PAGE_TITLE_TYPOGRAPHY_CLASS} from '~/lib/pageTitle'; import {useSwipeNavigate} from '~/lib/useSwipeNavigate'; /** Desktop featured grid matches `ABIIR_PRODUCT_LIST_GRID_CLASS` at `lg:` only (`abiirProductListGrid.ts`). */ const FEATURED_DESKTOP_GRID_CLASS = 'hidden w-full max-w-full lg:grid lg:grid-cols-4 lg:gap-x-[34px] lg:gap-y-10'; /** * GROQ: `home` singleton → featured products (Most Loved) module. * Use with `context.sanity.loadQuery(FEATURED_PRODUCTS_GROQ)` or `useQuery` in the browser. */ export const FEATURED_PRODUCTS_GROQ = `*[_type == "home" && _id == "home"][0].featuredProducts{ eyebrow, title, filterPills[]{label, href}, viewAllLabel, viewAllHref, items[]{ productHandle, hideBadge, cardTitle, subtitle, benefitChips, rating, reviewCount } }` as const; export type {FeaturedProductsCms, AbiirProductListItemData}; export {MOCK_FEATURED_PRODUCTS_CMS, MOCK_FEATURED_PRODUCT_ROWS}; const sectionBg = '#f8f4ef'; const burgundy = '#5d0b24'; const charcoal = '#1a1a18'; /** Mobile / tablet featured strip: auto-advance interval (desktop keeps grid). */ const FEATURED_MOBILE_SLIDE_MS = 4000; function FeaturedProductsMobileSlider({rows}: {rows: AbiirProductListItemData[]}) { const [active, setActive] = useState(0); const rowsKey = useMemo( () => rows.map((r) => r.handle).join('\0'), [rows], ); useEffect(() => { setActive(0); }, [rowsKey]); useEffect(() => { if (rows.length <= 1) return; if (typeof window === 'undefined') return; if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; const id = window.setInterval(() => { setActive((i) => (i + 1) % rows.length); }, FEATURED_MOBILE_SLIDE_MS); return () => window.clearInterval(id); }, [rows.length]); const goPrev = useCallback(() => { if (rows.length <= 1) return; setActive((i) => (i - 1 + rows.length) % rows.length); }, [rows.length]); const goNext = useCallback(() => { if (rows.length <= 1) return; setActive((i) => (i + 1) % rows.length); }, [rows.length]); const swipe = useSwipeNavigate(goPrev, goNext); return (
1 ? swipe : {})} >
{rows.map((item, index) => (
))}
{rows.map((_, i) => ( ))}
); } function isExternalHref(href: string) { return /^https?:\/\//i.test(href); } function FilterLink({ href, children, }: { href: string; children: React.ReactNode; }) { const className = 'inline-flex min-h-[44.07px] cursor-pointer items-center justify-center border border-[rgba(26,26,24,0.2)] px-[26.5px] py-[13.75px] font-["Jost",sans-serif] text-[12px] font-medium uppercase leading-[16.575px] tracking-[1.326px] text-[#1a1a18] no-underline transition-opacity duration-200 box-border hover:opacity-70'; if (isExternalHref(href)) { return ( {children} ); } return ( {children} ); } function ViewAllLink({ href, children, }: { href: string; children: React.ReactNode; }) { const className = 'inline-flex min-h-[51.844px] cursor-pointer items-center justify-center bg-[#b39763] px-[34px] py-[17px] font-["Raleway",sans-serif] text-[14px] font-bold uppercase leading-[19.89px] tracking-[2.3868px] text-white no-underline transition-opacity duration-200 box-border hover:opacity-90'; if (isExternalHref(href)) { return ( {children} ); } return ( {children} ); } export type FeaturedProductsProps = { cms: FeaturedProductsCms; /** Merged Shopify + Sanity rows (loader) or mock rows for static preview. */ rows: AbiirProductListItemData[]; }; /** * Homepage “Most Loved” strip — `React/src/app/components/FeaturedProducts.tsx` (Tailwind). * Placed below `BrandValues` on the index route. */ export function FeaturedProducts({cms, rows}: FeaturedProductsProps) { const pills = cms.filterPills.length > 0 ? cms.filterPills : MOCK_FEATURED_PRODUCTS_CMS.filterPills; return (
{cms.eyebrow} {cms.title}
{rows.map((item, index) => ( ))}
{cms.viewAllLabel}
); }