import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'package:real_estate_mobile/core/constants/app_colors.dart'; import 'package:real_estate_mobile/core/widgets/s3_image.dart'; import 'package:real_estate_mobile/features/agents/presentation/providers/agent_network_provider.dart'; class AgentNetworkScreen extends ConsumerWidget { const AgentNetworkScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final state = ref.watch(agentNetworkProvider); if (state.isLoading) { return const Center( child: CircularProgressIndicator(color: AppColors.accentOrange)); } if (state.error != null) { return _buildError(context, ref, state.error!); } return _buildContent(context, ref, state); } Widget _buildError(BuildContext context, WidgetRef ref, String error) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, size: 48, color: AppColors.accentOrange), const SizedBox(height: 16), Text(error, style: const TextStyle( fontFamily: 'Fractul', fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.primaryDark, )), const SizedBox(height: 12), TextButton( onPressed: () => ref.read(agentNetworkProvider.notifier).loadData(), child: const Text('Try Again', style: TextStyle(color: AppColors.accentOrange)), ), ], ), ); } Widget _buildContent( BuildContext context, WidgetRef ref, AgentNetworkState state) { return RefreshIndicator( color: AppColors.accentOrange, onRefresh: () => ref.read(agentNetworkProvider.notifier).loadData(), child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), // ── Pending Requests Section ── if (state.pendingRequests.isNotEmpty) ...[ _buildSectionHeader('Pending Requests', count: state.pendingRequests.length), const SizedBox(height: 12), ...state.pendingRequests.map((request) => _RequestCard( data: request, isProcessing: state.processingIds.contains(request['id']), )), const SizedBox(height: 16), Divider( color: AppColors.primaryDark.withValues(alpha: 0.1), height: 1), const SizedBox(height: 16), ], // ── Manage My Network Section ── _buildManageNetworkHeader(context), const SizedBox(height: 12), if (state.connections.isEmpty) _buildEmptyState( icon: Icons.people_outline, message: 'No connections yet', subtitle: 'When you accept connection requests, they will appear here.', ) else ...state.connections .map((conn) => _ConnectionCard(data: conn)), const SizedBox(height: 24), ], ), ), ); } Widget _buildSectionHeader(String title, {int? count}) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( children: [ Text( title, style: const TextStyle( fontFamily: 'Fractul', fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.primaryDark, ), ), if (count != null && count > 0) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: AppColors.accentOrange, borderRadius: BorderRadius.circular(10), ), child: Text( '$count', style: const TextStyle( fontFamily: 'Fractul', fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ], ], ), ); } Widget _buildManageNetworkHeader(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(horizontal: 0), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), decoration: BoxDecoration( border: Border.all( color: AppColors.primaryDark.withValues(alpha: 0.1), width: 0.5), borderRadius: BorderRadius.circular(7), ), child: Row( children: [ const Text( 'Manage My Network', style: TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.primaryDark, ), ), const Spacer(), Icon(Icons.arrow_forward, size: 20, color: AppColors.primaryDark.withValues(alpha: 0.7)), ], ), ); } Widget _buildEmptyState({ required IconData icon, required String message, String? subtitle, }) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40), child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 48, color: AppColors.primaryDark.withValues(alpha: 0.3)), const SizedBox(height: 12), Text( message, style: const TextStyle( fontFamily: 'Fractul', fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.primaryDark, ), ), if (subtitle != null) ...[ const SizedBox(height: 6), Text( subtitle, textAlign: TextAlign.center, style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, color: AppColors.primaryDark.withValues(alpha: 0.6), ), ), ], ], ), ), ); } } // ── Relative time formatting (same as web) ── String _formatRelativeTime(String? dateStr) { if (dateStr == null || dateStr.isEmpty) return ''; try { final date = DateTime.parse(dateStr); final now = DateTime.now(); final diff = now.difference(date); if (diff.inSeconds < 60) return 'Just now'; if (diff.inMinutes < 60) return '${diff.inMinutes}m ago'; if (diff.inHours < 24) return '${diff.inHours}h ago'; if (diff.inDays < 7) return '${diff.inDays}d ago'; return DateFormat('MMM d').format(date); } catch (_) { return ''; } } // ── Date formatting for connections ── String _formatConnectedDate(String? dateStr) { if (dateStr == null || dateStr.isEmpty) return ''; try { final date = DateTime.parse(dateStr); return 'Connected on ${DateFormat('MMM d, y').format(date)}'; } catch (_) { return ''; } } // ── Request Card (pending connection requests with accept/reject) ── class _RequestCard extends ConsumerWidget { final Map data; final bool isProcessing; const _RequestCard({required this.data, required this.isProcessing}); @override Widget build(BuildContext context, WidgetRef ref) { final user = data['user'] as Map? ?? {}; final userName = user['email'] as String? ?? user['name'] as String? ?? 'User'; final avatar = user['avatar'] as String?; final message = data['message'] as String?; final requestId = data['id'] as String; final createdAt = data['createdAt'] as String?; final relativeTime = _formatRelativeTime(createdAt); return Opacity( opacity: isProcessing ? 0.5 : 1.0, child: AbsorbPointer( absorbing: isProcessing, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ // Avatar ClipOval( child: SizedBox( width: 56, height: 56, child: avatar != null && avatar.isNotEmpty ? S3Image( imageUrl: avatar, width: 56, height: 56, fit: BoxFit.cover, ) : Container( color: const Color(0xFFE0E0E0), child: const Icon(Icons.person, size: 28, color: AppColors.primaryDark), ), ), ), const SizedBox(width: 12), // Name + message + time Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Flexible( child: Text( userName, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.primaryDark, ), overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 4), Icon(Icons.verified_user_outlined, size: 16, color: AppColors.primaryDark .withValues(alpha: 0.5)), ], ), if (message != null && message.isNotEmpty) Text( message, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, color: AppColors.primaryDark, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), Row( children: [ Icon(Icons.link, size: 14, color: AppColors.primaryDark .withValues(alpha: 0.5)), const SizedBox(width: 4), const Text( 'Mutual Connection', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w200, color: AppColors.primaryDark, ), ), if (relativeTime.isNotEmpty) ...[ const Spacer(), Text( relativeTime, style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 12, color: AppColors.primaryDark .withValues(alpha: 0.5), ), ), ], ], ), ], ), ), const SizedBox(width: 8), // Reject button (X) GestureDetector( onTap: () => ref .read(agentNetworkProvider.notifier) .rejectRequest(requestId), child: Container( width: 33, height: 31, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: AppColors.primaryDark.withValues(alpha: 0.3), width: 1), ), child: Icon(Icons.close, size: 18, color: AppColors.primaryDark.withValues(alpha: 0.6)), ), ), const SizedBox(width: 10), // Accept button (checkmark) GestureDetector( onTap: () => ref .read(agentNetworkProvider.notifier) .acceptRequest(requestId), child: Container( width: 33, height: 31, decoration: const BoxDecoration( shape: BoxShape.circle, color: AppColors.accentOrange, ), child: const Icon(Icons.check, size: 20, color: Colors.white), ), ), ], ), ), ), ); } } // ── Connection Card (accepted connections in Manage My Network) ── class _ConnectionCard extends StatelessWidget { final Map data; const _ConnectionCard({required this.data}); @override Widget build(BuildContext context) { final user = data['user'] as Map? ?? {}; final userName = user['email'] as String? ?? user['name'] as String? ?? 'User'; final userEmail = user['email'] as String? ?? ''; final avatar = user['avatar'] as String?; final respondedAt = data['respondedAt'] as String?; final connectedDate = _formatConnectedDate(respondedAt); return Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ // Avatar ClipOval( child: SizedBox( width: 56, height: 56, child: avatar != null && avatar.isNotEmpty ? S3Image( imageUrl: avatar, width: 56, height: 56, fit: BoxFit.cover, ) : Container( color: const Color(0xFFE0E0E0), child: const Icon(Icons.person, size: 28, color: AppColors.primaryDark), ), ), ), const SizedBox(width: 12), // Name + email + connected date Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Flexible( child: Text( userName, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.primaryDark, ), overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 4), Icon(Icons.verified_user_outlined, size: 16, color: AppColors.primaryDark .withValues(alpha: 0.5)), ], ), if (userEmail.isNotEmpty) Text( userEmail, style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 13, color: AppColors.primaryDark.withValues(alpha: 0.6), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (connectedDate.isNotEmpty) Text( connectedDate, style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 12, color: AppColors.primaryDark.withValues(alpha: 0.5), ), ), ], ), ), const SizedBox(width: 8), // Message button GestureDetector( onTap: () { // Navigate to messages — if we have userId, could create/open conversation context.push('/messages'); }, child: Container( width: 36, height: 36, decoration: BoxDecoration( shape: BoxShape.circle, color: AppColors.accentOrange.withValues(alpha: 0.1), ), child: const Icon(Icons.chat_bubble_outline, size: 18, color: AppColors.accentOrange), ), ), ], ), ); } }