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
+28 -11
View File
@@ -3,8 +3,9 @@ import { Box, Drawer } from '@mui/material'
import { useNavigate } from 'react-router'
import { PageHeader } from '../../components/layout'
import { DecisionContextPanel } from '../../components/ui'
import { ViewToggle } from '../../components/shared'
import { useProperties } from '../../hooks/useProperties'
import { PropertyFilterBar, PropertyTable, PropertyDetailView } from '../../components/supply'
import { PropertyFilterBar, PropertyTable, PropertyDetailView, PropertyIntelligenceCard } from '../../components/supply'
import type { PropertyTableFilters } from '../../components/supply'
import type { Property } from '../../domain/property'
@@ -50,6 +51,9 @@ export default function Properties() {
const navigate = useNavigate()
const [selectedId, setSelectedId] = useState<string | null>(null)
const [filters, setFilters] = useState<PropertyTableFilters>({})
const [view, setView] = useState<'list' | 'grid'>(() =>
(localStorage.getItem('view-properties') as 'list' | 'grid') ?? 'list'
)
const { data: properties = [], isLoading, isError } = useProperties()
const filtered = applyFilters(properties, filters)
@@ -65,7 +69,6 @@ export default function Properties() {
p => p.dataQuality.freshness === 'STALE' || p.dataQuality.freshness === 'OUTDATED'
)
// Unique missing fields across all objects
const allMissingFields = [...new Set(
properties.flatMap(p => p.dataQuality.missingCriticalFields)
)].slice(0, 4)
@@ -75,6 +78,12 @@ export default function Properties() {
<PageHeader
title="Objektverwaltung"
subtitle={`${filtered.length} von ${properties.length} Objekten`}
secondaryActions={
<ViewToggle
view={view}
onChange={v => { setView(v); localStorage.setItem('view-properties', v) }}
/>
}
/>
{!isLoading && properties.length > 0 && (
@@ -123,15 +132,23 @@ export default function Properties() {
<PropertyFilterBar filters={filters} onFiltersChange={setFilters} />
<Box sx={{ flex: 1, overflowY: 'auto' }}>
<PropertyTable
properties={filtered}
isLoading={isLoading}
isError={isError}
selectedId={selectedId}
onSelect={setSelectedId}
filters={filters}
onFiltersChange={setFilters}
/>
{view === 'grid' ? (
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 2, p: 2 }}>
{filtered.map(p => (
<PropertyIntelligenceCard key={p.id} property={p} onSelect={setSelectedId} />
))}
</Box>
) : (
<PropertyTable
properties={filtered}
isLoading={isLoading}
isError={isError}
selectedId={selectedId}
onSelect={setSelectedId}
filters={filters}
onFiltersChange={setFilters}
/>
)}
</Box>
<Drawer