// desktop-screen-detail.jsx — Listing Detail + AI Eval desktop screens function desktopEvalArray(primary, fallback = []) { return Array.isArray(primary) ? primary : (Array.isArray(fallback) ? fallback : []); } function desktopEvalText(value) { return String(value || '').replace(/\\r\\n/g, '\n').replace(/\\n/g, '\n').replace(/\\t/g, '\t'); } function desktopRentalPersonalizationEnabled(user) { const value = user?.rental_profile?.personalized_inquiry_enabled; return !(value === false || value === 'false' || value === 0 || value === '0'); } function DesktopEvalList({ items, tone = 'neutral' }) { const color = tone === 'danger' ? 'var(--danger)' : (tone === 'success' ? 'var(--success)' : 'var(--ink-muted)'); const marker = tone === 'danger' ? '−' : (tone === 'success' ? '+' : '•'); return (
{items.map((item, i) => (
{marker} {item}
))}
); } // ─── LISTING DETAIL ──────────────────────────────────────────────────── function DetailDesktop({ active = 'kauf', onNav = () => {}, onOpenAI = () => {}, id = null, ctx = {}, topBarProps = {}, } = {}) { const l = desktopFindListing(ctx, id); const safeListing = l || {}; const eval_ = desktopEvaluationForListing(ctx, safeListing); const photos = desktopPhotos(safeListing); const [lightboxIndex, setLightboxIndex] = React.useState(null); const isRental = safeListing.type === 'miete'; const detailActive = isRental ? 'miete' : active; const isFav = ctx.favs?.has(String(safeListing.id)) || ctx.favs?.has(safeListing.id); const adId = safeListing.adId || safeListing.id; const taskId = safeListing.task_id || safeListing.searchId; const personalizedInquiryEnabled = isRental && desktopRentalPersonalizationEnabled(ctx.user); const messageKey = isRental ? 'immobot.desktop.contactMessage.rental' : 'immobot.desktop.contactMessage.purchase'; const evalModelLabel = eval_ ? [eval_.provider === 'codex' ? 'Codex' : eval_.provider, eval_.model].filter(Boolean).join(' · ') : ''; const cachedInquiryMessage = personalizedInquiryEnabled ? (safeListing.rentalInquiryMessage || '') : ''; const standardMessage = isRental ? (ctx.user?.default_reply_message || 'Sehr geehrte Damen und Herren,\n\nich habe Interesse an dieser Mietwohnung und möchte gerne einen Besichtigungstermin vereinbaren.') : 'Sehr geehrte Damen und Herren,\n\nich habe Interesse an dieser Immobilie und möchte gerne einen Besichtigungstermin vereinbaren.'; const defaultMessage = cachedInquiryMessage || standardMessage; const [contactMessage, setContactMessage] = React.useState(() => personalizedInquiryEnabled ? cachedInquiryMessage : ((isRental ? null : localStorage.getItem(messageKey)) || standardMessage)); const [contactLoading, setContactLoading] = React.useState(() => personalizedInquiryEnabled && !cachedInquiryMessage); React.useEffect(() => { if (isRental && personalizedInquiryEnabled) return; localStorage.setItem(messageKey, contactMessage); }, [isRental, personalizedInquiryEnabled, messageKey, contactMessage]); React.useEffect(() => { if (!isRental || personalizedInquiryEnabled) return; setContactLoading(false); setContactMessage(localStorage.getItem(messageKey) || defaultMessage); }, [isRental, personalizedInquiryEnabled, messageKey, defaultMessage]); React.useEffect(() => { if (!isRental || !personalizedInquiryEnabled || !taskId || !adId || !ctx.inquiryPreview) return undefined; if (cachedInquiryMessage) { setContactMessage(cachedInquiryMessage); setContactLoading(false); return undefined; } let active = true; setContactMessage(''); setContactLoading(true); ctx.inquiryPreview(taskId, adId) .then(result => { if (active && result?.message) setContactMessage(result.message); }) .catch(() => { if (active) setContactMessage(defaultMessage); }) .finally(() => { if (active) setContactLoading(false); }); return () => { active = false; }; }, [isRental, personalizedInquiryEnabled, taskId, adId, ctx.inquiryPreview, defaultMessage, cachedInquiryMessage]); React.useEffect(() => { if (lightboxIndex == null) return undefined; const onKey = (event) => { if (event.key === 'Escape') setLightboxIndex(null); if (event.key === 'ArrowRight') setLightboxIndex(i => (i + 1) % photos.length); if (event.key === 'ArrowLeft') setLightboxIndex(i => (i + photos.length - 1) % photos.length); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [lightboxIndex, photos.length]); const copyAndOpenSource = async () => { if (contactLoading) return; const text = contactMessage || defaultMessage; try { await navigator.clipboard?.writeText(text); } catch (error) { const ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); } await ctx.contactAd?.(taskId, adId); if (safeListing.sourceUrl) window.open(safeListing.sourceUrl, '_blank', 'noopener'); }; const openInquirySettings = () => { sessionStorage.setItem('immobotScrollInquiryProfile', '1'); onNav('profile'); }; const generatingText = personalizedInquiryEnabled && (contactLoading || contactMessage === ''); if (!l) return } />; return (
{l.address}, {l.plz} {l.city} · {l.district} · inseriert {fmtRelative(l.posted)} } actions={<> : } onClick={() => ctx.toggleFav?.(l.id)} title={isFav ? 'Gemerkt' : 'Merken'} /> } onClick={() => ctx.toggleSharing?.(taskId, adId)} title="Teilen" /> } onClick={copyAndOpenSource} disabled={generatingText}>Text kopieren & öffnen } {...topBarProps} showSearch={false} compactActions />
{/* LEFT — gallery + body */}
{/* Gallery */}
{/* Key specs strip */}
} label={fmtArea(l.area)} /> } label={`${l.rooms} Zimmer`} /> } label={`Baujahr ${l.baujahr}`} /> } label={l.floor} /> } label={`Energie ${l.energieklasse}`} />
{/* Specs grid */}

Kennzahlen

{/* Description */}

Beschreibung

Quelle: {l.platform === 'immoscout24' ? 'ImmoScout24' : 'Kleinanzeigen'}

{l.description}

Ausstattung

{(l.features || []).map(f => ( {f} ))}
{/* Source link card */}
Original-Inserat
{l.sourceUrl}
l.sourceUrl && window.open(l.sourceUrl, '_blank', 'noopener')}> Auf {l.platform === 'immoscout24' ? 'Scout24' : 'Kleinanzeigen'} öffnen ↗
{/* RIGHT — sticky rail */}