From fa56fd4a8240bd0bbc97850d6503a36843c4fee2 Mon Sep 17 00:00:00 2001 From: shafi54 <108669266+shafi-aviz@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:06:27 +0530 Subject: [PATCH] enh --- .../app/(drawer)/(tabs)/home/index.tsx | 320 ++++++++++-------- apps/user-ui/components/ProductCard.tsx | 38 ++- apps/user-ui/components/TabLayoutWrapper.tsx | 8 +- 3 files changed, 199 insertions(+), 167 deletions(-) diff --git a/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx b/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx index 3ae8a63..59bc521 100755 --- a/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx +++ b/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx @@ -9,8 +9,7 @@ import { useMarkDataFetchers, LoadingDialog, MyTouchableOpacity, - MyText, SearchBar, useStatusBarStore, - colors + MyText, SearchBar } from "common-ui"; import dayjs from "dayjs"; @@ -35,8 +34,8 @@ dayjs.extend(relativeTime); const { width: screenWidth } = Dimensions.get("window"); const itemWidth = screenWidth * 0.45; +const heroItemWidth = (screenWidth - 72) / 3; const gridItemWidth = (screenWidth - 48) / 2; -const headerColor = colors.secondaryPink; const formatTimeRange = (deliveryTime: string) => { const time = dayjs(deliveryTime); @@ -58,16 +57,15 @@ const staticStyles = { slotsListContent: { paddingBottom: 24 }, }; -// Light/pastel color pairs for the Explore Products tabs. const TAG_COLORS = [ - { bg: '#FFE4E6', border: '#FECDD3', text: '#BE123C' }, // rose - { bg: '#FEF3C7', border: '#FDE68A', text: '#B45309' }, // amber - { bg: '#DCFCE7', border: '#BBF7D0', text: '#15803D' }, // green - { bg: '#DBEAFE', border: '#BFDBFE', text: '#1D4ED8' }, // blue - { bg: '#EDE9FE', border: '#DDD6FE', text: '#6D28D9' }, // violet - { bg: '#FFE4CC', border: '#FFD6B0', text: '#C2410C' }, // orange - { bg: '#CFFAFE', border: '#A5F3FC', text: '#0E7490' }, // cyan - { bg: '#FCE7F3', border: '#FBCFE8', text: '#BE185D' }, // pink + { pageBg: '#FFF8F9', bg: '#FFF1F2', border: '#FECDD3', text: '#9F1239', dot: '#E11D48' }, // rose + { pageBg: '#FFFCF1', bg: '#FFFBEB', border: '#FDE68A', text: '#92400E', dot: '#D97706' }, // amber + { pageBg: '#F6FEF9', bg: '#F0FDF4', border: '#BBF7D0', text: '#166534', dot: '#16A34A' }, // green + { pageBg: '#F5FAFF', bg: '#EFF6FF', border: '#BFDBFE', text: '#1E3A8A', dot: '#2563EB' }, // blue + { pageBg: '#FAF8FF', bg: '#F5F3FF', border: '#DDD6FE', text: '#5B21B6', dot: '#7C3AED' }, // violet + { pageBg: '#FFF9F2', bg: '#FFF7ED', border: '#FFD6B0', text: '#9A3412', dot: '#EA580C' }, // orange + { pageBg: '#F3FEFF', bg: '#ECFEFF', border: '#A5F3FC', text: '#155E75', dot: '#0891B2' }, // cyan + { pageBg: '#FFF7FB', bg: '#FDF2F8', border: '#FBCFE8', text: '#9D174D', dot: '#DB2777' }, // pink ]; const getTagColor = (id: number) => TAG_COLORS[id % TAG_COLORS.length]; @@ -217,28 +215,34 @@ interface ExploreTabProps { } const ExploreTab = memo(({ tag, isSelected, onPress }: ExploreTabProps) => { - const color = getTagColor(tag.id); - const productCount = tag.productIds?.length || 0; - return ( - - {tag.tagName} ({productCount}) - + + + {tag.tagName} + + + ); }); @@ -252,14 +256,15 @@ const ExploreProductItem = memo(({ item, onPress }: ExploreProductItemProps) => const handlePress = useCallback(() => onPress(item.id), [item.id, onPress]); return ( - + ); @@ -316,19 +321,20 @@ const ListHeader = memo(({ activeTagProducts, onSelectTag, }: ListHeaderProps) => { + const [showAllActiveProducts, setShowAllActiveProducts] = useState(false); const handleLayout = useCallback((event: any) => { const { y, height } = event.nativeEvent.layout; onGradientLayout(y + height); }, [onGradientLayout]); + React.useEffect(() => { + setShowAllActiveProducts(false); + }, [activeTagId]); + const renderPopularItem = useCallback(({ item }: { item: any }) => ( ), [onProductPress]); - const renderExploreItem = useCallback(({ item }: { item: any }) => ( - - ), [onProductPress]); - const renderSlotItem = useCallback(({ item }: { item: any }) => ( ), []); @@ -337,25 +343,92 @@ const ListHeader = memo(({ tw`absolute left-0 right-0 shadow-lg`, { height: gradientHeight + 32, zIndex: -1 } ], [gradientHeight]); + const activeColor = activeTagId == null ? null : getTagColor(activeTagId); + const pageTint = activeColor?.pageBg ?? '#FFFFFF'; + const visibleActiveTagProducts = showAllActiveProducts ? activeTagProducts : activeTagProducts.slice(0, 6); + const hasMoreActiveTagProducts = activeTagProducts.length > visibleActiveTagProducts.length; return ( <> - + + {dashboardTags.length > 0 && ( + + + {dashboardTags.map((tag) => ( + onSelectTag(tag.id)} + /> + ))} + + {activeTagProducts.length > 0 ? ( + + + {visibleActiveTagProducts.map((product: any) => ( + + ))} + + {hasMoreActiveTagProducts && ( + setShowAllActiveProducts(true)} + > + Show More + + )} + + ) : ( + + + No products in this category yet + + + )} + + )} + + + + + + + {storesData?.stores && storesData.stores.length > 0 && ( - + - + Our Stores - Fresh from our locations + Fresh from our locations @@ -369,13 +442,7 @@ const ListHeader = memo(({ )} - - - - - - @@ -404,55 +471,6 @@ const ListHeader = memo(({ /> - {dashboardTags.length > 0 && ( - - - Explore Products - Browse by category - - - {dashboardTags.map((tag) => ( - onSelectTag(tag.id)} - /> - ))} - - {activeTagProducts.length > 0 ? ( - - item.id.toString()} - horizontal - showsHorizontalScrollIndicator={false} - contentContainerStyle={staticStyles.popularListContent} - renderItem={renderExploreItem} - removeClippedSubviews={true} - /> - - - ) : ( - - - No products in this category yet - - - )} - - )} - {sortedSlots.length > 0 && ( @@ -497,7 +515,6 @@ export default function Dashboard() { const [displayedProducts, setDisplayedProducts] = useState([]); const [hasMore, setHasMore] = useState(true); const [isLoadingMore, setIsLoadingMore] = useState(false); - const { backgroundColor } = useStatusBarStore(); const { getQuickestSlot } = useProductSlotIdentifier(); const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap); const refetchProducts = useCentralProductStore((state) => state.refetchProducts); @@ -660,10 +677,16 @@ export default function Dashboard() { /> ), [gradientHeight, handleGradientLayout, storesData, popularProducts, sortedSlots, handleProductPress, dashboardTags, activeTagId, activeTagProducts]); + const pageTint = activeTagId == null ? '#FFFFFF' : getTagColor(activeTagId).pageBg; + const pageTintStyle = useMemo(() => [ + tw`flex-1`, + { backgroundColor: pageTint } + ], [pageTint]); + const searchBarContainerStyle = useMemo(() => [ - tw`w-full px-4 pt-4 pb-2`, - { backgroundColor } - ], [backgroundColor]); + tw`w-full px-4 pt-4 pb-0`, + { backgroundColor: pageTint } + ], [pageTint]); const listContentContainerStyle = useMemo(() => [ tw`pb-24`, @@ -695,55 +718,58 @@ export default function Dashboard() { displayedProducts.forEach(product => str += `${product.id}-`) // console.log(str) return ( - - - { }} - onPress={handleSearchPress} - editable={false} - containerStyle={tw`bg-white`} - onSubmitEditing={() => { - if (inputQuery.trim()) { - router.push(`/(drawer)/(tabs)/home/search-results?q=${encodeURIComponent(inputQuery.trim())}`); - } - }} - returnKeyType="search" - /> - - - item.id.toString()} - numColumns={2} - contentContainerStyle={listContentContainerStyle} - columnWrapperStyle={staticStyles.columnWrapper} - renderItem={renderProductItem} - ListHeaderComponent={listHeader} - refreshControl={ - + + + { }} + onPress={handleSearchPress} + editable={false} + containerStyle={tw`bg-white`} + onSubmitEditing={() => { + if (inputQuery.trim()) { + router.push(`/(drawer)/(tabs)/home/search-results?q=${encodeURIComponent(inputQuery.trim())}`); + } + }} + returnKeyType="search" /> - } - ListEmptyComponent={ - - No products available - - } - removeClippedSubviews={true} - maxToRenderPerBatch={10} - windowSize={5} - initialNumToRender={10} - updateCellsBatchingPeriod={50} - /> + - - - - + item.id.toString()} + numColumns={2} + style={{ backgroundColor: pageTint }} + contentContainerStyle={listContentContainerStyle} + columnWrapperStyle={staticStyles.columnWrapper} + renderItem={renderProductItem} + ListHeaderComponent={listHeader} + refreshControl={ + + } + ListEmptyComponent={ + + No products available + + } + removeClippedSubviews={true} + maxToRenderPerBatch={10} + windowSize={5} + initialNumToRender={10} + updateCellsBatchingPeriod={50} + /> + + + + + + ); diff --git a/apps/user-ui/components/ProductCard.tsx b/apps/user-ui/components/ProductCard.tsx index 4c16022..549705c 100644 --- a/apps/user-ui/components/ProductCard.tsx +++ b/apps/user-ui/components/ProductCard.tsx @@ -24,6 +24,7 @@ interface ProductCardProps { onPress?: () => void; showDeliveryInfo?: boolean; miniView?: boolean; + variant?: 'default' | 'hero'; nullIfNotAvailable?: boolean; containerComp?: React.ComponentType | React.JSXElementConstructor; useAddToCartDialog?: boolean; @@ -42,10 +43,12 @@ const ProductCard: React.FC = ({ onPress, showDeliveryInfo = true, miniView = false, + variant = 'default', nullIfNotAvailable = false, containerComp: ContainerComp = React.Fragment, useAddToCartDialog = false, }) => { + const isHero = variant === 'hero'; const imageUri = item.images?.[0] const [imageStatus, setImageStatus] = React.useState<'loading' | 'loaded' | 'error'>('loading') const [imageError, setImageError] = React.useState(null) @@ -152,7 +155,8 @@ const ProductCard: React.FC = ({ {/* TODO: Navigate to product detail */})} @@ -162,7 +166,7 @@ const ProductCard: React.FC = ({ { setImageStatus('loading') setImageError(null) @@ -194,36 +198,36 @@ const ProductCard: React.FC = ({ )} {miniView && ( - + {quantity > 0 ? ( ) : ( handleQuantityChange(1)} - activeOpacity={0.8} - > - - - )} + style={isHero ? tw`w-7 h-7 rounded-full bg-white items-center justify-center shadow-md` : tw`w-8 h-8 rounded-full bg-white items-center justify-center shadow-md`} + onPress={() => handleQuantityChange(1)} + activeOpacity={0.8} + > + + + )} )} - - + + {item.name} - - ₹{item.price} + + ₹{item.price} {item.marketPrice && Number(item.marketPrice) > Number(item.price) && ( - ₹{item.marketPrice} + ₹{item.marketPrice} )} - + {item.productType !== 'combo' && ( - Quantity: {item.unitNotation} + Quantity: {item.unitNotation} )} diff --git a/apps/user-ui/components/TabLayoutWrapper.tsx b/apps/user-ui/components/TabLayoutWrapper.tsx index 7292a0b..44d411b 100644 --- a/apps/user-ui/components/TabLayoutWrapper.tsx +++ b/apps/user-ui/components/TabLayoutWrapper.tsx @@ -1,15 +1,17 @@ import React from 'react'; +import type { StyleProp, ViewStyle } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { tw } from 'common-ui'; interface TabLayoutWrapperProps { children: React.ReactNode; + style?: StyleProp; } -export default function TabLayoutWrapper({ children }: TabLayoutWrapperProps) { +export default function TabLayoutWrapper({ children, style }: TabLayoutWrapperProps) { return ( - + {children} ); -} \ No newline at end of file +}