import React, { useState } from "react";
import { View, Dimensions, Image, Alert, ScrollView, StatusBar as RNStatusBar } from "react-native";
import { StatusBar as ExpoStatusBar } from 'expo-status-bar';
import { LinearGradient } from "expo-linear-gradient";
import { useRouter } from "expo-router";
import {
theme,
tw,
useManualRefresh,
useMarkDataFetchers,
LoadingDialog,
AppContainer,
MyTouchableOpacity,
MyText, MyTextInput, SearchBar, useStatusBarStore,
colors
} from "common-ui";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import FontAwesome5 from "@expo/vector-icons/FontAwesome5";
import { Ionicons } from "@expo/vector-icons";
import ProductCard from "@/components/ProductCard";
import MyFlatList from "common-ui/src/components/flat-list";
import { trpc } from "@/src/trpc-client";
import { useGetCart } from "@/hooks/cart-query-hooks";
import { useProductSlotIdentifier } from "@/hooks/useProductSlotIdentifier";
import FloatingCartBar from "@/components/floating-cart-bar";
import AddressSelector from "@/components/AddressSelector";
import BannerCarousel from "@/components/BannerCarousel";
import TestingPhaseNote from "@/components/TestingPhaseNote";
import { useUserDetails } from "@/src/contexts/AuthContext";
import TabLayoutWrapper from "@/components/TabLayoutWrapper";
import { useNavigationStore } from "@/src/store/navigationStore";
import { useGetEssentialConsts } from "@/src/api-hooks/essential-consts.api";
dayjs.extend(relativeTime);
// import { StatusBar } from "expo-status-bar";
const { width: screenWidth } = Dimensions.get("window");
const itemWidth = screenWidth * 0.45; // 45% of screen width
const gridItemWidth = (screenWidth * 0.9) / 2; // Half of screen width minus padding
const RenderStore = ({
item,
}: {
item: any;
}) => {
const router = useRouter();
const { setNavigatedFromHome, setSelectedStoreId } = useNavigationStore();
const handlePress = () => {
setNavigatedFromHome(true);
setSelectedStoreId(item.id);
router.push('/(drawer)/(tabs)/stores');
};
return (
{item.signedImageUrl ? (
) : (
)}
{item.name.replace(/^The\s+/i, "")}
);
};
const headerColor = colors.secondaryPink;
// Format time range helper
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}`;
}
};
export default function Dashboard() {
const router = useRouter();
const userDetails = useUserDetails();
const [inputQuery, setInputQuery] = useState("");
const [searchQuery, setSearchQuery] = useState("");
const [selectedTagId, setSelectedTagId] = useState(null);
const [isLoadingDialogOpen, setIsLoadingDialogOpen] = useState(false);
const [gradientHeight, setGradientHeight] = useState(0);
const [stickyBarLayout, setStickyBarLayout] = useState({ y: 0, height: 0 });
const [whiteSectionLayout, setWhiteSectionLayout] = useState({ y: 0 });
const [displayedProducts, setDisplayedProducts] = useState([]);
const [endIndex, setEndIndex] = useState(10);
const [hasMore, setHasMore] = useState(true);
const [isLoadingMore, setIsLoadingMore] = useState(false);
const { backgroundColor } = useStatusBarStore();
const { getQuickestSlot } = useProductSlotIdentifier();
const {
data: productsData,
isLoading,
error,
refetch,
} = trpc.common.product.getAllProductsSummary.useQuery({
searchQuery: searchQuery || undefined,
tagId: selectedTagId || undefined,
});
const { data: essentialConsts, isLoading: isLoadingConsts, error: constsError } = useGetEssentialConsts();
const { data: storesData } = trpc.user.stores.getStores.useQuery();
const { data: slotsData } = trpc.user.slots.getSlotsWithProducts.useQuery();
const products = productsData?.products || [];
// Initialize with the first batch of products (only those with available slots)
React.useEffect(() => {
if (products.length > 0 && displayedProducts.length === 0) {
const initialBatchRaw = products;
// Filter to include only products with available slots
const initialBatch = initialBatchRaw.filter(product => {
const slot = getQuickestSlot(product.id);
return slot !== null && slot !== undefined && !product.isOutOfStock;
});
setDisplayedProducts(initialBatch);
setHasMore(products.length > 10);
setEndIndex(10);
}
}, [productsData]);
// Extract popular items IDs as an array to preserve order
const popularItemIds = (() => {
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 [];
})();
const sortedSlots = React.useMemo(() => {
if (!slotsData?.slots) return [];
return slotsData.slots.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));
});
}, [slotsData]);
// Filter products to only include those whose ID exists in popularItemIds, preserving order
// Only filter when both products and essentialConsts are loaded
const popularProducts = popularItemIds
.map(id => products.find(product => product.id === id))
.filter((product): product is NonNullable => product != null);
useManualRefresh(() => {
refetch();
});
useMarkDataFetchers(() => {
refetch();
});
const handleScroll = (event: any) => {
};
if (isLoading || isLoadingConsts) {
return (
{isLoading ? 'Loading products...' : 'Loading app settings...'}
);
}
if (error || constsError) {
return (
Oops!
{error ? 'Failed to load products' : 'Failed to load app settings'}
);
}
return (
{/* */}
{
handleScroll(e);
// Check if we're near the end of the scroll for vertical loading
const { layoutMeasurement, contentOffset, contentSize } = e.nativeEvent;
const paddingToBottom = 40;
if (layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom) {
// Load more products when reaching near the end
if (!isLoadingMore && hasMore) {
// loadMoreProducts();
}
}
}}
scrollEventThrottle={16}
>
router.push("/(drawer)/(tabs)/me")}
style={tw`bg-white/10 rounded-full border border-white/10`}
>
{userDetails?.profileImage ? (
) : (
)}
{
const { y, height } = event.nativeEvent.layout;
setStickyBarLayout({ y, height });
}}
>
{ }}
onPress={() => router.push("/(drawer)/(tabs)/home/search-results")}
editable={false}
containerStyle={tw` bg-white`}
onSubmitEditing={() => {
if (inputQuery.trim()) {
router.push(
`/(drawer)/(tabs)/home/search-results?q=${encodeURIComponent(
inputQuery.trim()
)}`
);
}
}}
returnKeyType="search"
/>
{
const { y, height } = event.nativeEvent.layout;
setGradientHeight(y + height);
}}
>
{/* Stores Section */}
{storesData?.stores && storesData.stores.length > 0 && (
Our Stores
Fresh from our locations
{storesData.stores.map((store, index) => (
))}
)}
{/* Banner Carousel */}
{/* White Section */}
{
const { y } = event.nativeEvent.layout;
setWhiteSectionLayout({ y });
}}
>
{/* Section Title */}
Popular Items
Trending fresh picks just for you
{popularProducts.map((item, index: number) => (
router.push(
`/(drawer)/(tabs)/home/product-detail/${item.id}`
)
}
showDeliveryInfo={false}
miniView={true}
/>
))}
{/* Upcoming Deliveries Section */}
{sortedSlots.length > 0 && (
Upcoming Delivery Slots
Plan your fresh deliveries ahead
{sortedSlots.slice(0, 5).map((slot) => {
const now = dayjs();
const freezeTime = dayjs(slot.freezeTime);
const isClosingSoon =
freezeTime.diff(now, "hour") < 4 && freezeTime.isAfter(now);
return (
router.push(
`/(drawer)/(tabs)/home/slot-view?slotId=${slot.id}`
)
}
activeOpacity={0.9}
>
{/* Status Badge */}
{isClosingSoon && (
CLOSING SOON
)}
{/* Main Time Grid */}
Delivery At
{formatTimeRange(slot.deliveryTime)}
Delivery Slot
Order By
{dayjs(slot.freezeTime).format("h:mm A")}
{dayjs(slot.freezeTime).format("ddd, MMM DD")}
{/* Product Teaser */}
{slot.products.slice(0, 3).map((p, i) => (
0 && tw`-ml-3`,
]}
>
{p.images?.[0] ? (
) : (
)}
))}
View all {slot.products.length} items
);
})}
)}
{/* All Products Section - Vertical Infinite Scroll */}
All Available Products
Browse our complete selection
{/* Product List */}
item.id.toString()}
numColumns={2}
contentContainerStyle={tw`pb-8`}
columnWrapperStyle={tw`py-2`}
renderItem={({ item, index }) => (
router.push(
`/(drawer)/(tabs)/home/product-detail/${item.id}`
)
}
showDeliveryInfo={true}
miniView={false}
key={item.id}
/>
)}
// onEndReached={() => {
// if (!isLoadingMore && hasMore) {
// loadMoreProducts();
// }
// }}
onEndReachedThreshold={0.5}
ListFooterComponent={
isLoadingMore ? (
Loading more...
) : null
}
ListEmptyComponent={
No products available
}
/>
);
}