// lib/app/widgets/modal_schedule_add_edit.dart import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../modules/schedule/controllers/schedule_controller.dart'; class ModalScheduleAddEdit extends StatefulWidget { final PatientSchedule? schedule; final bool isEdit; const ModalScheduleAddEdit({super.key, this.schedule, this.isEdit = false}); @override State createState() => _ModalScheduleAddEditState(); } class _ModalScheduleAddEditState extends State { final _formKey = GlobalKey(); String? _selectedPatient; String? _selectedTime; // Pagi, Siang, Sore, Malam late TextEditingController _medicineController; late TextEditingController _fluidController; // Dummy data pasien final List> _patients = [ { 'id': '1', 'name': 'Mrs. Shinta', 'room': 'Ruang Mawar 002', 'device': 'SI001', }, { 'id': '2', 'name': 'Mr. Parjo', 'room': 'Ruang Melati 001', 'device': 'SI002', }, { 'id': '3', 'name': 'Mrs. Siti', 'room': 'Ruang Anggrek 003', 'device': 'SI003', }, { 'id': '4', 'name': 'Mr. Budi', 'room': 'Ruang Dahlia 004', 'device': 'SI004', }, ]; // Waktu final List _times = ['Pagi', 'Siang', 'Sore', 'Malam']; @override void initState() { super.initState(); _medicineController = TextEditingController( text: widget.schedule?.medicineTime ?? '', ); _fluidController = TextEditingController( text: widget.schedule?.fluidTime ?? '', ); if (widget.isEdit && widget.schedule != null) { _selectedPatient = widget.schedule!.id; _selectedTime = 'Pagi'; // Default atau bisa diambil dari schedule } } @override void dispose() { _medicineController.dispose(); _fluidController.dispose(); super.dispose(); } Map? get _selectedPatientData { if (_selectedPatient == null) return null; return _patients.firstWhere((p) => p['id'] == _selectedPatient); } @override Widget build(BuildContext context) { return Dialog( backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40), child: Container( constraints: const BoxConstraints(maxWidth: 500), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Header Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF0091EA), Color(0xFF0277BD)], ), borderRadius: const BorderRadius.only( topLeft: Radius.circular(24), topRight: Radius.circular(24), ), ), child: Row( children: [ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.calendar_today, color: Colors.white, size: 24, ), ), const SizedBox(width: 16), Expanded( child: Text( widget.isEdit ? 'Edit Jadwal' : 'Tambah Jadwal Baru', style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), IconButton( onPressed: () => Get.back(), icon: const Icon(Icons.close, color: Colors.white), splashRadius: 20, ), ], ), ), // Form Content Flexible( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 1. Pilih Pasien (Search Dropdown) _buildLabel('Pilih Pasien', Icons.person), const SizedBox(height: 8), _buildPatientDropdown(), // Info Pasien Terpilih if (_selectedPatientData != null) ...[ const SizedBox(height: 12), _buildSelectedPatientInfo(), ], const SizedBox(height: 20), // 2. Pilih Waktu (Pagi, Siang, Sore, Malam) _buildLabel('Pilih Waktu', Icons.access_time), const SizedBox(height: 8), _buildTimeDropdown(), const SizedBox(height: 20), // 3. Informasi Obat _buildLabel('Informasi Obat', Icons.medication), const SizedBox(height: 8), _buildTextField( controller: _medicineController, hint: 'Contoh: Amoxicillin 3 ml', icon: Icons.medical_services_outlined, validator: (value) { if (value == null || value.isEmpty) { return 'Informasi obat harus diisi'; } return null; }, ), const SizedBox(height: 20), // 4. Detail Cairan & Tetes _buildLabel('Detail Cairan & Tetes', Icons.water_drop), const SizedBox(height: 8), _buildTextField( controller: _fluidController, hint: 'Contoh: Mnt 30 tetes/jam', icon: Icons.opacity, validator: (value) { if (value == null || value.isEmpty) { return 'Detail cairan harus diisi'; } return null; }, ), ], ), ), ), ), // Bottom Buttons Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24), ), ), child: Row( children: [ Expanded( child: OutlinedButton( onPressed: () => Get.back(), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), side: BorderSide(color: Colors.grey.shade300, width: 2), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text( 'Batal', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.grey[700], ), ), ), ), const SizedBox(width: 12), Expanded( child: Container( decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF0091EA), Color(0xFF0277BD)], ), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: const Color(0xFF0091EA).withOpacity(0.4), blurRadius: 8, offset: const Offset(0, 4), ), ], ), child: ElevatedButton( onPressed: _handleSave, style: ElevatedButton.styleFrom( backgroundColor: Colors.transparent, shadowColor: Colors.transparent, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text( widget.isEdit ? 'Simpan' : 'Tambah', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ), ), ], ), ), ], ), ), ); } Widget _buildLabel(String text, IconData icon) { return Row( children: [ Icon(icon, size: 20, color: const Color(0xFF0091EA)), const SizedBox(width: 8), Text( text, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF424242), ), ), ], ); } Widget _buildPatientDropdown() { return DropdownButtonFormField( value: _selectedPatient, decoration: InputDecoration( hintText: 'Cari dan pilih pasien...', hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14), filled: true, fillColor: Colors.grey[50], border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFF0091EA), width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.redAccent), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), ), icon: const Icon(Icons.arrow_drop_down, color: Color(0xFF0091EA)), isExpanded: true, items: _patients.map((patient) { return DropdownMenuItem( value: patient['id'], child: Text( patient['name']!, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF424242), ), ), ); }).toList(), onChanged: (value) { setState(() { _selectedPatient = value; }); }, validator: (value) { if (value == null) { return 'Pilih pasien terlebih dahulu'; } return null; }, ); } Widget _buildSelectedPatientInfo() { final patient = _selectedPatientData!; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0091EA).withOpacity(0.05), borderRadius: BorderRadius.circular(12), border: Border.all( color: const Color(0xFF0091EA).withOpacity(0.2), width: 1, ), ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFF0091EA).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.person, color: Color(0xFF0091EA), size: 20, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ===== NAMA ===== Text( patient['name']!, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Color(0xFF424242), ), ), const SizedBox(height: 6), // ===== ROOM + DEVICE ID ===== Row( children: [ // ROOM Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4, ), decoration: BoxDecoration( color: const Color(0xFFE3F2FD), borderRadius: BorderRadius.circular(6), ), child: Text( patient['room']!, style: const TextStyle( fontSize: 11, color: Color(0xFF2196F3), fontWeight: FontWeight.w600, ), ), ), const SizedBox(width: 8), // DEVICE ID Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4, ), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 6, height: 6, decoration: const BoxDecoration( color: Color(0xFF4CAF50), shape: BoxShape.circle, ), ), const SizedBox(width: 6), Text( patient['device']!, style: TextStyle( fontSize: 11, color: Colors.grey[700], fontWeight: FontWeight.w600, ), ), ], ), ), ], ), ], ), ), ], ), ); } Widget _buildTimeDropdown() { return DropdownButtonFormField( value: _selectedTime, decoration: InputDecoration( hintText: 'Pilih waktu...', hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14), filled: true, fillColor: Colors.grey[50], border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFF0091EA), width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.redAccent), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), ), icon: const Icon(Icons.arrow_drop_down, color: Color(0xFF0091EA)), isExpanded: true, items: _times.map((time) { IconData icon; Color color; switch (time) { case 'Pagi': icon = Icons.wb_sunny; color = Colors.orange; break; case 'Siang': icon = Icons.wb_sunny_outlined; color = Colors.amber; break; case 'Sore': icon = Icons.wb_twilight; color = Colors.deepOrange; break; case 'Malam': icon = Icons.nights_stay; color = Colors.indigo; break; default: icon = Icons.access_time; color = Colors.grey; } return DropdownMenuItem( value: time, child: Row( children: [ Icon(icon, size: 18, color: color), const SizedBox(width: 12), Text( time, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF424242), ), ), ], ), ); }).toList(), onChanged: (value) { setState(() { _selectedTime = value; }); }, validator: (value) { if (value == null) { return 'Pilih waktu terlebih dahulu'; } return null; }, ); } Widget _buildTextField({ required TextEditingController controller, required String hint, required IconData icon, String? Function(String?)? validator, }) { return TextFormField( controller: controller, validator: validator, decoration: InputDecoration( hintText: hint, hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14), filled: true, fillColor: Colors.grey[50], border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFF0091EA), width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.redAccent), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.redAccent, width: 2), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), ), ); } void _handleSave() { if (_formKey.currentState!.validate()) { final controller = Get.find(); final selectedPatientData = _patients.firstWhere( (p) => p['id'] == _selectedPatient, ); if (widget.isEdit && widget.schedule != null) { controller.updateSchedule( widget.schedule!.id, PatientSchedule( id: widget.schedule!.id, name: selectedPatientData['name']!, room: selectedPatientData['room']!, deviceId: selectedPatientData['device']!, medicineType: 'Obat & Dosis', fluidType: 'Cairan & Tetes', medicineTime: _medicineController.text.trim(), fluidTime: _fluidController.text.trim(), ), ); Get.back(); Get.snackbar( 'Berhasil', 'Jadwal $_selectedTime berhasil diperbarui', snackPosition: SnackPosition.BOTTOM, backgroundColor: const Color(0xFF4CAF50), colorText: Colors.white, margin: const EdgeInsets.all(16), icon: const Icon(Icons.check_circle, color: Colors.white), duration: const Duration(seconds: 2), ); } else { controller.addSchedule( PatientSchedule( id: DateTime.now().millisecondsSinceEpoch.toString(), name: selectedPatientData['name']!, room: selectedPatientData['room']!, deviceId: selectedPatientData['device']!, medicineType: 'Obat & Dosis', fluidType: 'Cairan & Tetes', medicineTime: _medicineController.text.trim(), fluidTime: _fluidController.text.trim(), ), ); Get.back(); Get.snackbar( 'Berhasil', 'Jadwal $_selectedTime berhasil ditambahkan', snackPosition: SnackPosition.BOTTOM, backgroundColor: const Color(0xFF4CAF50), colorText: Colors.white, margin: const EdgeInsets.all(16), icon: const Icon(Icons.check_circle, color: Colors.white), duration: const Duration(seconds: 2), ); } } } }