import 'package:flutter/material.dart'; import 'package:SIBAYAM/api_services/api_services.dart'; class UserListPage extends StatefulWidget { @override _UserListPageState createState() => _UserListPageState(); } class _UserListPageState extends State { final ApiService apiService = ApiService(); List> users = []; List> filteredUsers = []; bool isLoading = true; final _formKey = GlobalKey(); final _nameController = TextEditingController(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _alamatController = TextEditingController(); final _nomorTeleponController = TextEditingController(); final _searchController = TextEditingController(); @override void initState() { super.initState(); _loadUsers(); } @override void dispose() { _nameController.dispose(); _emailController.dispose(); _passwordController.dispose(); _alamatController.dispose(); _nomorTeleponController.dispose(); _searchController.dispose(); super.dispose(); } Future _addUser() async { try { await apiService.registerUser( name: _nameController.text, email: _emailController.text, password: _passwordController.text, alamat: _alamatController.text, nomorTelepon: _nomorTeleponController.text, ); _nameController.clear(); _emailController.clear(); _passwordController.clear(); _alamatController.clear(); _nomorTeleponController.clear(); await _loadUsers(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('User berhasil ditambahkan'), backgroundColor: Colors.green, ), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal menambahkan user: ${e.toString()}'), backgroundColor: Colors.red, ), ); } } Future _updateUser(Map user) async { try { String? newPassword = _passwordController.text.isEmpty ? null : _passwordController.text; await apiService.updateUser( id: user['id'], name: _nameController.text, email: _emailController.text, password: newPassword, alamat: _alamatController.text, nomorTelepon: _nomorTeleponController.text, ); _nameController.clear(); _emailController.clear(); _passwordController.clear(); _alamatController.clear(); _nomorTeleponController.clear(); await _loadUsers(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( newPassword != null ? 'User berhasil diperbarui termasuk password' : 'User berhasil diperbarui', ), backgroundColor: Colors.green, ), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal memperbarui user: ${e.toString()}'), backgroundColor: Colors.red, ), ); } } void _showDeleteConfirmation(Map user) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Konfirmasi Hapus'), content: Text( 'Apakah Anda yakin ingin menghapus user ${user['name']}?', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Batal'), ), ElevatedButton( onPressed: () async { try { Navigator.pop(context); await apiService.deleteUser(user['id']); await _loadUsers(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('User berhasil dihapus'), backgroundColor: Colors.green, ), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal menghapus user: ${e.toString()}'), backgroundColor: Colors.red, ), ); } }, style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: Text('Hapus'), ), ], ), ); } void _showUpdateDialog(Map user) { _nameController.text = user['name'] ?? ''; _emailController.text = user['email'] ?? ''; _alamatController.text = user['alamat'] ?? ''; _nomorTeleponController.text = user['nomorTelepon'] ?? ''; _passwordController.text = ''; showDialog( context: context, builder: (context) => AlertDialog( title: Text('Update User'), content: SingleChildScrollView( child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextFormField( controller: _nameController, decoration: InputDecoration(labelText: 'Nama'), validator: (value) => value?.isEmpty ?? true ? 'Nama tidak boleh kosong' : null, ), TextFormField( controller: _emailController, decoration: InputDecoration(labelText: 'Email'), validator: (value) { if (value?.isEmpty ?? true) return 'Email tidak boleh kosong'; if (!value!.contains('@')) return 'Email tidak valid'; return null; }, ), TextFormField( controller: _passwordController, decoration: InputDecoration( labelText: 'Password Baru', helperText: 'Kosongkan jika tidak ingin mengubah password', ), obscureText: true, validator: (value) { if (value?.isNotEmpty ?? false) { if (value!.length < 6) return 'Password minimal 6 karakter'; } return null; }, ), TextFormField( controller: _alamatController, decoration: InputDecoration(labelText: 'Alamat'), validator: (value) => value?.isEmpty ?? true ? 'Alamat tidak boleh kosong' : null, ), TextFormField( controller: _nomorTeleponController, decoration: InputDecoration(labelText: 'Nomor Telepon'), keyboardType: TextInputType.phone, validator: (value) => value?.isEmpty ?? true ? 'Nomor telepon tidak boleh kosong' : null, ), ], ), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Batal'), ), ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { Navigator.pop(context); _updateUser(user); } }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF9DC08D), ), child: Text('Update'), ), ], ), ); } void _showAddUserDialog() { _nameController.clear(); _emailController.clear(); _passwordController.clear(); _alamatController.clear(); _nomorTeleponController.clear(); showDialog( context: context, builder: (context) => AlertDialog( title: Text('Tambah User Baru'), content: SingleChildScrollView( child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextFormField( controller: _nameController, decoration: InputDecoration(labelText: 'Nama'), validator: (value) { if (value == null || value.isEmpty) { return 'Nama tidak boleh kosong'; } return null; }, ), TextFormField( controller: _emailController, decoration: InputDecoration(labelText: 'Email'), validator: (value) { if (value == null || value.isEmpty) { return 'Email tidak boleh kosong'; } if (!value.contains('@')) { return 'Email tidak valid'; } return null; }, ), TextFormField( controller: _passwordController, decoration: InputDecoration(labelText: 'Password'), obscureText: true, validator: (value) { if (value == null || value.isEmpty) { return 'Password tidak boleh kosong'; } if (value.length < 6) { return 'Password minimal 6 karakter'; } return null; }, ), TextFormField( controller: _alamatController, decoration: InputDecoration(labelText: 'Alamat'), validator: (value) { if (value == null || value.isEmpty) { return 'Alamat tidak boleh kosong'; } return null; }, ), TextFormField( controller: _nomorTeleponController, decoration: InputDecoration(labelText: 'Nomor Telepon'), keyboardType: TextInputType.phone, validator: (value) { if (value == null || value.isEmpty) { return 'Nomor telepon tidak boleh kosong'; } return null; }, ), ], ), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Batal'), ), ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { Navigator.pop(context); _addUser(); } }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF9DC08D), ), child: Text('Simpan'), ), ], ), ); } Future _loadUsers() async { try { final userList = await apiService.getUsers(); setState(() { users = userList; filteredUsers = userList; isLoading = false; }); } catch (e) { print('Error loading users: $e'); setState(() { isLoading = false; }); } } void _filterUsers(String query) { setState(() { if (query.isEmpty) { filteredUsers = users; } else { filteredUsers = users.where((user) { final name = user['name']?.toString().toLowerCase() ?? ''; final role = user['role']?.toString().toLowerCase() ?? ''; final searchQuery = query.toLowerCase(); return name.contains(searchQuery) || role.contains(searchQuery); }).toList(); } }); } void _navigateToUserDetail(Map user) { Navigator.push( context, MaterialPageRoute( builder: (context) => UserDetailPage( user: user, onUserUpdated: _loadUsers, onUserDeleted: _loadUsers, ), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Daftar Pengguna'), backgroundColor: Color(0xFF9DC08D), ), floatingActionButton: FloatingActionButton( onPressed: _showAddUserDialog, backgroundColor: Color(0xFF9DC08D), child: Icon(Icons.add), ), body: isLoading ? Center(child: CircularProgressIndicator()) : Column( children: [ // Search Bar Container( padding: EdgeInsets.all(16), child: TextField( controller: _searchController, onChanged: _filterUsers, decoration: InputDecoration( hintText: 'Cari berdasarkan nama atau role...', prefixIcon: Icon(Icons.search, color: Color(0xFF9DC08D)), suffixIcon: _searchController.text.isNotEmpty ? IconButton( icon: Icon(Icons.clear, color: Colors.grey), onPressed: () { _searchController.clear(); _filterUsers(''); }, ) : null, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey[300]!), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color(0xFF9DC08D), width: 2), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey[300]!), ), filled: true, fillColor: Colors.grey[50], ), ), ), // User List Expanded( child: filteredUsers.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( _searchController.text.isNotEmpty ? Icons.search_off : Icons.people_outline, size: 64, color: Colors.grey, ), SizedBox(height: 16), Text( _searchController.text.isNotEmpty ? 'Tidak ada pengguna yang ditemukan' : 'Tidak ada pengguna', style: TextStyle( fontSize: 16, color: Colors.grey, ), ), if (_searchController.text.isNotEmpty) ...[ SizedBox(height: 8), Text( 'Coba kata kunci lain', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ], ), ) : ListView.builder( padding: EdgeInsets.symmetric(horizontal: 16), itemCount: filteredUsers.length, itemBuilder: (context, index) { final user = filteredUsers[index]; return Card( elevation: 2, margin: EdgeInsets.only(bottom: 12), child: ListTile( leading: CircleAvatar( backgroundColor: user['role'] == 'admin' ? Colors.blue.withOpacity(0.2) : Colors.green.withOpacity(0.2), child: Icon( user['role'] == 'admin' ? Icons.admin_panel_settings : Icons.person, color: user['role'] == 'admin' ? Colors.blue : Colors.green, ), ), title: Text( user['name'] ?? 'Nama tidak tersedia', style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, ), ), subtitle: Container( margin: EdgeInsets.only(top: 4), padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: user['role'] == 'admin' ? Colors.blue.withOpacity(0.1) : Colors.green.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( user['role'] ?? 'user', style: TextStyle( color: user['role'] == 'admin' ? Colors.blue : Colors.green, fontSize: 12, fontWeight: FontWeight.w500, ), ), ), trailing: Icon(Icons.arrow_forward_ios, size: 16), onTap: () => _navigateToUserDetail(user), ), ); }, ), ), ], ), ); } } // Halaman Detail User class UserDetailPage extends StatelessWidget { final Map user; final VoidCallback onUserUpdated; final VoidCallback onUserDeleted; const UserDetailPage({ Key? key, required this.user, required this.onUserUpdated, required this.onUserDeleted, }) : super(key: key); void _showDeleteConfirmation(BuildContext context) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Konfirmasi Hapus'), content: Text( 'Apakah Anda yakin ingin menghapus user ${user['name']}?', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Batal'), ), ElevatedButton( onPressed: () async { try { Navigator.pop(context); // Close dialog final apiService = ApiService(); await apiService.deleteUser(user['id']); Navigator.pop(context); // Go back to list onUserDeleted(); // Refresh list ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('User berhasil dihapus'), backgroundColor: Colors.green, ), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal menghapus user: ${e.toString()}'), backgroundColor: Colors.red, ), ); } }, style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: Text('Hapus'), ), ], ), ); } void _showUpdateDialog(BuildContext context) { final _formKey = GlobalKey(); final _nameController = TextEditingController(text: user['name'] ?? ''); final _emailController = TextEditingController(text: user['email'] ?? ''); final _alamatController = TextEditingController(text: user['alamat'] ?? ''); final _nomorTeleponController = TextEditingController(text: user['nomorTelepon'] ?? ''); final _passwordController = TextEditingController(); showDialog( context: context, builder: (context) => AlertDialog( title: Text('Update User'), content: SingleChildScrollView( child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextFormField( controller: _nameController, decoration: InputDecoration(labelText: 'Nama'), validator: (value) => value?.isEmpty ?? true ? 'Nama tidak boleh kosong' : null, ), TextFormField( controller: _emailController, decoration: InputDecoration(labelText: 'Email'), validator: (value) { if (value?.isEmpty ?? true) return 'Email tidak boleh kosong'; if (!value!.contains('@')) return 'Email tidak valid'; return null; }, ), TextFormField( controller: _passwordController, decoration: InputDecoration( labelText: 'Password Baru', helperText: 'Kosongkan jika tidak ingin mengubah password', ), obscureText: true, validator: (value) { if (value?.isNotEmpty ?? false) { if (value!.length < 6) return 'Password minimal 6 karakter'; } return null; }, ), TextFormField( controller: _alamatController, decoration: InputDecoration(labelText: 'Alamat'), validator: (value) => value?.isEmpty ?? true ? 'Alamat tidak boleh kosong' : null, ), TextFormField( controller: _nomorTeleponController, decoration: InputDecoration(labelText: 'Nomor Telepon'), keyboardType: TextInputType.phone, validator: (value) => value?.isEmpty ?? true ? 'Nomor telepon tidak boleh kosong' : null, ), ], ), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Batal'), ), ElevatedButton( onPressed: () async { if (_formKey.currentState!.validate()) { try { Navigator.pop(context); String? newPassword = _passwordController.text.isEmpty ? null : _passwordController.text; final apiService = ApiService(); await apiService.updateUser( id: user['id'], name: _nameController.text, email: _emailController.text, password: newPassword, alamat: _alamatController.text, nomorTelepon: _nomorTeleponController.text, ); Navigator.pop(context); // Go back to list onUserUpdated(); // Refresh list ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( newPassword != null ? 'User berhasil diperbarui termasuk password' : 'User berhasil diperbarui', ), backgroundColor: Colors.green, ), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal memperbarui user: ${e.toString()}'), backgroundColor: Colors.red, ), ); } } }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF9DC08D), ), child: Text('Update'), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Detail Pengguna'), backgroundColor: Color(0xFF9DC08D), actions: [ IconButton( icon: Icon(Icons.edit), onPressed: () => _showUpdateDialog(context), ), IconButton( icon: Icon(Icons.delete, color: Colors.red), onPressed: () => _showDeleteConfirmation(context), ), ], ), body: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Profile Header Container( width: double.infinity, padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Color(0xFF9DC08D).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Column( children: [ CircleAvatar( radius: 40, backgroundColor: user['role'] == 'admin' ? Colors.blue.withOpacity(0.2) : Colors.green.withOpacity(0.2), child: Icon( user['role'] == 'admin' ? Icons.admin_panel_settings : Icons.person, size: 40, color: user['role'] == 'admin' ? Colors.blue : Colors.green, ), ), SizedBox(height: 12), Text( user['name'] ?? 'Nama tidak tersedia', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), SizedBox(height: 8), Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( color: user['role'] == 'admin' ? Colors.blue.withOpacity(0.2) : Colors.green.withOpacity(0.2), borderRadius: BorderRadius.circular(16), ), child: Text( user['role'] ?? 'user', style: TextStyle( color: user['role'] == 'admin' ? Colors.blue : Colors.green, fontWeight: FontWeight.w600, ), ), ), ], ), ), SizedBox(height: 24), // Detail Information Text( 'Informasi Detail', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.grey[700], ), ), SizedBox(height: 16), _buildDetailItem( icon: Icons.email, title: 'Email', value: user['email'] ?? 'Email tidak tersedia', ), _buildDetailItem( icon: Icons.location_on, title: 'Alamat', value: user['alamat'] ?? 'Alamat tidak tersedia', ), _buildDetailItem( icon: Icons.phone, title: 'Nomor Telepon', value: user['nomorTelepon'] ?? 'Nomor telepon tidak tersedia', ), ], ), ), ); } Widget _buildDetailItem({ required IconData icon, required String title, required String value, }) { return Container( margin: EdgeInsets.only(bottom: 16), padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey[200]!), ), child: Row( children: [ Icon( icon, color: Color(0xFF9DC08D), size: 24, ), SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 12, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), SizedBox(height: 4), Text( value, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), ), ], ), ); } }