This commit is contained in:
shafi54 2026-01-24 15:39:42 +05:30
parent 1f801f2146
commit ccde89b5c8
2 changed files with 198 additions and 86 deletions

View file

@ -96,6 +96,10 @@ export default function Dashboard() {
const [gradientHeight, setGradientHeight] = useState(0);
const [stickyBarLayout, setStickyBarLayout] = useState({ y: 0, height: 0 });
const [whiteSectionLayout, setWhiteSectionLayout] = useState({ y: 0 });
const [displayedProducts, setDisplayedProducts] = useState<any[]>([]);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [isLoadingMore, setIsLoadingMore] = useState(false);
const { backgroundColor } = useStatusBarStore();
const {
@ -121,6 +125,47 @@ export default function Dashboard() {
const defaultAddress = defaultAddressResponse?.data;
const { getQuickestSlot } = useProductSlotIdentifier();
// Function to load more products
const loadMoreProducts = () => {
if (!hasMore || isLoadingMore) return;
setIsLoadingMore(true);
// Simulate loading more products by taking the next batch
// In a real app, you would make an API call with pagination params
setTimeout(() => {
const batchSize = 10;
const startIndex = page * batchSize;
const endIndex = startIndex + batchSize;
// Get the next batch of products
const nextBatch = products.slice(startIndex, endIndex);
if (nextBatch.length > 0) {
setDisplayedProducts(prev => [...prev, ...nextBatch]);
setPage(prev => prev + 1);
setHasMore(endIndex < products.length);
} else {
setHasMore(false);
}
setIsLoadingMore(false);
}, 500); // Simulate network delay
};
// Initialize with the first batch of products
React.useEffect(() => {
if (products.length > 0) {
const initialBatch = products.slice(0, 10); // First 10 products
setDisplayedProducts(initialBatch);
setHasMore(products.length > 10);
setPage(1);
} else {
setDisplayedProducts([]);
setHasMore(false);
}
}, [products]);
// Extract popular items IDs as an array to preserve order
const popularItemIds = (() => {
const popularItems = essentialConsts?.popularItems;
@ -202,7 +247,20 @@ export default function Dashboard() {
<ScrollView
style={[tw`flex-1 bg-white`, { position: 'relative' }]}
stickyHeaderIndices={[2]}
onScroll={handleScroll}
onScroll={(e) => {
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}
>
<LinearGradient
@ -363,7 +421,7 @@ export default function Dashboard() {
<MyText
style={tw`text-2xl font-extrabold text-gray-900 tracking-tight`}
>
Delivery Slots
Upcoming Delivery Slots
</MyText>
<MyText style={tw`text-sm text-gray-500 font-medium mt-1`}>
Plan your fresh deliveries ahead
@ -516,6 +574,49 @@ export default function Dashboard() {
</ScrollView>
</View>
)}
{/* All Products Section - Vertical Infinite Scroll */}
<View style={tw`mt-2 mb-4`}>
<View style={tw`flex-row items-center justify-between px-1 mb-6`}>
<View>
<MyText
style={tw`text-2xl font-extrabold text-gray-900 tracking-tight`}
>
All Available Products
</MyText>
<MyText style={tw`text-sm text-gray-500 font-medium mt-1`}>
Browse our complete selection
</MyText>
</View>
</View>
{/* Product Grid */}
<View style={tw`flex-row flex-wrap`}>
{displayedProducts.map((item, index: number) => (
<ProductCard
item={item}
itemWidth={(screenWidth * 0.9) / 2} // Half of screen width minus padding
onPress={() =>
router.push(
`/(drawer)/(tabs)/home/product-detail/${item.id}`
)
}
showDeliveryInfo={true}
miniView={false}
nullIfNotAvailable={true}
containerComp={({children}) => <View key={item.id} style={tw`w-1/2 pr-2 pb-4`}>{children}</View>}
/>
))}
</View>
{isLoadingMore && (
<View style={tw`items-center py-4`}>
<MyText>Loading more...</MyText>
</View>
)}
</View>
</View>
<LoadingDialog open={isLoadingDialogOpen} message="Adding to cart..." />

View file

@ -21,6 +21,8 @@ interface ProductCardProps {
onPress?: () => void;
showDeliveryInfo?: boolean;
miniView?: boolean;
nullIfNotAvailable?: boolean;
containerComp?: React.ComponentType<any> | React.JSXElementConstructor<any>;
}
const formatQuantity = (quantity: number, unit: string): { value: string; display: string } => {
@ -36,7 +38,9 @@ const ProductCard: React.FC<ProductCardProps> = ({
onPress,
showDeliveryInfo = true,
miniView = false,
}) => {
nullIfNotAvailable = false,
containerComp: ContainerComp = React.Fragment,
}) => {
const { data: cartData } = useGetCart();
const { getQuickestSlot } = useProductSlotIdentifier();
const updateCartItem = useUpdateCartItem({
@ -63,6 +67,11 @@ const ProductCard: React.FC<ProductCardProps> = ({
const slotId = getQuickestSlot(item.id);
const displayIsOutOfStock = item.isOutOfStock || !slotId;
// Return null if nullIfNotAvailable is true and the product is out of stock
if (nullIfNotAvailable && displayIsOutOfStock) {
return null;
}
const handleQuantityChange = (newQuantity: number) => {
if (newQuantity === 0 && cartItem) {
removeFromCart.mutate({ itemId: cartItem.id });
@ -79,95 +88,97 @@ const ProductCard: React.FC<ProductCardProps> = ({
};
return (
<MyTouchableOpacity
style={[
tw`bg-white rounded-2xl overflow-hidden border border-gray-200`,
{ width: itemWidth },
]}
onPress={onPress || (() => {/* TODO: Navigate to product detail */})}
activeOpacity={0.9}
>
<View style={tw`relative`}>
<Image
source={{ uri: item.images?.[0] }}
style={{ width: "100%", height: itemWidth, resizeMode: "cover" }}
/>
{displayIsOutOfStock && (
<View style={tw`absolute inset-0 bg-black/40 items-center justify-center`}>
<View style={tw`bg-red-500 px-3 py-1 rounded-full`}>
<MyText style={tw`text-white text-xs font-bold`}>Out of Stock</MyText>
<ContainerComp>
<MyTouchableOpacity
style={[
tw`bg-white rounded-2xl overflow-hidden border border-gray-200 pb-2`,
{ width: itemWidth },
]}
onPress={onPress || (() => {/* TODO: Navigate to product detail */})}
activeOpacity={0.9}
>
<View style={tw`relative`}>
<Image
source={{ uri: item.images?.[0] }}
style={{ width: "100%", height: itemWidth, resizeMode: "cover" }}
/>
{displayIsOutOfStock && (
<View style={tw`absolute inset-0 bg-black/40 items-center justify-center`}>
<View style={tw`bg-red-500 px-3 py-1 rounded-full`}>
<MyText style={tw`text-white text-xs font-bold`}>Out of Stock</MyText>
</View>
</View>
)}
{miniView && (
<View style={tw`absolute bottom-2 right-2`}>
{quantity > 0 ? (
<MiniQuantifier value={quantity} onChange={handleQuantityChange} step={item.incrementStep} />
) : (
<MyTouchableOpacity
style={tw`w-8 h-8 rounded-full bg-white items-center justify-center shadow-md`}
onPress={() => handleQuantityChange(1)}
activeOpacity={0.8}
>
<CartIcon focused={false} size={16} color="#2E90FA" />
</MyTouchableOpacity>
)}
</View>
</View>
)}
{miniView && (
<View style={tw`absolute bottom-2 right-2`}>
{quantity > 0 ? (
<MiniQuantifier value={quantity} onChange={handleQuantityChange} step={item.incrementStep} />
) : (
<MyTouchableOpacity
style={tw`w-8 h-8 rounded-full bg-white items-center justify-center shadow-md`}
onPress={() => handleQuantityChange(1)}
activeOpacity={0.8}
>
<CartIcon focused={false} size={16} color="#2E90FA" />
</MyTouchableOpacity>
)}
</View>
)}
</View>
<View style={tw`px-3 pt-3`}>
<MyText style={tw`text-gray-900 font-bold text-sm mb-1`} numberOfLines={2}>
{item.name}
</MyText>
<View style={tw`flex-row items-baseline mb-2`}>
<MyText style={tw`text-brand500 font-bold text-base`}>{item.price}</MyText>
{item.marketPrice && Number(item.marketPrice) > Number(item.price) && (
<MyText style={tw`text-gray-400 text-xs ml-2 line-through`}>{item.marketPrice}</MyText>
)}
</View>
<View style={tw`flex-row items-center mb-2`}>
<MyText style={tw`text-gray-500 text-xs font-medium`}>Quantity: <MyText style={tw`text-[#f81260] font-semibold`}>{formatQuantity(item.productQuantity || 1, item.unitNotation).display}</MyText></MyText>
</View>
{showDeliveryInfo && item.nextDeliveryDate && (
<View style={tw`flex-row items-center bg-brand50 px-2 py-1.5 rounded-lg self-start mb-2 border border-brand100`}>
<MaterialIcons name="local-shipping" size={12} color="#2E90FA" />
<MyText style={tw`text-[10px] text-brand700 ml-1.5 font-bold`}>
{dayjs(item.nextDeliveryDate).format("ddd, DD MMM • h:mm A")}
</MyText>
</View>
)}
<View style={tw`px-3 pt-3`}>
<MyText style={tw`text-gray-900 font-bold text-sm mb-1`} numberOfLines={2}>
{item.name}
</MyText>
{!miniView && (
<>
{displayIsOutOfStock ? (
<View style={tw`bg-gray-100 rounded-lg items-center mt-1`}>
<MyText style={tw`text-gray-400 text-xs font-bold uppercase tracking-wide`}>Unavailable</MyText>
</View>
) : quantity > 0 ? (
<Quantifier
value={quantity}
setValue={handleQuantityChange}
step={item.incrementStep}
unit={item.unitNotation}
/>
) : (
<MyTouchableOpacity
style={tw`bg-brand500 py-2 rounded-lg items-center mt-1`}
onPress={() => handleQuantityChange(1)}
>
<View style={tw`flex-row items-center`}>
<CartIcon focused={false} size={16} color="white" />
<MyText style={tw`text-white text-xs font-bold uppercase tracking-wide ml-1`}>Add to Cart</MyText>
</View>
</MyTouchableOpacity>
<View style={tw`flex-row items-baseline mb-2`}>
<MyText style={tw`text-brand500 font-bold text-base`}>{item.price}</MyText>
{item.marketPrice && Number(item.marketPrice) > Number(item.price) && (
<MyText style={tw`text-gray-400 text-xs ml-2 line-through`}>{item.marketPrice}</MyText>
)}
</>
)}
</View>
</MyTouchableOpacity>
</View>
<View style={tw`flex-row items-center mb-2`}>
<MyText style={tw`text-gray-500 text-xs font-medium`}>Quantity: <MyText style={tw`text-[#f81260] font-semibold`}>{formatQuantity(item.productQuantity || 1, item.unitNotation).display}</MyText></MyText>
</View>
{showDeliveryInfo && item.nextDeliveryDate && (
<View style={tw`flex-row items-center bg-brand50 px-2 py-1.5 rounded-lg self-start mb-2 border border-brand100`}>
<MaterialIcons name="local-shipping" size={12} color="#2E90FA" />
<MyText style={tw`text-[10px] text-brand700 ml-1.5 font-bold`}>
{dayjs(item.nextDeliveryDate).format("ddd, DD MMM • h:mm A")}
</MyText>
</View>
)}
{!miniView && (
<>
{displayIsOutOfStock ? (
<View style={tw`bg-gray-100 rounded-lg items-center mt-1`}>
<MyText style={tw`text-gray-400 text-xs font-bold uppercase tracking-wide`}>Unavailable</MyText>
</View>
) : quantity > 0 ? (
<Quantifier
value={quantity}
setValue={handleQuantityChange}
step={item.incrementStep}
unit={item.unitNotation}
/>
) : (
<MyTouchableOpacity
style={tw`bg-brand500 py-2 rounded-lg items-center mt-1`}
onPress={() => handleQuantityChange(1)}
>
<View style={tw`flex-row items-center`}>
<CartIcon focused={false} size={16} color="white" />
<MyText style={tw`text-white text-xs font-bold uppercase tracking-wide ml-1`}>Add to Cart</MyText>
</View>
</MyTouchableOpacity>
)}
</>
)}
</View>
</MyTouchableOpacity>
</ContainerComp>
);
};