// screens/control_screen.dart (Sudah Lengkap) import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../providers/schedule_provider.dart'; // Pastikan path benar class ControlScreen extends StatefulWidget { const ControlScreen({super.key}); @override _ControlScreenState createState() => _ControlScreenState(); } class _ControlScreenState extends State { // Hapus TextEditingController dan FormKey // final _startTimeController = TextEditingController(); // final _endTimeController = TextEditingController(); // final _formKey = GlobalKey(); // Gunakan state lokal untuk menyimpan waktu yang DIPILIH user (sebelum disimpan) TimeOfDay? _selectedStartTime; TimeOfDay? _selectedEndTime; // State lokal untuk menyimpan waktu dari provider (untuk tampilan jadwal saat ini) TimeOfDay? _fetchedStartTime; TimeOfDay? _fetchedEndTime; @override void initState() { super.initState(); print("ControlScreen: initState called."); WidgetsBinding.instance.addPostFrameCallback((_) { print("ControlScreen: Requesting fetchSchedule."); final provider = context.read(); provider.fetchSchedule().then((_) { if (mounted) { print( "ControlScreen: fetchSchedule completed. Updating local state."); // Set state lokal dari provider saat pertama kali load setState(() { _fetchedStartTime = provider.startTime; _fetchedEndTime = provider.endTime; // Juga set waktu terpilih awal agar tombol menampilkan nilai yang ada _selectedStartTime = provider.startTime; _selectedEndTime = provider.endTime; }); } }); }); } // Hapus dispose untuk controller // @override // void dispose() { // _startTimeController.dispose(); // _endTimeController.dispose(); // super.dispose(); // } // Fungsi untuk menampilkan Time Picker Future _selectTime(BuildContext context, bool isStartTime) async { print( "ControlScreen: Opening time picker for ${isStartTime ? 'start' : 'end'} time."); // Tentukan waktu awal untuk picker final TimeOfDay initialTime = isStartTime ? (_selectedStartTime ?? _fetchedStartTime ?? TimeOfDay.now()) : (_selectedEndTime ?? _fetchedEndTime ?? TimeOfDay( hour: (TimeOfDay.now().hour + 1) % 24, minute: TimeOfDay.now().minute)); // Default end time +1 jam final TimeOfDay? picked = await showTimePicker( context: context, initialTime: initialTime, helpText: isStartTime ? 'PILIH WAKTU MULAI' : 'PILIH WAKTU SELESAI', // Gunakan tema dari MaterialApp // builder: (context, child) { // return Theme( // data: Theme.of(context), // Menggunakan tema utama // child: child!, // ); // }, ); print("ControlScreen: Time picker closed. Picked: $picked"); if (picked != null) { setState(() { if (isStartTime) { _selectedStartTime = picked; } else { _selectedEndTime = picked; } }); } } // Format TimeOfDay ke string HH:mm (24 jam) String _formatTimeOfDay(TimeOfDay? time) { if (time == null) return 'Belum diatur'; final hour = time.hour.toString().padLeft(2, '0'); final minute = time.minute.toString().padLeft(2, '0'); return '$hour:$minute'; } // Fungsi untuk menyimpan jadwal Future _saveNewSchedule() async { print("ControlScreen: Save button pressed."); // Validasi sederhana: pastikan kedua waktu sudah dipilih if (_selectedStartTime == null || _selectedEndTime == null) { print("ControlScreen: Save aborted, start or end time not selected."); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Harap pilih waktu mulai dan selesai.'), backgroundColor: Colors.orangeAccent), ); return; } // Validasi tambahan (opsional): cek apakah waktu mulai sebelum waktu selesai final startTimeMinutes = _selectedStartTime!.hour * 60 + _selectedStartTime!.minute; final endTimeMinutes = _selectedEndTime!.hour * 60 + _selectedEndTime!.minute; if (startTimeMinutes >= endTimeMinutes) { print("ControlScreen: Save aborted, start time is not before end time."); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Waktu mulai harus sebelum waktu selesai.'), backgroundColor: Colors.orangeAccent), ); return; } final provider = context.read(); final success = await provider.saveSchedule(_selectedStartTime!, _selectedEndTime!); print("ControlScreen: saveSchedule result: $success"); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(success ? 'Jadwal berhasil disimpan!' : provider.errorMessage ?? 'Gagal menyimpan jadwal.'), backgroundColor: success ? Colors.green[600] : Colors.redAccent, behavior: SnackBarBehavior.floating, ), ); if (success) { // Update state fetched time setelah berhasil simpan setState(() { _fetchedStartTime = _selectedStartTime; _fetchedEndTime = _selectedEndTime; }); } } } @override Widget build(BuildContext context) { print("ControlScreen: Building UI."); return Scaffold( body: Padding( padding: const EdgeInsets.all(16.0), child: Consumer( builder: (context, provider, child) { // Sinkronisasi state lokal jika provider berubah (misal setelah fetch awal) WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted && (_fetchedStartTime != provider.startTime || _fetchedEndTime != provider.endTime)) { print( "ControlScreen Consumer: Provider state changed (fetch), updating local state."); setState(() { _fetchedStartTime = provider.startTime; _fetchedEndTime = provider.endTime; // Set juga selected time jika belum pernah dipilih user _selectedStartTime ??= provider.startTime; _selectedEndTime ??= provider.endTime; }); } }); print( "ControlScreen Consumer: Building content. isLoading: ${provider.isLoading}, error: ${provider.errorMessage}"); // Logika tampilan jadwal saat ini (sama seperti sebelumnya) String currentScheduleDisplay; bool isScheduleSet = _fetchedStartTime != null && _fetchedEndTime != null; bool hasSchedulePassedToday = false; if (isScheduleSet) { final now = TimeOfDay.now(); final nowInMinutes = now.hour * 60 + now.minute; final endTimeInMinutes = _fetchedEndTime!.hour * 60 + _fetchedEndTime!.minute; if (nowInMinutes > endTimeInMinutes) { hasSchedulePassedToday = true; } } if (!isScheduleSet || (isScheduleSet && hasSchedulePassedToday)) { currentScheduleDisplay = "Belum diatur atau jadwal hari ini telah lewat."; } else { currentScheduleDisplay = '${_formatTimeOfDay(_fetchedStartTime)} - ${_formatTimeOfDay(_fetchedEndTime)}'; } return RefreshIndicator( onRefresh: () async { print("ControlScreen: Refresh requested."); await provider.fetchSchedule(); // Update state lokal setelah refresh setState(() { _fetchedStartTime = provider.startTime; _fetchedEndTime = provider.endTime; _selectedStartTime = provider.startTime; // Reset pilihan user ke data terbaru _selectedEndTime = provider.endTime; }); }, color: Colors.teal[600]!, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), // Hapus Form widget // child: Form( // key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Jadwal Nonaktif CCTV', style: Theme.of(context) .textTheme .headlineSmall ?.copyWith( fontWeight: FontWeight.bold, color: Colors.teal[800]), ), const SizedBox(height: 8), Text( 'Atur rentang waktu kapan perangkat CCTV tidak akan merekam atau mengirim data.', style: Theme.of(context) .textTheme .bodyMedium ?.copyWith(color: Colors.grey[700]), ), const SizedBox(height: 25), // Tampilkan jadwal saat ini Card( /* ... (UI Card sama, menampilkan currentScheduleDisplay) ... */ child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row(/* ... (Header Card sama) ... */), const SizedBox(height: 10), if (provider.isLoading && !isScheduleSet) const Padding( padding: EdgeInsets.symmetric(vertical: 10.0), child: Center( child: CircularProgressIndicator( strokeWidth: 3, color: Colors.teal)), ) else if (provider.errorMessage != null && !isScheduleSet) Text(provider.errorMessage!, style: const TextStyle(color: Colors.redAccent)) else Text( currentScheduleDisplay, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: (isScheduleSet && !hasSchedulePassedToday) ? Colors.black87 : Colors.grey[600]), ), ], ), ), ), const SizedBox(height: 25), // Tombol untuk memilih waktu (kembali ke OutlinedButton) Text( 'Setel Jadwal Baru:', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, color: Colors.teal[700]), ), const SizedBox(height: 12), Row( children: [ Expanded( child: OutlinedButton.icon( icon: const Icon(Icons.timer_outlined, size: 18), label: Text(_selectedStartTime != null ? _formatTimeOfDay(_selectedStartTime) : 'Pilih Mulai'), onPressed: () => _selectTime( context, true), // Panggil _selectTime style: OutlinedButton.styleFrom( foregroundColor: Colors.teal, side: BorderSide(color: Colors.teal[200]!), padding: const EdgeInsets.symmetric(vertical: 12)), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Text("-", style: TextStyle( fontSize: 24, color: Colors.grey[600])), ), Expanded( child: OutlinedButton.icon( icon: const Icon(Icons.timer_off_outlined, size: 18), label: Text(_selectedEndTime != null ? _formatTimeOfDay(_selectedEndTime) : 'Pilih Selesai'), onPressed: () => _selectTime( context, false), // Panggil _selectTime style: OutlinedButton.styleFrom( foregroundColor: Colors.teal, side: BorderSide(color: Colors.teal[200]!), padding: const EdgeInsets.symmetric(vertical: 12)), ), ), ], ), const SizedBox(height: 30), // Tombol Simpan Center( child: provider.isLoading ? CircularProgressIndicator(color: Colors.teal[600]) : ElevatedButton.icon( icon: const Icon(Icons.save_alt_outlined, size: 20), label: const Text('Simpan Jadwal'), onPressed: (_selectedStartTime == null || _selectedEndTime == null) ? null // Disable jika waktu belum dipilih : _saveNewSchedule, // Panggil fungsi simpan ), ), // Tampilkan error saving jika ada if (provider.errorMessage != null && !provider.isLoading && (_selectedStartTime != null || _selectedEndTime != null)) // Tampilkan jika ada error DAN user sudah mencoba memilih Padding( padding: const EdgeInsets.only(top: 15.0), child: Center( child: Text(provider.errorMessage!, style: const TextStyle(color: Colors.redAccent), textAlign: TextAlign.center), ), ), const SizedBox(height: 20), ], ), // ), // Tutup Form (dihapus) ), ); }, ), ), ); } } // class ControlScreen extends StatefulWidget { // const ControlScreen({super.key}); // @override // _ControlScreenState createState() => _ControlScreenState(); // } // class _ControlScreenState extends State { // TimeOfDay? _selectedStartTime; // TimeOfDay? _selectedEndTime; // @override // void initState() { // super.initState(); // print("ControlScreen: initState called."); // WidgetsBinding.instance.addPostFrameCallback((_) { // print("ControlScreen: Requesting fetchSchedule."); // final provider = context.read(); // provider.fetchSchedule().then((_) { // if (mounted) { // print( // "ControlScreen: fetchSchedule completed. Updating local state."); // setState(() { // _selectedStartTime = provider.startTime; // _selectedEndTime = provider.endTime; // }); // } // }); // }); // } // Future _selectTime( // BuildContext context, TimeOfDay? initialTime) async { // print("ControlScreen: Opening time picker."); // final TimeOfDay? picked = await showTimePicker( // context: context, // initialTime: initialTime ?? TimeOfDay.now(), // helpText: 'PILIH WAKTU', // Ubah teks helper // builder: (context, child) { // // Gunakan tema dari MaterialApp // return child!; // // Atau override tema spesifik di sini jika perlu // // return Theme( // // data: Theme.of(context).copyWith( // // timePickerTheme: TimePickerThemeData(...) // Override spesifik // // ), // // child: child!, // // ); // }, // ); // print("ControlScreen: Time picker closed. Picked: $picked"); // return picked; // } // String _formatTimeOfDay(TimeOfDay? time) { // if (time == null) return 'Belum diatur'; // final now = DateTime.now(); // final dt = DateTime(now.year, now.month, now.day, time.hour, time.minute); // return DateFormat('HH:mm', 'id_ID') // .format(dt); // Gunakan format 24 jam agar lebih jelas // } // @override // Widget build(BuildContext context) { // print("ControlScreen: Building UI."); // return Scaffold( // // AppBar tidak perlu karena sudah ada di Dashboard // body: Padding( // padding: const EdgeInsets.all(16.0), // child: Consumer( // builder: (context, provider, child) { // // Sinkronisasi state lokal // WidgetsBinding.instance.addPostFrameCallback((_) { // if (mounted && // (_selectedStartTime != provider.startTime || // _selectedEndTime != provider.endTime)) { // print( // "ControlScreen Consumer: Provider state changed, updating local state."); // setState(() { // _selectedStartTime = provider.startTime; // _selectedEndTime = provider.endTime; // }); // } // }); // print( // "ControlScreen Consumer: Building content. isLoading: ${provider.isLoading}, error: ${provider.errorMessage}"); // return RefreshIndicator( // // Tambahkan RefreshIndicator // onRefresh: () async { // print("ControlScreen: Refresh requested."); // await provider.fetchSchedule(); // }, // color: Colors.teal, // child: SingleChildScrollView( // physics: // const AlwaysScrollableScrollPhysics(), // Agar bisa refresh walau konten pendek // child: Column( // crossAxisAlignment: // CrossAxisAlignment.stretch, // Buat elemen memenuhi lebar // children: [ // Text( // 'Jadwal Nonaktif CCTV', // style: Theme.of(context) // .textTheme // .headlineSmall // ?.copyWith( // fontWeight: FontWeight.bold, // color: Colors.teal[800]), // ), // const SizedBox(height: 8), // Text( // 'Atur rentang waktu kapan perangkat CCTV tidak akan merekam atau mengirim data.', // style: Theme.of(context) // .textTheme // .bodyMedium // ?.copyWith(color: Colors.grey[700]), // ), // const SizedBox(height: 25), // // Tampilkan jadwal saat ini // Card( // child: Padding( // padding: const EdgeInsets.all(16.0), // child: Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Row( // // Tambahkan ikon dan tombol refresh // mainAxisAlignment: MainAxisAlignment.spaceBetween, // children: [ // Text( // 'Jadwal Saat Ini:', // style: Theme.of(context) // .textTheme // .titleMedium // ?.copyWith( // fontWeight: FontWeight.w600, // color: Colors.teal[700]), // ), // // Tombol refresh kecil // IconButton( // icon: Icon(Icons.refresh, // color: Colors.grey[500]), // tooltip: 'Muat Ulang Jadwal', // iconSize: 20, // onPressed: provider.isLoading // ? null // : () async { // print( // "ControlScreen: Inline refresh pressed."); // await provider.fetchSchedule(); // }, // ) // ], // ), // const SizedBox(height: 10), // if (provider.isLoading && // _selectedStartTime == null) // const Padding( // padding: EdgeInsets.symmetric(vertical: 10.0), // child: Center( // child: CircularProgressIndicator( // strokeWidth: 3, color: Colors.teal)), // ) // else if (provider.errorMessage != null && // _selectedStartTime == null) // Text(provider.errorMessage!, // style: // const TextStyle(color: Colors.redAccent)) // else // Row( // mainAxisAlignment: // MainAxisAlignment.spaceAround, // Beri jarak // children: [ // _buildTimeDisplay( // 'Mulai Nonaktif', _selectedStartTime), // Icon(Icons.arrow_forward, // color: Colors // .grey[400]), // Panah penanda rentang // _buildTimeDisplay( // 'Selesai Nonaktif', _selectedEndTime), // ], // ), // ], // ), // ), // ), // const SizedBox(height: 25), // // Tombol untuk memilih waktu // Text( // 'Setel Jadwal Baru:', // style: Theme.of(context).textTheme.titleMedium?.copyWith( // fontWeight: FontWeight.w600, color: Colors.teal[700]), // ), // const SizedBox(height: 10), // Row( // children: [ // Expanded( // child: OutlinedButton.icon( // icon: const Icon(Icons.timer_outlined, // size: 18), // Kecilkan ikon // label: Text(_selectedStartTime != null // ? _formatTimeOfDay(_selectedStartTime) // : 'Pilih Mulai'), // onPressed: () async { // final time = await _selectTime( // context, _selectedStartTime); // if (time != null) { // setState(() { // _selectedStartTime = time; // }); // } // }, // style: OutlinedButton.styleFrom( // foregroundColor: Colors.teal, // side: BorderSide(color: Colors.teal[200]!), // padding: const EdgeInsets.symmetric( // vertical: 12) // Sesuaikan padding // ), // ), // ), // const SizedBox(width: 10), // Expanded( // child: OutlinedButton.icon( // icon: const Icon(Icons.timer_off_outlined, // size: 18), // Kecilkan ikon // label: Text(_selectedEndTime != null // ? _formatTimeOfDay(_selectedEndTime) // : 'Pilih Selesai'), // onPressed: () async { // final time = // await _selectTime(context, _selectedEndTime); // if (time != null) { // setState(() { // _selectedEndTime = time; // }); // } // }, // style: OutlinedButton.styleFrom( // foregroundColor: Colors.teal, // side: BorderSide(color: Colors.teal[200]!), // padding: const EdgeInsets.symmetric( // vertical: 12) // Sesuaikan padding // ), // ), // ), // ], // ), // const SizedBox( // height: 30), // Beri jarak lebih sebelum tombol simpan // // Tombol Simpan // Center( // child: provider.isLoading // ? const CircularProgressIndicator(color: Colors.teal) // : ElevatedButton.icon( // icon: // const Icon(Icons.save_alt_outlined, size: 20), // label: const Text('Simpan Jadwal', // style: TextStyle(fontSize: 16)), // style: ElevatedButton.styleFrom( // padding: const EdgeInsets.symmetric( // vertical: 14, // horizontal: // 30)), // Buat tombol lebih besar // onPressed: (_selectedStartTime == null || // _selectedEndTime == null) // ? null // Disable jika waktu belum dipilih // : () async { // print( // "ControlScreen: Save button pressed."); // final success = await context // .read() // .saveSchedule(_selectedStartTime!, // _selectedEndTime!); // print( // "ControlScreen: saveSchedule result: $success"); // if (mounted) { // ScaffoldMessenger.of(context) // .showSnackBar( // SnackBar( // content: Text(success // ? 'Jadwal berhasil disimpan!' // : context // .read< // ScheduleProvider>() // .errorMessage ?? // 'Gagal menyimpan jadwal.'), // backgroundColor: success // ? Colors.green[600] // : Colors.redAccent, // behavior: SnackBarBehavior // .floating, // SnackBar mengambang // ), // ); // } // }, // ), // ), // // Tampilkan error saving jika ada // if (provider.errorMessage != null && // !provider.isLoading && // _selectedStartTime != // null) // Tampilkan hanya jika user mencoba menyimpan // Padding( // padding: const EdgeInsets.only(top: 15.0), // child: Center( // child: Text(provider.errorMessage!, // style: const TextStyle(color: Colors.redAccent), // textAlign: TextAlign.center), // ), // ), // const SizedBox(height: 20), // Padding bawah // ], // ), // ), // ); // }, // ), // ), // ); // } // // Helper widget untuk menampilkan waktu // Widget _buildTimeDisplay(String label, TimeOfDay? time) { // return Column( // crossAxisAlignment: CrossAxisAlignment.center, // children: [ // Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 12)), // const SizedBox(height: 2), // Text( // _formatTimeOfDay(time), // style: const TextStyle( // fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87), // ), // ], // ); // } // }