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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user