Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
LISTING_TYPE_COLORS,
PROVIDER_COLORS,
PROVIDER_LABELS,
getListingTypeFilterIds,
normalizeListingTypeFilter,
} from './constants.js';

const FILTERS_STORAGE_KEY = 'immo.filters.v1';
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) =>
Expand Down Expand Up @@ -275,7 +285,8 @@ export default function App() {
return list;
}, [
listings,
listingTypeFilter,
isListingTypeFilterActive,
listingTypeFilterIds,
publisherFilter,
searchQuery,
minPrice,
Expand Down Expand Up @@ -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);
Expand All @@ -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(
() => ({
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/FilterBar.jsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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 (
Expand Down
55 changes: 51 additions & 4 deletions client/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand All @@ -45,5 +94,3 @@ export const PROVIDER_COLORS = {
'kleinanzeigen': { bg: '#f0fdf4', text: '#166534', border: '#86efac' },
'immoscout24': { bg: '#eff6ff', text: '#1d4ed8', border: '#93c5fd' },
};


Loading