refactor: split large page components + taxonomy/HeatBadge/FutureAvailability improvements
- Taxonomy: merge VERIFIED_PORTFOLIO + EXTERNAL_MARKET display → 'Plattform' (dark blue) across all surfaces - HeatBadge: new flame indicator for hot properties (grid, list, pipeline views) - FutureAvailabilityContextPanel: richer detail page with AI summary, strategic assessment, sources - Refactor Pipeline.tsx (630→152 lines) → pipeline/PipelineCard, PipelineColumn, PipelineDetailPanel, pipelineConstants, pipelineUtils - Refactor IntelligenceMatchCard.tsx (483→179 lines) → FutureAvailabilityCard extracted - Refactor MatchDetail.tsx (559→464 lines) → useMatchDetailData hook, MatchDetailPropertyDetails - Refactor Compare.tsx (638→485 lines) → compareUtils, CompareCriteriaCard extracted Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import type { PipelineItem } from '../../domain/pipeline'
|
||||
|
||||
export function scoreColor(score: number) {
|
||||
return score >= 80 ? '#1a7a4a' : score >= 65 ? '#d97706' : '#c0392b'
|
||||
}
|
||||
|
||||
export function detailPath(item: PipelineItem): string | null {
|
||||
// propertyId is always stable across sessions — prefer it
|
||||
if (item.propertyId) return `/demand/property/${item.propertyId}`
|
||||
// matchId / UUID only works in the same session (matchStore is ephemeral)
|
||||
if (item.matchId) return `/demand/results/${item.matchId}`
|
||||
if (item.id.startsWith('match-')) return `/demand/results/${item.id}`
|
||||
return null
|
||||
}
|
||||
|
||||
export function getKiInsight(item: PipelineItem): { summary: string; positives: string[]; risks: string[] } {
|
||||
if (item.stage === 'SAVED') return {
|
||||
summary: `Merkliste-Eintrag mit ${item.matchScore}% Match. Prüfen Sie, ob dieses Objekt qualifiziert werden soll.`,
|
||||
positives: [`Match ${item.matchScore}%`],
|
||||
risks: ['Noch nicht qualifiziert'],
|
||||
}
|
||||
if (item.stage === 'CLOSED_WON') return {
|
||||
summary: `Abschluss erfolgreich. ${item.title} wurde zu ${item.matchScore}% Match abgeschlossen.`,
|
||||
positives: ['Vertraglich gesichert', `Match ${item.matchScore}%`, 'Alle Kriterien erfüllt'],
|
||||
risks: [],
|
||||
}
|
||||
if (item.stage === 'CLOSED_LOST') return {
|
||||
summary: item.notes ?? 'Objekt nicht realisiert.',
|
||||
positives: [],
|
||||
risks: ['Nicht verfügbar', 'Alternative Optionen prüfen'],
|
||||
}
|
||||
const s = item.matchScore
|
||||
return {
|
||||
summary: s >= 80
|
||||
? `Starkes Objekt (${s}%) — deckt die wesentlichen Suchkriterien ab. Prozess aktiv weitertreiben.`
|
||||
: s >= 65
|
||||
? `Solides Objekt (${s}%) mit Potenzial. Gezielte Klärung offener Punkte empfohlen.`
|
||||
: `Schwächerer Match (${s}%). Abweichungen kritisch prüfen bevor weitere Ressourcen investiert werden.`,
|
||||
positives: [
|
||||
...(s >= 80 ? [`Match ${s}% — hohe Übereinstimmung`] : []),
|
||||
...(item.areaLabel ? [`Fläche: ${item.areaLabel}`] : []),
|
||||
...(item.resultType === 'VERIFIED_PORTFOLIO' ? ['Geprüftes Portfolio-Objekt'] : []),
|
||||
...(item.stage === 'NEGOTIATION' ? ['Verhandlung läuft — kurz vor Abschluss'] : []),
|
||||
].slice(0, 3),
|
||||
risks: [
|
||||
...(s < 80 ? [`Match ${s}% — Abweichungen prüfen`] : []),
|
||||
...(item.notes?.includes('Budget') ? ['Budget-Diskrepanz erwähnt'] : []),
|
||||
...(item.resultType === 'FUTURE_AVAILABILITY' ? ['Verfügbarkeit noch nicht bestätigt'] : []),
|
||||
].slice(0, 2),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user