// ignore_for_file: unused_local_variable import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; import 'package:sidak_desa_mobile/presentation/views/main_screen.dart'; import '../../data/fetch/attedance_api.dart'; import '../../data/model/user_model.dart'; import '../../data/shared/auth_storage.dart'; import '../../utils/device_id.dart'; class AbsenController extends GetxController { AbsenController({required Map qrData}) { this.qrData.value = qrData; } final _api = AttedanceApi(); final _storage = AuthStorage(); final user = Rxn(); final qrData = {}.obs; final nameController = TextEditingController(); final isSubmitting = false.obs; final isLoadingDaily = false.obs; final isLoadingLocation = false.obs; final latitude = RxnDouble(); final longitude = RxnDouble(); final locationError = RxnString(); final selectedDate = ''.obs; final daily = Rxn>(); @override void onInit() { _init(); super.onInit(); } Future _init() async { selectedDate.value = _todayDate(); await loadUser(); await fetchDaily(); await getCurrentLocation(); } Future loadUser() async { user.value = await _storage.getUser(); nameController.text = user.value?.name ?? ''; } @override void onClose() { nameController.dispose(); super.onClose(); } String get checkInText => _formatTime(daily.value?['check_in']); String get checkOutText => _formatTime(daily.value?['check_out']); Future fetchDaily() async { final uid = user.value?.id; if (uid == null) return; isLoadingDaily.value = true; try { final res = await _api.getDailyAttendance( userId: uid, date: selectedDate.value, ); if (res['ok'] == true) { if (res['data'] != null && res['data'] is Map) { daily.value = Map.from(res['data']); } else { daily.value = null; } } else { daily.value = null; } } catch (_) { daily.value = null; } finally { isLoadingDaily.value = false; } } Future submitAbsen() async { if (isSubmitting.value) return; final token = (qrData['raw'] ?? '').toString().trim(); final uid = user.value?.id; if (token.isEmpty || uid == null) return; isSubmitting.value = true; try { final deviceId = await DeviceIdUtil.getDeviceId(); final response = await _api.verifyAttendance( token: token, userId: uid, deviceInfo: deviceId, latitude: latitude.value, longitude: longitude.value, ); await fetchDaily(); // ✅ DIALOG BERHASIL Get.dialog( Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 80, height: 80, decoration: const BoxDecoration( color: Color(0xFF0F766E), // hijau teal shape: BoxShape.circle, ), child: const Icon(Icons.check, color: Colors.white, size: 45), ), const SizedBox(height: 16), const Text( "Absensi Berhasil", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( "Absensi berhasil dilakukan", textAlign: TextAlign.center, ), const SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () { Get.offAll(() => const MainScreen()); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.orange, ), child: const Text( "Oke", style: TextStyle(color: Colors.white), ), ), ), ], ), ), ), barrierDismissible: false, ); } catch (e) { // ❌ DIALOG DITOLAK Get.dialog( Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 80, height: 80, decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), child: const Icon(Icons.close, color: Colors.white, size: 45), ), const SizedBox(height: 16), const Text( "Absen Ditolak", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text( e.toString().replaceAll('Exception:', ''), textAlign: TextAlign.center, ), const SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Get.back(), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, ), child: const Text( "Tutup", style: TextStyle(color: Colors.white), ), ), ), ], ), ), ), barrierDismissible: false, ); } finally { isSubmitting.value = false; } } Future getCurrentLocation() async { isLoadingLocation.value = true; locationError.value = null; try { final isServiceEnabled = await Geolocator.isLocationServiceEnabled(); if (!isServiceEnabled) { locationError.value = 'Layanan lokasi tidak aktif'; return; } var permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); } if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { locationError.value = 'Izin lokasi ditolak'; return; } final position = await Geolocator.getCurrentPosition( locationSettings: const LocationSettings( accuracy: LocationAccuracy.high, ), ); latitude.value = position.latitude; longitude.value = position.longitude; } catch (_) { locationError.value = 'Gagal mengambil lokasi terkini'; } finally { isLoadingLocation.value = false; } } String _todayDate() { final now = DateTime.now(); final yyyy = now.year.toString().padLeft(4, '0'); final mm = now.month.toString().padLeft(2, '0'); final dd = now.day.toString().padLeft(2, '0'); return '$yyyy-$mm-$dd'; } String _formatTime(dynamic value) { if (value == null) return '-'; final s = value.toString().trim(); if (s.isEmpty || s == 'null') return '-'; if (s.contains('T')) { try { final dt = DateTime.parse(s).toLocal(); final hh = dt.hour.toString().padLeft(2, '0'); final mm = dt.minute.toString().padLeft(2, '0'); return '$hh:$mm'; } catch (_) {} } final parts = s.split(':'); if (parts.length >= 2) { return '${parts[0].padLeft(2, '0')}:${parts[1].padLeft(2, '0')}'; } return s; } String get tanggalFormatted { final now = DateTime.now(); return "${now.day} ${_bulanIndo(now.month)} ${now.year}"; } String _bulanIndo(int bulan) { const bulanList = [ "", "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember", ]; return bulanList[bulan]; } String get statusText { if (daily.value == null) return "-"; if (daily.value?['check_in'] != null) { return "Hadir"; } return "-"; } String get keteranganText { if (daily.value == null) return "-"; final checkIn = daily.value?['check_in']; if (checkIn == null) return "-"; return "Tepat Waktu"; } }