From 1f0ba8f564bd04f473e7921a09fb1a1554c2ef5a Mon Sep 17 00:00:00 2001 From: Meik Date: Mon, 10 Nov 2025 17:00:59 +0100 Subject: [PATCH] =?UTF-8?q?Neue=20Seite=20um=20Betriebe=20zu=20=C3=BCberwa?= =?UTF-8?q?chen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 2 +- src/components/StoreWatchPage.js | 123 ++++++++++++++++++++++++++++--- 2 files changed, 114 insertions(+), 11 deletions(-) diff --git a/src/App.js b/src/App.js index a66f424..6e44ab1 100644 --- a/src/App.js +++ b/src/App.js @@ -696,7 +696,7 @@ function App() { - } /> + } /> } /> diff --git a/src/components/StoreWatchPage.js b/src/components/StoreWatchPage.js index 690ca08..e6eaf73 100644 --- a/src/components/StoreWatchPage.js +++ b/src/components/StoreWatchPage.js @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; -const StoreWatchPage = ({ authorizedFetch }) => { +const StoreWatchPage = ({ authorizedFetch, knownStores = [] }) => { const [regions, setRegions] = useState([]); const [selectedRegionId, setSelectedRegionId] = useState(''); const [storesByRegion, setStoresByRegion] = useState({}); @@ -12,6 +12,8 @@ const StoreWatchPage = ({ authorizedFetch }) => { const [error, setError] = useState(''); const [dirty, setDirty] = useState(false); const [saving, setSaving] = useState(false); + const [filterText, setFilterText] = useState(''); + const [sortBy, setSortBy] = useState('name'); const watchedIds = useMemo( () => new Set(watchList.map((entry) => String(entry.storeId))), @@ -36,6 +38,58 @@ const StoreWatchPage = ({ authorizedFetch }) => { [currentStores] ); + const membershipMap = useMemo(() => { + const map = new Map(); + (knownStores || []).forEach((store) => { + if (store?.id) { + map.set(String(store.id), store); + } + }); + return map; + }, [knownStores]); + + const filteredStores = useMemo(() => { + const search = filterText.trim().toLowerCase(); + const data = !search + ? [...eligibleStores] + : eligibleStores.filter((store) => { + const haystack = [ + store.name, + store.city, + store.street, + store.zipCode, + store.id + ] + .filter(Boolean) + .map((value) => String(value).toLowerCase()); + return haystack.some((value) => value.includes(search)); + }); + const compareString = (a = '', b = '') => a.localeCompare(b, 'de', { sensitivity: 'base' }); + data.sort((a, b) => { + switch (sortBy) { + case 'city': + return compareString(a.city || '', b.city || '') || compareString(a.name || '', b.name || ''); + case 'created-desc': { + const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0; + const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0; + if (timeA === timeB) { + return compareString(a.name || '', b.name || ''); + } + return timeB - timeA; + } + case 'membership': + return ( + Number(membershipMap.has(String(b.id))) - Number(membershipMap.has(String(a.id))) || + compareString(a.name || '', b.name || '') + ); + case 'name': + default: + return compareString(a.name || '', b.name || ''); + } + }); + return data; + }, [eligibleStores, filterText, sortBy, membershipMap]); + const loadRegions = useCallback(async () => { if (!authorizedFetch) { return; @@ -279,6 +333,42 @@ const StoreWatchPage = ({ authorizedFetch }) => {

+
+
+
+ + setFilterText(event.target.value)} + className="border rounded-md p-2 w-full" + placeholder="Name, Ort oder PLZ" + disabled={!selectedRegionId} + /> +
+
+ + +
+
+
+

Betriebe in der Region

@@ -290,27 +380,33 @@ const StoreWatchPage = ({ authorizedFetch }) => { )}
{storesLoading &&

Lade Betriebe...

} - {!storesLoading && (!selectedRegionId || eligibleStores.length === 0) && ( + {!storesLoading && !selectedRegionId && ( +

Bitte zuerst eine Region auswählen.

+ )} + {!storesLoading && selectedRegionId && filteredStores.length === 0 && (

- {selectedRegionId - ? 'Keine geeigneten Betriebe (Status "aktiv") in dieser Region.' - : 'Bitte zuerst eine Region auswählen.'} + Keine Betriebe gefunden. Prüfe Filter oder sortiere anders.

)} - {!storesLoading && eligibleStores.length > 0 && ( + {!storesLoading && filteredStores.length > 0 && (
- + + - {eligibleStores.map((store) => { + {filteredStores.map((store) => { const checked = watchedIds.has(String(store.id)); + const isMember = membershipMap.has(String(store.id)); + const sinceLabel = store.createdAt + ? new Date(store.createdAt).toLocaleDateString('de-DE') + : 'unbekannt'; return ( - +
Betrieb OrtKooperationKooperation seitMitglied Überwachen
@@ -321,8 +417,15 @@ const StoreWatchPage = ({ authorizedFetch }) => {

{store.city || 'unbekannt'}

{store.street || ''}

- Seit {store.createdAt ? new Date(store.createdAt).toLocaleDateString('de-DE') : 'n/a'} + {sinceLabel} + + {isMember ? 'Ja' : 'Nein'} +