import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:chepuhagram/core/constants.dart'; import 'package:chepuhagram/domain/services/api_service.dart'; class AdminPanelScreen extends StatefulWidget { const AdminPanelScreen({super.key}); @override State createState() => _AdminPanelScreenState(); } class _AdminPanelScreenState extends State with SingleTickerProviderStateMixin { late TabController _tabController; final ApiService _apiService = ApiService(); List _users = []; bool _isLoadingUsers = true; // Контроллеры формы создания пользователя final _idController = TextEditingController(); final _createFormKey = GlobalKey(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); final _firstNameController = TextEditingController(); final _lastNameController = TextEditingController(); bool _isCreating = false; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); _loadUsers(); } Future _loadUsers() async { setState(() => _isLoadingUsers = true); try { final token = await _apiService.getAccessToken(); final response = await Dio().get( '${AppConstants.baseUrl}/admin/users', options: Options(headers: {'Authorization': 'Bearer $token'}), ); setState(() { _users = response.data; _isLoadingUsers = false; }); } catch (e) { setState(() => _isLoadingUsers = false); _showSnackBar('Ошибка загрузки пользователей: $e'); } } Future _toggleBlock(int userId, bool currentBlockStatus) async { final action = currentBlockStatus ? 'unblock' : 'block'; try { final token = await _apiService.getAccessToken(); await Dio().post( '${AppConstants.baseUrl}/admin/users/$userId/$action', options: Options(headers: {'Authorization': 'Bearer $token'}), ); _showSnackBar( currentBlockStatus ? 'Пользователь разблокирован' : 'Пользователь заблокирован', ); _loadUsers(); } catch (e) { _showSnackBar('Ошибка изменения статуса: $e'); } } Future _createUser() async { if (!_createFormKey.currentState!.validate()) return; setState(() => _isCreating = true); try { final token = await _apiService.getAccessToken(); final Map requestData = { 'username': _usernameController.text.trim(), 'password': _passwordController.text.trim(), 'first_name': _firstNameController.text.trim(), 'last_name': _lastNameController.text.trim(), }; final idText = _idController.text.trim(); if (idText.isNotEmpty) { requestData['id'] = int.tryParse(idText); } await Dio().post( '${AppConstants.baseUrl}/admin/users', data: requestData, options: Options(headers: {'Authorization': 'Bearer $token'}), ); _showSnackBar('Пользователь успешно создан!'); _idController.clear(); _usernameController.clear(); _passwordController.clear(); _firstNameController.clear(); _lastNameController.clear(); _loadUsers(); _tabController.animateTo(0); } catch (e) { _showSnackBar('Ошибка создания: $e'); } finally { setState(() => _isCreating = false); } } void _showSnackBar(String text) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(text), behavior: SnackBarBehavior.floating), ); } @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Scaffold( backgroundColor: colorScheme.background, appBar: AppBar( title: const Text( 'Панель администратора', style: TextStyle(fontWeight: FontWeight.bold), ), elevation: 0, backgroundColor: Colors.transparent, bottom: TabBar( controller: _tabController, tabs: const [ Tab(icon: Icon(Icons.people_alt_rounded), text: 'Пользователи'), Tab( icon: Icon(Icons.person_add_alt_1_rounded), text: 'Создать аккаунт', ), ], ), ), body: TabBarView( controller: _tabController, children: [_buildUsersTab(colorScheme), _buildCreateTab(colorScheme)], ), ); } Widget _buildUsersTab(ColorScheme colorScheme) { if (_isLoadingUsers) return const Center(child: CircularProgressIndicator()); return RefreshIndicator( onRefresh: _loadUsers, child: ListView.builder( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.all(16), itemCount: _users.length, itemBuilder: (context, index) { final user = _users[index]; final bool isBlocked = user['is_blocked'] == 1; final int userId = user['id']; if (userId == 1) return const SizedBox.shrink(); // Скрываем супер-админа из списка менеджмента return Card( margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), color: colorScheme.surfaceVariant.withOpacity(0.2), elevation: 0, child: ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), title: Text( '${user['first_name']} ${user['last_name'] ?? ''}'.trim(), style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text( '@${user['username']}\nID: $userId', style: TextStyle(color: colorScheme.outline, fontSize: 13), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.edit_note_rounded), onPressed: () => _showEditUserDialog(user), ), IconButton( icon: Icon( isBlocked ? Icons.lock_open_rounded : Icons.lock_person_rounded, ), color: isBlocked ? Colors.green : colorScheme.error, onPressed: () => _toggleBlock(userId, isBlocked), ), ], ), ), ); }, ), ); } Widget _buildCreateTab(ColorScheme colorScheme) { return Form( key: _createFormKey, child: ListView( padding: const EdgeInsets.all(24), children: [ _buildAdminInputField( _idController, 'ID пользователя (оставьте пустым для автогенерации)', Icons.fingerprint_rounded, (v) { if (v != null && v.isNotEmpty && int.tryParse(v) == null) { return 'ID должен быть числом'; } return null; }, keyboardType: TextInputType.number, ), _buildAdminInputField( _usernameController, 'Имя пользователя (username)', Icons.alternate_email_rounded, (v) => v!.isEmpty ? 'Заполните юзернейм' : null, ), _buildAdminInputField( _passwordController, 'Временный пароль аккаунта', Icons.password_rounded, (v) => v!.length < 6 ? 'Минимум 6 символов' : null, obscure: true, ), _buildAdminInputField( _firstNameController, 'Имя', Icons.person_rounded, (v) => v!.isEmpty ? 'Введите имя' : null, ), _buildAdminInputField( _lastNameController, 'Фамилия', Icons.people_rounded, null, ), const SizedBox(height: 20), SizedBox( height: 50, child: ElevatedButton( onPressed: _isCreating ? null : _createUser, style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), child: _isCreating ? const CircularProgressIndicator() : const Text( 'Зарегистрировать пользователя', style: TextStyle(fontWeight: FontWeight.bold), ), ), ), ], ), ); } Widget _buildAdminInputField( TextEditingController controller, String label, IconData icon, String? Function(String?)? validator, { bool obscure = false, TextInputType keyboardType = TextInputType.text, }) { final colorScheme = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.only(bottom: 16.0), child: Container( decoration: BoxDecoration( color: colorScheme.surfaceVariant.withOpacity(0.15), borderRadius: BorderRadius.circular(16), ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2), child: TextFormField( controller: controller, obscureText: obscure, validator: validator, keyboardType: keyboardType, decoration: InputDecoration( icon: Icon(icon, color: colorScheme.primary), labelText: label, border: InputBorder.none, ), ), ), ); } void _showEditUserDialog(Map user) { final fNameController = TextEditingController(text: user['first_name']); final lNameController = TextEditingController(text: user['last_name']); final aboutController = TextEditingController(text: user['about']); final phoneController = TextEditingController(text: user['phone']); final emailController = TextEditingController(text: user['email']); showDialog( context: context, builder: (ctx) => AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), title: Text('Редактирование ID: ${user['id']}'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: fNameController, decoration: const InputDecoration(labelText: 'Имя'), ), TextField( controller: lNameController, decoration: const InputDecoration(labelText: 'Фамилия'), ), TextField( controller: aboutController, decoration: const InputDecoration(labelText: 'О себе'), ), TextField( controller: phoneController, decoration: const InputDecoration(labelText: 'Телефон'), ), TextField( controller: emailController, decoration: const InputDecoration(labelText: 'Почта'), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('Отмена'), ), ElevatedButton( onPressed: () async { try { final token = await _apiService.getAccessToken(); await Dio().put( '${AppConstants.baseUrl}/admin/users/${user['id']}/profile', data: { 'first_name': fNameController.text.trim(), 'last_name': lNameController.text.trim(), 'about': aboutController.text.trim(), 'phone': phoneController.text.trim(), 'email': emailController.text.trim(), }, options: Options(headers: {'Authorization': 'Bearer $token'}), ); Navigator.pop(ctx); _showSnackBar('Профиль успешно обновлен!'); _loadUsers(); } catch (e) { _showSnackBar('Ошибка обновления: $e'); } }, child: const Text('Сохранить'), ), ], ), ); } }