feat: F013 match center decision workspace
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { Box, CircularProgress, Typography } from '@mui/material'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { useMatchesByProperty, useApproveMatch } from '../../hooks/useMatches'
|
||||
import { usePropertyById } from '../../hooks/useProperties'
|
||||
import { useMatchCenterStore } from '../../stores/matchCenterStore'
|
||||
import { useCompareStore } from '../../stores/compareStore'
|
||||
import { MatchCardExpanded } from '../match-card/MatchCardExpanded'
|
||||
import { buildMatchCardViewModel } from '../../features/matching/matchCardAdapter'
|
||||
import { reviewService } from '../../services/reviewService'
|
||||
import { MatchCenterEmptyState } from './MatchCenterEmptyState'
|
||||
import { MatchStatusBadge } from './MatchStatusBadge'
|
||||
import type { MatchCardAction } from '../match-card/MatchCardViewModel'
|
||||
import type { VerifiedPortfolioResult } from '../../domain/unifiedResult'
|
||||
|
||||
export function MatchBriefingPanel() {
|
||||
const navigate = useNavigate()
|
||||
const { selectedPropertyId, selectedNeedId } = useMatchCenterStore()
|
||||
const { addToCompare } = useCompareStore()
|
||||
const approveMatch = useApproveMatch()
|
||||
|
||||
const { data: matches = [], isLoading: matchesLoading } = useMatchesByProperty(selectedPropertyId ?? '')
|
||||
const { data: property = null, isLoading: propLoading } = usePropertyById(selectedPropertyId)
|
||||
|
||||
if (!selectedPropertyId && !selectedNeedId) return <MatchCenterEmptyState context="select-both" />
|
||||
if (selectedPropertyId && !selectedNeedId) return <MatchCenterEmptyState context="no-need" />
|
||||
if (!selectedPropertyId && selectedNeedId) return <MatchCenterEmptyState context="no-property" />
|
||||
|
||||
if (matchesLoading || propLoading) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const match = matches.find(m => m.needId === selectedNeedId) ?? null
|
||||
|
||||
if (!match || !property) return <MatchCenterEmptyState context="no-match" />
|
||||
|
||||
const result: VerifiedPortfolioResult = {
|
||||
resultType: 'VERIFIED_PORTFOLIO',
|
||||
matchId: match.id,
|
||||
needId: match.needId,
|
||||
matchScore: match.matchScore,
|
||||
match,
|
||||
property,
|
||||
}
|
||||
|
||||
const actions: MatchCardAction[] = [
|
||||
{
|
||||
id: 'approve',
|
||||
label: 'Genehmigen',
|
||||
actionType: 'APPROVE',
|
||||
variant: 'primary',
|
||||
onClick: () => approveMatch.mutate(match.id),
|
||||
},
|
||||
{
|
||||
id: 'review',
|
||||
label: 'Zur Prüfung',
|
||||
actionType: 'SEND_REVIEW',
|
||||
variant: 'secondary',
|
||||
onClick: () => { reviewService.createReviewTask(match.id) },
|
||||
},
|
||||
{
|
||||
id: 'compare',
|
||||
label: 'Vergleichen',
|
||||
actionType: 'ADD_COMPARE',
|
||||
variant: 'secondary',
|
||||
onClick: () => { addToCompare(property.id); navigate('/demand/compare') },
|
||||
},
|
||||
{
|
||||
id: 'details',
|
||||
label: 'Details',
|
||||
actionType: 'OPEN_DETAIL',
|
||||
variant: 'secondary',
|
||||
onClick: () => navigate(`/demand/results/${match.id}`),
|
||||
},
|
||||
]
|
||||
|
||||
const vm = buildMatchCardViewModel(result, actions)
|
||||
|
||||
return (
|
||||
<Box sx={{ overflowY: 'auto', flex: 1, p: 2.5 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>Match-Briefing</Typography>
|
||||
<MatchStatusBadge status={match.status} />
|
||||
</Box>
|
||||
<MatchCardExpanded vm={vm} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user