import { createFileRoute, useNavigate } from '@tanstack/react-router' import { useState, useEffect, useMemo, useRef } from 'react' import dayjs from 'dayjs' import { p, div, SearchBar, } from 'web-components' import { useAllProducts, useStores, useBanners, useSlots, useGetEssentialConsts, } from '../hooks/prominent-api-hooks' import { useCartStore } from '../lib/stores/cart-store' import { useCentralSlotStore } from '../lib/stores/central-slot-store' import { useCentralProductStore } from '../lib/stores/central-product-store' import { AppLayout } from '../components/AppLayout' import { ProductCard } from '../components/ProductCard' import AddToCartDialog from '../components/AddToCartDialog' import { useProductSlotIdentifier } from '../hooks/useProductSlotIdentifier' import { usePopulateCentralStores } from '../hooks/usePopulateCentralStores' import { Store, ImageOff } from 'lucide-react' // Scroll Indicator Component function ScrollIndicator({ containerRef, itemCount, itemWidth }: { containerRef: React.RefObject itemCount: number itemWidth: number }) { const [activeIndex, setActiveIndex] = useState(0) useEffect(() => { const container = containerRef.current if (!container) return const handleScroll = () => { const scrollLeft = container.scrollLeft const maxScroll = container.scrollWidth - container.clientWidth const scrollProgress = maxScroll > 0 ? scrollLeft / maxScroll : 0 const totalDots = Math.min(itemCount, 5) const newIndex = Math.round(scrollProgress * (totalDots - 1)) setActiveIndex(Math.min(newIndex, totalDots - 1)) } container.addEventListener('scroll', handleScroll, { passive: true }) handleScroll() return () => container.removeEventListener('scroll', handleScroll) }, [containerRef, itemCount]) const totalDots = Math.min(itemCount, 5) if (totalDots <= 1) return null return (
{Array.from({ length: totalDots }).map((_, i) => (
))}
) } export const Route = createFileRoute('/home/')({ component: HomePage }) function HomePage() { const navigate = useNavigate() const { data: productsData } = useAllProducts() const { data: storesData } = useStores() const { data: bannersData } = useBanners() const { data: slotsData } = useSlots() const { data: essentialConsts } = useGetEssentialConsts() const { setAddedToCartProduct } = useCartStore() const { getQuickestSlot } = useProductSlotIdentifier() const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap) // Refs for scrollable sections const popularScrollRef = useRef(null) const slotsScrollRef = useRef(null) // Populate central stores with slots and product data usePopulateCentralStores() const stores = storesData?.stores || [] // const banners = bannersData?.banners || [] const allProducts = productsData?.products || [] const slots = slotsData?.slots || [] // Sort products: in-stock first, then by slot availability const sortedProducts = useMemo(() => { return [...allProducts] .filter((p) => typeof p.id === 'number') .sort((a, b) => { const slotA = getQuickestSlot(a.id) const slotB = getQuickestSlot(b.id) if (slotA && !slotB) return -1 if (!slotA && slotB) return 1 const aOutOfStock = productSlotsMap[a.id]?.isOutOfStock const bOutOfStock = productSlotsMap[b.id]?.isOutOfStock if (aOutOfStock && !bOutOfStock) return 1 if (!aOutOfStock && bOutOfStock) return -1 return 0 }) }, [allProducts, productSlotsMap, getQuickestSlot]) // Get popular products from essential consts const popularItemIds = useMemo(() => { const popularItems = essentialConsts?.popularItems if (!popularItems) return [] if (Array.isArray(popularItems)) { return popularItems.map((id: any) => parseInt(id)).filter((id: number) => !isNaN(id)) } else if (typeof popularItems === 'string') { return popularItems .split(',') .map((id: string) => parseInt(id.trim())) .filter((id: number) => !isNaN(id)) } return [] }, [essentialConsts?.popularItems]) const popularProducts = useMemo(() => { return popularItemIds .map((id) => allProducts.find((product) => product.id === id)) .filter((product): product is NonNullable => product != null) }, [popularItemIds, allProducts]) // Sort slots by delivery time const sortedSlots = useMemo(() => { const now = dayjs() return [...slots] .filter((slot) => dayjs(slot.deliveryTime).isAfter(now)) .sort((a, b) => { const deliveryDiff = dayjs(a.deliveryTime).diff(dayjs(b.deliveryTime)) if (deliveryDiff !== 0) return deliveryDiff return dayjs(a.freezeTime).diff(dayjs(b.freezeTime)) }) }, [slots]) const handleProductPress = (id: number) => { navigate({ to: '/home/product/$id', params: { id: String(id) }, }) } const handleAddToCart = (product: any) => { setAddedToCartProduct({ productId: product.id, product }) } return (
{/* Search Bar */}
navigate({ to: '/home/search' })} />
{/* Banner Carousel - Commented out */} {/* {banners.length > 0 && (
)} */} {/* Download App Banner */}

