67d3d06a53
- New /supply/new-listing page (Path A: pre-filled from portfolio unit, Path B: standalone blank form); creates EXTERNAL_MARKET + sourceType DIRECT - "+ Inserat" button on available units in PropertyDetailView navigates with prefilled address/area/rent - Nav entry "Neues Inserat" added to supply sidebar - SourceProvenancePanel hidden for DIRECT source listings (no external source) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
72 lines
2.8 KiB
TypeScript
72 lines
2.8 KiB
TypeScript
import { Box, Chip, Link, Paper, Typography } from '@mui/material'
|
||
import type { Property } from '../../domain/property'
|
||
|
||
const FRESHNESS_META: Record<string, { label: string; color: 'success' | 'warning' | 'error' }> = {
|
||
FRESH: { label: 'Aktuell (< 48h)', color: 'success' },
|
||
STALE: { label: 'Veraltet (2–14d)', color: 'warning' },
|
||
OUTDATED: { label: 'Outdated (> 14d)', color: 'error' },
|
||
}
|
||
|
||
interface Props {
|
||
property: Property | null
|
||
}
|
||
|
||
export function SourceProvenancePanel({ property }: Props) {
|
||
if (!property) return null
|
||
if (property.sourceType === 'DIRECT' || property.sourceMeta?.sourceType === 'DIRECT') return null
|
||
|
||
const freshness = property.dataQuality?.freshness
|
||
const freshnessMeta = freshness ? (FRESHNESS_META[freshness] ?? null) : null
|
||
const sourceUrl = property.sourceUrl ?? property.sourceMeta?.sourceUrl
|
||
const sourceLabel = property.sourceLabel ?? property.sourceMeta?.sourceLabel ?? property.sourceType ?? '–'
|
||
const sourceUpdatedAt = property.sourceUpdatedAt ?? property.sourceMeta?.sourceUpdatedAt
|
||
const lastVerified = property.dataQuality?.lastVerifiedAt
|
||
|
||
const rows: { label: string; value: React.ReactNode }[] = [
|
||
{ label: 'Quelltyp', value: property.sourceType ?? '–' },
|
||
{
|
||
label: 'Quelle',
|
||
value: sourceUrl ? (
|
||
<Link href={sourceUrl} target="_blank" rel="noopener noreferrer" variant="body2">
|
||
{sourceLabel}
|
||
</Link>
|
||
) : (
|
||
<Typography variant="body2">{sourceLabel}</Typography>
|
||
),
|
||
},
|
||
...(sourceUpdatedAt ? [{ label: 'Letzte Aktualisierung', value: <Typography variant="body2">{sourceUpdatedAt}</Typography> }] : []),
|
||
...(lastVerified ? [{ label: 'Letzte Verifikation', value: <Typography variant="body2">{lastVerified}</Typography> }] : []),
|
||
...(freshnessMeta ? [{
|
||
label: 'Aktualität',
|
||
value: <Chip label={freshnessMeta.label} size="small" color={freshnessMeta.color} />,
|
||
}] : []),
|
||
]
|
||
|
||
const warnings = property.dataQuality?.warnings ?? []
|
||
|
||
return (
|
||
<Paper sx={{ p: 2.5, mb: 2 }}>
|
||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 1.5 }}>Quelle & Herkunft</Typography>
|
||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75 }}>
|
||
{rows.map((row, i) => (
|
||
<Box key={i} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||
<Typography variant="body2" color="text.secondary" sx={{ minWidth: 160 }}>
|
||
{row.label}
|
||
</Typography>
|
||
{row.value}
|
||
</Box>
|
||
))}
|
||
</Box>
|
||
{warnings.length > 0 && (
|
||
<Box sx={{ mt: 1.5, p: 1, bgcolor: '#fffbeb', borderRadius: 1 }}>
|
||
{warnings.map((w, i) => (
|
||
<Typography key={i} variant="caption" color="text.secondary" sx={{ display: 'block' }}>
|
||
⚠ {w}
|
||
</Typography>
|
||
))}
|
||
</Box>
|
||
)}
|
||
</Paper>
|
||
)
|
||
}
|