'use client'; import { useEffect, useRef, useState } from 'react'; import type { ObjektOption, SelectedObjekt } from '@/types/logbuch'; interface Props { selected: SelectedObjekt[]; onChange: (objekte: SelectedObjekt[]) => void; } export default function ObjektSelector({ selected, onChange }: Props) { const [all, setAll] = useState([]); const [search, setSearch] = useState(''); const [dropdownOpen, setDropdownOpen] = useState(false); const wrapperRef = useRef(null); useEffect(() => { fetch('/api/objekte') .then((r) => { if (!r.ok) throw new Error('Fehler'); return r.json(); }) .then(setAll) .catch(() => {}); }, []); useEffect(() => { function handleOutside(e: MouseEvent) { if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) { setDropdownOpen(false); } } if (dropdownOpen) document.addEventListener('mousedown', handleOutside); return () => document.removeEventListener('mousedown', handleOutside); }, [dropdownOpen]); const selectedNames = new Set(selected.map((o) => o.Name.toLowerCase())); const available = all.filter((o) => !selectedNames.has(o.Name.toLowerCase())); const filtered = search ? available.filter((o) => o.Name.toLowerCase().startsWith(search.toLowerCase())) : available; const searchTrimmed = search.trim(); const alreadySelected = searchTrimmed !== '' && selectedNames.has(searchTrimmed.toLowerCase()); const exactAvailableMatch = available.find((o) => o.Name.toLowerCase() === searchTrimmed.toLowerCase()); const showAddNew = searchTrimmed !== '' && !alreadySelected && !exactAvailableMatch; function add(obj: ObjektOption) { onChange([...selected, { ID: obj.ID, Name: obj.Name }]); setSearch(''); setDropdownOpen(false); } function addNew(name: string) { const trimmed = name.trim(); if (!trimmed || selectedNames.has(trimmed.toLowerCase())) return; const existing = all.find((o) => o.Name.toLowerCase() === trimmed.toLowerCase()); if (existing) { onChange([...selected, { ID: existing.ID, Name: existing.Name }]); } else { onChange([...selected, { ID: null, Name: trimmed }]); } setSearch(''); setDropdownOpen(false); } function remove(name: string) { onChange(selected.filter((o) => o.Name !== name)); } function handleKeyDown(e: React.KeyboardEvent) { if (e.key !== 'Enter') return; e.preventDefault(); if (filtered.length === 1 && !showAddNew) { add(filtered[0]); } else if (filtered.length === 0 && searchTrimmed) { addNew(searchTrimmed); } } return (
{selected.map((o) => ( {o.Name} ))}
{ setSearch(e.target.value); setDropdownOpen(true); }} onFocus={() => setDropdownOpen(true)} onKeyDown={handleKeyDown} placeholder="Objekt suchen oder neu eingeben..." className="w-full px-3 py-2 border-2 border-gray-400 rounded-lg bg-white text-sm text-gray-900 focus:border-blue-500 focus:outline-none" /> {dropdownOpen && (filtered.length > 0 || showAddNew) && (
{filtered.map((o) => ( ))} {showAddNew && ( )}
)}
); }