import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../data/providers/auth_provider.dart'; import '../../../routes/app_routes.dart'; import 'dart:math'; class AuthController extends GetxController { final AuthProvider _authProvider = Get.find(); final emailController = TextEditingController(); final passwordController = TextEditingController(); final formKey = GlobalKey(); final nameController = TextEditingController(); final confirmPasswordController = TextEditingController(); final RxBool isConfirmPasswordVisible = false.obs; // Form fields for registration final RxString email = ''.obs; final RxString password = ''.obs; final RxString nik = ''.obs; final RxString phoneNumber = ''.obs; final RxString selectedRole = 'WARGA'.obs; // Default role final RxString alamatLengkap = ''.obs; final Rx tanggalLahir = Rx(null); final RxString rtRw = ''.obs; final RxString kelurahan = ''.obs; final RxString kecamatan = ''.obs; // Form status final RxBool isLoading = false.obs; final RxBool isPasswordVisible = false.obs; final RxString errorMessage = ''.obs; // Role options final List roleOptions = ['WARGA', 'PETUGAS_MITRA']; void togglePasswordVisibility() { isPasswordVisible.value = !isPasswordVisible.value; } void toggleConfirmPasswordVisibility() { isConfirmPasswordVisible.value = !isConfirmPasswordVisible.value; } // Change role selection void setRole(String? role) { if (role != null) { selectedRole.value = role; } } void login() async { // Clear previous error messages errorMessage.value = ''; // Basic validation if (emailController.text.isEmpty || passwordController.text.isEmpty) { errorMessage.value = 'Email dan password tidak boleh kosong'; return; } if (!GetUtils.isEmail(emailController.text.trim())) { errorMessage.value = 'Format email tidak valid'; return; } try { isLoading.value = true; // Use the actual Supabase authentication final response = await _authProvider.signIn( email: emailController.text.trim(), password: passwordController.text, ); // Check if login was successful if (response.user != null) { await _checkRoleAndNavigate(); } else { errorMessage.value = 'Login gagal. Periksa email dan password Anda.'; } } catch (e) { errorMessage.value = 'Terjadi kesalahan: ${e.toString()}'; } finally { isLoading.value = false; } } Future _checkRoleAndNavigate() async { try { // Get the user's role ID from the auth provider final roleId = await _authProvider.getUserRoleId(); if (roleId == null) { errorMessage.value = 'Tidak dapat memperoleh peran pengguna'; return; } // Get role name based on role ID final roleName = await _authProvider.getRoleName(roleId); // Navigate based on role name if (roleName == null) { await _checkWargaStatusAndNavigate(); // Default to warga if role name not found return; } switch (roleName.toUpperCase()) { case 'PETUGAS_BUMDES': _navigateToPetugasBumdesDashboard(); break; case 'WARGA': // For WARGA role, check account status in warga_desa table await _checkWargaStatusAndNavigate(); break; default: _navigateToWargaDashboard(); break; } } catch (e) { errorMessage.value = 'Gagal navigasi: ${e.toString()}'; } } // Check warga status in warga_desa table and navigate accordingly Future _checkWargaStatusAndNavigate() async { try { final user = _authProvider.currentUser; if (user == null) { errorMessage.value = 'Tidak dapat memperoleh data pengguna'; return; } // Get user data from warga_desa table final userData = await _authProvider.client .from('warga_desa') .select('status, keterangan') .eq('user_id', user.id) .maybeSingle(); if (userData == null) { errorMessage.value = 'Data pengguna tidak ditemukan'; return; } final status = userData['status'] as String?; switch (status?.toLowerCase()) { case 'active': // Allow login for active users _navigateToWargaDashboard(); break; case 'suspended': // Show error for suspended users final keterangan = userData['keterangan'] as String? ?? 'Tidak ada keterangan'; errorMessage.value = 'Akun Anda dinonaktifkan oleh petugas. Keterangan: $keterangan'; // Sign out the user await _authProvider.signOut(); break; case 'pending': // Show error for pending users errorMessage.value = 'Akun Anda sedang dalam proses verifikasi. Silakan tunggu hingga verifikasi selesai.'; // Sign out the user await _authProvider.signOut(); break; default: errorMessage.value = 'Status akun tidak valid'; // Sign out the user await _authProvider.signOut(); break; } } catch (e) { errorMessage.value = 'Gagal memeriksa status akun: ${e.toString()}'; // Sign out the user on error await _authProvider.signOut(); } } void _navigateToPetugasBumdesDashboard() { Get.offAllNamed(Routes.PETUGAS_BUMDES_DASHBOARD); } void _navigateToWargaDashboard() { Get.offAllNamed(Routes.WARGA_DASHBOARD); } void forgotPassword() async { // Clear previous error messages errorMessage.value = ''; // Basic validation if (emailController.text.isEmpty) { errorMessage.value = 'Email tidak boleh kosong'; return; } if (!GetUtils.isEmail(emailController.text.trim())) { errorMessage.value = 'Format email tidak valid'; return; } try { isLoading.value = true; // Call Supabase to send password reset email await _authProvider.client.auth.resetPasswordForEmail( emailController.text.trim(), ); // Show success message Get.snackbar( 'Berhasil', 'Link reset password telah dikirim ke email Anda', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.green[100], colorText: Colors.green[800], icon: const Icon(Icons.check_circle, color: Colors.green), ); // Return to login page after a short delay await Future.delayed(const Duration(seconds: 2)); Get.back(); } catch (e) { errorMessage.value = 'Terjadi kesalahan: ${e.toString()}'; } finally { isLoading.value = false; } } void goToSignUp() { // Clear error message when navigating away errorMessage.value = ''; Get.toNamed(Routes.REGISTER); } void goToForgotPassword() { // Clear error message when navigating away errorMessage.value = ''; Get.toNamed(Routes.FORGOT_PASSWORD); } @override void onClose() { emailController.dispose(); passwordController.dispose(); nameController.dispose(); confirmPasswordController.dispose(); super.onClose(); } // Register user implementation Future registerUser() async { // Clear previous error messages errorMessage.value = ''; // Validate form fields if (!formKey.currentState!.validate()) { return; } // Validate date of birth separately (since it's not a standard form field) if (!validateDateOfBirth()) { return; } try { isLoading.value = true; // Format tanggal lahir to string (YYYY-MM-DD) final formattedTanggalLahir = tanggalLahir.value != null ? '${tanggalLahir.value!.year}-${tanggalLahir.value!.month.toString().padLeft(2, '0')}-${tanggalLahir.value!.day.toString().padLeft(2, '0')}' : ''; // Generate register_id with format REG-YYYY-1234567 final currentYear = DateTime.now().year.toString(); final randomDigits = _generateRandomDigits(7); // Generate 7 random digits final registerId = 'REG-$currentYear-$randomDigits'; // 1. Register user with Supabase Auth and add role_id to metadata final response = await _authProvider.client.auth.signUp( email: email.value.trim(), password: password.value, data: { 'role_id': 'bb5360d5-8fd0-404e-8f6f-71ec4d8ad0ae', // Fixed role_id for WARGA }, ); // Check if registration was successful if (response.user != null) { // 2. Get the UID from the created auth user final userId = response.user!.id; // 3. Insert user data into the warga_desa table await _authProvider.client.from('warga_desa').insert({ 'user_id': userId, 'email': email.value.trim(), 'nama_lengkap': nameController.text.trim(), 'nik': nik.value.trim(), 'status': 'pending', 'tanggal_lahir': formattedTanggalLahir, 'no_hp': phoneNumber.value.trim(), 'rt_rw': rtRw.value.trim(), 'kelurahan_desa': kelurahan.value.trim(), 'kecamatan': kecamatan.value.trim(), 'alamat': alamatLengkap.value.trim(), 'register_id': registerId, // Add register_id to the warga_desa table }); // Registration successful Get.offNamed( Routes.REGISTRATION_SUCCESS, arguments: {'register_id': registerId}, ); } else { errorMessage.value = 'Gagal mendaftar. Silakan coba lagi.'; } } catch (e) { errorMessage.value = 'Terjadi kesalahan: ${e.toString()}'; print('Registration error: ${e.toString()}'); } finally { isLoading.value = false; } } // Generate random digits of specified length String _generateRandomDigits(int length) { final random = Random(); final buffer = StringBuffer(); for (var i = 0; i < length; i++) { buffer.write(random.nextInt(10)); } return buffer.toString(); } // Validation methods String? validateEmail(String? value) { if (value == null || value.isEmpty) { return 'Email tidak boleh kosong'; } if (!GetUtils.isEmail(value)) { return 'Format email tidak valid'; } return null; } String? validatePassword(String? value) { if (value == null || value.isEmpty) { return 'Password tidak boleh kosong'; } if (value.length < 8) { return 'Password minimal 8 karakter'; } if (!value.contains(RegExp(r'[A-Z]'))) { return 'Password harus memiliki minimal 1 huruf besar'; } if (!value.contains(RegExp(r'[0-9]'))) { return 'Password harus memiliki minimal 1 angka'; } return null; } String? validateConfirmPassword(String? value) { if (value == null || value.isEmpty) { return 'Konfirmasi password tidak boleh kosong'; } if (value != password.value) { return 'Password tidak cocok'; } return null; } String? validateName(String? value) { if (value == null || value.isEmpty) { return 'Nama lengkap tidak boleh kosong'; } if (value.length < 3) { return 'Nama lengkap minimal 3 karakter'; } if (!RegExp(r"^[a-zA-Z\s\.]+$").hasMatch(value)) { return 'Nama hanya boleh berisi huruf, spasi, titik, dan apostrof'; } return null; } String? validateNIK(String? value) { if (value == null || value.isEmpty) { return 'NIK tidak boleh kosong'; } if (value.length != 16) { return 'NIK harus 16 digit'; } if (!RegExp(r'^[0-9]+$').hasMatch(value)) { return 'NIK hanya boleh berisi angka'; } return null; } String? validatePhone(String? value) { if (value == null || value.isEmpty) { return 'No HP tidak boleh kosong'; } if (!value.startsWith('08')) { return 'Nomor HP harus diawali dengan 08'; } if (value.length < 10 || value.length > 13) { return 'Nomor HP harus antara 10-13 digit'; } if (!RegExp(r'^[0-9]+$').hasMatch(value)) { return 'Nomor HP hanya boleh berisi angka'; } return null; } String? validateRTRW(String? value) { if (value == null || value.isEmpty) { return 'RT/RW tidak boleh kosong'; } if (!RegExp(r'^\d{1,3}\/\d{1,3}$').hasMatch(value)) { return 'Format RT/RW tidak valid (contoh: 001/002)'; } return null; } String? validateKelurahan(String? value) { if (value == null || value.isEmpty) { return 'Kelurahan/Desa tidak boleh kosong'; } if (value.length < 3) { return 'Kelurahan/Desa minimal 3 karakter'; } return null; } String? validateKecamatan(String? value) { if (value == null || value.isEmpty) { return 'Kecamatan tidak boleh kosong'; } if (value.length < 3) { return 'Kecamatan minimal 3 karakter'; } return null; } String? validateAlamat(String? value) { if (value == null || value.isEmpty) { return 'Alamat lengkap tidak boleh kosong'; } if (value.length < 5) { return 'Alamat terlalu pendek, minimal 5 karakter'; } return null; } bool validateDateOfBirth() { if (tanggalLahir.value == null) { errorMessage.value = 'Tanggal lahir harus diisi'; return false; } // Check if user is at least 17 years old final DateTime today = DateTime.now(); final DateTime minimumAge = DateTime( today.year - 17, today.month, today.day, ); if (tanggalLahir.value!.isAfter(minimumAge)) { errorMessage.value = 'Anda harus berusia minimal 17 tahun'; return false; } return true; } }