Files
property-match/src/components/match-detail/SourceProvenancePanel.tsx
T
Benjamin Sutter 67d3d06a53 feat: Inserat erstellen — direct listing flow for units and standalone
- 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>
2026-05-21 21:10:41 +02:00

72 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 (214d)', 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>
)
}