import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../../../domain/entities/server.dart'; import '../../blocs/auth/auth_bloc.dart'; import '../../blocs/auth/auth_event.dart'; import '../../blocs/auth/auth_state.dart'; import '../../blocs/server/server_bloc.dart'; import '../../blocs/server/server_event.dart'; import '../../blocs/server/server_state.dart'; import '../../widgets/app_drawer.dart'; class ServersManagementScreen extends StatefulWidget { const ServersManagementScreen({super.key}); @override State createState() => _ServersManagementScreenState(); } class _ServersManagementScreenState extends State { String _filterType = 'all'; // 'all', 'gcore', 'geviscope' @override Widget build(BuildContext context) { return Scaffold( drawer: const AppDrawer(currentRoute: '/servers'), appBar: AppBar( title: Row( children: [ const Icon(Icons.dns, size: 24), const SizedBox(width: 8), const Text('Server Management'), ], ), actions: [ // Sync button with dirty count badge BlocBuilder( builder: (context, state) { final dirtyCount = state is ServerLoaded ? state.dirtyCount : 0; return Stack( children: [ IconButton( icon: const Icon(Icons.sync), onPressed: dirtyCount > 0 ? () { context.read().add(const SyncServersEvent()); } : null, tooltip: dirtyCount > 0 ? 'Sync $dirtyCount unsaved change${dirtyCount != 1 ? 's' : ''}' : 'No changes to sync', ), if (dirtyCount > 0) Positioned( right: 4, top: 4, child: Container( padding: const EdgeInsets.all(4), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), child: Text( '$dirtyCount', style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), ), ), ], ); }, ), // Download/refresh button Builder( builder: (context) => IconButton( icon: const Icon(Icons.cloud_download), onPressed: () { context.read().add(const DownloadServersEvent()); }, tooltip: 'Download latest from server', ), ), const SizedBox(width: 8), BlocBuilder( builder: (context, state) { if (state is Authenticated) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Row( children: [ Icon( state.user.role == 'Administrator' ? Icons.admin_panel_settings : Icons.person, size: 20, ), const SizedBox(width: 8), Text(state.user.username), const SizedBox(width: 16), IconButton( icon: const Icon(Icons.logout), onPressed: () { context.read().add(const LogoutRequested()); }, tooltip: 'Logout', ), ], ), ); } return const SizedBox.shrink(); }, ), ], ), body: Column( children: [ // Filter tabs Container( padding: const EdgeInsets.all(16.0), child: Row( children: [ _buildFilterChip('All Servers', 'all'), const SizedBox(width: 8), _buildFilterChip('G-Core', 'gcore'), const SizedBox(width: 8), _buildFilterChip('GeViScope', 'geviscope'), const Spacer(), ElevatedButton.icon( onPressed: () { _showAddServerDialog(context); }, icon: const Icon(Icons.add), label: const Text('Add Server'), ), ], ), ), // Server list Expanded( child: BlocConsumer( listener: (context, state) { if (state is ServerError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), backgroundColor: Colors.red, ), ); } else if (state is ServerOperationSuccess) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), backgroundColor: Colors.green, ), ); } else if (state is ServerSyncSuccess) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), backgroundColor: Colors.green, ), ); } }, builder: (context, state) { if (state is ServerLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is ServerSyncing) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const CircularProgressIndicator(), const SizedBox(height: 16), Text( state.message, style: Theme.of(context).textTheme.titleMedium, ), ], ), ); } else if (state is ServerDownloading) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Downloading servers...'), ], ), ); } else if (state is ServerLoaded) { final filteredServers = _filterServers(state.servers); if (filteredServers.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.dns_outlined, size: 64, color: Colors.grey[400]), const SizedBox(height: 16), Text( 'No servers found', style: Theme.of(context).textTheme.titleLarge?.copyWith( color: Colors.grey[600], ), ), const SizedBox(height: 8), Text( 'Add a server to get started', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey[500], ), ), ], ), ); } return ListView.builder( padding: const EdgeInsets.all(16.0), itemCount: filteredServers.length, itemBuilder: (context, index) { final server = filteredServers[index]; return _buildServerCard(context, server); }, ); } else if (state is ServerError) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 64, color: Colors.red[300]), const SizedBox(height: 16), Text( 'Error loading servers', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 8), Text( state.message, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey[600], ), textAlign: TextAlign.center, ), const SizedBox(height: 24), ElevatedButton.icon( onPressed: () { context.read().add(const LoadServers()); }, icon: const Icon(Icons.refresh), label: const Text('Retry'), ), ], ), ); } // Handle ServerInitial or any other unknown states with a loading indicator // instead of "No data" to prevent confusion during state transitions return const Center(child: CircularProgressIndicator()); }, ), ), ], ), ); } List _filterServers(List servers) { if (_filterType == 'all') { return servers; } else if (_filterType == 'gcore') { return servers.where((s) => s.type == ServerType.gcore).toList(); } else { return servers.where((s) => s.type == ServerType.geviscope).toList(); } } Widget _buildFilterChip(String label, String value) { final isSelected = _filterType == value; return FilterChip( label: Text(label), selected: isSelected, onSelected: (selected) { setState(() { _filterType = value; }); }, ); } Widget _buildServerCard(BuildContext context, Server server) { return Card( margin: const EdgeInsets.only(bottom: 12.0), child: ListTile( leading: CircleAvatar( backgroundColor: server.type == ServerType.gcore ? Colors.green.withOpacity(0.2) : Colors.purple.withOpacity(0.2), child: Icon( Icons.dns, color: server.type == ServerType.gcore ? Colors.green : Colors.purple, ), ), title: Row( children: [ Text( server.alias, style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: server.enabled ? Colors.green : Colors.grey, borderRadius: BorderRadius.circular(12), ), child: Text( server.enabled ? 'Enabled' : 'Disabled', style: const TextStyle( color: Colors.white, fontSize: 12, ), ), ), ], ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), Text('Host: ${server.host}'), Text('User: ${server.user}'), Text('Type: ${server.type == ServerType.gcore ? "G-Core" : "GeViScope"}'), ], ), isThreeLine: true, trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.edit), onPressed: () { context.push('/servers/edit/${server.id}', extra: server); }, tooltip: 'Edit', ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () { _showDeleteConfirmation(context, server); }, tooltip: 'Delete', ), ], ), ), ); } void _showDeleteConfirmation(BuildContext context, Server server) { showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('Delete Server'), content: Text('Are you sure you want to delete "${server.alias}"?'), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(), child: const Text('Cancel'), ), TextButton( onPressed: () { context.read().add(DeleteServerEvent(server.id, server.type)); Navigator.of(dialogContext).pop(); }, child: const Text('Delete', style: TextStyle(color: Colors.red)), ), ], ), ); } void _showAddServerDialog(BuildContext context) { showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('Add Server'), content: const Text('Choose the server type:'), actions: [ TextButton( onPressed: () { Navigator.of(dialogContext).pop(); context.push('/servers/create?type=gcore'); }, child: const Text('G-Core Server'), ), TextButton( onPressed: () { Navigator.of(dialogContext).pop(); context.push('/servers/create?type=geviscope'); }, child: const Text('GeViScope Server'), ), TextButton( onPressed: () => Navigator.of(dialogContext).pop(), child: const Text('Cancel'), ), ], ), ); } }