import { useEffect, useRef, useState } from 'react' import { api } from '../utils/api' import FoodNutrientFields, { foodCreatePayload } from './FoodNutrientFields' import FoodEditDialog from './FoodEditDialog' import { foodVisibilitySuffix } from '../utils/foodVisibility' const COUNT_UNITS = new Set(['stück', 'scheibe', 'portion', 'becher', 'tasse']) const UNIT_LABEL = { stück: 'Stück', scheibe: 'Scheibe', portion: 'Portion', becher: 'Becher', tasse: 'Tasse', el: 'EL', tl: 'TL', prise: 'Prise', } function detectUnit(...texts) { for (const text of texts) { const m = String(text || '').match( /(\d+(?:[.,]\d+)?)\s*(stück|stk|st\.?|scheibe|portion(?:en)?|becher|tasse|el|esslöffel|tl|teelöffel|prise)\b/i ) if (!m) continue const raw = m[2].toLowerCase().replace(/\.$/, '') const unit = raw.startsWith('st') && !raw.startsWith('scheibe') ? 'stück' : raw.startsWith('ess') || raw === 'el' ? 'el' : raw.startsWith('tee') || raw === 'tl' ? 'tl' : raw.startsWith('portion') ? 'portion' : raw return unit } return null } export default function FoodSearchModal({ title, initialQuery, quantityHint, allowRecipe, onSelect, onWantRecipe, onClose, zIndex }) { const [q, setQ] = useState(initialQuery || '') const [hits, setHits] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [creating, setCreating] = useState(false) const [showCreate, setShowCreate] = useState(false) const [createKind, setCreateKind] = useState(null) const [editId, setEditId] = useState(null) const [manual, setManual] = useState({ name_de: initialQuery || '', kcal: '', protein_g: '', fat_g: '', carbs_g: '', }) const [nutrientExtras, setNutrientExtras] = useState([]) const [servingG, setServingG] = useState('100') const suggestedUnit = detectUnit(quantityHint, initialQuery) const [gramsPerUnit, setGramsPerUnit] = useState( suggestedUnit === 'el' ? '15' : suggestedUnit === 'tl' ? '5' : '' ) const inputRef = useRef(null) const timer = useRef(null) const abortRef = useRef(null) const seqRef = useRef(0) const unitExtras = () => { const g = parseFloat(String(gramsPerUnit).replace(',', '.')) if (!suggestedUnit || !g || g <= 0) return {} return { grams_per_unit: g, source_unit: suggestedUnit } } const runSearch = async (term) => { const query = (term || '').trim() abortRef.current?.abort() if (query.length < 2) { setHits([]) setLoading(false) return } const seq = ++seqRef.current const ac = new AbortController() abortRef.current = ac setLoading(true) setError(null) try { const next = await api.searchBlsFoods(query, 20, ac.signal) if (seq !== seqRef.current) return setHits(next) } catch (e) { if (e.name === 'AbortError') return if (seq !== seqRef.current) return setError(e.message) setHits([]) } finally { if (seq === seqRef.current) setLoading(false) } } useEffect(() => { inputRef.current?.focus() inputRef.current?.select() if ((initialQuery || '').trim().length >= 2) runSearch(initialQuery) const onKey = (e) => { if (e.key === 'Escape') onClose() } window.addEventListener('keydown', onKey) return () => { window.removeEventListener('keydown', onKey) clearTimeout(timer.current) abortRef.current?.abort() } }, []) const onChange = (value) => { setQ(value) clearTimeout(timer.current) if (value.trim().length < 2) { abortRef.current?.abort() setHits([]) setLoading(false) return } timer.current = setTimeout(() => runSearch(value), 400) } const createManual = async () => { const name = (manual.name_de || q || '').trim() if (!name) { setError('Name für den Eintrag fehlt') return } setCreating(true) setError(null) try { const food = await api.createUserFood(foodCreatePayload(name, manual, nutrientExtras, servingG)) onSelect(food, unitExtras()) } catch (e) { setError(e.message) } finally { setCreating(false) } } const unitLabel = UNIT_LABEL[suggestedUnit] || suggestedUnit return ( <>
Fettgehalt mit eingeben, z. B. Joghurt 10 % oder 9,5 %. Ohne Zahl erscheint oft nur die 3,5 %-Klasse. Fehlt der Treffer, eigenen Eintrag anlegen.
{suggestedUnit && ( )}Suche…
} {error &&{error}
} {!loading && q.trim().length >= 2 && hits.length === 0 && (Kein Treffer im Katalog.
)} {hits.map((h) => { const manual = h.catalog_kind && h.catalog_kind !== 'official_bls' && h.catalog_kind !== 'manual_admin' return (Was möchtest du anlegen?
Lebensmittel = ein Katalogeintrag. Liste = Roh-Kombination (z. B. Müsli) mit Zutatensuche.
Eigener Eintrag
setManual({ ...manual, name_de: e.target.value })} />