import { useMemo, useState } from 'react'; import { StyleSheet, Text, TextInput, View } from 'react-native'; import PrimaryButton from '../components/PrimaryButton'; import SecondaryButton from '../components/SecondaryButton'; import AppHeader from '../components/layout/AppHeader'; import ScreenContainer from '../components/layout/ScreenContainer'; import SectionTitle from '../components/layout/SectionTitle'; import ProductImage from '../components/product/ProductImage'; import ProductScoreBadge from '../components/product/ProductScoreBadge'; import RatingCategoryRow from '../components/product/RatingCategoryRow'; import { calculateProductRating } from '../services/ratingService'; import { colors } from '../utils/constants'; import { formatPrice } from '../utils/formatPrice'; export default function ProductDetailScreen({ product, preferences, onBack, onAddToList, onShowAlternatives }) { const [savedMessage, setSavedMessage] = useState(''); const [manualPrice, setManualPrice] = useState(''); const [priceError, setPriceError] = useState(''); const rating = useMemo(() => calculateProductRating(product, preferences), [product, preferences]); const hasProductPrice = typeof product.price === 'number'; const currency = product.currency || preferences.currency; function addToList() { const parsedManualPrice = parseManualPrice(manualPrice); if (!hasProductPrice && parsedManualPrice === undefined) { setSavedMessage(''); setPriceError('Bitte gib einen Preis ein, bevor du das Produkt hinzufügst.'); return; } onAddToList({ ...product, currency, price: hasProductPrice ? product.price : parsedManualPrice }); setPriceError(''); setSavedMessage('Produkt wurde zur Einkaufsliste hinzugefügt.'); } return ( {product.brand || 'Ohne Marke'} {product.name} {product.category || 'Produkt'} · {formatPrice(product.price, currency)} Empfehlung: {rating.overallStatus === 'good' ? 'kaufen' : rating.overallStatus === 'medium' ? 'prüfen' : 'meiden'}. {!hasProductPrice ? ( Preis ergänzen Open Food Facts liefert keine Preise. Gib den Ladenpreis ein, damit die Einkaufsliste und Budgetprüfung stimmen. {currency} { setManualPrice(value); setPriceError(''); }} placeholder="z. B. 3.50" placeholderTextColor={colors.muted} style={styles.priceInput} value={manualPrice} /> {priceError ? {priceError} : null} ) : null} Bewertungskriterien {Object.entries(rating.categoryScores).map(([category, categoryRating]) => ( ))} Produktdaten Barcode: {product.barcode} Zutaten: {(product.ingredients || []).join(', ') || 'Keine Angaben'} Allergene: {(product.allergens || []).join(', ') || 'Keine'} Nutri-Score: {product.nutrition?.nutriScore || 'Keine Angabe'} Eco-Score: {product.sustainability?.ecoScore || 'Keine Angabe'} {savedMessage ? {savedMessage} : null} onShowAlternatives(product)} /> ); } function parseManualPrice(value) { const normalizedValue = value.trim().replace(',', '.'); const parsedValue = Number(normalizedValue); if (!normalizedValue || !Number.isFinite(parsedValue) || parsedValue <= 0) { return undefined; } return Math.round(parsedValue * 100) / 100; } const styles = StyleSheet.create({ productPanel: { backgroundColor: colors.surface, borderColor: colors.border, borderRadius: 8, borderWidth: 1, gap: 14, padding: 18 }, topRow: { alignItems: 'flex-start', flexDirection: 'row', gap: 12 }, copy: { flex: 1 }, brand: { color: colors.primary, fontSize: 13, fontWeight: '800', textTransform: 'uppercase' }, name: { color: colors.text, fontSize: 25, fontWeight: '800', lineHeight: 31, marginTop: 4 }, meta: { color: colors.muted, fontSize: 15, marginTop: 6 }, summary: { color: colors.text, fontSize: 16, fontWeight: '700' }, pricePanel: { backgroundColor: colors.surface, borderColor: colors.border, borderRadius: 8, borderWidth: 1, gap: 10, padding: 16 }, priceTitle: { color: colors.text, fontSize: 17, fontWeight: '800' }, priceCopy: { color: colors.muted, fontSize: 14, lineHeight: 20 }, priceInputRow: { alignItems: 'center', flexDirection: 'row', gap: 10 }, currency: { color: colors.muted, fontSize: 16, fontWeight: '800' }, priceInput: { borderColor: colors.border, borderRadius: 8, borderWidth: 1, color: colors.text, flex: 1, fontSize: 16, paddingHorizontal: 12, paddingVertical: 10 }, errorText: { color: colors.danger, fontSize: 13, fontWeight: '800' }, dataPanel: { backgroundColor: colors.surface, borderColor: colors.border, borderRadius: 8, borderWidth: 1, gap: 8, padding: 14 }, dataText: { color: colors.muted, fontSize: 14, lineHeight: 20 }, saved: { color: colors.success, fontSize: 14, fontWeight: '800' } });