import React, { useEffect, useState, useRef } from 'react'; import { View, Text, TouchableOpacity, ScrollView, Image, Dimensions, NativeSyntheticEvent, NativeScrollEvent, } from 'react-native'; import { useNavigation, useRoute, RouteProp } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; import { getStyles } from './providerDetailsScreen.styles'; import { useAppTheme } from '@theme'; import { addToCartThunk, updateCartItemThunk, } from '../../../store/commonreducers/cart'; import { AppStackParamList } from '../../../navigation/appStack'; import { useAppDispatch, useAppSelector } from '@store'; import { getProductDetailsThunk } from './thunk'; import { getDiscountPercentage, formatPrice } from '@components'; import { getFullUrl } from '@utils'; type ProviderDetailsNavProp = StackNavigationProp< AppStackParamList, 'ProviderDetailsScreen' >; type ProviderDetailsRouteProp = RouteProp< AppStackParamList, 'ProviderDetailsScreen' >; const { width } = Dimensions.get('window'); // TODO: move these into your shared Product type once the backend // response includes them. interface ProductReview { id: string; userName?: string; rating: number; comment?: string; createdAt?: string; } type RatingBreakdown = Partial>; export const ProviderDetailsScreen: React.FC = () => { const { colors } = useAppTheme(); const styles = getStyles(colors); const dispatch = useAppDispatch(); const navigation = useNavigation(); const route = useRoute(); // Notice we still use providerId param name to avoid breaking routing everywhere const { providerId } = route.params; const cartItems = useAppSelector(state => state.cart.items); const { product, isLoading } = useAppSelector(state => state.providerDetails); // Find this product in the cart (if already added) const cartItem = product ? cartItems.find(i => i.productId === product.id) : undefined; const isInCart = !!cartItem; const [activeImageIndex, setActiveImageIndex] = useState(0); const [localQty, setLocalQty] = useState(0); useEffect(() => { if (providerId) { dispatch(getProductDetailsThunk(providerId)); } }, [providerId, dispatch]); // Sync localQty with cart quantity when cart loads or product changes useEffect(() => { if (cartItem) { setLocalQty(cartItem.quantity); } else { setLocalQty(0); } }, [cartItem?.quantity, cartItem?.productId]); const handleScroll = (e: NativeSyntheticEvent) => { const slide = Math.round(e.nativeEvent.contentOffset.x / width); setActiveImageIndex(slide); }; const handleAddToCart = () => { if (!product) return; dispatch( addToCartThunk({ productId: product.id, quantity: Math.max(1, localQty), // always add at least 1 }), ); }; const handleIncrement = () => { if (!product) return; const newQty = localQty + 1; setLocalQty(newQty); dispatch(updateCartItemThunk({ productId: product.id, quantity: newQty })); }; const handleDecrement = () => { if (!product || localQty <= 1) return; const newQty = localQty - 1; setLocalQty(newQty); dispatch(updateCartItemThunk({ productId: product.id, quantity: newQty })); }; const handleRateProduct = () => { navigation.navigate('WriteReviewScreen', { productId: product?.id ?? '' }); }; const renderStars = (value: number, size = 14) => { const rounded = Math.round(value); const starColor = (colors as any).warning ?? '#F5A623'; return ( {[1, 2, 3, 4, 5].map(i => ( {i <= rounded ? '★' : '☆'} ))} ); }; const renderImages = () => { if (isLoading) { return ( ); } const images = product?.media?.length ? product.media : [ { id: 'fallback', url: product?.imageUrl || '', mediaType: 'IMAGE', sortOrder: 0, productId: '', createdAt: '', }, ]; return ( {images.map(img => ( ))} {images.length > 1 && ( {images.map((_, i) => ( ))} )} ); }; const renderProductInfo = () => { if (isLoading || !product) { return ( ); } const discount = getDiscountPercentage( product.price, product.compareAtPrice, ); const currency = product.currency === 'INR' ? '₹' : product.currency; return ( {product.brand || product.merchant?.name || 'BRAND'} {parseFloat(product.avgRating).toFixed(1)} {product.name} {formatPrice(product.price, currency)} {product.compareAtPrice && parseFloat(product.compareAtPrice) > parseFloat(product.price) && ( {formatPrice(product.compareAtPrice, currency)} )} {discount && ( {discount} )} Inclusive of all taxes ); }; const renderRatingsSection = () => { if (isLoading) { return ( ); } const avgRating = product?.avgRating ? parseFloat(product.avgRating) : 0; const reviews: ProductReview[] = (product as any)?.reviews ?? []; const totalRatings: number = (product as any)?.ratingsCount ?? reviews.length ?? 0; const breakdown: RatingBreakdown | undefined = (product as any) ?.ratingBreakdown; const hasRatings = totalRatings > 0; return ( Ratings & Reviews {reviews.length > 3 && ( See all )} {hasRatings ? ( {avgRating.toFixed(1)} {renderStars(avgRating, 16)} {totalRatings} {totalRatings === 1 ? 'rating' : 'ratings'} {breakdown && ( {[5, 4, 3, 2, 1].map(star => { const count = breakdown[star as 1 | 2 | 3 | 4 | 5] ?? 0; const pct = totalRatings > 0 ? (count / totalRatings) * 100 : 0; return ( {star}★ ); })} )} ) : ( No ratings yet Be the first to share what you think of this product. Rate this product )} {reviews.length > 0 && ( {reviews.slice(0, 3).map(review => ( {(review.userName || 'U').charAt(0).toUpperCase()} {review.userName || 'Anonymous'} {renderStars(review.rating, 12)} {review.createdAt ? new Date(review.createdAt).toLocaleDateString('en-IN', { day: 'numeric', month: 'short', }) : ''} {!!review.comment && ( {review.comment} )} ))} )} ); }; return ( {renderImages()} navigation.goBack()} > 🔗 🤍 {renderProductInfo()} Product Details {product?.description || 'No description available for this product.'} Category {product?.category?.name || 'N/A'} SKU {product?.sku || 'N/A'} Stock {product?.stockQuantity ? 'In Stock' : 'Out of Stock'} Merchant {product?.merchant?.name || 'N/A'} {renderRatingsSection()} {/* Sticky Bottom Bar */} {isInCart ? ( // ── Already in cart: show inline qty stepper + Go to Cart ── <> - {localQty} + navigation.navigate('CartScreen')} > Go to Cart ) : ( // ── Not in cart: qty picker + Add to Cart ── <> setLocalQty(q => Math.max(0, q - 1))} activeOpacity={0.7} disabled={localQty <= 0} > - {localQty} setLocalQty(q => q + 1)} activeOpacity={0.7} > + {!product ? 'Loading...' : product.isTrackStock && product.stockQuantity === 0 ? 'Out of Stock' : 'Add to Cart'} )} ); };