import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class VisaScreen extends StatefulWidget { final List> flightData; final Map? apiData; final List? apiCountryData; final Function(String, bool) onClose; final Function(Map) onSaveVisa; final Map? selectedItem; final String? loginUser; VisaScreen({ required this.onClose, required this.onSaveVisa, this.apiData, required this.selectedItem, required this.apiCountryData, required this.loginUser, required this.flightData, }); @override _VisaScreenState createState() => _VisaScreenState(); } class _VisaScreenState extends State { final GlobalKey _formKey = GlobalKey(); ApiService apiService = ApiService(); // late Map countryMap; Map countryMap = {}; late ValueNotifier flightFirstTripDateNotifier; late ValueNotifier flightLastTripDateNotifier; late ValueNotifier flightFirstToDestinationNotifier; Map selectedValues = {}; List countryList = []; final FocusNode _tripTypeFocusNode = FocusNode(); final FocusNode _countryFocusNode = FocusNode(); final FocusNode _hotelNameFocusNode = FocusNode(); final FocusNode _dateFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController(); late TextEditingController _fromController = TextEditingController(); late TextEditingController _toController = TextEditingController(); late TextEditingController _dateController = TextEditingController(); late TextEditingController _timeController = TextEditingController(); late TextEditingController _visaCommentsController = TextEditingController(); bool _tripTypeFocused = false; bool _countryFocused = false; bool _isHotelNameFocused = false; bool _dateFocus = false; bool _commentsFocus = false; String? selectedPurpose; String? selectedCountry; Color layoutColor = Colors.grey; Map errorMessages = {}; Map get visaData { Map data = { "type_of_visa": selectedPurpose, // "country": selectedCountry, "country_code": selectedCountry, "start_date": _dateController.text, "comments": _visaCommentsController.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?["visa_id"] != null && widget.selectedItem?["visa_id"] != 0) { data["visa_id"] = widget.selectedItem!["visa_id"]; } } return data; } @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { loadInitialData(); }); _addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus); _addFocusListener(_countryFocusNode, (focus) => _countryFocused = focus); _addFocusListener( _hotelNameFocusNode, (focus) => _isHotelNameFocused = focus, ); _addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _visaCommentsController = TextEditingController( text: widget.selectedItem?["comments"] ?? "", ); _dateController = TextEditingController( text: widget.selectedItem?["start_date"] ?? "", ); // Set the selected value if available if (widget.selectedItem != null && widget.selectedItem!["type_of_visa"] != null) { selectedPurpose = widget.selectedItem!["type_of_visa"].toString(); } if (widget.selectedItem != null && widget.selectedItem!["country_code"] != null) { // selectedPurpose = widget.selectedItem!["selectedCountry"].toString(); selectedCountry = widget.selectedItem!["country_code"] as String?; } flightFirstTripDateNotifier = ValueNotifier(null); flightLastTripDateNotifier = ValueNotifier(null); WidgetsBinding.instance.addPostFrameCallback((_) { final result = getFlightTripDateRange(widget.flightData); flightFirstTripDateNotifier.value = result['firstTripDate']; flightLastTripDateNotifier.value = result['lastTripDate']; // ✅ Only set controller after value is updated // final parsedDate = // DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); final parsedDate = DateFormat( "dd-MM-yyyy", ).parse(flightFirstTripDateNotifier.value ?? ''); if (parsedDate != null) { _dateController.text = DateFormat('dd-MM-yyyy').format(parsedDate); } }); } void loadInitialData() async { String? layoutString = await getLayoutColor(); // setState(() { // layoutColor = // layoutString != null // ? Color(int.parse(layoutString)) // : Colors.redAccent; // }); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; }); } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } // Future loadCountryList() async { // final result = await apiService.fetchFlightsCountryList(); // // print("ResultCountry : $result"); // // // Create a map: Country_Code -> "City, Airport" // Map tempCountryMap = {}; // // for (var country in result) { // String city = country['City'] ?? ''; // String airport = country['Airport'] ?? ''; // String displayName = '${country['City']}'; // // String displayName = '${country['City']} | ${country['Airport']}'; // // tempCountryMap[country['Code']] = displayName; // } // // setState(() { // countryMap = tempCountryMap; // Update the map // _updateDestinationCity(); // }); // } void _updateDestinationCity() { final toDestination = flightFirstToDestinationNotifier.value ?? ''; // final toDestinationCity = countryMap[toDestination] ?? ""; if (toDestination != '') { // _destinationController.text = toDestinationCity; selectedCountry = widget.selectedItem!["toDestination"] as String?; } } Map getFlightTripDateRange( List> flightData, ) { final allTrips = flightData .expand((flight) => flight['trips'] ?? []) .whereType>() .toList(); if (allTrips.isEmpty) { return {'firstTripDate': null, 'lastTripDate': null}; } allTrips.sort((a, b) { final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900); final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900); return aDate.compareTo(bDate); }); final firstTrip = allTrips.first; final lastTrip = allTrips.last; return { 'firstTripDate': firstTrip['date'], 'lastTripDate': lastTrip['date'], }; } @override void dispose() { _tripTypeFocusNode.dispose(); _countryFocusNode.dispose(); _tripTypeController.dispose(); _hotelNameController.dispose(); _fromController.dispose(); _toController.dispose(); _dateController.dispose(); _timeController.dispose(); _visaCommentsController.dispose(); super.dispose(); } bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = [ "type_of_visa", "country_code", "start_date", ]; // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { errorMessages[field] = "This field is required"; } } return errorMessages.isEmpty; // Valid if there are no errors } void handleSave() { print("Handle Save visaData $visaData"); Map data = visaData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails } else { widget.onSaveVisa(visaData); } widget.onClose("Visa", false); // widget.onClose(false); // Close screen after saving } @override Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, sizingInfo) { bool isMobile = sizingInfo.isMobile; bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Container( child: Form( key: _formKey, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Padding( padding: const EdgeInsets.only(top: 30.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 = [_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 ]; } List _buildFirstRow(isDesktop) { return [if (isDesktop) Spacer() else SizedBox(height: 8)]; } List _buildTripType(bool isDesktop) { return []; } List _buildSecondRow(bool isDesktop) { // List countryList = widget.apiCountryData ?? []; // // List> dropdownItems = countryList // .map((item)=>DropdownMenuItem( // value: item['country_code'], // Use 'country_code' from API response // child: Text(item['country_name']), // )).toList(); // // if (dropdownItems.isEmpty) { // dropdownItems.add( // DropdownMenuItem( // value: null, // child: Text("No options available", style: TextStyle(color: Colors.grey)), // ), // ); // } // // // Default selected value // selectedCountry ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; // List countryNames = countryList.map((item) => item['country_name'] as String).toList(); // late Map countryMap; // Mapping country_code -> country_name late List countryCodes; // List of country codes countryList = widget.apiCountryData ?? []; // Map country codes to country names // countryMap = { // for (var item in countryList) // item['country_code'] as String: item['country_name'] as String, // }; countryMap = { for (var country in countryList) (country['country_code'] ?? ''): '${country['country_name'] ?? ''} (${country['country_code'] ?? ''})', }; // Extract only country codes for processing countryCodes = countryMap.keys.toList(); selectedCountry ??= null; // ____________ DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; Future _selectCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); // Parse date from notifier if available, else use today DateTime initialDate; if (flightFirstTripDateNotifier.value != null) { try { initialDate = DateTime.parse(flightFirstTripDateNotifier.value!); } catch (e) { initialDate = today; } } else { initialDate = today; } // Use previously selected date if valid if (_selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)) { initialDate = _selectedCheckOutDate!; } final pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: initialDate, lastDate: DateTime(2100), initialEntryMode: DatePickerEntryMode.calendarOnly, ); // 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; _dateController.text = DateFormat('dd-MM-yyyy').format(pickedDate); }); } } // --------------------- List purposeList = widget.apiData?['visa_type_of_visa'] ?? []; 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 selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Country *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), // CustomTextFieldWrapper( // isFocused: _isHotelNameFocused, // isDesktop: isDesktop, // // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, // width: // isDesktop // ? MediaQuery.of(context).size.width * 0.34 // : MediaQuery.of(context).size.width * 0.66, // child: SizedBox( // height: 40, // child: DropdownSearch( // selectedItem: countryMap[selectedCountry], // popupProps: PopupProps.menu( // showSearchBox: true, // Enables search functionality // searchFieldProps: TextFieldProps( // decoration: InputDecoration( // hintText: "Search ...", // contentPadding: EdgeInsets.symmetric(horizontal: 10), // ), // ), // ), // items: countryMap.values.toList(), // dropdownDecoratorProps: DropDownDecoratorProps( // dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(horizontal: 1), // ), // ), // dropdownBuilder: // (context, selectedItem) => Align( // // Center-align selected item // alignment: Alignment.centerLeft, // child: Text( // selectedItem ?? "Select ", // style: TextStyle(fontSize: 12), // ), // ), // onChanged: (String? newValue) { // setState(() { // // Find the country_code based on selected country_name // selectedCountry = // countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; // // if (selectedCountry!.isNotEmpty) { // errorMessages.remove("country_code"); // } // }); // }, // ), // ), // ), CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, padding: const EdgeInsets.symmetric(horizontal: 0), width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,//MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: Focus( focusNode: _countryFocusNode, onFocusChange: (hasFocus) { setState(() { _countryFocused = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps _countryFocusNode?.requestFocus(); }, child: DropdownSearch( selectedItem: countryMap[selectedCountry], popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 200), menuProps: const MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) { print("contryItem - $item"); final match = RegExp(r'^(.*)\s\((.*)\)$').firstMatch(item); final countryName = match?.group(1) ?? ''; final countryCode = match?.group(2) ?? ''; return Container( color: Colors.white, padding: EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Padding( padding: const EdgeInsets.only(right: 1.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( countryName, style: GoogleFonts.poppins(fontSize: 11.5), ), Text( countryCode, style: GoogleFonts.poppins( fontSize: 11.5, color: Colors.grey, ), ), ], ), ), ); }, searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search ...", contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide( color: (_countryFocused ?? false) ? layoutColor! : Colors.white, width: 1, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_countryFocused ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 1, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 1), ), contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), dropdownBuilder: (context, selectedItem) => Align( alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select ", style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ), ), onChanged: (String? newValue) { setState(() { selectedCountry = countryMap.entries .firstWhere((entry) => entry.value == newValue) .key; }); }, ),),), ), ), if (errorMessages["country_code"] != 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( "Type of Visa", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), // CustomTextFieldWrapper( // isFocused: _tripTypeFocused, // isDesktop: isDesktop, // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, // // width: isDesktop // // ? MediaQuery.of(context).size.width * 0.34 // // : MediaQuery.of(context).size.width * 0.66, // child: SizedBox( // height: 40, // child: DropdownButtonFormField( // focusNode: _tripTypeFocusNode, // Assign the correct focus node // value: selectedPurpose, // style: TextStyle(fontSize: 12), // decoration: InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric( // horizontal: 10, // ), // Proper padding // ), // onChanged: // purposeList.isNotEmpty // ? (newValue) { // setState(() { // selectedPurpose = newValue; // }); // print( // "Updating form data: Flight -> trip_type -> ${newValue ?? ""}", // ); // } // : null, // // items: dropdownItems, // ), // ), // ), CustomTextFieldWrapper( isFocused: _tripTypeFocused, isDesktop: isDesktop, padding: const EdgeInsets.symmetric(horizontal: 0), width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _tripTypeFocusNode, onFocusChange: (hasFocus) { setState(() { _tripTypeFocused = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps _tripTypeFocusNode?.requestFocus(); }, child: DropdownSearch>( items: purposeList.cast>(), selectedItem: purposeList.firstWhere( (item) => item['dropdown_key'] == selectedPurpose, orElse: () => {}, ), itemAsString: (item) => item['dropdown_value'] ?? '', popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, menuProps: const MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) { return Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), child: Text( item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ), ); }, ), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide( color: (_tripTypeFocused ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_tripTypeFocused ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 0.5), ), contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 10, ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( "Select ", style: GoogleFonts.poppins( color: Colors.grey, fontSize: 13, ), ); } return Text( selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ); }, // focusNode: _tripTypeFocusNode, // Assign the correct focus node // value: selectedPurpose, // style: TextStyle(fontSize: 12), // decoration: InputDecoration( // border: InputBorder.none, // contentPadding: // EdgeInsets.symmetric(horizontal: 10), // Proper padding // ), onChanged: purposeList.isNotEmpty ? (Map? newValue) { setState(() { selectedPurpose = newValue?['dropdown_key']; print("Selected Purpose: ${selectedPurpose}"); }); } : null, ),),), ), ), if (errorMessages["type_of_visa"] != 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( "Start Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _dateFocus, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { _dateFocusNode.requestFocus(); await _selectCheckOutDate(context); if (_dateController.text.isNotEmpty) { setState(() { errorMessages.remove("start_date"); }); } }, child: AbsorbPointer( child: TextField( focusNode: _dateFocusNode, controller: _dateController, readOnly: true, 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)), ], ], ), ]; } List _buildThirdRow(bool isDesktop) { return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Comments", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, //MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( focusNode: _commentsFocusNode, controller: _visaCommentsController, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ), if (isDesktop) Spacer(), SizedBox(height: 5), Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: _handleAction(isDesktop), ), ], ), ]; } List _handleAction(bool isDesktop) { return [ // Close Button ElevatedButton( onPressed: () { widget.onClose("Visa", false); // 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: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons // Save Changes Button ElevatedButton( onPressed: () { handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Save Changes", style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; } }