update
This commit is contained in:
250
components/EmailManager.tsx
Normal file
250
components/EmailManager.tsx
Normal file
@@ -0,0 +1,250 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { EmailResponder } from './EmailResponder';
|
||||
|
||||
interface ContactMessage {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
timestamp: string;
|
||||
responded: boolean;
|
||||
}
|
||||
|
||||
export const EmailManager: React.FC = () => {
|
||||
const [messages, setMessages] = useState<ContactMessage[]>([]);
|
||||
const [selectedMessage, setSelectedMessage] = useState<ContactMessage | null>(null);
|
||||
const [showResponder, setShowResponder] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [filter, setFilter] = useState<'all' | 'unread' | 'responded'>('all');
|
||||
|
||||
// Mock data for demonstration - in real app, fetch from API
|
||||
useEffect(() => {
|
||||
const mockMessages: ContactMessage[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Max Mustermann',
|
||||
email: 'max@example.com',
|
||||
subject: 'Projekt-Anfrage',
|
||||
message: 'Hallo Dennis,\n\nich interessiere mich für eine Zusammenarbeit an einem Web-Projekt. Können wir uns mal unterhalten?\n\nViele Grüße\nMax',
|
||||
timestamp: new Date().toISOString(),
|
||||
responded: false
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Anna Schmidt',
|
||||
email: 'anna@example.com',
|
||||
subject: 'Frage zu deinem Portfolio',
|
||||
message: 'Hi Dennis,\n\nsehr cooles Portfolio! Wie lange hast du an dem Design gearbeitet?\n\nLG Anna',
|
||||
timestamp: new Date(Date.now() - 86400000).toISOString(),
|
||||
responded: true
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Tom Weber',
|
||||
email: 'tom@example.com',
|
||||
subject: 'Job-Anfrage',
|
||||
message: 'Hallo,\n\nwir suchen einen Full-Stack Developer. Bist du interessiert?\n\nTom',
|
||||
timestamp: new Date(Date.now() - 172800000).toISOString(),
|
||||
responded: false
|
||||
}
|
||||
];
|
||||
|
||||
setTimeout(() => {
|
||||
setMessages(mockMessages);
|
||||
setIsLoading(false);
|
||||
}, 1000);
|
||||
}, []);
|
||||
|
||||
const filteredMessages = messages.filter(message => {
|
||||
switch (filter) {
|
||||
case 'unread':
|
||||
return !message.responded;
|
||||
case 'responded':
|
||||
return message.responded;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
const handleRespond = (message: ContactMessage) => {
|
||||
setSelectedMessage(message);
|
||||
setShowResponder(true);
|
||||
};
|
||||
|
||||
const handleResponseSent = () => {
|
||||
if (selectedMessage) {
|
||||
setMessages(prev => prev.map(msg =>
|
||||
msg.id === selectedMessage.id
|
||||
? { ...msg, responded: true }
|
||||
: msg
|
||||
));
|
||||
}
|
||||
setShowResponder(false);
|
||||
setSelectedMessage(null);
|
||||
};
|
||||
|
||||
const formatDate = (timestamp: string) => {
|
||||
return new Date(timestamp).toLocaleString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
const getMessagePreview = (message: string) => {
|
||||
return message.length > 100 ? message.substring(0, 100) + '...' : message;
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
|
||||
{/* Header */}
|
||||
<div className="bg-white rounded-xl shadow-sm border p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">📧 E-Mail Manager</h2>
|
||||
<p className="text-gray-600 mt-1">Verwalte Kontaktanfragen und sende schöne Antworten</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-sm text-gray-600">
|
||||
{filteredMessages.length} von {messages.length} Nachrichten
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="bg-white rounded-xl shadow-sm border p-4">
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setFilter('all')}
|
||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
||||
filter === 'all'
|
||||
? 'bg-blue-100 text-blue-700 border border-blue-200'
|
||||
: 'text-gray-600 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
Alle ({messages.length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('unread')}
|
||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
||||
filter === 'unread'
|
||||
? 'bg-red-100 text-red-700 border border-red-200'
|
||||
: 'text-gray-600 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
Ungelesen ({messages.filter(m => !m.responded).length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('responded')}
|
||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
||||
filter === 'responded'
|
||||
? 'bg-green-100 text-green-700 border border-green-200'
|
||||
: 'text-gray-600 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
Beantwortet ({messages.filter(m => m.responded).length})
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Messages List */}
|
||||
<div className="space-y-4">
|
||||
{filteredMessages.length === 0 ? (
|
||||
<div className="bg-white rounded-xl shadow-sm border p-12 text-center">
|
||||
<div className="text-6xl mb-4">📭</div>
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-2">Keine Nachrichten</h3>
|
||||
<p className="text-gray-600">
|
||||
{filter === 'unread' && 'Alle Nachrichten wurden beantwortet!'}
|
||||
{filter === 'responded' && 'Noch keine Nachrichten beantwortet.'}
|
||||
{filter === 'all' && 'Noch keine Kontaktanfragen eingegangen.'}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
filteredMessages.map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`bg-white rounded-xl shadow-sm border p-6 transition-all hover:shadow-md ${
|
||||
!message.responded ? 'border-l-4 border-l-red-500' : 'border-l-4 border-l-green-500'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className={`w-3 h-3 rounded-full ${
|
||||
message.responded ? 'bg-green-500' : 'bg-red-500'
|
||||
}`}></div>
|
||||
<h3 className="font-semibold text-gray-900">{message.name}</h3>
|
||||
<span className="text-sm text-gray-500">{message.email}</span>
|
||||
{!message.responded && (
|
||||
<span className="px-2 py-1 bg-red-100 text-red-700 text-xs rounded-full font-medium">
|
||||
Neu
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h4 className="font-medium text-gray-800 mb-2">{message.subject}</h4>
|
||||
|
||||
<p className="text-gray-600 text-sm mb-3 whitespace-pre-wrap">
|
||||
{getMessagePreview(message.message)}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center gap-4 text-xs text-gray-500">
|
||||
<span>📅 {formatDate(message.timestamp)}</span>
|
||||
{message.responded && (
|
||||
<span className="text-green-600 font-medium">✅ Beantwortet</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 ml-4">
|
||||
{!message.responded && (
|
||||
<button
|
||||
onClick={() => handleRespond(message)}
|
||||
className="px-4 py-2 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all font-medium text-sm flex items-center gap-2"
|
||||
>
|
||||
📧 Antworten
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
setSelectedMessage(message);
|
||||
// Show full message modal
|
||||
}}
|
||||
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors font-medium text-sm"
|
||||
>
|
||||
👁️ Ansehen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Email Responder Modal */}
|
||||
{showResponder && selectedMessage && (
|
||||
<EmailResponder
|
||||
contactEmail={selectedMessage.email}
|
||||
contactName={selectedMessage.name}
|
||||
originalMessage={selectedMessage.message}
|
||||
onClose={handleResponseSent}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user