enh
This commit is contained in:
parent
1f801f2146
commit
ccde89b5c8
2 changed files with 198 additions and 86 deletions
|
|
@ -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..." />
|
||||
|
|
|
|||
|
|
@ -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,6 +38,8 @@ const ProductCard: React.FC<ProductCardProps> = ({
|
|||
onPress,
|
||||
showDeliveryInfo = true,
|
||||
miniView = false,
|
||||
nullIfNotAvailable = false,
|
||||
containerComp: ContainerComp = React.Fragment,
|
||||
}) => {
|
||||
const { data: cartData } = useGetCart();
|
||||
const { getQuickestSlot } = useProductSlotIdentifier();
|
||||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue