feat: F029 intelligence card system — premium AI-native CRE cards

Gold/Silver/Bronze FIFA-style score badges overlaid on location imagery.
List/grid view toggle on Results and Properties pages (persisted in
localStorage). IntelligenceMatchCard for match results grid view;
PropertyIntelligenceCard for property grid view with warm brown theme.
LocationPreview component with Google Maps link. 10 mock properties
enriched with Unsplash building images.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-18 13:25:27 +02:00
parent 9e7a09f8a2
commit c75d3c8121
13 changed files with 581 additions and 26 deletions
+32 -4
View File
@@ -1,5 +1,6 @@
import { useNavigate } from 'react-router'
import { MatchCardCompact } from '../match-card/MatchCardCompact'
import { IntelligenceMatchCard } from '../match-card/IntelligenceMatchCard'
import { buildMatchCardViewModel } from '../../features/matching/matchCardAdapter'
import { useCompareStore } from '../../stores/compareStore'
import { useShortlistStore } from '../../stores/shortlistStore'
@@ -8,16 +9,17 @@ import type { MatchCardAction } from '../match-card/MatchCardViewModel'
interface Props {
result: UnifiedMatchResult
view?: 'list' | 'grid'
}
function getResultTitle(result: UnifiedMatchResult): string {
if (result.resultType !== 'FUTURE_AVAILABILITY') {
return (result as any).property?.title ?? result.matchId
return result.property?.title ?? result.matchId
}
return (result as any).signal?.companyName ?? result.matchId
return result.signal?.companyName ?? result.matchId
}
export function UnifiedResultCard({ result }: Props) {
export function UnifiedResultCard({ result, view = 'list' }: Props) {
const navigate = useNavigate()
const { addToCompare, removeFromCompare, isInCompare, isFull } = useCompareStore()
const { openAddDialog } = useShortlistStore()
@@ -52,7 +54,7 @@ export function UnifiedResultCard({ result }: Props) {
},
{
id: 'details',
label: 'Details',
label: 'Details',
actionType: 'OPEN_DETAIL',
variant: 'primary',
onClick: () => navigate(`/demand/results/${result.matchId}`),
@@ -60,5 +62,31 @@ export function UnifiedResultCard({ result }: Props) {
]
const vm = buildMatchCardViewModel(result, actions)
if (view === 'grid') {
const imageUrl = result.resultType !== 'FUTURE_AVAILABILITY'
? result.property.images?.[0]
: undefined
const lat = result.resultType !== 'FUTURE_AVAILABILITY'
? result.property.location.coordinates?.lat
: undefined
const lng = result.resultType !== 'FUTURE_AVAILABILITY'
? result.property.location.coordinates?.lng
: undefined
const cityLabel = result.resultType !== 'FUTURE_AVAILABILITY'
? result.property.location.city
: result.signal.locationHint ?? undefined
return (
<IntelligenceMatchCard
vm={vm}
imageUrl={imageUrl}
lat={lat}
lng={lng}
cityLabel={cityLabel}
/>
)
}
return <MatchCardCompact vm={vm} />
}