diff --git a/src/components/form/BudgetInput.js b/src/components/form/BudgetInput.js index 7bb294d..3f7f245 100644 --- a/src/components/form/BudgetInput.js +++ b/src/components/form/BudgetInput.js @@ -16,7 +16,7 @@ export default function BudgetInput({ value, currency, period, onChangePeriod, o return ( - Dein Budget (optional) + Betrag (optional) Ohne Budget bleibt die Preisbewertung offen. {currency} @@ -54,12 +54,7 @@ export default function BudgetInput({ value, currency, period, onChangePeriod, o const styles = StyleSheet.create({ wrap: { - backgroundColor: colors.surface, - borderColor: colors.border, - borderRadius: 18, - borderWidth: 1, - gap: 10, - padding: 14 + gap: 10 }, hint: { color: colors.muted, @@ -102,6 +97,8 @@ const styles = StyleSheet.create({ borderRadius: 12, borderWidth: 1, flexGrow: 1, + minHeight: 44, + justifyContent: 'center', paddingHorizontal: 10, paddingVertical: 9 }, diff --git a/src/components/form/SettingsSwitchRow.js b/src/components/form/SettingsSwitchRow.js index 65bc1ee..3429dd5 100644 --- a/src/components/form/SettingsSwitchRow.js +++ b/src/components/form/SettingsSwitchRow.js @@ -2,9 +2,9 @@ import { StyleSheet, Switch, Text, View } from 'react-native'; import { colors } from '../../utils/constants'; -export default function SettingsSwitchRow({ label, description, value, onValueChange }) { +export default function SettingsSwitchRow({ label, description, value, onValueChange, embedded = false }) { return ( - + {label} {description} @@ -31,6 +31,12 @@ const styles = StyleSheet.create({ gap: 12, padding: 14 }, + embeddedRow: { + backgroundColor: 'transparent', + borderWidth: 0, + borderRadius: 0, + padding: 0 + }, copy: { flex: 1 }, diff --git a/src/screens/ProfileScreen.js b/src/screens/ProfileScreen.js index 84535c6..31f953b 100644 --- a/src/screens/ProfileScreen.js +++ b/src/screens/ProfileScreen.js @@ -4,108 +4,150 @@ import BudgetInput from '../components/form/BudgetInput'; import SettingsSwitchRow from '../components/form/SettingsSwitchRow'; import AppHeader from '../components/layout/AppHeader'; import ScreenContainer from '../components/layout/ScreenContainer'; -import SectionTitle from '../components/layout/SectionTitle'; -import { colors, dietLabels } from '../utils/constants'; +import { budgetPeriodLabels, colors, dietLabels } from '../utils/constants'; +import { formatPrice } from '../utils/formatPrice'; const dietOptions = ['none', 'vegetarian', 'vegan', 'lowSugar', 'highProtein']; +const allergenOptions = ['Erdnüsse', 'Milch', 'Hafer', 'Gluten']; const priorityOptions = ['low', 'medium', 'high']; +const priorityLabels = { low: 'Niedrig', medium: 'Mittel', high: 'Hoch' }; export default function ProfileScreen({ preferences, onChangePreferences }) { function updatePreference(key, value) { - onChangePreferences({ - ...preferences, - [key]: value - }); + onChangePreferences({ ...preferences, [key]: value }); } function toggleAllergen(allergen) { const current = preferences.avoidAllergens || []; - const nextAllergens = current.includes(allergen) + updatePreference('avoidAllergens', current.includes(allergen) ? current.filter((item) => item !== allergen) - : [...current, allergen]; - updatePreference('avoidAllergens', nextAllergens); + : [...current, allergen]); } + const allergenCount = preferences.avoidAllergens?.length || 0; + const budget = preferences.maxBudgetPerShoppingTrip; + const budgetSummary = typeof budget === 'number' && budget > 0 + ? `${formatPrice(budget, preferences.currency)} pro ${budgetPeriodLabels[preferences.budgetPeriod] || 'Woche'}` + : 'Kein Budget festgelegt'; + return ( - + - Ernährungsziel - - {dietOptions.map((option) => ( - updatePreference('preferredDiet', option)} - style={[styles.chip, preferences.preferredDiet === option ? styles.activeChip : null]} - > - - {dietLabels[option]} - - - ))} - + + + Ernährungsziel + + {dietOptions.map((option) => ( + updatePreference('preferredDiet', option)} + /> + ))} + + + + + Allergene vermeiden + Wähle nur relevante Allergene. Fehlen Produktangaben, bleibt die Bewertung unsicher. + + {allergenOptions.map((allergen) => ( + toggleAllergen(allergen)} + /> + ))} + + + - Allergien vermeiden - Wähle nur Allergene, die für dich relevant sind. Fehlende Produktangaben bleiben als unsicher markiert. - - {['Erdnüsse', 'Milch', 'Hafer', 'Gluten'].map((allergen) => { - const isActive = preferences.avoidAllergens?.includes(allergen); - return ( - toggleAllergen(allergen)} - style={[styles.chip, isActive ? styles.dangerChip : null]} - > - {allergen} - - ); - })} - + + updatePreference('budgetPeriod', value)} + onChangeValue={(value) => updatePreference('maxBudgetPerShoppingTrip', value)} + period={preferences.budgetPeriod || 'week'} + value={budget} + /> + - updatePreference('budgetPeriod', value)} - onChangeValue={(value) => updatePreference('maxBudgetPerShoppingTrip', value)} - period={preferences.budgetPeriod || 'week'} - value={preferences.maxBudgetPerShoppingTrip} - /> + + updatePreference('preferBio', value)} + value={preferences.preferBio} + /> + + Prioritäten + Hoch bedeutet, dass dieser Bereich stärker in die Gesamtbewertung einfliesst. + updatePreference('nutritionPriority', value)} + value={preferences.nutritionPriority} + /> + updatePreference('sustainabilityPriority', value)} + value={preferences.sustainabilityPriority} + /> + - updatePreference('preferBio', value)} - value={preferences.preferBio} - /> - - updatePreference('offlineModeEnabled', value)} - value={preferences.offlineModeEnabled} - /> - - Prioritäten - updatePreference('nutritionPriority', value)} - value={preferences.nutritionPriority} - /> - updatePreference('sustainabilityPriority', value)} - value={preferences.sustainabilityPriority} - /> + + updatePreference('offlineModeEnabled', value)} + value={preferences.offlineModeEnabled} + /> + ); } +function SettingsSection({ title, summary, children }) { + return ( + + + {title} + {summary} + + {children} + + ); +} + +function SelectionChip({ active, danger = false, label, onPress }) { + return ( + + + {label} + + + ); +} + function PrioritySelector({ label, value, onChange }) { return ( @@ -117,10 +159,10 @@ function PrioritySelector({ label, value, onChange }) { accessibilityState={{ selected: value === option }} key={option} onPress={() => onChange(option)} - style={[styles.priorityChip, value === option ? styles.activeChip : null]} + style={[styles.priorityChip, value === option && styles.activeChip]} > - - {option === 'low' ? 'Niedrig' : option === 'medium' ? 'Mittel' : 'Hoch'} + + {priorityLabels[option]} ))} @@ -130,24 +172,62 @@ function PrioritySelector({ label, value, onChange }) { } const styles = StyleSheet.create({ + section: { + backgroundColor: colors.surface, + borderColor: colors.border, + borderRadius: 20, + borderWidth: 1, + overflow: 'hidden' + }, + sectionHeader: { + backgroundColor: colors.surfaceTint, + gap: 4, + paddingHorizontal: 18, + paddingVertical: 15 + }, + sectionTitle: { + color: colors.text, + fontSize: 19, + fontWeight: '800' + }, + sectionSummary: { + color: colors.primaryDark, + fontSize: 13, + lineHeight: 18 + }, + sectionContent: { + gap: 14, + padding: 18 + }, + field: { gap: 10 }, + fieldLabel: { + color: colors.text, + fontSize: 15, + fontWeight: '800' + }, + fieldHint: { + color: colors.muted, + fontSize: 13, + lineHeight: 19 + }, + divider: { + backgroundColor: colors.border, + height: 1 + }, optionGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 }, - sectionHint: { - color: colors.muted, - fontSize: 13, - lineHeight: 19, - marginTop: -10 - }, chip: { backgroundColor: colors.surface, borderColor: colors.border, borderRadius: 12, borderWidth: 1, + justifyContent: 'center', + minHeight: 44, paddingHorizontal: 12, - paddingVertical: 10 + paddingVertical: 9 }, activeChip: { backgroundColor: colors.primary, @@ -162,35 +242,26 @@ const styles = StyleSheet.create({ fontSize: 14, fontWeight: '800' }, - activeChipText: { - color: colors.surface - }, - dangerChipText: { - color: colors.danger - }, - priorityRow: { - backgroundColor: colors.surface, - borderColor: colors.border, - borderRadius: 16, - borderWidth: 1, - gap: 10, - padding: 14 - }, + activeChipText: { color: colors.surface }, + dangerChipText: { color: colors.danger }, + priorityRow: { gap: 8 }, priorityLabel: { color: colors.text, - fontSize: 15, - fontWeight: '800' + fontSize: 14, + fontWeight: '700' }, priorityOptions: { flexDirection: 'row', gap: 8 }, priorityChip: { + alignItems: 'center', borderColor: colors.border, borderRadius: 12, borderWidth: 1, flex: 1, - paddingVertical: 10, - alignItems: 'center' + justifyContent: 'center', + minHeight: 44, + paddingHorizontal: 4 } });