174 lines
No EOL
6.3 KiB
TypeScript
174 lines
No EOL
6.3 KiB
TypeScript
import React from 'react';
|
|
import { View, Alert, TouchableOpacity, Text } from 'react-native';
|
|
import { Image } from 'expo-image';
|
|
import { tw, theme, MyText, MyTouchableOpacity, Quantifier, MiniQuantifier } from 'common-ui';
|
|
import CartIcon from '@/components/icons/CartIcon';
|
|
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
|
import Ionicons from '@expo/vector-icons/Ionicons';
|
|
import dayjs from 'dayjs';
|
|
import {
|
|
useGetCart,
|
|
useUpdateCartItem,
|
|
useRemoveFromCart,
|
|
useAddToCart,
|
|
} from '@/hooks/cart-query-hooks';
|
|
import { useProductSlotIdentifier } from '@/hooks/useProductSlotIdentifier';
|
|
|
|
|
|
interface ProductCardProps {
|
|
item: any;
|
|
itemWidth: number;
|
|
onPress?: () => void;
|
|
showDeliveryInfo?: boolean;
|
|
miniView?: boolean;
|
|
}
|
|
|
|
const formatQuantity = (quantity: number, unit: string): { value: string; display: string } => {
|
|
if (unit?.toLowerCase() === 'kg' && quantity < 1) {
|
|
return { value: `${Math.round(quantity * 1000)} g`, display: `${Math.round(quantity * 1000)}g` };
|
|
}
|
|
return { value: `${quantity} ${unit}(s)`, display: `${quantity}${unit}` };
|
|
};
|
|
|
|
const ProductCard: React.FC<ProductCardProps> = ({
|
|
item,
|
|
itemWidth,
|
|
onPress,
|
|
showDeliveryInfo = true,
|
|
miniView = false,
|
|
}) => {
|
|
const { data: cartData } = useGetCart();
|
|
const { getQuickestSlot } = useProductSlotIdentifier();
|
|
const updateCartItem = useUpdateCartItem({
|
|
showSuccessAlert: false,
|
|
showErrorAlert: false,
|
|
refetchCart: true,
|
|
});
|
|
const removeFromCart = useRemoveFromCart({
|
|
showSuccessAlert: false,
|
|
showErrorAlert: false,
|
|
refetchCart: true,
|
|
});
|
|
const { addToCart = () => {} } = useAddToCart({
|
|
showSuccessAlert: false,
|
|
showErrorAlert: false,
|
|
refetchCart: true,
|
|
}) || {};
|
|
|
|
// Find current quantity from cart data
|
|
const cartItem = cartData?.items?.find((cartItem: any) => cartItem.productId === item.id);
|
|
const quantity = cartItem?.quantity || 0;
|
|
|
|
// Precompute the next slot and determine display out of stock status
|
|
const slotId = getQuickestSlot(item.id);
|
|
const displayIsOutOfStock = item.isOutOfStock || !slotId;
|
|
|
|
const handleQuantityChange = (newQuantity: number) => {
|
|
if (newQuantity === 0 && cartItem) {
|
|
removeFromCart.mutate({ itemId: cartItem.id });
|
|
} else if (newQuantity === 1 && !cartItem) {
|
|
const slotId = getQuickestSlot(item.id);
|
|
if (!slotId) {
|
|
Alert.alert("Error", "No available delivery slot for this product");
|
|
return;
|
|
}
|
|
addToCart(item.id, 1, slotId, () => {});
|
|
} else if (cartItem) {
|
|
updateCartItem.mutate({ itemId: cartItem.id, quantity: newQuantity });
|
|
}
|
|
};
|
|
|
|
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>
|
|
</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>
|
|
)}
|
|
|
|
{!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>
|
|
);
|
|
};
|
|
|
|
export default ProductCard; |