import { useState, useEffect, useRef } from 'react' import { Box, Typography, TextField, Chip, Avatar, IconButton, InputAdornment, Paper, } from '@mui/material' import { Search, Send, Paperclip, ArrowLeft, FileText, Bot, Building2 } from 'lucide-react' import { mockInquiries } from '../../mock-data/inquiries' import type { InquiryMessage } from '../../domain/inquiry' const STATUS_CONFIG: Record = { new: { label: 'Neu', color: '#dc2626', bgColor: '#fef2f2' }, in_progress: { label: 'Aktiv', color: '#d97706', bgColor: '#fffbeb' }, answered: { label: 'Beantwortet', color: '#1a7a4a', bgColor: '#f0fdf4' }, archived: { label: 'Archiviert', color: '#64748b', bgColor: '#f8fafc' }, } const FILTER_TABS = [ { key: 'all', label: 'Alle' }, { key: 'new', label: 'Neu' }, { key: 'in_progress', label: 'Aktiv' }, { key: 'answered', label: 'Beantwortet' }, ] function MessageBubble({ msg }: { msg: InquiryMessage }) { const isOwnMessage = msg.senderType === 'tenant' const isAI = msg.senderType === 'ai' const time = new Date(msg.createdAt).toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' }) const date = new Date(msg.createdAt).toLocaleDateString('de-CH', { day: '2-digit', month: '2-digit' }) return ( {!isOwnMessage && isAI && } {!isOwnMessage && msg.senderType === 'supply_user' && } {msg.senderName} · {date} {time} {msg.body} {msg.attachments.length > 0 && ( {msg.attachments.map(att => ( {att.fileName} {att.fileSize && ( {att.fileSize < 1024 * 1024 ? `${Math.round(att.fileSize / 1024)} KB` : `${(att.fileSize / 1024 / 1024).toFixed(1)} MB`} )} ))} )} ) } export default function Anfragen() { const [inquiries, setInquiries] = useState(mockInquiries) const [selectedId, setSelectedId] = useState(mockInquiries[0]?.id ?? null) const [search, setSearch] = useState('') const [statusFilter, setStatusFilter] = useState('all') const [replyText, setReplyText] = useState('') const [mobileShowChat, setMobileShowChat] = useState(false) const threadRef = useRef(null) const filtered = inquiries.filter(inq => { const q = search.toLowerCase() const matchesSearch = !q || inq.tenantName.toLowerCase().includes(q) || inq.subject.toLowerCase().includes(q) || (inq.tenantCompany?.toLowerCase().includes(q) ?? false) const matchesStatus = statusFilter === 'all' || inq.status === statusFilter return matchesSearch && matchesStatus }) const selected = inquiries.find(i => i.id === selectedId) ?? null const totalUnread = inquiries.reduce((sum, i) => sum + i.unreadCount, 0) useEffect(() => { if (threadRef.current) { threadRef.current.scrollTop = threadRef.current.scrollHeight } }, [selected?.thread.length]) function handleSelect(id: string) { setSelectedId(id) setInquiries(prev => prev.map(i => i.id === id ? { ...i, isRead: true, unreadCount: 0 } : i)) setMobileShowChat(true) } function handleSend() { if (!replyText.trim() || !selectedId) return const msg: InquiryMessage = { id: `msg-${Date.now()}`, inquiryId: selectedId, senderType: 'tenant', senderName: 'Sie', body: replyText.trim(), attachments: [], createdAt: new Date().toISOString(), } setInquiries(prev => prev.map(i => i.id === selectedId ? { ...i, thread: [...i.thread, msg], status: 'in_progress', updatedAt: new Date().toISOString() } : i )) setReplyText('') } return ( {/* ── Left panel: inquiry list ── */} {/* List header */} Anfragen {totalUnread > 0 && ( )} setSearch(e.target.value)} InputProps={{ startAdornment: ( ), }} sx={{ mb: 1.25 }} /> {FILTER_TABS.map(tab => ( setStatusFilter(tab.key)} sx={{ height: 22, fontSize: '0.7rem', cursor: 'pointer', bgcolor: statusFilter === tab.key ? '#1e3a5f' : '#f1f5f9', color: statusFilter === tab.key ? 'white' : '#475569', fontWeight: statusFilter === tab.key ? 700 : 400, '&:hover': { bgcolor: statusFilter === tab.key ? '#1a3050' : '#e2e8f0' }, }} /> ))} {/* List body */} {filtered.length === 0 ? ( Keine Anfragen gefunden. ) : ( filtered.map(inq => { const cfg = STATUS_CONFIG[inq.status ?? 'new'] const isSelected = inq.id === selectedId const displayDate = new Date(inq.updatedAt).toLocaleDateString('de-CH', { day: '2-digit', month: '2-digit' }) const lastMsg = inq.thread[inq.thread.length - 1] return ( handleSelect(inq.id)} sx={{ px: 2, py: 1.5, borderBottom: '1px solid #f1f5f9', cursor: 'pointer', bgcolor: isSelected ? '#eff6ff' : !inq.isRead ? 'rgba(220,38,38,0.03)' : 'transparent', borderLeft: isSelected ? '3px solid #1e3a5f' : '3px solid transparent', '&:hover': { bgcolor: isSelected ? '#eff6ff' : '#f8fafc' }, transition: 'background-color 0.1s ease', }} > {!inq.isRead && ( )} {inq.tenantName} {inq.unreadCount > 0 && ( {inq.unreadCount} )} {displayDate} {inq.tenantCompany && ( {inq.tenantCompany} )} {inq.subject} {lastMsg && ( {lastMsg.senderType === 'tenant' ? 'Sie: ' : `${lastMsg.senderName}: `} {lastMsg.body.split('\n')[0]} )} {inq.matchScore && ( = 80 ? '#1a7a4a' : '#d97706', fontWeight: 700, fontSize: '0.7rem' }} > Match {inq.matchScore}% )} ) }) )} {/* ── Right panel: chat thread ── */} {!selected ? ( Anfrage auswählen Wählen Sie links eine Anfrage aus, um die Konversation zu lesen. ) : ( <> {/* Chat header */} setMobileShowChat(false)} > {selected.tenantName.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2)} {selected.tenantName} {selected.tenantCompany && ( {selected.tenantCompany} )} {selected.subject} {selected.matchScore && ( = 80 ? '#f0fdf4' : '#fffbeb', color: selected.matchScore >= 80 ? '#1a7a4a' : '#d97706', fontWeight: 700, height: 22, fontSize: '0.75rem', border: `1px solid ${selected.matchScore >= 80 ? '#86efac' : '#fde68a'}`, }} /> )} {/* Thread */} {selected.thread.map(msg => ( ))} {/* Composer */} setReplyText(e.target.value)} onKeyDown={e => { if (e.key === 'Enter' && e.ctrlKey) handleSend() }} sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 }, }} /> Ctrl + Enter zum Senden )} ) }