import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:animate_do/animate_do.dart'; import 'package:timeago/timeago.dart' as timeago; import '../api/api.dart'; /// Conversations management page /// /// Displays all conversations with details about messages, activity, and status. /// Integrates with backend CQRS API for conversation listing and management. class ConversationsPage extends StatefulWidget { const ConversationsPage({super.key}); @override State createState() => _ConversationsPageState(); } class _ConversationsPageState extends State { final CqrsApiClient _apiClient = CqrsApiClient( config: ApiClientConfig.development, ); List? _conversations; bool _isLoading = true; String? _errorMessage; String _filterStatus = 'all'; // all, active, inactive @override void initState() { super.initState(); _loadConversations(); } @override void dispose() { _apiClient.dispose(); super.dispose(); } Future _loadConversations() async { setState(() { _isLoading = true; _errorMessage = null; }); final Result> result = await _apiClient.listConversations(); result.when( success: (List conversations) { if (mounted) { setState(() { _conversations = conversations; _isLoading = false; }); } }, error: (ApiErrorInfo error) { if (mounted) { setState(() { _errorMessage = error.message; _isLoading = false; }); } }, ); } List get _filteredConversations { if (_conversations == null) return []; switch (_filterStatus) { case 'active': return _conversations!.where((c) => c.isActive).toList(); case 'inactive': return _conversations!.where((c) => !c.isActive).toList(); default: return _conversations!; } } @override Widget build(BuildContext context) { final ColorScheme colorScheme = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header with actions _buildHeader(colorScheme), const SizedBox(height: 24), // Filter chips _buildFilterChips(colorScheme), const SizedBox(height: 24), // Conversations list Expanded( child: _buildConversationsList(colorScheme), ), ], ), ); } Widget _buildHeader(ColorScheme colorScheme) { return Row( children: [ Icon( Iconsax.messages_3, color: colorScheme.primary, size: 28, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Conversations', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: colorScheme.onSurface, ), ), Text( _conversations != null ? '${_conversations!.length} total conversations' : 'Loading...', style: TextStyle( fontSize: 14, color: colorScheme.onSurfaceVariant, ), ), ], ), ), IconButton( icon: Icon(Iconsax.refresh, color: colorScheme.primary), onPressed: _loadConversations, tooltip: 'Refresh conversations', ), ], ); } Widget _buildFilterChips(ColorScheme colorScheme) { return Row( children: [ _buildFilterChip('All', 'all', colorScheme), const SizedBox(width: 8), _buildFilterChip('Active', 'active', colorScheme), const SizedBox(width: 8), _buildFilterChip('Inactive', 'inactive', colorScheme), ], ); } Widget _buildFilterChip( String label, String value, ColorScheme colorScheme, ) { final bool isSelected = _filterStatus == value; return FilterChip( label: Text(label), selected: isSelected, onSelected: (bool selected) { setState(() { _filterStatus = value; }); }, backgroundColor: colorScheme.surfaceContainerHigh, selectedColor: colorScheme.primaryContainer, labelStyle: TextStyle( color: isSelected ? colorScheme.onPrimaryContainer : colorScheme.onSurface, ), ); } Widget _buildConversationsList(ColorScheme colorScheme) { if (_isLoading) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(color: colorScheme.primary), const SizedBox(height: 16), Text( 'Loading conversations...', style: TextStyle(color: colorScheme.onSurfaceVariant), ), ], ), ); } if (_errorMessage != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Iconsax.warning_2, size: 48, color: colorScheme.error, ), const SizedBox(height: 16), Text( 'Failed to load conversations', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: colorScheme.onSurface, ), ), const SizedBox(height: 8), Text( _errorMessage!, style: TextStyle(color: colorScheme.error), textAlign: TextAlign.center, ), const SizedBox(height: 24), ElevatedButton.icon( onPressed: _loadConversations, icon: const Icon(Iconsax.refresh), label: const Text('Retry'), ), ], ), ); } final List filtered = _filteredConversations; if (filtered.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Iconsax.message_text, size: 64, color: colorScheme.onSurfaceVariant.withValues(alpha: 0.3), ), const SizedBox(height: 16), Text( 'No conversations found', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: colorScheme.onSurface, ), ), const SizedBox(height: 8), Text( _filterStatus == 'all' ? 'Start a conversation to see it here' : 'No $_filterStatus conversations', style: TextStyle(color: colorScheme.onSurfaceVariant), ), ], ), ); } return ListView.builder( itemCount: filtered.length, itemBuilder: (BuildContext context, int index) { return FadeInUp( duration: Duration(milliseconds: 300 + (index * 50)), child: _buildConversationCard(filtered[index], colorScheme), ); }, ); } Widget _buildConversationCard( ConversationListItemDto conversation, ColorScheme colorScheme, ) { return Card( margin: const EdgeInsets.only(bottom: 16), elevation: 0, color: colorScheme.surfaceContainerLow, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: colorScheme.outline.withValues(alpha: 0.2), width: 1, ), ), child: InkWell( borderRadius: BorderRadius.circular(12), onTap: () { // TODO: Navigate to conversation detail ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('View conversation: ${conversation.title} (not implemented)'), ), ); }, child: Padding( padding: const EdgeInsets.all(16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Icon Container( width: 48, height: 48, decoration: BoxDecoration( color: conversation.isActive ? colorScheme.primaryContainer : colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), child: Icon( Iconsax.messages_3, color: conversation.isActive ? colorScheme.onPrimaryContainer : colorScheme.onSurfaceVariant, size: 24, ), ), const SizedBox(width: 16), // Content Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Title and status Row( children: [ Expanded( child: Text( conversation.title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: colorScheme.onSurface, ), ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: conversation.isActive ? colorScheme.primaryContainer : colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(4), ), child: Text( conversation.isActive ? 'Active' : 'Inactive', style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold, color: conversation.isActive ? colorScheme.onPrimaryContainer : colorScheme.onSurfaceVariant, ), ), ), ], ), const SizedBox(height: 8), // Summary if (conversation.summary != null) Text( conversation.summary!, style: TextStyle( fontSize: 14, color: colorScheme.onSurfaceVariant, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 12), // Stats Row( children: [ _buildStat( Iconsax.message_text, '${conversation.messageCount}', 'messages', colorScheme, ), const SizedBox(width: 16), _buildStat( Iconsax.flash_1, '${conversation.executionCount}', 'executions', colorScheme, ), const Spacer(), Icon( Iconsax.clock, size: 14, color: colorScheme.onSurfaceVariant, ), const SizedBox(width: 4), Text( timeago.format(conversation.lastMessageAt), style: TextStyle( fontSize: 12, color: colorScheme.onSurfaceVariant, ), ), ], ), ], ), ), ], ), ), ), ); } Widget _buildStat( IconData icon, String value, String label, ColorScheme colorScheme, ) { return Row( children: [ Icon( icon, size: 16, color: colorScheme.primary, ), const SizedBox(width: 4), Text( value, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: colorScheme.onSurface, ), ), const SizedBox(width: 2), Text( label, style: TextStyle( fontSize: 12, color: colorScheme.onSurfaceVariant, ), ), ], ); } }