e36c5bc979
New hook files: - useAuth.ts: useLogin, useSwitchDemoRole, useSwitchOrganization - useAI.ts: useParseNeed, useGenerateOfferEmail, useGenerateDecisionBrief, useParseListingText - useAssistant.ts: useAssistantSuggestions (useQuery), useAssistantAnswer (useMutation) - useOfferReport.ts: useOfferReportByInquiry, useCreateOfferReport, useUpdateOfferReport, useGenerateOfferReportPdf - useInquiryReport.ts: useInquiryReportByInquiry, useCreateInquiryReport, useUpdateInquiryReport, useFinalizeInquiryReport - useMarketReport.ts: useMarketReport - useWeighting.ts: useDefaultWeights (synchronous wrapper) - useUnitMatches.ts: useUnitMatchesMap, useUnitBundle, useBundleMatches (useMemo wrappers) Extended hooks: useProperties (add update/create/remove mutations), useNeeds (add useCreateNeed), useMatches (add useNeedMatchesForProperty, useAdditionalMatchesForInquiry), useReviewQueue (add useCreateReviewTask) Updated 21 components/pages: all direct service imports replaced with hooks. Deliberate exception: getRecommendedActions in DataQuality.tsx (pure sync utility, no provider access). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
98 lines
3.5 KiB
TypeScript
98 lines
3.5 KiB
TypeScript
import { Box, CircularProgress, Typography } from '@mui/material'
|
|
import { useNavigate } from 'react-router'
|
|
import { useMatchesByProperty, useApproveMatch } from '../../hooks/useMatches'
|
|
import { usePropertyById } from '../../hooks/useProperties'
|
|
import { useCreateReviewTask } from '../../hooks/useReviewQueue'
|
|
import { useMatchCenterStore } from '../../stores/matchCenterStore'
|
|
import { useCompareStore } from '../../stores/compareStore'
|
|
import { useToastStore } from '../../stores/toastStore'
|
|
import { MatchCardExpanded } from '../match-card/MatchCardExpanded'
|
|
import { buildMatchCardViewModel } from '../../features/matching/matchCardAdapter'
|
|
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 createReviewTask = useCreateReviewTask()
|
|
const showToast = useToastStore((s) => s.showToast)
|
|
|
|
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, {
|
|
onSuccess: () => showToast('Match genehmigt.'),
|
|
onError: () => showToast('Genehmigung fehlgeschlagen.', 'error'),
|
|
}),
|
|
},
|
|
{
|
|
id: 'review',
|
|
label: 'Zur Prüfung',
|
|
actionType: 'SEND_REVIEW',
|
|
variant: 'secondary',
|
|
onClick: () => { createReviewTask.mutate(match.id) },
|
|
},
|
|
{
|
|
id: 'compare',
|
|
label: 'Vergleichen',
|
|
actionType: 'ADD_COMPARE',
|
|
variant: 'secondary',
|
|
onClick: () => { addToCompare(result); 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>
|
|
)
|
|
}
|