feat: clickable NeedMatchCard in Matchability tab navigates to Anfragencenter

Clicking a need card in the Matchability tab pre-selects the property
and navigates to Anfragencenter → Latente Anfragen tab. Angebot creation
lives here (demand-side), not in Marktsignale. NeedMatchCard gains
onClick prop with navy hover + shadow treatment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-19 16:05:18 +02:00
parent 18f4b2b37a
commit 0651333030
3 changed files with 27 additions and 35 deletions
+6 -3
View File
@@ -4,6 +4,7 @@ import type { PropertyNeedMatch } from '../../domain/match'
interface Props { interface Props {
match: PropertyNeedMatch match: PropertyNeedMatch
onClick?: () => void
} }
function ScoreBadge({ score }: { score: number }) { function ScoreBadge({ score }: { score: number }) {
@@ -43,17 +44,19 @@ function InfoRow({ icon, label, value }: { icon: React.ReactNode; label: string;
) )
} }
export function NeedMatchCard({ match }: Props) { export function NeedMatchCard({ match, onClick }: Props) {
return ( return (
<Card <Card
elevation={0} elevation={0}
onClick={onClick}
sx={{ sx={{
p: 2, p: 2,
mb: 1.5, mb: 1.5,
border: '1px solid #e2e8f0', border: '1px solid #e2e8f0',
borderRadius: 1.5, borderRadius: 1.5,
'&:hover': { borderColor: '#cbd5e1', bgcolor: '#fafafa' }, cursor: onClick ? 'pointer' : 'default',
transition: 'border-color 0.15s, background-color 0.15s', '&:hover': onClick ? { borderColor: '#1e3a5f', bgcolor: '#f8fafc', boxShadow: '0 2px 8px rgba(30,58,95,0.08)' } : { borderColor: '#cbd5e1', bgcolor: '#fafafa' },
transition: 'border-color 0.15s, background-color 0.15s, box-shadow 0.15s',
}} }}
> >
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1.5 }}> <Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1.5 }}>
+11 -2
View File
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router'
import { import {
Alert, Alert,
Box, Box,
@@ -15,6 +16,7 @@ import {
} from '@mui/material' } from '@mui/material'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { Edit2, Save, X } from 'lucide-react' import { Edit2, Save, X } from 'lucide-react'
import { useOfferWizardStore } from '../../stores/offerWizardStore'
import { PropertyMap } from '../shared' import { PropertyMap } from '../shared'
import { NeedMatchCard } from './NeedMatchCard' import { NeedMatchCard } from './NeedMatchCard'
import { PropertyMarketSignalsTab } from './PropertyMarketSignalsTab' import { PropertyMarketSignalsTab } from './PropertyMarketSignalsTab'
@@ -238,6 +240,8 @@ function OverviewPanel({ p, editing, draft, onDraftChange }: OverviewPanelProps)
function MatchabilityTabPanel({ propertyId }: { propertyId: string }) { function MatchabilityTabPanel({ propertyId }: { propertyId: string }) {
const [matches, setMatches] = useState<PropertyNeedMatch[]>([]) const [matches, setMatches] = useState<PropertyNeedMatch[]>([])
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const navigate = useNavigate()
const setSelectedProperties = useOfferWizardStore(s => s.setSelectedProperties)
useEffect(() => { useEffect(() => {
let cancelled = false let cancelled = false
@@ -248,6 +252,11 @@ function MatchabilityTabPanel({ propertyId }: { propertyId: string }) {
return () => { cancelled = true } return () => { cancelled = true }
}, [propertyId]) }, [propertyId])
function handleNeedCardClick() {
setSelectedProperties([propertyId])
navigate('/supply/anfragen', { state: { tab: 1 } })
}
if (loading) { if (loading) {
return ( return (
<Box sx={{ p: 3, display: 'flex', justifyContent: 'center' }}> <Box sx={{ p: 3, display: 'flex', justifyContent: 'center' }}>
@@ -263,7 +272,7 @@ function MatchabilityTabPanel({ propertyId }: { propertyId: string }) {
Matchability Matchability
</Typography> </Typography>
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
Bedarfsprofile mit 80% Match-Score für dieses Objekt Bedarfsprofile mit 80% Match-Score Karte klicken um Angebot zu erstellen
</Typography> </Typography>
</Box> </Box>
{matches.length === 0 ? ( {matches.length === 0 ? (
@@ -271,7 +280,7 @@ function MatchabilityTabPanel({ propertyId }: { propertyId: string }) {
Keine Bedarfsprofile mit 80% Match-Score für dieses Objekt gefunden. Keine Bedarfsprofile mit 80% Match-Score für dieses Objekt gefunden.
</Alert> </Alert>
) : ( ) : (
matches.map(m => <NeedMatchCard key={m.matchId} match={m} />) matches.map(m => <NeedMatchCard key={m.matchId} match={m} onClick={handleNeedCardClick} />)
)} )}
</Box> </Box>
) )
@@ -1,9 +1,7 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router'
import { Alert, Box, Button, Chip, CircularProgress, Dialog, DialogContent, DialogTitle, LinearProgress, Typography } from '@mui/material' import { Alert, Box, Button, Chip, CircularProgress, Dialog, DialogContent, DialogTitle, LinearProgress, Typography } from '@mui/material'
import { BarChart2, Building2, CheckCircle, Download, FileText, Lightbulb, Send, TrendingUp } from 'lucide-react' import { BarChart2, Building2, CheckCircle, Download, FileText, Lightbulb, TrendingUp } from 'lucide-react'
import { marketReportService } from '../../services/marketReportService' import { marketReportService } from '../../services/marketReportService'
import { useOfferWizardStore } from '../../stores/offerWizardStore'
import type { MarketReport, PropertyMarketSignal, SignalCategory } from '../../domain/marketReport' import type { MarketReport, PropertyMarketSignal, SignalCategory } from '../../domain/marketReport'
interface Props { interface Props {
@@ -222,13 +220,6 @@ export function PropertyMarketSignalsTab({ propertyId }: Props) {
const [report, setReport] = useState<MarketReport | null>(null) const [report, setReport] = useState<MarketReport | null>(null)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [dialogOpen, setDialogOpen] = useState(false) const [dialogOpen, setDialogOpen] = useState(false)
const navigate = useNavigate()
const setSelectedProperties = useOfferWizardStore(s => s.setSelectedProperties)
function handleAngebotErstellen() {
setSelectedProperties([propertyId])
navigate('/supply/anfragen', { state: { tab: 1 } })
}
useEffect(() => { useEffect(() => {
let cancelled = false let cancelled = false
@@ -260,26 +251,15 @@ export function PropertyMarketSignalsTab({ propertyId }: Props) {
</Typography> </Typography>
)} )}
</Box> </Box>
<Box sx={{ display: 'flex', gap: 1 }}> <Button
<Button variant="outlined"
variant="contained" size="small"
size="small" startIcon={<FileText size={14} />}
startIcon={<Send size={14} />} onClick={() => setDialogOpen(true)}
onClick={handleAngebotErstellen} sx={{ textTransform: 'none', fontSize: '0.8125rem' }}
sx={{ textTransform: 'none', fontSize: '0.8125rem', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }} >
> Bericht erstellen
Angebot erstellen </Button>
</Button>
<Button
variant="outlined"
size="small"
startIcon={<FileText size={14} />}
onClick={() => setDialogOpen(true)}
sx={{ textTransform: 'none', fontSize: '0.8125rem' }}
>
Bericht erstellen
</Button>
</Box>
</Box> </Box>
{!report ? ( {!report ? (