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:
@@ -0,0 +1,87 @@
|
||||
import { useState } from 'react'
|
||||
import { Box, IconButton, Tooltip, Typography } from '@mui/material'
|
||||
import { MapPin, Map } from 'lucide-react'
|
||||
|
||||
interface Props {
|
||||
imageUrl?: string
|
||||
lat?: number
|
||||
lng?: number
|
||||
address?: string
|
||||
cityLabel?: string
|
||||
height?: number
|
||||
}
|
||||
|
||||
// TODO: replace placeholder with Google Maps Static API:
|
||||
// https://maps.googleapis.com/maps/api/staticmap?center={lat},{lng}&zoom=15&size=800x400&key=YOUR_KEY
|
||||
|
||||
function buildMapsUrl(lat?: number, lng?: number, address?: string): string {
|
||||
if (lat != null && lng != null) {
|
||||
return `https://www.google.com/maps/search/?api=1&query=${lat},${lng}`
|
||||
}
|
||||
if (address) {
|
||||
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(address)}`
|
||||
}
|
||||
return 'https://www.google.com/maps'
|
||||
}
|
||||
|
||||
export function LocationPreview({ imageUrl, lat, lng, address, cityLabel, height = 180 }: Props) {
|
||||
const [imgError, setImgError] = useState(false)
|
||||
const mapsUrl = buildMapsUrl(lat, lng, address)
|
||||
const showImage = !!imageUrl && !imgError
|
||||
|
||||
return (
|
||||
<Box sx={{ position: 'relative', width: '100%', height, overflow: 'hidden', flexShrink: 0 }}>
|
||||
{showImage ? (
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={cityLabel ?? 'Standort'}
|
||||
onError={() => setImgError(true)}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
|
||||
/>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background: 'linear-gradient(160deg,#dce8f2 0%,#b8cfe0 100%)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 0.5,
|
||||
}}
|
||||
>
|
||||
<MapPin size={28} color="#7ba3bf" />
|
||||
{cityLabel && (
|
||||
<Typography variant="body2" sx={{ color: '#5a87a3', fontWeight: 600, letterSpacing: 0.5 }}>
|
||||
{cityLabel}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Google Maps button */}
|
||||
<Tooltip title="In Google Maps öffnen" placement="top">
|
||||
<IconButton
|
||||
component="a"
|
||||
href={mapsUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
size="small"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
bottom: 8,
|
||||
right: 8,
|
||||
bgcolor: 'rgba(255,255,255,0.88)',
|
||||
backdropFilter: 'blur(4px)',
|
||||
width: 28,
|
||||
height: 28,
|
||||
'&:hover': { bgcolor: 'white' },
|
||||
}}
|
||||
>
|
||||
<Map size={14} color="#1e3a5f" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user