import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'dart:async'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'jadwal_kotak_obat1_screen.dart'; import 'jadwal_kotak_obat2_screen.dart'; import 'login_screen.dart'; //import 'firebase_messaging_service.dart'; import 'notifikasi_service.dart'; import 'toast.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({ super.key, required String username, required void Function() onSignOut, }); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { Timer? _syncTimer; final bool _hasSentKotak1 = false; final bool _hasSentKotak2 = false; DateTime? _lastSentKotak1; DateTime? _lastSentKotak2; static const Duration minInterval = Duration(seconds: 10); String username = ''; bool isLoading = true; // Tambahan state untuk tombol dan status bool button1 = false; bool button2 = false; bool buzzer = false; bool lampu = false; bool _canAccessKotak1 = false; bool _canAccessKotak2 = false; String infoText = "Tidak Ada Informasi"; @override void initState() { super.initState(); //NotifikasiService.listenToAllNotifikasi(); // Realtime listener _initializeAsyncTasks(); // panggil fungsi async tanpa await _checkUserAccessToKotak(); _syncTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _updateStatus(); // update tombol dan info tiap 2 detik }); } @override void dispose() { _syncTimer?.cancel(); super.dispose(); } Future _initializeAsyncTasks() async { await fetchUsername(); } Future fetchUsername() async { try { final user = FirebaseAuth.instance.currentUser; if (user != null) { final uid = user.uid; final doc = await FirebaseFirestore.instance.collection('users').doc(uid).get(); if (doc.exists && doc.data() != null) { if (!mounted) return; setState(() { username = doc['username'] ?? 'User'; isLoading = false; }); } } } catch (e) { print('Error fetching username: $e'); if (!mounted) return; setState(() { username = 'User'; isLoading = false; }); } } Future _showLogoutConfirmationDialog() async { final shouldLogout = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Konfirmasi Logout'), content: const Text( 'Apakah Anda yakin ingin logout dari akun ini?', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('Tidak'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('Ya'), ), ], ), ); if (shouldLogout == true) { _logout(); } } // Fungsi baru: Update status tombol dan info Future _updateStatus() async { final user = FirebaseAuth.instance.currentUser; if (user == null) return; final uid = user.uid; try { final button1Ref = FirebaseDatabase.instance.ref('status/button1'); final button2Ref = FirebaseDatabase.instance.ref('status/button2'); final buzzerRef = FirebaseDatabase.instance.ref('status/buzzer'); final lampuRef = FirebaseDatabase.instance.ref('status/led'); final kotak1UserIdRef = FirebaseDatabase.instance.ref( 'jadwal/kotak1/userId', ); final kotak2UserIdRef = FirebaseDatabase.instance.ref( 'jadwal/kotak2/userId', ); final button1Snap = await button1Ref.get(); final button2Snap = await button2Ref.get(); final buzzerSnap = await buzzerRef.get(); final lampuSnap = await lampuRef.get(); final kotak1UserIdSnap = await kotak1UserIdRef.get(); final kotak2UserIdSnap = await kotak2UserIdRef.get(); // Cek userId sesuai final button1UserMatch = kotak1UserIdSnap.exists && kotak1UserIdSnap.value == uid; final button2UserMatch = kotak2UserIdSnap.exists && kotak2UserIdSnap.value == uid; if (!mounted) return; setState(() { button1 = button1Snap.exists && button1Snap.value == true; button2 = button2Snap.exists && button2Snap.value == true; buzzer = buzzerSnap.exists && buzzerSnap.value == true; lampu = lampuSnap.exists && lampuSnap.value == true; // Jika userId tidak cocok maka tombol tetap tampil tapi tidak bisa ditekan, tapi kita simpan status tombol sesuai DB if (!button1UserMatch) button1 = button1; // tetap tampil status if (!button2UserMatch) button2 = button2; // tetap tampil status }); // Update infoText sesuai kondisi: String info = "Tidak Ada Informasi"; // Cek alarm1 dan alarm2 final alarm1Snap = await FirebaseDatabase.instance.ref('status/alarm1').get(); final alarm2Snap = await FirebaseDatabase.instance.ref('status/alarm2').get(); // Cek jumlahObat kotak1 dan kotak2 final jumlahObat1Snap = await FirebaseDatabase.instance.ref('jadwal/kotak1/jumlahObat').get(); final jumlahObat2Snap = await FirebaseDatabase.instance.ref('jadwal/kotak2/jumlahObat').get(); final kotak1UserIdCheck = kotak1UserIdSnap.exists && kotak1UserIdSnap.value == uid; final kotak2UserIdCheck = kotak2UserIdSnap.exists && kotak2UserIdSnap.value == uid; if (kotak1UserIdCheck) { if (alarm1Snap.exists && alarm1Snap.value == true) { info = "Alarm Kotak 1 Aktif"; } else if (jumlahObat1Snap.exists) { final val = jumlahObat1Snap.value; int? jumlah = (val is int) ? val : int.tryParse(val.toString()); if (jumlah != null) { if (jumlah == 0) { info = "Obat Kotak 1 Habis, Segera isi ulang"; } else if (jumlah <= 3 && jumlah > 0) { info = "Obat Kotak 1 Sisa $jumlah, Waktunya isi ulang"; } } } } if (kotak2UserIdCheck) { if (alarm2Snap.exists && alarm2Snap.value == true) { info = "Alarm Kotak 2 Aktif"; } else if (jumlahObat2Snap.exists) { final val = jumlahObat2Snap.value; int? jumlah = (val is int) ? val : int.tryParse(val.toString()); if (jumlah != null) { if (jumlah == 0) { info = "Obat Kotak 2 Habis, Segera isi ulang"; } else if (jumlah <= 3 && jumlah > 0) { info = "Obat Kotak 2 Sisa $jumlah, Waktunya isi ulang"; } } } } if (!mounted) return; setState(() { infoText = info; }); } catch (e) { print('Gagal update status tombol dan info: $e'); } } Future _logout() async { await FirebaseAuth.instance.signOut(); SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.remove('userId'); FlutterBackgroundService().invoke("stopService"); print("✅ Logout berhasil dan background service dihentikan"); print('❌ userId dihapus dari SharedPreferences'); Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const LoginScreen()), ); showToast(message: "Successfully Logout"); } // Fungsi untuk toggle button kotak 1 void _toggleButton1() async { final user = FirebaseAuth.instance.currentUser; if (user == null) return; final userId = user.uid; final kotak1UserIdSnap = await FirebaseDatabase.instance.ref('jadwal/kotak1/userId').get(); if (!kotak1UserIdSnap.exists || kotak1UserIdSnap.value != userId) return; await FirebaseDatabase.instance.ref('status/button1').set(!button1); } // Fungsi untuk toggle button kotak 2 void _toggleButton2() async { final user = FirebaseAuth.instance.currentUser; if (user == null) return; final userId = user.uid; final kotak2UserIdSnap = await FirebaseDatabase.instance.ref('jadwal/kotak2/userId').get(); if (!kotak2UserIdSnap.exists || kotak2UserIdSnap.value != userId) return; await FirebaseDatabase.instance.ref('status/button2').set(!button2); } Future _checkUserAccessToKotak() async { final user = FirebaseAuth.instance.currentUser; if (user == null) return; final uid = user.uid; try { final kotak1UserIdSnap = await FirebaseDatabase.instance.ref('jadwal/kotak1/userId').get(); final kotak2UserIdSnap = await FirebaseDatabase.instance.ref('jadwal/kotak2/userId').get(); bool canAccessKotak1 = false; bool canAccessKotak2 = false; // Jika userId kosong atau tidak ada, maka semua user boleh akses if (!kotak1UserIdSnap.exists || kotak1UserIdSnap.value == null || kotak1UserIdSnap.value.toString().trim().isEmpty) { canAccessKotak1 = true; } else { canAccessKotak1 = kotak1UserIdSnap.value == uid; } if (!kotak2UserIdSnap.exists || kotak2UserIdSnap.value == null || kotak2UserIdSnap.value.toString().trim().isEmpty) { canAccessKotak2 = true; } else { canAccessKotak2 = kotak2UserIdSnap.value == uid; } if (!mounted) return; setState(() { _canAccessKotak1 = canAccessKotak1; _canAccessKotak2 = canAccessKotak2; }); } catch (e) { print('Error checking access to kotak: $e'); } } void _showAccessDeniedDialog(String kotakName) { showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Akses Ditolak'), content: Text('$kotakName Sudah diisi user lain'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('OK'), ), ], ), ); } Future _refresh() async { setState(() { isLoading = true; }); await Future.wait([ _initializeAsyncTasks(), _checkUserAccessToKotak(), _updateStatus(), ]); if (mounted) { setState(() { isLoading = false; }); } } @override Widget build(BuildContext context) { if (isLoading) { return const Center(child: CircularProgressIndicator()); } return RefreshIndicator( onRefresh: _refresh, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 35, horizontal: 24), decoration: const BoxDecoration( color: Color(0xFF3FA535), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(40), bottomRight: Radius.circular(40), ), ), child: Row( mainAxisAlignment: MainAxisAlignment .center, // Menyusun elemen-elemen ke tengah horizontal crossAxisAlignment: CrossAxisAlignment .center, // Menyusun elemen-elemen ke tengah vertikal children: [ const Icon( Icons.account_circle, color: Colors.white, size: 35, ), const SizedBox(width: 10), Text( 'Hello, $username', style: const TextStyle( fontSize: 20, color: Colors.white, fontWeight: FontWeight.bold, ), ), const Spacer(), IconButton( onPressed: _showLogoutConfirmationDialog, icon: const Icon( Icons.logout, color: Colors.white, size: 30, ), ), ], ), ), Container( margin: const EdgeInsets.all(24), padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.grey.shade300, blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( 'assets/images/logowarna.png', width: 60, height: 60, ), SizedBox(width: 10), // jarak antar gambar Image.asset( 'assets/images/logopolije.png', width: 60, height: 60, ), ], ), const SizedBox(height: 10), const Text( 'SmartMediBox', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 10), const Text( 'Pilih kotak obat untuk atur jadwal obat', textAlign: TextAlign.center, style: TextStyle(fontSize: 14, color: Colors.black54), ), const SizedBox(height: 30), // Tombol Jadwal Kotak Obat 1 dengan pengecekan akses ElevatedButton( onPressed: _canAccessKotak1 ? () { Navigator.push( context, MaterialPageRoute( builder: (context) => const JadwalKotakObat1Screen(), ), ); } : () => _showAccessDeniedDialog('Kotak Obat 1'), style: ElevatedButton.styleFrom( backgroundColor: _canAccessKotak1 ? const Color(0xFF3FA535) : Colors.grey, padding: const EdgeInsets.symmetric(vertical: 16), minimumSize: const Size.fromHeight(50), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text( 'Jadwal Kotak Obat 1', style: TextStyle( fontSize: 16, color: _canAccessKotak1 ? Colors.white : Colors.white70, ), ), ), const SizedBox(height: 20), // Tombol Jadwal Kotak Obat 2 dengan pengecekan akses ElevatedButton( onPressed: _canAccessKotak2 ? () { Navigator.push( context, MaterialPageRoute( builder: (context) => const JadwalKotakObat2Screen(), ), ); } : () => _showAccessDeniedDialog('Kotak Obat 2'), style: ElevatedButton.styleFrom( backgroundColor: _canAccessKotak2 ? const Color(0xFF3FA535) : Colors.grey, padding: const EdgeInsets.symmetric(vertical: 16), minimumSize: const Size.fromHeight(50), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text( 'Jadwal Kotak Obat 2', style: TextStyle( fontSize: 16, color: _canAccessKotak2 ? Colors.white : Colors.white70, ), ), ), ], ), ), // ======== TAMBAHAN CONTAINER SESUAI DESAIN ========= Container( margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 0), padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.grey.shade300, blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: Column( children: [ // Row Tombol Kotak 1 & Kotak 2 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Tombol Kotak 1 Expanded( child: Column( children: [ Text( "Tombol Kotak 1", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), const SizedBox(height: 6), ElevatedButton( onPressed: _toggleButton1, style: ElevatedButton.styleFrom( backgroundColor: button1 ? Colors.green : Colors.red, minimumSize: const Size(80, 36), padding: const EdgeInsets.symmetric( horizontal: 8, ), ), child: Text( button1 ? 'ON' : 'OFF', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ], ), ), const SizedBox(width: 16), // Tombol Kotak 2 Expanded( child: Column( children: [ Text( "Tombol Kotak 2", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), const SizedBox(height: 6), ElevatedButton( onPressed: _toggleButton2, style: ElevatedButton.styleFrom( backgroundColor: button2 ? Colors.green : Colors.red, minimumSize: const Size(80, 36), padding: const EdgeInsets.symmetric( horizontal: 8, ), ), child: Text( button2 ? 'ON' : 'OFF', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ], ), ), ], ), const SizedBox(height: 16), // Row Buzzer & Lampu (status read-only) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Buzzer Expanded( child: Column( children: [ Text( "Buzzer", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), const SizedBox(height: 6), Container( alignment: Alignment.center, decoration: BoxDecoration( color: buzzer ? Colors.green : Colors.red, borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.symmetric(vertical: 8), child: Text( buzzer ? 'ON' : 'OFF', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ], ), ), const SizedBox(width: 16), // Lampu Expanded( child: Column( children: [ Text( "Lampu", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), const SizedBox(height: 6), Container( alignment: Alignment.center, decoration: BoxDecoration( color: lampu ? Colors.green : Colors.red, borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.symmetric(vertical: 8), child: Text( lampu ? 'ON' : 'OFF', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ], ), ), ], ), const SizedBox(height: 20), // Info Text Container( width: double.infinity, padding: const EdgeInsets.symmetric( vertical: 16, horizontal: 20, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Text( "Info :\n$infoText", textAlign: TextAlign.center, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, height: 1.4, ), ), ), ], ), ), ], ), ), ); } }