import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class InsuranceScreen extends StatefulWidget { final Map? apiData; final Function(bool) onClose; final Function(Map) onSaveInsurance; final Map? selectedItem; final String? loginUser; InsuranceScreen({ required this.onClose, required this.apiData, required this.onSaveInsurance, required this.selectedItem,required this.loginUser}); @override _InsuranceScreenState createState() => _InsuranceScreenState(); } class _InsuranceScreenState extends State { final GlobalKey _formKey = GlobalKey(); Map selectedValues = {}; final FocusNode _tripTypeFocusNode = FocusNode(); final FocusNode _hotelNameFocusNode = FocusNode(); final FocusNode _fromFocusNode = FocusNode(); final FocusNode _dateFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _startdateController = TextEditingController(); late TextEditingController _endDateController = TextEditingController(); late TextEditingController _insuranceCommentsController = TextEditingController(); bool _isHotelNameFocused = false; bool _dateFocus = false; bool _commentsFocus = false; String? selectedTripType; String? selectedInsuranceType; Map errorMessages = {}; Map get InsuranceData{ Map data = { "type_of_insurance": selectedInsuranceType, "start_date": _startdateController.text, "end_date": _endDateController.text, "comments": _insuranceCommentsController.text, "created_by": widget.loginUser, "updated_by": widget.loginUser, }; if (widget.selectedItem != null) { if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; } else if (widget.selectedItem?["insurance_id"] != null && widget.selectedItem?["insurance_id"] != 0) { data["insurance_id"] = widget.selectedItem!["insurance_id"]; } } return data; } @override void initState() { super.initState(); _hotelNameFocusNode.addListener(() { setState(() {_isHotelNameFocused = _hotelNameFocusNode.hasFocus;});}); _dateFocusNode.addListener(() { setState(() {_dateFocus = _fromFocusNode.hasFocus;});}); _commentsFocusNode.addListener(() { setState(() {_commentsFocus = _commentsFocusNode.hasFocus;});}); _insuranceCommentsController = TextEditingController(text: widget.selectedItem?["comments"] ?? ""); _startdateController = TextEditingController(text: widget.selectedItem?["start_date"] ?? ""); _endDateController = TextEditingController(text: widget.selectedItem?['end_date'] ?? ""); // Set the selected value if available if (widget.selectedItem != null && widget.selectedItem!["type_of_insurance"] != null) { selectedInsuranceType = widget.selectedItem!["type_of_insurance"].toString(); } } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = ["type_of_insurance", "start_date","end_date"]; // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { errorMessages[field] = "Required"; } } return errorMessages.isEmpty; // Valid if there are no errors } void handleSave(){ print( "Handle Save InsuranceData $InsuranceData"); Map data = InsuranceData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails }else { widget.onSaveInsurance(InsuranceData); } widget.onClose(false);// Close screen after saving } DateTime? _parseDate(String date) { try { return DateFormat("yyyy-MM-dd").parse(date); // Change format if needed } catch (e) { return null; } } @override void dispose() { _tripTypeFocusNode.dispose(); _tripTypeController.dispose(); _startdateController.dispose(); _insuranceCommentsController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { bool isMobile = sizingInfo.isMobile; bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Container( color: Color(0xFFF4F4FB), child: Form( key: _formKey, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Align( alignment: Alignment.centerRight, child: InkWell( onTap: () { widget.onClose(false); }, child: Icon( Icons.close, size: 18, color: Color(0xFF575A74), ), ), ), Text("Insurance Booking List", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), SizedBox( height: 6, ), Padding( padding: const EdgeInsets.all(28.0), child: Center( child: Column(children: _buildAccomadtionForm(isDesktop)), ), ) ], ), ), ), ); }); } List _buildAccomadtionForm (bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), SizedBox(height: 10), ]; } List> rowBuilders = [ // _builClassType(isDesktop), _buildSecondRow(isDesktop) ]; return [ ...buildResponsiveRow(_buildFirstRow(isDesktop)), // Iterate over rowBuilders and wrap each in a responsive container ...rowBuilders.expand((row) => buildResponsiveRow(row)), ...buildResponsiveRow(_buildThirdRow(isDesktop)), // Actions row remains a Row Row( mainAxisAlignment: MainAxisAlignment.end, children: _handleAction(isDesktop), ), ]; } List _buildFirstRow(isDesktop) { return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Insurance Type", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), isDesktop ? Row(children: _buildTripType(isDesktop) ) : Column( children: _buildTripType(isDesktop) ) ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), ]; } List _buildTripType(bool isDesktop){ List purposeList = widget.apiData?['insurance_type_of_insurance'] ?? []; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text("No options available", style: TextStyle(color: Colors.grey)), ), ); } // Default selected value selectedInsuranceType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, child: SizedBox( height: 40, child: DropdownButtonFormField( focusNode: _tripTypeFocusNode, // Assign the correct focus node value: selectedInsuranceType, style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { setState(() { selectedInsuranceType = newValue; if (selectedInsuranceType!.isNotEmpty) { errorMessages.remove("type_of_insurance"); } }); print(selectedInsuranceType); } : null, items: dropdownItems, ), ), ), ]; } List _buildSecondRow(bool isDesktop) { DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; Future _selectCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; _startdateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); }); } } Future _selectEndCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; _endDateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); }); } } return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Start Date", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _dateFocus, isDesktop: isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: () async{ await _selectCheckOutDate(context); if(_startdateController.text.isNotEmpty){ setState(() { errorMessages.remove("start_date"); // Removes the key completely }); } }, child: AbsorbPointer( child: TextField( focusNode: _dateFocusNode, controller: _startdateController, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Date", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon(Icons.calendar_today, size: 16, color: Colors.grey), ), ), ), ), ), ), if (errorMessages["start_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( "Required", style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "End Date", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _dateFocus, isDesktop: isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { await _selectEndCheckOutDate(context); if (_endDateController.text.isNotEmpty) { DateTime? startDate = _parseDate(_startdateController.text); DateTime? endDate = _parseDate(_endDateController.text); if (startDate != null && endDate != null && endDate.isBefore(startDate)) { setState(() { errorMessages["end_date"] = "End date cannot be earlier than start date"; }); } else { setState(() { errorMessages.remove("end_date"); }); } } }, child: AbsorbPointer( child: TextField( focusNode: _dateFocusNode, controller: _endDateController, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Date", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon(Icons.calendar_today, size: 16, color: Colors.grey), ), ), ), ), ), ), if (errorMessages["end_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( // "Required", errorMessages["end_date"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), ]; } List _buildThirdRow(bool isDesktop) { return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Comments", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: _commentsFocusNode, controller: _insuranceCommentsController, maxLines: 6, keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Description", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 4), ), ), ), ], ) ]; } List _handleAction(bool isDesktop) { return [ // Close Button ElevatedButton( onPressed: () { widget.onClose(false); // Close the dialog or screen }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Close", style: TextStyle(color: Colors.white, fontSize: 14), ), ), SizedBox(width: 10), // Space between buttons // Save Changes Button ElevatedButton( onPressed: () { handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Save Changes", style: TextStyle(color: Colors.white, fontSize: 14), ), ), ]; } }