From e115908cd959f21110f22651b0ad46ab2aeb9fae Mon Sep 17 00:00:00 2001 From: ah-admin Date: Sun, 4 Oct 2026 17:38:12 +0200 Subject: [PATCH] v1.28: Zaehleruebersicht mit Suche und Listenansicht --- kimi.md | 7 +++ src/pages/Zaehler.tsx | 123 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 115 insertions(+), 15 deletions(-) diff --git a/kimi.md b/kimi.md index b4ee12d..7009cd1 100644 --- a/kimi.md +++ b/kimi.md @@ -189,6 +189,13 @@ tar -czf mp-dist.tar.gz -C dist . ## 8. Verlauf / offene Punkte +**Erledigt 04.10. (v1.28):** Zählerübersicht mit Suche + Listenansicht +(Analogie zur Hausübersicht v1.26): Suchfeld über Nummer/Art/Haus/Wohneinheit +(mit Trefferzähler), Toggle Karten/Liste; Liste = Tabelle (Art-Badge mit Icon, +Nummer, Haus, Wohneinheit, Stand rechts tabular-nums, Stand am, Anomalie-Icon, +Aktionen), responsive via hidden sm/md/lg. Bestehende Filter Haus/Art bleiben +und kombinieren mit der Suche. + **Erledigt 04.10. (v1.27):** Zähleranlage ohne Haus-Feld, mit Ablesedatum. POST /api/meters akzeptiert standAm (Validierung via zaehlerVerlauf.parseDatum, Default heute); stand_am + erster Verlaufseintrag (erstbefund) nutzen das Datum. diff --git a/src/pages/Zaehler.tsx b/src/pages/Zaehler.tsx index 4c45e22..226d5f8 100644 --- a/src/pages/Zaehler.tsx +++ b/src/pages/Zaehler.tsx @@ -1,5 +1,5 @@ import { useMemo, useState } from 'react' -import { Gauge, Zap, Droplets, Flame, Building2, Hash, CalendarDays, Pencil, Plus, History, TriangleAlert, Trash2 } from 'lucide-react' +import { Gauge, Zap, Droplets, Flame, Building2, Hash, CalendarDays, Pencil, Plus, History, TriangleAlert, Trash2, Search, List, LayoutGrid } from 'lucide-react' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { @@ -390,28 +390,32 @@ export default function Zaehler() { const { meters, units, role } = useStore() const [filterHaus, setFilterHaus] = useState('alle') const [filterArt, setFilterArt] = useState<'alle' | ZaehlerArt>('alle') + const [suche, setSuche] = useState('') + const [ansicht, setAnsicht] = useState<'karten' | 'liste'>('karten') const gebaeude = useMemo( () => Array.from(new Set(meters.map((m) => m.building))).sort(), [meters], ) - const visible = useMemo( - () => - meters.filter( - (m) => - (filterHaus === 'alle' || m.building === filterHaus) && - (filterArt === 'alle' || m.art === filterArt), - ), - [meters, filterHaus, filterArt], - ) - const einheitLabel = (m: Meter) => { if (!m.unitId) return 'Allgemeinzähler' const u = units.find((x) => x.id === m.unitId) return u ? `Whg. ${u.number}` : m.unitId } + const visible = useMemo(() => { + const q = suche.trim().toLowerCase() + return meters.filter( + (m) => + (filterHaus === 'alle' || m.building === filterHaus) && + (filterArt === 'alle' || m.art === filterArt) && + (!q || + [m.nummer, m.art, m.building, einheitLabel(m)] + .some((f) => f.toLowerCase().includes(q))), + ) + }, [meters, filterHaus, filterArt, suche, units]) + return (
@@ -426,7 +430,17 @@ export default function Zaehler() { {role === 'admin' && }
-
+
+
+ + setSuche(e.target.value)} + aria-label="Zähler suchen" + /> +
+
+ + +
+ {suche && ( + + {visible.length} von {meters.length} Zählern + + )}
{visible.length === 0 ? (
- - Keine Zähler für diesen Filter. + + {suche ? `Kein Zähler passt zur Suche „${suche}".` : 'Keine Zähler für diesen Filter.'}
- ) : ( + ) : ansicht === 'karten' ? (
{visible.map((m) => { const Icon = artIcon(m.art) @@ -501,6 +540,60 @@ export default function Zaehler() { ) })}
+ ) : ( + /* ── Listenansicht ─────────────────────────────── */ +
+ + + + + + + + + + + + + + {visible.map((m) => { + const Icon = artIcon(m.art) + return ( + + + + + + + + + + + ) + })} + +
ArtZählernummerHausWohneinheitStandStand am + Aktion
+ + {m.art} + + {m.nummer}{m.building}{einheitLabel(m)} + {fmtStand(m.stand)} + {m.standAm} + {m.letzteAnomalie && ( + + )} + +
+ + + {role === 'admin' && } +
+
+
)}
)