import { createFileRoute, useNavigate, useSearch, } from "@tanstack/react-router"; import { useState, useMemo, useEffect } from "react"; import dayjs from "dayjs"; import { p, div, Quantifier, MiniQuantifier, } from "web-components"; import { useSlots, useAllProducts, useStores, } from "../hooks/prominent-api-hooks"; import { useCentralProductStore } from "../lib/stores/central-product-store"; import { useCentralSlotStore } from "../lib/stores/central-slot-store"; import { useAddToCart, useGetCart, useUpdateCartItem, useRemoveFromCart, } from "../hooks/cart-query-hooks"; import { usePopulateCentralProductStore } from "../hooks/usePopulateCentralProductStore"; import { AppLayout } from "../components/AppLayout"; import { Truck, Store, Grid3X3, ChevronLeft, ShoppingCart, Clock, ChevronDown } from "lucide-react"; import { Dialog } from "../components/Dialog"; export const Route = createFileRoute("/slot-view")({ component: SlotViewPage, }); function SlotViewPage() { const search = useSearch({ from: "/slot-view" }) as { slotId?: string; storeId?: string; }; const slotId = search.slotId ? Number(search.slotId) : undefined; const storeId = search.storeId ? Number(search.storeId) : undefined; const navigate = useNavigate(); const { data: slotsData } = useSlots(); const { productsById } = useCentralProductStore(); const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap); const { data: storesData } = useStores(); const [showSlotDialog, setShowSlotDialog] = useState(false); // Populate central product store with products data usePopulateCentralProductStore(); const stores = storesData?.stores || []; // Find the specific slot from cached data const slot = slotsData?.slots?.find((s: any) => s.id === slotId); // Find earliest slot for pre-selection const earliestSlot = slotsData?.slots ?.filter((s: any) => dayjs(s.deliveryTime).isAfter(dayjs())) .sort((a: any, b: any) => dayjs(a.deliveryTime).diff(dayjs(b.deliveryTime)))[0]; const addToCart = useAddToCart("regular"); const { data: cartData } = useGetCart("regular"); 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}`; } }; const formatFullDisplay = (deliveryTime: string) => { const time = dayjs(deliveryTime); const endTime = time.add(1, "hour"); const startPeriod = time.format("A"); const endPeriod = endTime.format("A"); let timeRange; if (startPeriod === endPeriod) { timeRange = `${time.format("h")}-${endTime.format("h")} ${startPeriod}`; } else { timeRange = `${time.format("h:mm")} ${startPeriod} - ${endTime.format("h:mm")} ${endPeriod}`; } return `${time.format("ddd, DD MMM ")}${timeRange}`; }; // Get current selected slot display const getCurrentSlotDisplay = () => { if (slotId) { const s = slotsData?.slots?.find((s: any) => s.id === slotId); return s ? formatFullDisplay(s.deliveryTime as any) : 'Select time'; } if (earliestSlot) { return formatFullDisplay(earliestSlot.deliveryTime as any); } return 'Select time'; }; const handleAddToCart = (productId: number) => { const item = filteredProducts.find((p) => p.id === productId); const deliveryTime = slot?.deliveryTime ? dayjs(slot.deliveryTime).format("ddd, DD MMM • h:mm A") : ""; addToCart.mutate( { productId, quantity: 1, slotId: slotId || 0, storeId: item?.storeId }, { onSuccess: () => { alert( `Added ${item?.name || "item"} for delivery at ${deliveryTime}`, ); }, }, ); }; const handleSlotChange = (newSlotId: number) => { navigate({ to: "/slot-view", search: { slotId: newSlotId, storeId }, }); setShowSlotDialog(false); }; // Get product details from central store using slot product IDs const slotProducts = slot?.products ?.map((p: any) => productsById[p.id]) ?.filter( (product: any): product is NonNullable => product !== null && product !== undefined, ) ?.filter((product: any) => !productSlotsMap[product.id]?.isOutOfStock) || []; const filteredProducts = storeId ? slotProducts.filter((p: any) => p.storeId === storeId) : slotProducts; // Transform slots for display const slotOptions = slotsData?.slots ?.filter((s: any) => dayjs(s.deliveryTime).isAfter(dayjs())) .sort((a: any, b: any) => dayjs(a.deliveryTime).diff(dayjs(b.deliveryTime))) .map((s: any) => ({ id: s.id, deliveryTime: formatFullDisplay(s.deliveryTime), closeTime: dayjs(s.freezeTime).format("h:mm A"), })) || []; if (!slot) { return (

