172 lines
5 KiB
TypeScript
172 lines
5 KiB
TypeScript
import React, { useState } from "react";
|
|
import { View, Dimensions, ScrollView } from "react-native";
|
|
import { useRouter } from "expo-router";
|
|
import {
|
|
tw,
|
|
useManualRefresh,
|
|
useMarkDataFetchers,
|
|
AppContainer,
|
|
MyText,
|
|
MyTouchableOpacity,
|
|
} from "common-ui";
|
|
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
|
|
import ProductCard from "@/components/ProductCard";
|
|
import { trpc } from "@/src/trpc-client";
|
|
import FloatingCartBar from "@/components/floating-cart-bar";
|
|
import TabLayoutWrapper from "@/components/TabLayoutWrapper";
|
|
|
|
const { width: screenWidth } = Dimensions.get("window");
|
|
// Same 3-column hero card sizing used on the home page.
|
|
const heroItemWidth = (screenWidth - 72) / 3;
|
|
const SHOW_MORE_STEP = 6;
|
|
|
|
interface OffersSectionProps {
|
|
title: string;
|
|
subtitle: string;
|
|
products: any[];
|
|
expanded: boolean;
|
|
onExpand: () => void;
|
|
onProductPress: (id: number) => void;
|
|
}
|
|
|
|
// Grid section (home page style): shows the first SHOW_MORE_STEP products
|
|
// with a "Show More" button to reveal the rest.
|
|
const OffersSection = ({
|
|
title,
|
|
subtitle,
|
|
products,
|
|
expanded,
|
|
onExpand,
|
|
onProductPress,
|
|
}: OffersSectionProps) => {
|
|
const visible = expanded ? products : products.slice(0, SHOW_MORE_STEP);
|
|
const hasMore = products.length > SHOW_MORE_STEP && !expanded;
|
|
|
|
return (
|
|
<View style={tw`pt-4`}>
|
|
<View style={tw`mb-2 px-4`}>
|
|
<MyText style={tw`text-2xl font-extrabold text-gray-900 tracking-tight`}>
|
|
{title}
|
|
</MyText>
|
|
<MyText style={tw`text-sm text-gray-500 font-medium`}>
|
|
{subtitle}
|
|
</MyText>
|
|
</View>
|
|
|
|
{products.length === 0 ? (
|
|
<View style={tw`items-center justify-center py-10`}>
|
|
<MaterialIcons name="local-offer" size={28} color="#9CA3AF" />
|
|
<MyText style={tw`text-gray-500 mt-2`}>
|
|
No {title.toLowerCase()} available right now
|
|
</MyText>
|
|
</View>
|
|
) : (
|
|
<>
|
|
<View style={tw`flex-row flex-wrap justify-between px-4`}>
|
|
{visible.map((item: any) => (
|
|
<View key={item.id} style={tw`mb-3`}>
|
|
<ProductCard
|
|
item={item}
|
|
itemWidth={heroItemWidth}
|
|
onPress={() => onProductPress(item.id)}
|
|
showDeliveryInfo={false}
|
|
useAddToCartDialog={true}
|
|
miniView={true}
|
|
variant="hero"
|
|
/>
|
|
</View>
|
|
))}
|
|
</View>
|
|
{hasMore && (
|
|
<MyTouchableOpacity
|
|
style={tw`self-center mt-1 mb-2 px-5 py-2.5 rounded-full bg-brand500 shadow-sm`}
|
|
activeOpacity={0.85}
|
|
onPress={onExpand}
|
|
>
|
|
<MyText style={tw`text-white text-sm font-semibold`}>Show More</MyText>
|
|
</MyTouchableOpacity>
|
|
)}
|
|
</>
|
|
)}
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default function Offers() {
|
|
const router = useRouter();
|
|
const [expandedOffers, setExpandedOffers] = useState(false);
|
|
const [expandedCombos, setExpandedCombos] = useState(false);
|
|
|
|
const { data, isLoading, error, refetch } =
|
|
trpc.user.product.getOffersPage.useQuery();
|
|
|
|
const combos = data?.combos || [];
|
|
const offers = data?.offers || [];
|
|
|
|
useManualRefresh(() => {
|
|
refetch();
|
|
});
|
|
|
|
useMarkDataFetchers(() => {
|
|
refetch();
|
|
});
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<AppContainer>
|
|
<View style={tw`flex-1 justify-center items-center bg-gray-50`}>
|
|
<MaterialIcons name="local-offer" size={48} color="#3B82F6" />
|
|
<MyText style={tw`text-gray-500 font-medium mt-4`}>
|
|
Loading offers...
|
|
</MyText>
|
|
</View>
|
|
</AppContainer>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<AppContainer>
|
|
<View style={tw`flex-1 justify-center items-center bg-gray-50`}>
|
|
<MaterialIcons name="error-outline" size={48} color="#EF4444" />
|
|
<MyText style={tw`text-gray-900 text-lg font-bold mt-4`}>Oops!</MyText>
|
|
<MyText style={tw`text-gray-500 mt-2`}>
|
|
Failed to load offers
|
|
</MyText>
|
|
</View>
|
|
</AppContainer>
|
|
);
|
|
}
|
|
|
|
const handleProductPress = (id: number) => {
|
|
router.push(`/(drawer)/(tabs)/order-again/product-detail/${id}`);
|
|
};
|
|
|
|
return (
|
|
<TabLayoutWrapper>
|
|
<ScrollView style={tw`flex-1`} contentContainerStyle={tw`pb-20`}>
|
|
<OffersSection
|
|
title="Offers"
|
|
subtitle="Great prices on select items"
|
|
products={offers}
|
|
expanded={expandedOffers}
|
|
onExpand={() => setExpandedOffers(true)}
|
|
onProductPress={handleProductPress}
|
|
/>
|
|
|
|
<OffersSection
|
|
title="Combos"
|
|
subtitle="Bundle your favorites and save"
|
|
products={combos}
|
|
expanded={expandedCombos}
|
|
onExpand={() => setExpandedCombos(true)}
|
|
onProductPress={handleProductPress}
|
|
/>
|
|
</ScrollView>
|
|
|
|
<View style={tw`absolute bottom-2 left-4 right-4`}>
|
|
<FloatingCartBar />
|
|
</View>
|
|
</TabLayoutWrapper>
|
|
);
|
|
}
|