151 lines
4.5 KiB
TypeScript
151 lines
4.5 KiB
TypeScript
import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
|
|
import { View, Dimensions } from "react-native";
|
|
import { useRouter, useLocalSearchParams } from "expo-router";
|
|
import {
|
|
tw,
|
|
useManualRefresh,
|
|
useMarkDataFetchers,
|
|
MyFlatList,
|
|
MyText,
|
|
SearchBar,
|
|
} from "common-ui";
|
|
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
|
|
import Fuse from "fuse.js";
|
|
import { useAllProducts } from "@/src/hooks/prominent-api-hooks";
|
|
import ProductCard from "@/components/ProductCard";
|
|
import FloatingCartBar from "@/components/floating-cart-bar";
|
|
|
|
// Debounce hook for search
|
|
function useDebounce<T>(value: T, delay: number): T {
|
|
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
|
|
useEffect(() => {
|
|
const timer = setTimeout(() => {
|
|
setDebouncedValue(value);
|
|
}, delay);
|
|
|
|
return () => {
|
|
clearTimeout(timer);
|
|
};
|
|
}, [value, delay]);
|
|
|
|
return debouncedValue;
|
|
}
|
|
|
|
const { width: screenWidth } = Dimensions.get("window");
|
|
const itemWidth = (screenWidth - 48) / 2;
|
|
|
|
export default function SearchResults() {
|
|
const router = useRouter();
|
|
const { q } = useLocalSearchParams();
|
|
const query = (q as string) || "";
|
|
const [inputQuery, setInputQuery] = useState(query);
|
|
const searchInputRef = useRef<any>(null);
|
|
|
|
// Debounce the search query for automatic search
|
|
const debouncedQuery = useDebounce(inputQuery, 300);
|
|
|
|
useEffect(() => {
|
|
// Focus with requestAnimationFrame for better timing
|
|
requestAnimationFrame(() => {
|
|
searchInputRef.current?.focus();
|
|
});
|
|
}, []);
|
|
|
|
const { data: productsData, isLoading, error, refetch } = useAllProducts();
|
|
|
|
const allProducts = productsData?.products || [];
|
|
|
|
// Client-side search filtering using Fuse.js
|
|
const products = useMemo(() => {
|
|
if (!debouncedQuery.trim()) return allProducts;
|
|
|
|
const fuse = new Fuse(allProducts, {
|
|
keys: [
|
|
'name',
|
|
'shortDescription',
|
|
],
|
|
threshold: 0.3,
|
|
includeScore: true,
|
|
shouldSort: true,
|
|
});
|
|
|
|
const fuseResults = fuse.search(debouncedQuery);
|
|
return fuseResults.map(result => result.item);
|
|
}, [allProducts, debouncedQuery]);
|
|
|
|
useManualRefresh(() => {
|
|
refetch();
|
|
});
|
|
|
|
useMarkDataFetchers(() => {
|
|
refetch();
|
|
});
|
|
|
|
const handleSearch = useCallback(() => {
|
|
// Search is now automatic via debounce, but keep this for manual submit
|
|
}, []);
|
|
|
|
return (
|
|
<View style={tw`flex-1 bg-gray-50`}>
|
|
<MyFlatList
|
|
data={products}
|
|
numColumns={2}
|
|
renderItem={({ item }) => (
|
|
<ProductCard
|
|
item={item}
|
|
itemWidth={itemWidth}
|
|
onPress={() =>
|
|
router.push(`/(drawer)/(tabs)/home/product-detail/${item.id}`)
|
|
}
|
|
showDeliveryInfo={false}
|
|
useAddToCartDialog={true}
|
|
/>
|
|
)}
|
|
keyExtractor={(item) => item.id.toString()}
|
|
columnWrapperStyle={{ gap: 16, justifyContent: "center" }}
|
|
contentContainerStyle={[tw`pb-24`, { gap: 16 }]}
|
|
ListEmptyComponent={
|
|
isLoading ? (
|
|
<View style={tw`flex-1 justify-center items-center py-12 px-4`}>
|
|
<MyText style={tw`text-gray-500 font-medium`}>Loading products...</MyText>
|
|
</View>
|
|
) : (
|
|
<View style={tw`flex-1 justify-center items-center py-12 px-4`}>
|
|
<MaterialIcons name="search-off" size={64} color="#e5e7eb" />
|
|
<MyText style={tw`text-gray-500 mt-4 text-center text-lg font-medium`}>
|
|
No products found
|
|
</MyText>
|
|
{debouncedQuery && (
|
|
<MyText style={tw`text-gray-400 mt-2 text-center`}>
|
|
Try adjusting your search for "{debouncedQuery}"
|
|
</MyText>
|
|
)}
|
|
</View>
|
|
)
|
|
}
|
|
ListHeaderComponent={
|
|
<View style={tw`pt-4 pb-2 px-4`}>
|
|
<SearchBar
|
|
ref={searchInputRef}
|
|
value={inputQuery}
|
|
onChangeText={setInputQuery}
|
|
onSubmitEditing={handleSearch}
|
|
returnKeyType="search"
|
|
/>
|
|
<View style={tw`flex-row justify-between items-center mb-2`}>
|
|
<MyText style={tw`text-lg font-bold text-gray-900`}>
|
|
{debouncedQuery
|
|
? `Search Results for "${debouncedQuery}"`
|
|
: "All Products"}
|
|
</MyText>
|
|
</View>
|
|
</View>
|
|
}
|
|
/>
|
|
<View style={tw`absolute bottom-2 left-4 right-4`}>
|
|
<FloatingCartBar />
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|