v1.7.4: Suche in Listenansicht (Bemerkungen, Objekte, BEOs)
Suchfeld in der Toolbar der Listenansicht: Suche über alle Einträge der Kuppel in Bemerkungen, Objekte und BEOs. Monatsauswahl, Suchfeld und Drucken-Button in einer Zeile; Monatsauswahl wird bei aktiver Suche unsichtbar aber platzhaltend ausgeblendet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+65
-45
@@ -59,20 +59,27 @@ export default function LogbuchList({ kuppel, refreshKey, onEdit, limit = 10, co
|
||||
const [deleteId, setDeleteId] = useState<number | null>(null);
|
||||
const [error, setError] = useState('');
|
||||
const [printEntries, setPrintEntries] = useState<LogbuchEintrag[] | null>(null);
|
||||
const [search, setSearch] = useState('');
|
||||
const [activeSearch, setActiveSearch] = useState('');
|
||||
const printPending = useRef(false);
|
||||
|
||||
useEffect(() => { setPage(0); }, [kuppel, refreshKey, month]);
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => setActiveSearch(search.trim()), 300);
|
||||
return () => clearTimeout(t);
|
||||
}, [search]);
|
||||
|
||||
useEffect(() => { setPage(0); }, [kuppel, refreshKey, month, activeSearch]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
const offset = page * limit;
|
||||
const url = `/api/logbuch?kuppel=${encodeURIComponent(kuppel)}&limit=${limit}&offset=${offset}` +
|
||||
(month ? `&month=${encodeURIComponent(month)}` : '');
|
||||
(activeSearch ? `&search=${encodeURIComponent(activeSearch)}` : (month ? `&month=${encodeURIComponent(month)}` : ''));
|
||||
fetch(url)
|
||||
.then((r) => { if (!r.ok) throw new Error(); return r.json(); })
|
||||
.then((data) => { setEntries(data.entries); setTotal(data.total); setLoading(false); })
|
||||
.catch(() => { setError('Fehler beim Laden.'); setLoading(false); });
|
||||
}, [kuppel, refreshKey, limit, page, month]);
|
||||
}, [kuppel, refreshKey, limit, page, month, activeSearch]);
|
||||
|
||||
useEffect(() => {
|
||||
function onAfterPrint() { setPrintEntries(null); }
|
||||
@@ -108,32 +115,57 @@ export default function LogbuchList({ kuppel, refreshKey, onEdit, limit = 10, co
|
||||
}
|
||||
}
|
||||
|
||||
const monthNav = !compact && (
|
||||
const toolbar = !compact && (
|
||||
<div className="flex items-center gap-2 mb-3 print:hidden">
|
||||
<button
|
||||
onClick={() => setMonth((m) => prevMonth(m))}
|
||||
className="px-2 py-1 text-sm rounded-lg bg-gray-200 hover:bg-gray-300"
|
||||
>←</button>
|
||||
<input
|
||||
type="month"
|
||||
value={month}
|
||||
max={currentMonth()}
|
||||
onChange={(e) => setMonth(e.target.value > currentMonth() ? currentMonth() : e.target.value)}
|
||||
className="border border-gray-300 rounded-lg px-2 py-1 text-sm"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setMonth((m) => nextMonth(m))}
|
||||
disabled={month >= currentMonth()}
|
||||
className="px-2 py-1 text-sm rounded-lg bg-gray-200 hover:bg-gray-300 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>→</button>
|
||||
{month !== currentMonth() && (
|
||||
<div
|
||||
className="flex items-center gap-1 shrink-0"
|
||||
style={{ visibility: activeSearch ? 'hidden' : 'visible' }}
|
||||
>
|
||||
<button
|
||||
onClick={() => setMonth(currentMonth())}
|
||||
className="text-sm text-blue-600 hover:underline"
|
||||
>
|
||||
Aktueller Monat
|
||||
</button>
|
||||
)}
|
||||
onClick={() => setMonth((m) => prevMonth(m))}
|
||||
className="px-2 py-1 text-sm rounded-lg bg-gray-200 hover:bg-gray-300"
|
||||
>←</button>
|
||||
<input
|
||||
type="month"
|
||||
value={month}
|
||||
max={currentMonth()}
|
||||
onChange={(e) => setMonth(e.target.value > currentMonth() ? currentMonth() : e.target.value)}
|
||||
className="border border-gray-300 rounded-lg px-2 py-1 text-sm"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setMonth((m) => nextMonth(m))}
|
||||
disabled={month >= currentMonth()}
|
||||
className="px-2 py-1 text-sm rounded-lg bg-gray-200 hover:bg-gray-300 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>→</button>
|
||||
{month !== currentMonth() && (
|
||||
<button
|
||||
onClick={() => setMonth(currentMonth())}
|
||||
className="text-sm text-blue-600 hover:underline ml-1"
|
||||
>Aktueller Monat</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex-1 min-w-0 mx-3">
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="Suche in Bemerkungen, Objekte, BEOs…"
|
||||
className="w-full px-3 py-1.5 pr-8 border border-gray-300 rounded-lg text-sm focus:outline-none focus:border-blue-500"
|
||||
/>
|
||||
{search ? (
|
||||
<button
|
||||
onClick={() => setSearch('')}
|
||||
aria-label="Suche löschen"
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 text-sm leading-none"
|
||||
>✕</button>
|
||||
) : (
|
||||
<span className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-sm">🔍</span>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={handlePrint}
|
||||
className="text-sm px-3 py-1.5 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg shrink-0"
|
||||
>🖨 Drucken</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -150,27 +182,15 @@ export default function LogbuchList({ kuppel, refreshKey, onEdit, limit = 10, co
|
||||
? 'px-1.5 py-1 border border-gray-300 text-xs font-semibold'
|
||||
: 'px-3 py-2 border border-gray-300';
|
||||
|
||||
if (loading) return <>{monthNav}<div className="text-gray-500 text-sm py-4">Lade Einträge...</div></>;
|
||||
if (error) return <>{monthNav}<div className="text-red-600 text-sm py-4">{error}</div></>;
|
||||
|
||||
const displayEntries = printEntries ?? entries;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!compact && (
|
||||
<div className="flex justify-between items-center mb-2 print:hidden">
|
||||
<span className="text-sm font-semibold text-gray-600">Einträge {kuppel}-Kuppel</span>
|
||||
<button
|
||||
onClick={handlePrint}
|
||||
className="text-sm px-3 py-1.5 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg"
|
||||
>
|
||||
🖨 Drucken
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{monthNav}
|
||||
{toolbar}
|
||||
{printHeader}
|
||||
<div className="overflow-x-auto">
|
||||
{loading && <div className="text-gray-500 text-sm py-4">Lade Einträge...</div>}
|
||||
{error && <div className="text-red-600 text-sm py-4">{error}</div>}
|
||||
{!loading && !error && <div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse" style={{ fontSize: compact ? '0.75rem' : '0.875rem' }}>
|
||||
<thead>
|
||||
<tr className="bg-gray-100 text-left">
|
||||
@@ -196,7 +216,7 @@ export default function LogbuchList({ kuppel, refreshKey, onEdit, limit = 10, co
|
||||
{displayEntries.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={compact ? 7 : 10} className="px-3 py-4 text-gray-500 text-sm text-center">
|
||||
Keine Einträge für {monthLabel(month)}.
|
||||
{activeSearch ? `Keine Einträge für „${activeSearch}" gefunden.` : `Keine Einträge für ${monthLabel(month)}.`}
|
||||
</td>
|
||||
</tr>
|
||||
) : displayEntries.map((e) => (
|
||||
@@ -257,7 +277,7 @@ export default function LogbuchList({ kuppel, refreshKey, onEdit, limit = 10, co
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
{!compact && total > limit && (
|
||||
<div className="flex items-center justify-center gap-3 mt-3 print:hidden">
|
||||
|
||||
Reference in New Issue
Block a user