This commit is contained in:
2025-11-19 14:34:05 +07:00
parent 08b352f686
commit 3cd2a53a0e
6 changed files with 1107 additions and 14 deletions

View File

@@ -10,10 +10,12 @@ import {
Text,
TouchableOpacity,
View,
Alert,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useCart } from '../../../hooks/useCart';
import { useProduct } from '../../../hooks/useProducts';
import { wishlistApi } from '../../../services/wishlistApi ';
const { width } = Dimensions.get('window');
@@ -25,6 +27,8 @@ export default function ProductDetailScreen() {
const { product, loading, error } = useProduct(productId);
const { addToCart } = useCart();
const [isAddingToCart, setIsAddingToCart] = useState(false);
const [isInWishlist, setIsInWishlist] = useState(false);
const [isLoadingWishlist, setIsLoadingWishlist] = useState(false);
const formatPrice = (price: number) => {
return new Intl.NumberFormat('vi-VN', {
@@ -54,6 +58,35 @@ export default function ProductDetailScreen() {
}
};
const handleToggleWishlist = async () => {
if (!product) return;
setIsLoadingWishlist(true);
try {
if (isInWishlist) {
const response = await wishlistApi.removeFromWishlist(product.productId);
if (response.success) {
setIsInWishlist(false);
Alert.alert('Thành công', response.message);
} else {
Alert.alert('Lỗi', response.message);
}
} else {
const response = await wishlistApi.addToWishlist(product.productId);
if (response.success) {
setIsInWishlist(true);
Alert.alert('Thành công', response.message);
} else {
Alert.alert('Lỗi', response.message);
}
}
} catch (error) {
Alert.alert('Lỗi', error instanceof Error ? error.message : 'Lỗi không xác định');
} finally {
setIsLoadingWishlist(false);
}
};
if (loading) {
return (
<SafeAreaView style={styles.container}>
@@ -90,16 +123,37 @@ export default function ProductDetailScreen() {
<Text style={styles.headerTitle} numberOfLines={1}>
Chi tiết sản phẩm
</Text>
<View style={{ width: 40 }} />
<TouchableOpacity
style={styles.wishlistHeaderBtn}
onPress={handleToggleWishlist}
disabled={isLoadingWishlist}
>
{isLoadingWishlist ? (
<ActivityIndicator size="small" color="#ff6b6b" />
) : (
<Ionicons
name={isInWishlist ? 'heart' : 'heart-outline'}
size={24}
color={isInWishlist ? '#ff6b6b' : '#333'}
/>
)}
</TouchableOpacity>
</View>
<ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
{/* Product Image */}
<Image
source={{ uri: product.imageUrl || 'https://via.placeholder.com/400' }}
style={styles.productImage}
resizeMode="cover"
/>
<View style={styles.imageContainer}>
<Image
source={{ uri: product.imageUrl || 'https://via.placeholder.com/400' }}
style={styles.productImage}
resizeMode="cover"
/>
{product.stockQuantity <= 0 && (
<View style={styles.outOfStockOverlay}>
<Text style={styles.outOfStockText}>HẾT HÀNG</Text>
</View>
)}
</View>
{/* Product Info */}
<View style={styles.content}>
@@ -182,6 +236,26 @@ export default function ProductDetailScreen() {
{/* Bottom Action Buttons */}
<View style={styles.bottomActions}>
<TouchableOpacity
style={[
styles.actionButton,
styles.wishlistActionButton,
isInWishlist && styles.wishlistActionButtonActive,
]}
onPress={handleToggleWishlist}
disabled={isLoadingWishlist}
>
{isLoadingWishlist ? (
<ActivityIndicator size="small" color={isInWishlist ? '#fff' : '#ff6b6b'} />
) : (
<Ionicons
name={isInWishlist ? 'heart' : 'heart-outline'}
size={24}
color={isInWishlist ? '#fff' : '#ff6b6b'}
/>
)}
</TouchableOpacity>
<TouchableOpacity
style={[
styles.actionButton,
@@ -249,14 +323,41 @@ const styles = StyleSheet.create({
color: '#333',
textAlign: 'center',
},
wishlistHeaderBtn: {
width: 40,
height: 40,
justifyContent: 'center',
alignItems: 'center',
},
scrollView: {
flex: 1,
},
productImage: {
imageContainer: {
position: 'relative',
width: width,
height: width,
},
productImage: {
width: '100%',
height: '100%',
backgroundColor: '#f0f0f0',
},
outOfStockOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
outOfStockText: {
fontSize: 18,
fontWeight: '700',
color: '#fff',
letterSpacing: 2,
},
content: {
padding: 16,
},
@@ -308,9 +409,6 @@ const styles = StyleSheet.create({
outOfStockBadge: {
backgroundColor: '#ffebee',
},
outOfStockText: {
color: '#f44336',
},
divider: {
height: 1,
backgroundColor: '#e0e0e0',
@@ -369,6 +467,15 @@ const styles = StyleSheet.create({
borderRadius: 12,
gap: 8,
},
wishlistActionButton: {
flex: 0.5,
backgroundColor: '#fff',
borderWidth: 2,
borderColor: '#ff6b6b',
},
wishlistActionButtonActive: {
backgroundColor: '#ff6b6b',
},
addToCartButton: {
backgroundColor: '#ff9800',
},