import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sijentik/component/app_theme.dart'; import 'package:sijentik/screens/auth/registerkader.dart'; import 'package:sijentik/screens/auth/lupasandiemail.dart'; import 'package:sijentik/screens/kader/homekader.dart'; import 'package:sijentik/api/api.dart'; import 'package:sijentik/screens/petugas/dashboardpetugas.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { final TextEditingController emailController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); final FocusNode emailFocusNode = FocusNode(); final FocusNode passwordFocusNode = FocusNode(); bool obscurePassword = true; bool isLoading = false; @override void dispose() { emailController.dispose(); passwordController.dispose(); emailFocusNode.dispose(); passwordFocusNode.dispose(); super.dispose(); } // ============================= // SIMPAN DATA USER // ============================= Future saveUserData(Map user) async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt('id', int.tryParse(user['id'].toString()) ?? 0); await prefs.setString('name', user['name'] ?? ''); await prefs.setString('email', user['email'] ?? ''); await prefs.setString('address', user['address'] ?? ''); await prefs.setString('rtrw', user['rtrw'] ?? ''); await prefs.setString('role', user['role'] ?? ''); await prefs.setString('status', user['status'] ?? ''); } // ============================= // LOGIN // ============================= Future _login() async { if (emailController.text.isEmpty || passwordController.text.isEmpty) { _showErrorDialog('Email dan kata sandi harus diisi'); return; } if (!emailController.text.contains('@')) { _showErrorDialog('Format email tidak valid'); return; } setState(() { isLoading = true; }); try { final response = await http.post( Uri.parse('$baseUrl/login'), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: jsonEncode({ 'email': emailController.text.trim(), 'password': passwordController.text.trim(), }), ); print("STATUS LOGIN: ${response.statusCode}"); print("BODY LOGIN: ${response.body}"); final data = jsonDecode(response.body); if (!mounted) return; setState(() { isLoading = false; }); if (response.statusCode == 200) { final user = Map.from(data['user'] ?? {}); // 🔥 VALIDASI WAJIB if (user['id'] == null) { _showErrorDialog('ID user tidak ditemukan dari server'); return; } print("LOGIN SUCCESS"); print("USER FINAL: $user"); // SIMPAN await saveUserData(user); final String role = (user['role'] ?? '').toString().toLowerCase(); if (role == 'petugas') { _navigateToDashboardPetugas(); } else if (role == 'kader') { _navigateToHomeKader({ 'id': user['id'], 'name': user['name'], 'email': user['email'], 'address': user['address'], 'role': user['role'], }); } else { _showErrorDialog('Role pengguna tidak dikenali'); } } else { _showErrorDialog(data['error'] ?? data['message'] ?? 'Login gagal'); } } catch (e) { if (!mounted) return; setState(() { isLoading = false; }); _showErrorDialog('Tidak dapat terhubung ke server: $e'); } } // ============================= // ERROR DIALOG // ============================= void _showErrorDialog(String message) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Login Gagal'), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('OK'), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F9FF), body: Stack( children: [ Positioned( top: 0, left: 0, right: 0, child: Container( height: 280, decoration: BoxDecoration( color: AppColors.button, borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(195), bottomRight: Radius.circular(195), ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 15, spreadRadius: 1, offset: const Offset(0, 13), ), ], ), ), ), SafeArea( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 40), Image.asset('assets/images/logodua.png', width: 150), const SizedBox(height: 55), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 12, spreadRadius: 1, offset: const Offset(0, 6), ), ], ), child: Column( children: [ const Text( 'MASUK', style: TextStyle( fontSize: 26, fontWeight: FontWeight.bold, color: Colors.black87, ), ), const SizedBox(height: 30), Align( alignment: Alignment.centerLeft, child: Text( 'Email', style: TextStyle( fontSize: 16, color: Colors.grey[700], fontWeight: FontWeight.w500, ), ), ), const SizedBox(height: 8), TextField( controller: emailController, focusNode: emailFocusNode, decoration: InputDecoration( hintText: 'email@gmail.com', prefixIcon: const Icon(Icons.email_outlined), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), ), onSubmitted: (_) => _login(), ), const SizedBox(height: 20), Align( alignment: Alignment.centerLeft, child: Text( 'Kata Sandi', style: TextStyle( fontSize: 16, color: Colors.grey[700], fontWeight: FontWeight.w500, ), ), ), const SizedBox(height: 8), TextField( controller: passwordController, focusNode: passwordFocusNode, obscureText: obscurePassword, decoration: InputDecoration( hintText: '********', prefixIcon: const Icon(Icons.lock_outline), suffixIcon: IconButton( icon: Icon( obscurePassword ? Icons.visibility_off : Icons.visibility, ), onPressed: () { setState(() { obscurePassword = !obscurePassword; }); }, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), ), onSubmitted: (_) => _login(), ), const SizedBox(height: 10), Row( children: [ TextButton( onPressed: _navigateLupaKataSandi, child: const Text( 'Lupa Kata Sandi?', style: TextStyle( color: Color(0xFF1E88E5), fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 10), SizedBox( width: double.infinity, height: 48, child: ElevatedButton( onPressed: isLoading ? null : _login, style: ElevatedButton.styleFrom( backgroundColor: AppColors.button, ), child: isLoading ? const CircularProgressIndicator( color: Colors.white, ) : const Text( 'Masuk', style: TextStyle( fontSize: 17, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ), const SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Belum punya akun? ', style: TextStyle(color: Colors.black87), ), GestureDetector( onTap: _navigateToRegister, child: const Text( 'Daftar', style: TextStyle( color: Color(0xFF1E88E5), fontWeight: FontWeight.bold, ), ), ), ], ), ], ), ), ), ], ), ), ), ], ), ); } void _navigateToHomeKader(Map user) { Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => HomeKaderPage(user: user)), (route) => false, ); } void _navigateToDashboardPetugas() { Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => const DashboardPetugas()), (route) => false, ); } void _navigateToRegister() { Navigator.push( context, MaterialPageRoute(builder: (context) => const RegisterKaderPage()), ); } void _navigateLupaKataSandi() { Navigator.push( context, MaterialPageRoute(builder: (context) => const LupaKataSandiEmailPage()), ); } }