import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { MessageSquare, Search, Filter, Clock, Users, TrendingUp, Download, Eye } from 'lucide-react'; import { apiClient } from '../services/api'; import LoadingSpinner from '../components/Common/LoadingSpinner'; import toast from 'react-hot-toast'; interface Conversation { id: number; participants: string[]; topic?: string; message_count: number; start_time: string; end_time?: string; duration_minutes?: number; engagement_score: number; sentiment_score: number; has_conflict: boolean; creative_elements: string[]; } const Conversations: React.FC = () => { const [conversations, setConversations] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [filters, setFilters] = useState({ character_name: '', start_date: '', end_date: '' }); useEffect(() => { loadConversations(); }, []); const loadConversations = async () => { try { const response = await apiClient.getConversations(filters); setConversations(response.data); } catch (error) { console.error('Failed to load conversations:', error); // Show fallback demo data setConversations([ { id: 1, participants: ['Alex', 'Sage'], topic: 'The Nature of Consciousness', message_count: 23, start_time: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), end_time: new Date(Date.now() - 90 * 60 * 1000).toISOString(), duration_minutes: 30, engagement_score: 0.85, sentiment_score: 0.72, has_conflict: false, creative_elements: ['philosophical insights', 'metaphors'] }, { id: 2, participants: ['Luna', 'Echo', 'Alex'], topic: 'Creative Writing Collaboration', message_count: 41, start_time: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(), end_time: new Date(Date.now() - 3 * 60 * 60 * 1000).toISOString(), duration_minutes: 52, engagement_score: 0.92, sentiment_score: 0.88, has_conflict: false, creative_elements: ['poetry', 'storytelling', 'worldbuilding'] }, { id: 3, participants: ['Sage', 'Luna'], topic: 'Disagreement about AI Ethics', message_count: 15, start_time: new Date(Date.now() - 6 * 60 * 60 * 1000).toISOString(), end_time: new Date(Date.now() - 5.5 * 60 * 60 * 1000).toISOString(), duration_minutes: 28, engagement_score: 0.78, sentiment_score: 0.45, has_conflict: true, creative_elements: [] }, { id: 4, participants: ['Echo', 'Alex'], topic: null, message_count: 8, start_time: new Date(Date.now() - 30 * 60 * 1000).toISOString(), end_time: null, duration_minutes: null, engagement_score: 0.65, sentiment_score: 0.78, has_conflict: false, creative_elements: ['humor'] } ]); } finally { setLoading(false); } }; const handleSearch = async () => { if (!searchTerm.trim()) { loadConversations(); return; } try { const response = await apiClient.searchConversations(searchTerm); // Convert search results to conversation format const searchConversations = response.data.map((result: any) => ({ id: result.metadata.conversation_id, participants: [result.character_names[0]], topic: result.metadata.conversation_topic, message_count: 1, start_time: result.timestamp, engagement_score: result.relevance_score, sentiment_score: 0.7, has_conflict: false, creative_elements: [] })); setConversations(searchConversations); } catch (error) { toast.error('Search failed'); } }; const handleExportConversation = async (conversationId: number, format: string = 'json') => { try { const response = await apiClient.exportConversation(conversationId, format); const blob = new Blob([JSON.stringify(response.data, null, 2)], { type: format === 'json' ? 'application/json' : 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `conversation_${conversationId}.${format}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toast.success(`Conversation exported as ${format.toUpperCase()}`); } catch (error) { toast.error('Export failed'); } }; const filteredConversations = conversations.filter(conv => searchTerm === '' || conv.participants.some(p => p.toLowerCase().includes(searchTerm.toLowerCase())) || (conv.topic && conv.topic.toLowerCase().includes(searchTerm.toLowerCase())) ); const getSentimentColor = (score: number) => { if (score > 0.7) return 'text-green-600'; if (score > 0.4) return 'text-yellow-600'; return 'text-red-600'; }; const getEngagementColor = (score: number) => { if (score > 0.8) return 'bg-green-500'; if (score > 0.6) return 'bg-yellow-500'; return 'bg-red-500'; }; if (loading) { return (
); } return (
{/* Header */}

Conversations

Browse and analyze character conversations

{/* Search and Filters */}
setSearchTerm(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleSearch()} className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500" placeholder="Search conversations by participants, topic, or content..." />
{/* Conversations List */}
{filteredConversations.map((conversation) => (
{/* Header */}

{conversation.topic || 'General Conversation'}

{conversation.has_conflict && ( Conflict )} {conversation.creative_elements.length > 0 && ( Creative )}
{/* Participants */}
{conversation.participants.map((participant, index) => ( {participant} {index < conversation.participants.length - 1 && ', '} ))}
{/* Stats */}

Messages

{conversation.message_count}

Duration

{conversation.duration_minutes ? `${conversation.duration_minutes}m` : 'Ongoing'}

Engagement

{Math.round(conversation.engagement_score * 100)}%

Sentiment

{conversation.sentiment_score > 0.7 ? 'Positive' : conversation.sentiment_score > 0.4 ? 'Neutral' : 'Negative'}

{/* Metadata */}
Started {new Date(conversation.start_time).toLocaleString()}
{conversation.creative_elements.length > 0 && (
{conversation.creative_elements.join(', ')}
)}
{/* Actions */}
))} {filteredConversations.length === 0 && (

No Conversations Found

{searchTerm ? 'Try adjusting your search terms.' : 'No conversations have been recorded yet.'}

)}
); }; export default Conversations;