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:
@@ -1,5 +1,5 @@
|
||||
import { Chip } from '@mui/material'
|
||||
import { ShieldCheck, Globe, Sparkles } from 'lucide-react'
|
||||
import { ShieldCheck, Globe, Building2, Sparkles } from 'lucide-react'
|
||||
import type { ResultType } from '../../domain/enums'
|
||||
import { DS_COLORS } from '../../lib/ds'
|
||||
import { RESULT_TYPE_LABELS } from '../../lib/constants'
|
||||
@@ -12,6 +12,7 @@ interface ResultTypeBadgeProps {
|
||||
const ICONS: Record<ResultType, React.ElementType> = {
|
||||
VERIFIED_PORTFOLIO: ShieldCheck,
|
||||
EXTERNAL_MARKET: Globe,
|
||||
MAISON_WORK: Building2,
|
||||
FUTURE_AVAILABILITY: Sparkles,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Chip } from '@mui/material'
|
||||
import { ShieldCheck, Globe, Sparkles } from 'lucide-react'
|
||||
import { ShieldCheck, Globe, Building2, Sparkles } from 'lucide-react'
|
||||
import type { ResultType } from '../../domain/enums'
|
||||
import { RESULT_TYPE_LABELS } from '../../lib/constants'
|
||||
|
||||
@@ -11,6 +11,7 @@ interface SourceTypeBadgeProps {
|
||||
const CONFIG: Record<string, { bg: string; color: string; Icon: React.ElementType }> = {
|
||||
VERIFIED_PORTFOLIO: { bg: 'rgba(30,58,95,0.1)', color: '#1e3a5f', Icon: ShieldCheck },
|
||||
EXTERNAL_MARKET: { bg: 'rgba(217,119,6,0.1)', color: '#b45309', Icon: Globe },
|
||||
MAISON_WORK: { bg: 'rgba(3,105,161,0.1)', color: '#0369a1', Icon: Building2 },
|
||||
FUTURE_AVAILABILITY: { bg: 'rgba(124,58,237,0.1)', color: '#6d28d9', Icon: Sparkles },
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ import type { UnifiedMatchResult } from '../../domain/unifiedResult'
|
||||
|
||||
const TYPE_META: Record<string, { label: string; color: string }> = {
|
||||
VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' },
|
||||
EXTERNAL_MARKET: { label: 'Marktinserat', color: '#d97706' },
|
||||
EXTERNAL_MARKET: { label: 'Direktinserat', color: '#d97706' },
|
||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
||||
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' },
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useLayoutStore } from '../../stores/layoutStore'
|
||||
const TYPE_DOT: Record<string, string> = {
|
||||
VERIFIED_PORTFOLIO: '#1e3a5f',
|
||||
EXTERNAL_MARKET: '#d97706',
|
||||
MAISON_WORK: '#0369a1',
|
||||
FUTURE_AVAILABILITY: '#7c3aed',
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ import type { MatchCardViewModel } from './MatchCardViewModel'
|
||||
|
||||
const RESULT_TYPE_META: Record<string, { label: string; color: string }> = {
|
||||
VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' },
|
||||
EXTERNAL_MARKET: { label: 'Marktinserat', color: '#d97706' },
|
||||
EXTERNAL_MARKET: { label: 'Direktinserat', color: '#d97706' },
|
||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
||||
FUTURE_AVAILABILITY: { label: 'Schattenmarkt', color: '#7c3aed' },
|
||||
}
|
||||
|
||||
@@ -300,8 +301,16 @@ export function IntelligenceMatchCard({ vm, imageUrl, lat, lng, cityLabel }: Pro
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<Box sx={{ position: 'absolute', top: 10, right: 44 }}>
|
||||
<Box sx={{ position: 'absolute', top: 10, right: 44, display: 'flex', flexDirection: 'column', gap: 0.5, alignItems: 'flex-end' }}>
|
||||
<Chip label={rt.label} size="small" sx={{ bgcolor: rt.color, color: 'white', fontWeight: 600, fontSize: 10, height: 20 }} />
|
||||
{vm.resultType === 'VERIFIED_PORTFOLIO' && (
|
||||
<Chip
|
||||
icon={<Building2 size={9} color="#1e3a5f" />}
|
||||
label="Ihr Objekt"
|
||||
size="small"
|
||||
sx={{ bgcolor: 'rgba(30,58,95,0.85)', color: 'white', fontWeight: 700, fontSize: 9, height: 18, '& .MuiChip-icon': { ml: 0.5 } }}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -4,9 +4,10 @@ import { MatchScoreDisplay } from './MatchScoreDisplay'
|
||||
import type { MatchCardViewModel } from './MatchCardViewModel'
|
||||
|
||||
const RESULT_TYPE_META: Record<string, { label: string; color: string }> = {
|
||||
VERIFIED_PORTFOLIO: { label: 'Verified', color: '#1e3a5f' },
|
||||
EXTERNAL_MARKET: { label: 'Extern', color: '#d97706' },
|
||||
FUTURE_AVAILABILITY: { label: 'Signal', color: '#7c3aed' },
|
||||
VERIFIED_PORTFOLIO: { label: 'Verified', color: '#1e3a5f' },
|
||||
EXTERNAL_MARKET: { label: 'Direkt', color: '#d97706' },
|
||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
||||
FUTURE_AVAILABILITY: { label: 'Signal', color: '#7c3aed' },
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Box, Chip } from '@mui/material'
|
||||
import { Building2 } from 'lucide-react'
|
||||
import { MatchScoreDisplay } from './MatchScoreDisplay'
|
||||
import type { MatchCardViewModel } from './MatchCardViewModel'
|
||||
|
||||
const RESULT_TYPE_META: Record<string, { label: string; color: string }> = {
|
||||
VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' },
|
||||
EXTERNAL_MARKET: { label: 'Marktinserat', color: '#d97706' },
|
||||
EXTERNAL_MARKET: { label: 'Direktinserat', color: '#d97706' },
|
||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
||||
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' },
|
||||
}
|
||||
|
||||
@@ -40,6 +42,14 @@ export function MatchCardHeader({ vm, compact }: Props) {
|
||||
size="small"
|
||||
sx={{ bgcolor: rt.color, color: 'white', fontWeight: 600, fontSize: 11 }}
|
||||
/>
|
||||
{vm.resultType === 'VERIFIED_PORTFOLIO' && (
|
||||
<Chip
|
||||
icon={<Building2 size={10} color="#1e3a5f" />}
|
||||
label="Ihr Objekt"
|
||||
size="small"
|
||||
sx={{ bgcolor: 'rgba(30,58,95,0.12)', color: '#1e3a5f', fontWeight: 700, fontSize: 11, '& .MuiChip-icon': { ml: 0.5 } }}
|
||||
/>
|
||||
)}
|
||||
{vm.assetType && (
|
||||
<Chip label={vm.assetType} size="small" variant="outlined" sx={{ fontSize: 11 }} />
|
||||
)}
|
||||
|
||||
@@ -7,7 +7,8 @@ import type { FutureSignal } from '../../domain/futureSignal'
|
||||
|
||||
const RESULT_TYPE_META: Record<string, { label: string; color: string }> = {
|
||||
VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' },
|
||||
EXTERNAL_MARKET: { label: 'Marktinserat', color: '#d97706' },
|
||||
EXTERNAL_MARKET: { label: 'Direktinserat', color: '#d97706' },
|
||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
||||
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' },
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Box, Card, Chip, Divider, Stack, Typography } from '@mui/material'
|
||||
import { Zap } from 'lucide-react'
|
||||
import { Building2, Zap } from 'lucide-react'
|
||||
import type { ResultType } from '../../domain/enums'
|
||||
|
||||
type FilterSource = Exclude<ResultType, 'FUTURE_AVAILABILITY'> | 'ALL'
|
||||
type FilterSource = Exclude<ResultType, 'FUTURE_AVAILABILITY' | 'VERIFIED_PORTFOLIO'> | 'ALL'
|
||||
type SortBy = 'score' | 'rent' | 'area'
|
||||
|
||||
interface Props {
|
||||
@@ -12,12 +12,15 @@ interface Props {
|
||||
onSortChange: (v: SortBy) => void
|
||||
showSchattenmarkt: boolean
|
||||
onShowSchattenmarktChange: (v: boolean) => void
|
||||
showOwnProperties?: boolean
|
||||
onShowOwnPropertiesChange?: (v: boolean) => void
|
||||
isPropertyManager?: boolean
|
||||
}
|
||||
|
||||
const FILTER_OPTIONS: { value: FilterSource; label: string; color: string }[] = [
|
||||
{ value: 'ALL', label: 'Alle', color: '#1e3a5f' },
|
||||
{ value: 'VERIFIED_PORTFOLIO', label: 'Portfolio', color: '#1e3a5f' },
|
||||
{ value: 'EXTERNAL_MARKET', label: 'Marktinserate', color: '#d97706' },
|
||||
{ value: 'ALL', label: 'Alle', color: '#1e3a5f' },
|
||||
{ value: 'EXTERNAL_MARKET', label: 'Direktinserat', color: '#b45309' },
|
||||
{ value: 'MAISON_WORK', label: 'Maison Work', color: '#0369a1' },
|
||||
]
|
||||
|
||||
const SORT_OPTIONS: { value: SortBy; label: string }[] = [
|
||||
@@ -33,11 +36,14 @@ export function ResultFilterBar({
|
||||
onSortChange,
|
||||
showSchattenmarkt,
|
||||
onShowSchattenmarktChange,
|
||||
showOwnProperties,
|
||||
onShowOwnPropertiesChange,
|
||||
isPropertyManager,
|
||||
}: Props) {
|
||||
return (
|
||||
<Card sx={{ p: 1.5, mb: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 1 }}>
|
||||
{/* Source filter + Schattenmarkt toggle */}
|
||||
{/* Source filter + toggles */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 0.5 }}>
|
||||
<Stack direction="row" spacing={0.5} sx={{ flexWrap: 'wrap', gap: 0.5 }}>
|
||||
{FILTER_OPTIONS.map(({ value, label, color }) => {
|
||||
@@ -78,6 +84,24 @@ export function ResultFilterBar({
|
||||
'& .MuiChip-icon': { ml: 0.75 },
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Eigene Objekte toggle — only for Property Managers */}
|
||||
{isPropertyManager && (
|
||||
<Chip
|
||||
size="small"
|
||||
clickable
|
||||
icon={<Building2 size={11} color={showOwnProperties ? '#1e3a5f' : '#94a3b8'} />}
|
||||
label="Eigene Objekte einblenden"
|
||||
onClick={() => onShowOwnPropertiesChange?.(!showOwnProperties)}
|
||||
sx={{
|
||||
bgcolor: showOwnProperties ? 'rgba(30,58,95,0.10)' : 'transparent',
|
||||
color: showOwnProperties ? '#1e3a5f' : 'text.disabled',
|
||||
border: `1px solid ${showOwnProperties ? '#1e3a5f' : '#e2e8f0'}`,
|
||||
fontWeight: showOwnProperties ? 600 : 400,
|
||||
'& .MuiChip-icon': { ml: 0.75 },
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Sort */}
|
||||
|
||||
@@ -3,7 +3,8 @@ import { Chip } from '@mui/material'
|
||||
|
||||
const TYPE_META: Record<string, { label: string; color: string }> = {
|
||||
VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' },
|
||||
EXTERNAL_MARKET: { label: 'Marktinserat', color: '#d97706' },
|
||||
EXTERNAL_MARKET: { label: 'Direktinserat', color: '#d97706' },
|
||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
||||
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' },
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ export function ShortlistDetail({ shortlist }: Props) {
|
||||
matchId: item.resultId,
|
||||
needId: '',
|
||||
matchScore: item.matchScore,
|
||||
resultType: item.resultType as 'VERIFIED_PORTFOLIO' | 'EXTERNAL_MARKET',
|
||||
resultType: item.resultType as 'VERIFIED_PORTFOLIO' | 'EXTERNAL_MARKET' | 'MAISON_WORK',
|
||||
property: { id: item.propertyId ?? item.resultId, title: item.title } as any,
|
||||
match: { id: item.resultId, matchScore: item.matchScore, confidenceLevel: item.confidenceScore ?? 0.8 } as any,
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import type { ShortlistItem } from '../../domain/shortlist'
|
||||
|
||||
const RESULT_TYPE_LABEL: Record<string, string> = {
|
||||
VERIFIED_PORTFOLIO: 'Portfolio',
|
||||
EXTERNAL_MARKET: 'Markt',
|
||||
EXTERNAL_MARKET: 'Direkt',
|
||||
MAISON_WORK: 'Maison Work',
|
||||
FUTURE_AVAILABILITY: 'Signal',
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,8 @@ export function getAvailabilityChipColor(status: AvailabilityStatus): 'success'
|
||||
export function getResultTypeLabel(type: ResultType): string {
|
||||
const labels: Record<string, string> = {
|
||||
VERIFIED_PORTFOLIO: 'Portfolio',
|
||||
EXTERNAL_MARKET: 'Markt',
|
||||
EXTERNAL_MARKET: 'Direktinserat',
|
||||
MAISON_WORK: 'Maison Work',
|
||||
FUTURE_AVAILABILITY: 'Zukunft',
|
||||
}
|
||||
return labels[type] ?? type
|
||||
@@ -63,6 +64,7 @@ export function getResultTypeColor(type: ResultType): string {
|
||||
const colors: Record<string, string> = {
|
||||
VERIFIED_PORTFOLIO: '#1e3a5f',
|
||||
EXTERNAL_MARKET: '#1a7a4a',
|
||||
MAISON_WORK: '#0369a1',
|
||||
FUTURE_AVAILABILITY: '#7c3aed',
|
||||
}
|
||||
return colors[type] ?? '#6b7280'
|
||||
|
||||
Reference in New Issue
Block a user