From de57c99c6e4ba92a926d5852c620948416560eb2 Mon Sep 17 00:00:00 2001 From: chenkel-data Date: Sun, 7 Jun 2026 20:54:29 +0200 Subject: [PATCH] bug fix: type filter --- client/src/App.jsx | 30 ++++++++++++---- client/src/components/FilterBar.jsx | 4 +-- client/src/constants.js | 55 ++++++++++++++++++++++++++--- 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/client/src/App.jsx b/client/src/App.jsx index bad1565..d59b0fc 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -23,6 +23,8 @@ import { LISTING_TYPE_COLORS, PROVIDER_COLORS, PROVIDER_LABELS, + getListingTypeFilterIds, + normalizeListingTypeFilter, } from './constants.js'; const FILTERS_STORAGE_KEY = 'immo.filters.v1'; @@ -127,7 +129,9 @@ export default function App() { const [detailListing, setDetailListing] = useState(null); const [page, setPage] = useState(1); const [searchQuery, setSearchQuery] = useState(persistedFilters.searchQuery); - const [listingTypeFilter, setListingTypeFilter] = useState(persistedFilters.listingTypeFilter); + const [listingTypeFilter, setListingTypeFilter] = useState( + normalizeListingTypeFilter(persistedFilters.listingTypeFilter), + ); const [providerFilter, setProviderFilter] = useState(persistedFilters.providerFilter); const [publisherFilter, setPublisherFilter] = useState(persistedFilters.publisherFilter); const [minPrice, setMinPrice] = useState(persistedFilters.minPrice); @@ -230,14 +234,20 @@ export default function App() { [handleMarkSeen, showToast], ); - const isProviderFilterActive = !activeConfigId && activeTab === TABS.ALL; + const isProviderFilterActive = !activeConfigId; + const listingTypeFilterIds = useMemo( + () => getListingTypeFilterIds(listingTypeFilter), + [listingTypeFilter], + ); + const isListingTypeFilterActive = !activeConfigId && listingTypeFilterIds.length > 0; const uiFilteredListings = useMemo(() => { let list = [...listings]; const q = searchQuery.toLowerCase(); const keywords = scrapeConfig.blacklistKeywords ?? []; - if (listingTypeFilter) list = list.filter((l) => l.listing_type === listingTypeFilter); + if (isListingTypeFilterActive) + list = list.filter((l) => listingTypeFilterIds.includes(l.listing_type)); if (q) list = list.filter( (l) => @@ -275,7 +285,8 @@ export default function App() { return list; }, [ listings, - listingTypeFilter, + isListingTypeFilterActive, + listingTypeFilterIds, publisherFilter, searchQuery, minPrice, @@ -315,7 +326,8 @@ export default function App() { const tabCounts = useMemo(() => { let base = listings; - if (listingTypeFilter) base = base.filter((l) => l.listing_type === listingTypeFilter); + if (isListingTypeFilterActive) + base = base.filter((l) => listingTypeFilterIds.includes(l.listing_type)); if (isProviderFilterActive && providerFilter) base = base.filter((l) => l.provider === providerFilter); const nonBlacklisted = base.filter((l) => !l.is_blacklisted); @@ -325,7 +337,13 @@ export default function App() { [TABS.FAVORITES]: nonBlacklisted.filter((l) => l.is_favorite).length, [TABS.BLACKLISTED]: base.filter((l) => l.is_blacklisted).length, }; - }, [listings, listingTypeFilter, isProviderFilterActive, providerFilter]); + }, [ + listings, + isListingTypeFilterActive, + listingTypeFilterIds, + isProviderFilterActive, + providerFilter, + ]); const activeConfigStats = useMemo( () => ({ diff --git a/client/src/components/FilterBar.jsx b/client/src/components/FilterBar.jsx index 8108f86..3707dd9 100644 --- a/client/src/components/FilterBar.jsx +++ b/client/src/components/FilterBar.jsx @@ -1,4 +1,4 @@ -import { TABS, LISTING_TYPE_LABELS, PROVIDER_LABELS } from '../constants.js'; +import { TABS, LISTING_TYPE_FILTERS, PROVIDER_LABELS } from '../constants.js'; export default function FilterBar({ activeTab, stats, listingTypeFilter, @@ -32,7 +32,7 @@ export default function FilterBar({ const typeOptions = [ { id: '', label: 'Alle Typen' }, - ...Object.entries(LISTING_TYPE_LABELS).map(([id, label]) => ({ id, label })), + ...LISTING_TYPE_FILTERS.map(({ id, label }) => ({ id, label })), ]; return ( diff --git a/client/src/constants.js b/client/src/constants.js index d13fb46..313a2dc 100644 --- a/client/src/constants.js +++ b/client/src/constants.js @@ -11,16 +11,65 @@ export const TABS = { export const LISTING_TYPE_LABELS = { // Kleinanzeigen - 'miete': 'Mietwohnungen', + 'miete': 'Mietwohnung', 'wohnen-auf-zeit': 'Wohnen auf Zeit', // ImmobilienScout24 - 'apartmentrent': 'Wohnung mieten', + 'apartmentrent': 'Mietwohnung', 'apartmentbuy': 'Wohnung kaufen', 'houserent': 'Haus mieten', 'housebuy': 'Haus kaufen', 'shorttermaccommodation': 'Wohnen auf Zeit', }; +export const LISTING_TYPE_FILTERS = [ + { + id: 'apartment-rent', + label: 'Mietwohnung', + listingTypeIds: ['miete', 'apartmentrent'], + }, + { + id: 'short-term', + label: 'Wohnen auf Zeit', + listingTypeIds: ['wohnen-auf-zeit', 'shorttermaccommodation'], + }, + { + id: 'apartment-buy', + label: 'Wohnung kaufen', + listingTypeIds: ['apartmentbuy'], + }, + { + id: 'house-rent', + label: 'Haus mieten', + listingTypeIds: ['houserent'], + }, + { + id: 'house-buy', + label: 'Haus kaufen', + listingTypeIds: ['housebuy'], + }, +]; + +export const LISTING_TYPE_IDS_BY_FILTER = Object.fromEntries( + LISTING_TYPE_FILTERS.map((filter) => [filter.id, filter.listingTypeIds]), +); + +export const LISTING_TYPE_FILTER_BY_LISTING_TYPE = Object.fromEntries( + LISTING_TYPE_FILTERS.flatMap((filter) => + filter.listingTypeIds.map((listingTypeId) => [listingTypeId, filter.id]), + ), +); + +export function getListingTypeFilterIds(filterId) { + if (!filterId) return []; + return LISTING_TYPE_IDS_BY_FILTER[filterId] ?? [filterId]; +} + +export function normalizeListingTypeFilter(filterId) { + if (!filterId) return ''; + if (LISTING_TYPE_IDS_BY_FILTER[filterId]) return filterId; + return LISTING_TYPE_FILTER_BY_LISTING_TYPE[filterId] ?? filterId; +} + export const LISTING_TYPE_COLORS = { // Apartment rent → Sky 'miete': { bg: '#f0f9ff', text: '#0c4a6e', dot: '#38bdf8' }, @@ -45,5 +94,3 @@ export const PROVIDER_COLORS = { 'kleinanzeigen': { bg: '#f0fdf4', text: '#166534', border: '#86efac' }, 'immoscout24': { bg: '#eff6ff', text: '#1d4ed8', border: '#93c5fd' }, }; - -