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; late TextEditingController _emailController; late TextEditingController _passwordController; bool _isSaving = false; bool _obscurePassword = true; @override void initState() { super.initState(); _initControllers(); } void _initControllers() { _nameController = TextEditingController(text: widget.patient?.name ?? ''); _roomController = TextEditingController(text: widget.patient?.room ?? ''); _deviceIdController = TextEditingController(text: widget.patient?.deviceId ?? ''); _guardianController = TextEditingController(text: widget.patient?.guardian ?? ''); _emailController = TextEditingController(text: widget.patient?.email ?? ''); _passwordController = TextEditingController(); } @override void dispose() { _nameController.dispose(); _roomController.dispose(); _deviceIdController.dispose(); _guardianController.dispose(); _emailController.dispose(); _passwordController.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: _dialogDecoration(), child: Column( mainAxisSize: MainAxisSize.min, children: [ _buildHeader(), _buildForm(), _buildFooter(), ], ), ), ); } BoxDecoration _dialogDecoration() { return BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 20, offset: const Offset(0, 10), ), ], ); } Widget _buildHeader() { return Container( padding: const EdgeInsets.all(24), decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF0091EA), Color(0xFF0277BD)], ), borderRadius: BorderRadius.only( topLeft: Radius.circular(24), topRight: Radius.circular(24), ), ), child: Row( children: [ _buildHeaderIcon(), 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: _isSaving ? null : () => Get.back(), icon: const Icon(Icons.close, color: Colors.white), splashRadius: 20, ), ], ), ); } Widget _buildHeaderIcon() { return Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Icon( widget.isEdit ? Icons.edit : Icons.person_add, color: Colors.white, size: 24, ), ); } Widget _buildForm() { return Flexible( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildNameField(), const SizedBox(height: 20), _buildGuardianField(), const SizedBox(height: 20), _buildEmailField(), if (!widget.isEdit) ...[ const SizedBox(height: 20), _buildPasswordField(), ], const SizedBox(height: 20), _buildRoomField(), const SizedBox(height: 20), _buildDeviceIdField(), ], ), ), ), ); } Widget _buildNameField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabel('Nama Pasien', Icons.person), const SizedBox(height: 8), _buildTextField( controller: _nameController, hint: 'Contoh: Mrs. Shinta', icon: Icons.person_outline, validator: (value) => value?.isEmpty ?? true ? 'Wajib diisi' : null, ), ], ); } Widget _buildGuardianField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabel('Nama Wali', Icons.family_restroom), const SizedBox(height: 8), _buildTextField( controller: _guardianController, hint: 'Contoh: Budi Santoso', icon: Icons.family_restroom_outlined, validator: (value) => value?.isEmpty ?? true ? 'Wajib diisi' : null, ), ], ); } Widget _buildEmailField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabel('Email Wali', Icons.email), const SizedBox(height: 8), _buildTextField( controller: _emailController, hint: 'Contoh: wali@example.com', icon: Icons.email_outlined, keyboardType: TextInputType.emailAddress, enabled: !widget.isEdit, validator: (value) { if (widget.isEdit) return null; if (value?.isEmpty ?? true) return 'Wajib diisi'; if (!GetUtils.isEmail(value!)) return 'Email tidak valid'; return null; }, ), if (widget.isEdit) Padding( padding: const EdgeInsets.only(top: 4), child: Text( 'Email tidak dapat diubah', style: TextStyle( fontSize: 12, color: Colors.orange[700], fontStyle: FontStyle.italic, ), ), ), ], ); } Widget _buildPasswordField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabel('Password', Icons.lock), const SizedBox(height: 8), _buildTextField( controller: _passwordController, hint: 'Minimal 6 karakter', icon: Icons.lock_outline, obscureText: _obscurePassword, suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, color: Colors.grey[600], ), onPressed: () => setState(() => _obscurePassword = !_obscurePassword), ), validator: (value) { if (value?.isEmpty ?? true) return 'Wajib diisi'; if (value!.length < 6) return 'Minimal 6 karakter'; return null; }, ), ], ); } Widget _buildRoomField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabel('Ruangan', Icons.meeting_room), const SizedBox(height: 8), _buildTextField( controller: _roomController, hint: 'Contoh: Ruang Mawar', icon: Icons.meeting_room_outlined, validator: (value) => value?.isEmpty ?? true ? 'Wajib diisi' : null, ), const SizedBox(height: 4), Text( 'ID otomatis: "Ruang Mawar" → ruang_mawar', style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ], ); } Widget _buildDeviceIdField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabel('Device ID', Icons.devices), const SizedBox(height: 8), _buildTextField( controller: _deviceIdController, hint: 'Contoh: INF-ICU-001', icon: Icons.router_outlined, validator: (value) { if (value?.isEmpty ?? true) return 'Wajib diisi'; if (!RegExp(r'^[A-Z]+-[A-Z]+-\d{3}$').hasMatch(value!)) { return 'Format: INF-ROOM-XXX'; } return null; }, ), const SizedBox(height: 4), Text( 'Format: INF-ROOM-XXX', style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ], ); } Widget _buildFooter() { return 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: _buildCancelButton()), const SizedBox(width: 12), Expanded(child: _buildSaveButton()), ], ), ); } Widget _buildCancelButton() { return OutlinedButton( onPressed: _isSaving ? null : () => 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: _isSaving ? Colors.grey[400] : Colors.grey[700], ), ), ); } Widget _buildSaveButton() { return 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: _isSaving ? null : _handleSave, style: ElevatedButton.styleFrom( backgroundColor: Colors.transparent, shadowColor: Colors.transparent, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), child: _isSaving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2), ) : 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, bool obscureText = false, Widget? suffixIcon, TextInputType? keyboardType, bool enabled = true, }) { return TextFormField( controller: controller, validator: validator, enabled: !_isSaving && enabled, obscureText: obscureText, keyboardType: keyboardType, decoration: InputDecoration( hintText: hint, hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14), filled: true, fillColor: enabled ? Colors.grey[50] : Colors.grey[200], suffixIcon: suffixIcon, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), disabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), 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), ), ); } Future _handleSave() async { if (!_formKey.currentState!.validate()) return; setState(() => _isSaving = true); final controller = Get.find(); try { if (widget.isEdit && widget.patient != null) { await controller.updatePatient( patientId: widget.patient!.id, patientName: _nameController.text.trim(), guardianName: _guardianController.text.trim(), roomName: _roomController.text.trim(), deviceId: _deviceIdController.text.trim(), email: _emailController.text.trim(), newPassword: _passwordController.text.isNotEmpty ? _passwordController.text.trim() : null, ); } else { await controller.addPatient( patientName: _nameController.text.trim(), guardianName: _guardianController.text.trim(), roomName: _roomController.text.trim(), deviceId: _deviceIdController.text.trim(), email: _emailController.text.trim(), password: _passwordController.text.trim(), ); } } catch (e) { setState(() => _isSaving = false); } } }