Files
project_it207_client/components/CartItemCard.tsx
2025-11-18 16:18:43 +07:00

200 lines
4.8 KiB
TypeScript

import React from 'react';
import {
View,
Text,
Image,
StyleSheet,
TouchableOpacity,
Alert,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { CartItem } from '../services/cart';
interface CartItemCardProps {
item: CartItem;
onUpdateQuantity: (cartItemId: number, quantity: number) => Promise<boolean>;
onRemove: (cartItemId: number) => Promise<boolean>;
}
export default function CartItemCard({
item,
onUpdateQuantity,
onRemove,
}: CartItemCardProps) {
const handleDecrease = async () => {
if (item.quantity > 1) {
const success = await onUpdateQuantity(item.cartItemId, item.quantity - 1);
if (!success) {
Alert.alert('Lỗi', 'Không thể cập nhật số lượng');
}
} else {
Alert.alert(
'Xác nhận',
'Bạn có muốn xóa sản phẩm này khỏi giỏ hàng?',
[
{ text: 'Hủy', style: 'cancel' },
{
text: 'Xóa',
onPress: async () => {
const success = await onRemove(item.cartItemId);
if (!success) {
Alert.alert('Lỗi', 'Không thể xóa sản phẩm');
}
},
style: 'destructive'
},
]
);
}
};
const handleIncrease = async () => {
const success = await onUpdateQuantity(item.cartItemId, item.quantity + 1);
if (!success) {
Alert.alert('Lỗi', 'Không thể cập nhật số lượng');
}
};
const handleRemove = () => {
Alert.alert(
'Xác nhận',
'Bạn có chắc muốn xóa sản phẩm này?',
[
{ text: 'Hủy', style: 'cancel' },
{
text: 'Xóa',
onPress: async () => {
const success = await onRemove(item.cartItemId);
if (!success) {
Alert.alert('Lỗi', 'Không thể xóa sản phẩm');
}
},
style: 'destructive'
},
]
);
};
const formatPrice = (price: number) => {
return new Intl.NumberFormat('vi-VN', {
style: 'currency',
currency: 'VND',
}).format(price);
};
return (
<View style={styles.container}>
<Image
source={{ uri: item.product.imageUrl }}
style={styles.image}
resizeMode="cover"
/>
<View style={styles.info}>
<Text style={styles.name} numberOfLines={2}>
{item.product.productName}
</Text>
<Text style={styles.price}>{formatPrice(item.product.price)}</Text>
<View style={styles.footer}>
<View style={styles.quantityContainer}>
<TouchableOpacity
style={styles.quantityButton}
onPress={handleDecrease}
>
<Ionicons name="remove" size={18} color="#333" />
</TouchableOpacity>
<Text style={styles.quantity}>{item.quantity}</Text>
<TouchableOpacity
style={styles.quantityButton}
onPress={handleIncrease}
>
<Ionicons name="add" size={18} color="#333" />
</TouchableOpacity>
</View>
<Text style={styles.subtotal}>{formatPrice(item.subtotal)}</Text>
</View>
</View>
<TouchableOpacity style={styles.deleteButton} onPress={handleRemove}>
<Ionicons name="trash-outline" size={20} color="#ff4444" />
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
backgroundColor: '#fff',
padding: 12,
marginBottom: 12,
borderRadius: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
image: {
width: 80,
height: 80,
borderRadius: 8,
backgroundColor: '#f0f0f0',
},
info: {
flex: 1,
marginLeft: 12,
justifyContent: 'space-between',
},
name: {
fontSize: 15,
fontWeight: '600',
color: '#333',
marginBottom: 4,
},
price: {
fontSize: 14,
color: '#ff6b6b',
fontWeight: '500',
},
footer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 8,
},
quantityContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#f5f5f5',
borderRadius: 6,
paddingHorizontal: 4,
},
quantityButton: {
width: 28,
height: 28,
justifyContent: 'center',
alignItems: 'center',
},
quantity: {
fontSize: 15,
fontWeight: '600',
color: '#333',
minWidth: 30,
textAlign: 'center',
},
subtotal: {
fontSize: 16,
fontWeight: '700',
color: '#ff6b6b',
},
deleteButton: {
padding: 8,
justifyContent: 'flex-start',
},
});