import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/network/api_client.dart'; import '../../../core/network/signalr_service.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/widgets/glass_container.dart'; import '../../../core/widgets/status_badge.dart'; import '../models/admin_update_user_request_dto.dart'; import '../models/admin_user_model.dart'; import '../bloc/admin_bloc.dart'; import '../repositories/admin_repository.dart'; import '../widgets/admin_kpi_header.dart'; import '../widgets/create_user_dialog.dart'; import '../widgets/edit_user_dialog.dart'; import '../widgets/system_diagnostics_widget.dart'; /// Role-Restricted Admin Panel Screen managing users, service settings, and system health. class AdminUsersScreen extends StatelessWidget { final ApiClient apiClient; final SignalRService? signalRService; const AdminUsersScreen({ super.key, required this.apiClient, this.signalRService, }); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => AdminBloc( repository: AdminRepository(apiClient: apiClient), )..add(FetchAdminUsers()), child: _AdminUsersScreenContent( apiClient: apiClient, signalRService: signalRService, ), ); } } class _AdminUsersScreenContent extends StatefulWidget { final ApiClient apiClient; final SignalRService? signalRService; const _AdminUsersScreenContent({ required this.apiClient, this.signalRService, }); @override State<_AdminUsersScreenContent> createState() => _AdminUsersScreenContentState(); } class _AdminUsersScreenContentState extends State<_AdminUsersScreenContent> with SingleTickerProviderStateMixin { late TabController _tabController; String _searchQuery = ''; String _roleFilter = 'Alle'; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); } @override void dispose() { _tabController.dispose(); super.dispose(); } void _openCreateUser(BuildContext context) async { final res = await showDialog(context: context, builder: (_) => const CreateUserDialog()); if (res != null && context.mounted) { context.read().add(CreateAdminUser(res)); } } void _openEditUser(BuildContext context, AdminUserModel user) async { final res = await showDialog(context: context, builder: (_) => EditUserDialog(user: user)); if (res != null && context.mounted) { context.read().add(UpdateAdminUser(user.id, res as AdminUpdateUserRequestDto)); } } void _toggleUserActiveStatus(BuildContext context, AdminUserModel user, bool newActive) { final dto = AdminUpdateUserRequestDto(role: user.role, isActive: newActive); context.read().add(UpdateAdminUser(user.id, dto)); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: BlocBuilder( builder: (context, state) { final users = state is AdminLoaded ? state.users : []; return Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Top Header Ribbon Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppTheme.primaryEmerald.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), border: Border.all(color: AppTheme.primaryEmerald.withValues(alpha: 0.3)), ), child: Icon(Icons.admin_panel_settings_rounded, color: AppTheme.primaryEmerald, size: 22), ), const SizedBox(width: 12), const Text( 'Admin Control Panel', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, letterSpacing: -0.5), ), ], ), const SizedBox(height: 4), Text( 'Zentrales Management für Nutzer, Mikrodienste & MQTT System-Bus', style: TextStyle(fontSize: 12, color: AppTheme.textSecondary), ), ], ), ElevatedButton.icon( onPressed: () => _openCreateUser(context), icon: const Icon(Icons.person_add_outlined, size: 18), label: const Text('Nutzer Anlegen', style: TextStyle(fontWeight: FontWeight.bold)), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryEmerald, foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ), ], ), const SizedBox(height: 16), // KPI Header Metrics AdminKpiHeader( users: users, signalRService: widget.signalRService, ), const SizedBox(height: 16), // Tab Selector Ribbon Container( decoration: BoxDecoration( color: AppTheme.glassSurface, borderRadius: BorderRadius.circular(14), border: Border.all(color: AppTheme.glassBorder), ), child: TabBar( controller: _tabController, indicatorColor: AppTheme.primaryEmerald, indicatorSize: TabBarIndicatorSize.tab, labelColor: AppTheme.primaryEmerald, unselectedLabelColor: AppTheme.textMuted, labelStyle: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13), indicator: BoxDecoration( color: AppTheme.primaryEmerald.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(12), border: Border.all(color: AppTheme.primaryEmerald.withValues(alpha: 0.4)), ), tabs: const [ Tab(icon: Icon(Icons.people_alt_outlined, size: 18), text: 'Nutzerverwaltung'), Tab(icon: Icon(Icons.monitor_heart_outlined, size: 18), text: 'System-Diagnose & MQTT'), ], ), ), const SizedBox(height: 16), // Tab Content View Expanded( child: TabBarView( controller: _tabController, children: [ // Tab 1: User Management with Filter & Actions _buildUserManagementTab(context, state, users), // Tab 2: System Diagnostics & Microservices Health SystemDiagnosticsWidget( signalRService: widget.signalRService, ), ], ), ), ], ), ); }, ), ); } Widget _buildUserManagementTab(BuildContext context, AdminState state, List allUsers) { if (state is AdminLoading) { return Center(child: CircularProgressIndicator(color: AppTheme.primaryEmerald)); } if (state is AdminError) { return Center( child: GlassContainer( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.error_outline_rounded, color: AppTheme.accentRed, size: 40), const SizedBox(height: 12), Text(state.message, style: TextStyle(color: AppTheme.textPrimary, fontWeight: FontWeight.bold)), const SizedBox(height: 16), ElevatedButton( onPressed: () => context.read().add(FetchAdminUsers()), style: ElevatedButton.styleFrom(backgroundColor: AppTheme.primaryEmerald, foregroundColor: Colors.black), child: const Text('Erneut versuchen'), ), ], ), ), ); } // Apply Search Query & Role Filter final filteredUsers = allUsers.where((u) { final matchesSearch = u.fullName.toLowerCase().contains(_searchQuery.toLowerCase()) || u.email.toLowerCase().contains(_searchQuery.toLowerCase()); final matchesRole = _roleFilter == 'Alle' || u.role.toLowerCase() == _roleFilter.toLowerCase(); return matchesSearch && matchesRole; }).toList(); return Column( children: [ // Filter Bar (Search Field & Role Filter Chips) Row( children: [ Expanded( child: TextField( onChanged: (val) => setState(() => _searchQuery = val), decoration: InputDecoration( hintText: 'Benutzer nach Name oder E-Mail suchen...', prefixIcon: Icon(Icons.search_rounded, color: AppTheme.textMuted), suffixIcon: _searchQuery.isNotEmpty ? IconButton( icon: const Icon(Icons.clear, size: 18), onPressed: () => setState(() => _searchQuery = ''), ) : null, ), ), ), const SizedBox(width: 12), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: ['Alle', 'Admin', 'Premium', 'User'].map((role) { final isSelected = _roleFilter == role; return Padding( padding: const EdgeInsets.only(right: 6), child: ChoiceChip( label: Text(role), selected: isSelected, selectedColor: AppTheme.primaryEmerald, backgroundColor: AppTheme.glassSurface, labelStyle: TextStyle( color: isSelected ? Colors.black : AppTheme.textSecondary, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, fontSize: 12, ), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), side: BorderSide(color: isSelected ? AppTheme.primaryEmerald : AppTheme.glassBorder), onSelected: (val) { if (val) setState(() => _roleFilter = role); }, ), ); }).toList(), ), ), ], ), const SizedBox(height: 14), // User Cards List Expanded( child: filteredUsers.isEmpty ? Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.person_search_outlined, size: 48, color: AppTheme.textMuted), const SizedBox(height: 12), Text( 'Keine passenden Benutzer gefunden.', style: TextStyle(color: AppTheme.textMuted, fontSize: 14), ), ], ), ) : ListView.builder( itemCount: filteredUsers.length, itemBuilder: (context, index) { final u = filteredUsers[index]; final String role = u.role; final bool isActive = u.isActive; final Color roleColor = role == 'Admin' ? const Color(0xFFA855F7) : role == 'Premium' ? AppTheme.primaryEmerald : AppTheme.accentCyan; final String initials = _getInitials(u.fullName.isNotEmpty ? u.fullName : u.email); return GlassContainer( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(14), child: Row( children: [ // Avatar Initials Circle Container( width: 42, height: 42, alignment: Alignment.center, decoration: BoxDecoration( color: roleColor.withValues(alpha: 0.2), shape: BoxShape.circle, border: Border.all(color: roleColor, width: 1.5), ), child: Text( initials, style: TextStyle(fontWeight: FontWeight.bold, color: roleColor, fontSize: 14), ), ), const SizedBox(width: 14), // User Name & Email Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( u.fullName.isNotEmpty ? u.fullName : u.email, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), ), const SizedBox(width: 8), StatusBadge(label: role, color: roleColor), ], ), const SizedBox(height: 2), Text( u.email, style: TextStyle(fontSize: 12, color: AppTheme.textSecondary), ), ], ), ), // Active/Inactive Quick Switch Toggle Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( isActive ? 'Aktiv' : 'Gesperrt', style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: isActive ? AppTheme.primaryEmerald : AppTheme.accentRed, ), ), ], ), const SizedBox(width: 8), Switch( value: isActive, activeThumbColor: AppTheme.primaryEmerald, onChanged: (val) => _toggleUserActiveStatus(context, u, val), ), const SizedBox(width: 8), IconButton.filledTonal( icon: const Icon(Icons.edit_outlined, size: 18), tooltip: 'Benutzer Bearbeiten', onPressed: () => _openEditUser(context, u), ), ], ), ], ), ); }, ), ), ], ); } String _getInitials(String name) { if (name.isEmpty) return 'U'; final parts = name.trim().split(' '); if (parts.length >= 2) { return '${parts[0][0]}${parts[1][0]}'.toUpperCase(); } return name.substring(0, name.length >= 2 ? 2 : 1).toUpperCase(); } }