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
+}