Files
property-match/src/components/market-leads/MarketLeadPanels.tsx
T
Benjamin SutterandClaude Opus 5 a219d3f2ec feat(livia)!: Exposé-Funktion vollständig entfernt
Runde 8 hat Livia zur Research- & Market-Intelligence-Agentin gemacht. Die
Exposé-Erstellung war der Kern ihrer alten Rolle und fällt mit ihr weg — sie
wird nicht nachgeordnet weitergeführt. Ein zweiter Reiter hätte die Funktion
sichtbar gelassen und die neue Positionierung damit halbiert.

Entfernt: 23 Dateien (Arbeitsbereich, dreistufiger Assistent, Dossier mit elf
Bereichen, Provider, Services, Hooks, Feldkatalog, Mockdaten, deterministischer
Textbaustein) sowie `generateExposeText` aus `IAIService` und beiden
Implementierungen.

Livias Arbeitsplatz zeigt jetzt nur noch die Recherche-Leads — ohne
Reiterbalken, weil eine Leiste mit einem Eintrag eine Alternative behauptet,
die es nicht gibt. Ihr Personalblatt, die Bearbeitungsverlaufs-Einträge und die
Teamdokumentation sind auf die neue Rolle nachgezogen; die beiden
Beispielvorgänge zeigen jetzt eine Leaderzeugung und ein Review mit
widersprüchlichen Quellen statt Lagebeschrieb und Exposétext.

Bei Nora entfällt die Mehrfachauswahl in der Objektliste: sie diente allein der
Weiterleitung an die Exposé-Erstellung. Eine Auswahl ohne Ziel wäre ein
Bedienelement, das nichts auslöst. Die Matching-Darstellung selbst bleibt
unverändert.

Der Wiederaufbau ist in `archive/livia/` beschrieben; das Manifest hält jetzt
fest, welche seiner Annahmen eingetreten sind und welche nicht — Livia bleibt
im Team, nur die Funktion ist weg.

Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen, 423/423 Tests grün,
Produktionsbuild erfolgreich, Smoke-Test über sieben Seiten ohne
Laufzeitfehler, 12/12 Abnahmekriterien erfüllt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 22:44:08 +02:00

112 lines
4.5 KiB
TypeScript

import { Box, Chip, Typography } from '@mui/material'
import { Building2, ExternalLink } from 'lucide-react'
import type { Property } from '../../domain/property'
import type { ExtractedContact } from '../../domain/futureSignal'
import { ObjectDeepLink } from '../team'
import { CONTACT_ICONS } from './marketLeadHelpers'
import { DS_BG, DS_BRAND, DS_SLATE, DS_TEXT } from '../../lib/ds'
/**
* Bausteine der Signal-Detailansicht auf der Nora-Seite.
*
* Ausgezogen aus `MarketIntelligence.tsx` — die Seite hält jetzt nur noch
* Reiter, Auswahl und Rahmen.
*/
// ── Left pane list item ──────────────────────────────────────────────────────
// ── Contact row ──────────────────────────────────────────────────────────────
/**
* Extrahierte Kontaktdaten (Runde 4, §7.3).
*
* Ohne Konfidenzstufe und ohne Farbpunkt: gezeigt wird nur noch, was klar
* belegbar ist — siehe `confirmableContacts`. Ein Wert mit dem Hinweis
* «spekulativ» hilft niemandem; entweder man kann ihn anschreiben oder nicht.
*/
function ContactRow({ contact }: { contact: ExtractedContact }) {
const icon = CONTACT_ICONS[contact.type]
const isClickable = contact.type === 'WEBSITE' || contact.type === 'LINKEDIN' || contact.type === 'EMAIL'
const href = contact.type === 'EMAIL' ? `mailto:${contact.value}` : contact.type === 'WEBSITE' || contact.type === 'LINKEDIN' ? `https://${contact.value.replace(/^https?:\/\//, '')}` : undefined
return (
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1.25, py: 0.75, borderBottom: '1px solid #f8fafc' }}>
<Box sx={{ color: DS_SLATE[500], mt: 0.125, flexShrink: 0 }}>{icon}</Box>
<Box sx={{ flex: 1, minWidth: 0 }}>
{isClickable && href ? (
<Box
component="a"
href={href}
target="_blank"
rel="noopener noreferrer"
sx={{ color: DS_BRAND.main, fontWeight: 500, fontSize: '0.8125rem', textDecoration: 'none', '&:hover': { textDecoration: 'underline' }, display: 'flex', alignItems: 'center', gap: 0.5 }}
>
{contact.value}
<ExternalLink size={11} />
</Box>
) : (
<Typography variant="body2" sx={{ fontWeight: 500, fontSize: '0.8125rem', color: DS_SLATE[900] }}>
{contact.value}
</Typography>
)}
{contact.label && (
<Typography variant="caption" sx={{ color: DS_SLATE[400], fontSize: '0.7rem' }}>
{contact.label}
</Typography>
)}
</Box>
</Box>
)
}
// ── Objektzeile ──────────────────────────────────────────────────────────────
/**
* Ein passendes Portfolioobjekt zu einem Signal.
*
* Der Anschreiben-Entwurf und die Reminder-Aktion sind mit Runde 4 entfallen
* (§7.3), die Mehrfachauswahl mit Runde 8: sie diente allein dazu, Objekte an
* die Exposé-Erstellung weiterzureichen, und diese Funktion gibt es bei Livia
* nicht mehr. Eine Auswahl ohne Ziel wäre ein Bedienelement, das nichts
* auslöst — schlimmer als keines.
*
* Geblieben ist, was Nora für die Entscheidung braucht: welches Objekt, wo,
* wie gross, zu welchem Preis und wie gut die Fläche passt.
*/
function SignalPropertyRow({
p, fit,
}: {
p: Property
fit: number | null
}) {
return (
<Box
sx={{
display: 'flex', alignItems: 'center', gap: 1,
px: 1.5, py: 1.25, mb: 1,
border: '1px solid #e2e8f0',
borderRadius: 1.5,
bgcolor: 'white',
}}
>
<Building2 size={14} color="#152642" style={{ flexShrink: 0 }} />
<Box sx={{ flex: 1, minWidth: 0 }}>
<ObjectDeepLink propertyId={p.id} label={p.title} fontWeight={600} />
<Typography variant="caption" sx={{ color: DS_SLATE[500] }}>
{p.location.city} · {p.areaSqm.toLocaleString('de-CH')} m² · CHF {p.rentPricePerSqm}/m²/J
</Typography>
</Box>
{fit !== null && (
<Chip
label={`${fit}% Flächenmatch`}
size="small"
sx={{ height: 20, fontSize: '0.65rem', fontWeight: 600, flexShrink: 0, bgcolor: DS_BG.subtle, color: DS_TEXT.secondary }}
/>
)}
</Box>
)
}
export { ContactRow, SignalPropertyRow }