Get the FreshYo App

Download for exclusive offers & faster checkout

Get App
{/* Stores Section */} {stores.length > 0 && (

Our Stores

Fresh from our locations

{stores.map((store: any) => (
navigate({ to: '/stores/$storeId', params: { storeId: String(store.id) }, }) } />
))}
)} {/* Popular Items Section */} {popularProducts.length > 0 && (

Popular Items

Trending fresh picks just for you

{popularProducts.map((product) => (
handleProductPress(product.id)} showDeliveryInfo={false} miniView={true} useAddToCartDialog={true} />
))}
)} {/* Upcoming Delivery Slots Section */} {sortedSlots.length > 0 && (

Upcoming Delivery Slots

Plan your fresh deliveries ahead

{sortedSlots.slice(0, 5).map((slot) => ( ))}
)} {/* All Products Section */}

All Available Products

Browse our complete selection

{/* Responsive Grid for Products - 2 columns on mobile */}
{sortedProducts.map((product) => ( handleProductPress(product.id)} showDeliveryInfo={true} miniView={false} useAddToCartDialog={true} /> ))}
{sortedProducts.length === 0 && (

No products available

)}
) } function BannerCarousel({ banners }: { banners: any[] }) { const [index, setIndex] = useState(0) const images = banners.map((b: any) => b.imageUrl).filter(Boolean) useEffect(() => { if (images.length <= 1) return const timer = setInterval(() => { setIndex((i) => (i + 1) % images.length) }, 4000) return () => clearInterval(timer) }, [images.length]) if (images.length === 0) return null return (
Banner {images.length > 1 && ( <>
{images.map((_: any, i: number) => (
)}
) } function StoreCard({ store, onClick }: { store: any; onClick: () => void }) { return (
{store.signedImageUrl ? ( {store.name} ) : ( )}

{store.name.replace(/^The\s+/i, '')}

) } function SlotCard({ slot }: { slot: any }) { const navigate = useNavigate() const now = dayjs() const freezeTime = dayjs(slot.freezeTime) const isClosingSoon = freezeTime.diff(now, 'hour') < 4 && freezeTime.isAfter(now) const formatTimeRange = (deliveryTime: string) => { const time = dayjs(deliveryTime) const endTime = time.add(1, 'hour') const startPeriod = time.format('A') const endPeriod = endTime.format('A') if (startPeriod === endPeriod) { return `${time.format('h')}-${endTime.format('h')} ${startPeriod}` } else { return `${time.format('h:mm')} ${startPeriod} - ${endTime.format('h:mm')} ${endPeriod}` } } return (
navigate({ to: '/slot-view', search: { slotId: slot.id } })} className={`min-w-70 shrink-0 cursor-pointer rounded-3xl border border-slate-100 bg-white p-5 shadow-xl ${ isClosingSoon ? 'border-l-4 border-l-amber-400' : 'border-l-4 border-l-brand-500' }`} >
{isClosingSoon && (

CLOSING SOON

)}

Delivery At

{formatTimeRange(slot.deliveryTime)}

{dayjs(slot.deliveryTime).format('ddd, MMM DD')}

Order By

{dayjs(slot.freezeTime).format('h:mm A')}

{dayjs(slot.freezeTime).format('ddd, MMM DD')}

{slot.products?.slice(0, 3).map((p: any, i: number) => (
0 ? '-ml-3' : ''}`} > {p.images?.[0] ? ( ) : (
)}
))}

View all {slot.products?.length || 0} items

) }