// lib/app/widgets/modal_patient_add_edit.dart import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../modules/profile/controllers/profile_controller.dart'; class ModalPatientAddEdit extends StatefulWidget { final Patient? patient; final bool isEdit; const ModalPatientAddEdit({ super.key, this.patient, this.isEdit = false, }); @override State createState() => _ModalPatientAddEditState(); } class _ModalPatientAddEditState extends State { final _formKey = GlobalKey(); late TextEditingController _nameController; late TextEditingController _roomController; late TextEditingController _deviceIdController; late TextEditingController _guardianController; @override void initState() { super.initState(); _nameController = TextEditingController( text: widget.patient?.name ?? '', ); _roomController = TextEditingController( text: widget.patient?.room ?? '', ); _deviceIdController = TextEditingController( text: widget.patient?.deviceId ?? '', ); _guardianController = TextEditingController( text: widget.patient?.guardian ?? '', ); } @override void dispose() { _nameController.dispose(); _roomController.dispose(); _deviceIdController.dispose(); _guardianController.dispose(); super.dispose(); } @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.person_add, color: Colors.white, size: 24, ), ), const SizedBox(width: 16), Expanded( child: Text( widget.isEdit ? 'Edit Pasien' : 'Tambah Pasien 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: [ // Nama Pasien _buildLabel('Nama Pasien', Icons.person), const SizedBox(height: 8), _buildTextField( controller: _nameController, hint: 'Contoh: Mrs. Shinta', icon: Icons.person_outline, validator: (value) { if (value == null || value.isEmpty) { return 'Nama pasien harus diisi'; } return null; }, ), const SizedBox(height: 20), // Ruangan _buildLabel('Ruangan', Icons.meeting_room), const SizedBox(height: 8), _buildTextField( controller: _roomController, hint: 'Contoh: Ruang Mawar 002', icon: Icons.meeting_room_outlined, validator: (value) { if (value == null || value.isEmpty) { return 'Ruangan harus diisi'; } return null; }, ), const SizedBox(height: 20), // Device ID _buildLabel('Device ID', Icons.devices), const SizedBox(height: 8), _buildTextField( controller: _deviceIdController, hint: 'Contoh: SI001', icon: Icons.router_outlined, validator: (value) { if (value == null || value.isEmpty) { return 'Device ID harus diisi'; } return null; }, ), const SizedBox(height: 20), // Wali Pasien _buildLabel('Wali Pasien', Icons.family_restroom), const SizedBox(height: 8), _buildTextField( controller: _guardianController, hint: 'Contoh: Budi Santoso', icon: Icons.family_restroom_outlined, validator: (value) { if (value == null || value.isEmpty) { return 'Wali pasien 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 _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(); if (widget.isEdit && widget.patient != null) { // Update existing patient controller.updatePatient( widget.patient!.id, Patient( id: widget.patient!.id, name: _nameController.text.trim(), room: _roomController.text.trim(), deviceId: _deviceIdController.text.trim(), guardian: _guardianController.text.trim(), ), ); Get.back(); Get.snackbar( 'Berhasil', 'Data pasien 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 { // Add new patient controller.addPatient( Patient( id: DateTime.now().millisecondsSinceEpoch.toString(), name: _nameController.text.trim(), room: _roomController.text.trim(), deviceId: _deviceIdController.text.trim(), guardian: _guardianController.text.trim(), ), ); Get.back(); Get.snackbar( 'Berhasil', 'Pasien baru 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), ); } } } }