feat: add Maison Work source type, rename ExternalMarket to Direktinserat, hide own portfolio from demand search by default

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-19 23:02:05 +02:00
parent a521155bb2
commit 3756675b6f
29 changed files with 115 additions and 42 deletions
+14 -5
View File
@@ -1,5 +1,5 @@
import { useState } from 'react'
import { Box, Button, Card, Typography } from '@mui/material'
import { Box, Button, Card, Chip, Typography } from '@mui/material'
import { useNavigate, useLocation } from 'react-router'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { useUnifiedResults } from '../../hooks/useUnifiedResults'
@@ -13,10 +13,11 @@ import {
UnifiedResultFeed,
} from '../../components/results'
import { AddToShortlistDialog } from '../../components/shortlist'
import { useSessionStore } from '../../stores/sessionStore'
import type { ResultType } from '../../domain/enums'
import type { UnifiedMatchResult } from '../../domain/unifiedResult'
type FilterSource = Exclude<ResultType, 'FUTURE_AVAILABILITY'> | 'ALL'
type FilterSource = Exclude<ResultType, 'FUTURE_AVAILABILITY' | 'VERIFIED_PORTFOLIO'> | 'ALL'
type SortBy = 'score' | 'rent' | 'area'
function sortResults(results: UnifiedMatchResult[], sortBy: SortBy): UnifiedMatchResult[] {
@@ -34,9 +35,11 @@ export default function Results() {
const navigate = useNavigate()
const location = useLocation()
const queryClient = useQueryClient()
const { currentUser } = useSessionStore()
const [filterSource, setFilterSource] = useState<FilterSource>('ALL')
const [sortBy, setSortBy] = useState<SortBy>('score')
const [showSchattenmarkt, setShowSchattenmarkt] = useState(true)
const [showOwnProperties, setShowOwnProperties] = useState(false)
const [view, setView] = useState<'list' | 'grid'>(() =>
(localStorage.getItem('view-results') as 'list' | 'grid') ?? 'list'
)
@@ -69,6 +72,8 @@ export default function Results() {
const { data: results = [], isLoading } = useUnifiedResults(activeNeed?.id)
const filtered = results
// Exclude VERIFIED_PORTFOLIO by default; include only when toggled on by Property Manager
.filter(r => r.resultType !== 'VERIFIED_PORTFOLIO' || showOwnProperties)
.filter(r => filterSource === 'ALL' || r.resultType === filterSource)
.filter(r => showSchattenmarkt || r.resultType !== 'FUTURE_AVAILABILITY')
@@ -76,6 +81,7 @@ export default function Results() {
const verifiedCount = results.filter(r => r.resultType === 'VERIFIED_PORTFOLIO').length
const externalCount = results.filter(r => r.resultType === 'EXTERNAL_MARKET').length
const maisonWorkCount = results.filter(r => r.resultType === 'MAISON_WORK').length
const futureCount = results.filter(r => r.resultType === 'FUTURE_AVAILABILITY').length
const strongCount = results.filter(r => r.matchScore >= 80).length
const missingDataCount = results.filter(r =>
@@ -101,8 +107,8 @@ export default function Results() {
context={activeNeed ? `Suche: ${activeNeed.assetType} · ${activeNeed.requiredArea.min}${activeNeed.requiredArea.max} m² · ${activeNeed.preferredLocations.join(', ')}` : undefined}
metrics={[
...(strongCount > 0 ? [{ label: 'starke Treffer (≥80)', value: strongCount, severity: 'positive' as const }] : []),
...(verifiedCount > 0 ? [{ label: 'verifiziertes Portfolio', value: verifiedCount, severity: 'neutral' as const }] : []),
...(externalCount > 0 ? [{ label: 'externer Markt', value: externalCount, severity: 'neutral' as const }] : []),
...(externalCount > 0 ? [{ label: 'Direktinserate', value: externalCount, severity: 'neutral' as const }] : []),
...(maisonWorkCount > 0 ? [{ label: 'Maison Work', value: maisonWorkCount, severity: 'neutral' as const }] : []),
...(futureCount > 0 ? [{ label: 'Zukunftssignale', value: futureCount, severity: 'warning' as const }] : []),
...(missingDataCount > 0 ? [{ label: 'mit Datenlücken', value: missingDataCount, severity: 'warning' as const }] : []),
]}
@@ -171,12 +177,15 @@ export default function Results() {
onSortChange={setSortBy}
showSchattenmarkt={showSchattenmarkt}
onShowSchattenmarktChange={setShowSchattenmarkt}
showOwnProperties={showOwnProperties}
onShowOwnPropertiesChange={setShowOwnProperties}
isPropertyManager={currentUser?.role === 'PROPERTY_MANAGER'}
/>
{isLoading ? (
<FeedSkeleton />
) : sorted.length === 0 ? (
<FeedEmptyState filtered={filterSource !== 'ALL' || !showSchattenmarkt} />
<FeedEmptyState filtered={filterSource !== 'ALL' || !showSchattenmarkt || !showOwnProperties} />
) : (
<UnifiedResultFeed results={sorted} view={view} />
)}