Slot View

No delivery slot available.

); } return (
{/* Header with Dropdown - Matching Mobile App */}
{/* Back Button */} {/* Delivery Time Dropdown */}
{/* StoreSidebar - Fixed width on left for both mobile and desktop */}
navigate({ to: "/slot-view", search: { slotId, storeId: newStoreId }, }) } onAllSelect={() => navigate({ to: "/slot-view", search: { slotId } }) } />
{/* Products Grid */}

{storeId ? stores.find((s: any) => s.id === storeId)?.name || "Store Products" : "All Products"}

{filteredProducts.length} items

{filteredProducts.map((product: any) => ( navigate({ to: "/home/product/$id", params: { id: String(product.id) }, }) } /> ))}
{filteredProducts.length === 0 && (

{storeId ? "No products from this store in this slot." : "No products available for this slot."}

)}
{/* Slot Selection Dialog */} setShowSlotDialog(false)} title="Select Delivery Time">
{slotOptions.map((slotOption) => ( ))}
); } interface StoreSidebarProps { stores: any[]; slotId?: number; storeId?: number; onStoreSelect: (storeId: number) => void; onAllSelect: () => void; } function StoreSidebar({ stores, slotId, storeId, onStoreSelect, onAllSelect, }: StoreSidebarProps) { return (
{/* All Products Item */}

ALL

{/* Store Items */} {stores.map((store: any) => { const isActive = storeId === store.id; return (
onStoreSelect(store.id)} className={`flex flex-col items-center rounded-2xl p-2 ${ isActive ? "bg-gradient-to-br from-brand-400 to-brand-600 text-white shadow-lg" : "border border-gray-100 bg-white text-gray-500" }`} >
{store.signedImageUrl ? ( {store.name} ) : ( )}

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

); })}
); } const formatQuantity = ( quantity: number, unit: string, ): { value: string; display: string } => { if (unit?.toLowerCase() === "kg" && quantity < 1) { return { value: `${Math.round(quantity * 1000)} g`, display: `${Math.round(quantity * 1000)}g`, }; } return { value: `${quantity} ${unit}(s)`, display: `${quantity}${unit}` }; }; interface CompactProductCardProps { item: any; handleAddToCart: (productId: number) => void; onPress?: () => void; } function CompactProductCard({ item, handleAddToCart, onPress, }: CompactProductCardProps) { const { data: cartData } = useGetCart("regular"); const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap); const updateCartItem = useUpdateCartItem("regular"); const removeFromCart = useRemoveFromCart("regular"); const cartItem = cartData?.items?.find( (cartItem: any) => cartItem.productId === item.id, ); const quantity = cartItem?.quantity || 0; const isOutOfStock = productSlotsMap[item.id]?.isOutOfStock; const handleQuantityChange = (newQuantity: number) => { if (newQuantity === 0 && cartItem) { removeFromCart.mutate(cartItem.id); } else if (newQuantity === 1 && !cartItem) { handleAddToCart(item.id); } else if (cartItem) { updateCartItem.mutate({ productId: cartItem.id, quantity: newQuantity }); } }; return (
{item.name} {isOutOfStock && (

Out of Stock

)}
{quantity > 0 ? ( ) : (
{ e.stopPropagation(); handleQuantityChange(1); }} >
)}

{item.name}

₹{item.price}

{item.marketPrice && Number(item.marketPrice) > Number(item.price) && (

₹{item.marketPrice}

)}

Qty:{" "} { formatQuantity( item.productQuantity || 1, item.unit || item.unitNotation, ).display }

); }