import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.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_confirmation_dialog.dart'; import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class AccomodationScreen extends StatefulWidget { final List> flightData; final Map? apiDataForClass; final Function(String, bool) onClose; // Callback function final Function(Map) onSaveAccomadation; final void Function(bool)? onExceptionalClass; final Map? selectedItem; final String? loginUser; final String? tripType; AccomodationScreen({ required this.onClose, required this.onSaveAccomadation, required this.selectedItem, required this.loginUser, required this.flightData, this.tripType, this.apiDataForClass, this.onExceptionalClass, }); @override _AccomodationScreenState createState() => _AccomodationScreenState(); } class _AccomodationScreenState extends State { ApiService apiService = ApiService(); // late Map countryMap; Map countryMap = {}; List allItems = []; List filteredItems = []; String? selectedClass; String? exceptionalClass = "0"; bool hasExceptionalClass = false; late ValueNotifier flightFirstTripDateNotifier; late ValueNotifier flightLastTripDateNotifier; late ValueNotifier flightFirstToDestinationNotifier; final GlobalKey _formKey = GlobalKey(); final FocusNode _destinationFocusNode = FocusNode(); final FocusNode _hotelNameFocusNode = FocusNode(); final FocusNode _CategoryFocusNode = FocusNode(); final FocusNode _checkInFocusNode = FocusNode(); final FocusNode _checkInTimeFocusNode = FocusNode(); final FocusNode _checkOutFocusNode = FocusNode(); final FocusNode _checkOutTimeFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); late TextEditingController _destinationController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController(); late TextEditingController _checkInController = TextEditingController(); late TextEditingController _checkInTimeController = TextEditingController(); late TextEditingController _checkOutController = TextEditingController(); late TextEditingController _checkOutTimeController = TextEditingController(); late TextEditingController _commentsController = TextEditingController(); List? futureHotels; bool _destinationFocused = false; bool _isHotelNameFocused = false; bool _CategoryFocused = false; bool _checkInFocus = false; bool _checkInTimeFocus = false; bool _checkOutFocus = false; bool _checkOutTimeFocus = false; bool _commentsFocus = false; Color layoutColor = Colors.grey; void _addFocusListener(FocusNode node, Function(bool) onFocusChange) { node.addListener(() { setState(() { onFocusChange(node.hasFocus); }); }); } Map errorMessages = {}; Map get accomadationData { Map data = { "destination_city": _destinationController.text, "hotel_name": _hotelNameController.text, "class": selectedClass, "checkin_date": _checkInController.text, "checkin_time": _checkInTimeController.text, "checkout_date": _checkOutController.text, "checkout_time": _checkOutTimeController.text, "comments": _commentsController.text, "is_this_exceptional": exceptionalClass, "created_by": widget.loginUser, "updated_by": widget.loginUser, }; print(":("); print(_checkInController.text); print(_checkInTimeController.text); if (widget.selectedItem != null) { if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; } else if (widget.selectedItem?["accomodation_id"] != null && widget.selectedItem?["accomodation_id"] != 0) { data["accomodation_id"] = widget.selectedItem!["accomodation_id"]; } } return data; } TextEditingController initController(String key) { return TextEditingController(text: widget.selectedItem?[key] ?? ""); } @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { loadInitialData(); }); _addFocusListener( _destinationFocusNode, (focus) => _destinationFocused = focus, ); _addFocusListener( _hotelNameFocusNode, (focus) => _isHotelNameFocused = focus, ); _addFocusListener(_CategoryFocusNode, (focus) => _CategoryFocused = focus); _addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus); _addFocusListener( _checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus, ); _addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus); _addFocusListener( _checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus, ); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _destinationController = initController("destination_city"); _hotelNameController = initController("hotel_name"); _checkInController = initController("checkin_date"); _checkInTimeController = initController("checkin_time"); _checkOutController = initController("checkout_date"); _checkOutTimeController = initController("checkout_time"); _commentsController = initController("comments"); _destinationController.addListener(() => _clearError("destination_city")); _hotelNameController.addListener(() => _clearError("hotel_name")); _checkInController.addListener(() => _clearError("checkin_date")); _checkInTimeController.addListener(() => _clearError("checkin_time")); _checkOutController.addListener(() => _clearError("checkout_date")); _checkOutTimeController.addListener(() => _clearError("checkout_time")); flightFirstTripDateNotifier = ValueNotifier(null); flightLastTripDateNotifier = ValueNotifier(null); flightFirstToDestinationNotifier = ValueNotifier(null); // // ✅ Check and set default times if empty // if (_checkInTimeController.text.isEmpty) { // _checkInTimeController.text = '14:00'; // 2 PM // } // if (_checkOutTimeController.text.isEmpty) { // _checkOutTimeController.text = '12:00'; // 12 PM // } WidgetsBinding.instance.addPostFrameCallback((_) { loadCountryList(context); final result = getFlightTripDateRange(widget.flightData); print("Rs: $result"); print("updat- ${widget.selectedItem}"); final checkinDate = widget.selectedItem?["checkin_date"] ?? result['firstTripDate']; final checkoutDate = widget.selectedItem?["checkout_date"] ?? result['lastTripDate']; final destinationCity = widget.selectedItem?["destination_city"] ?? result['firstToDestination']; // if (checkinDate != null) { // flightFirstTripDateNotifier.value = checkinDate; // } // // if (checkoutDate != null) { // flightLastTripDateNotifier.value = checkoutDate; // } final tripType = result['tripType']; if (checkinDate != null) { flightFirstTripDateNotifier.value = checkinDate; if (tripType == "Oneway") { flightLastTripDateNotifier.value = checkinDate; } else { flightLastTripDateNotifier.value = checkoutDate ?? checkinDate; } } if (destinationCity != null) { flightFirstToDestinationNotifier.value = destinationCity; } // flightFirstTripDateNotifier.value = result['firstTripDate']; // flightLastTripDateNotifier.value = result['lastTripDate']; // flightFirstToDestinationNotifier.value = result['firstToDestination']; // print( "flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value ", ); // ✅ Only set controller after value is updated // final parsedDate = DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); final checkInDateInput = flightFirstTripDateNotifier.value; if (checkInDateInput != null && checkInDateInput.isNotEmpty) { try { final parsedDate = DateFormat( "dd-MM-yyyy", ).parseStrict(checkInDateInput); _checkInController.text = DateFormat("dd-MM-yyyy").format(parsedDate); } catch (e) { _checkInController.text = ''; } } // final parsedEndDate = DateTime.tryParse(flightLastTripDateNotifier.value ?? ''); final checkOutDateInput = flightLastTripDateNotifier.value; if (checkOutDateInput != null && checkOutDateInput.isNotEmpty) { try { final parsedDate = DateFormat( "dd-MM-yyyy", ).parseStrict(checkOutDateInput); _checkOutController.text = DateFormat( "dd-MM-yyyy", ).format(parsedDate); } catch (e) { _checkOutController.text = ''; } } // ✅ Check and set default times if empty if (_checkInTimeController.text.isEmpty) { _checkInTimeController.text = '14:00'; // 2 PM } if (_checkOutTimeController.text.isEmpty) { _checkOutTimeController.text = '12:00'; // 12 PM } }); if (widget.selectedItem != null && widget.selectedItem!["is_this_exceptional"] != null) { exceptionalClass = widget.selectedItem!["is_this_exceptional"].toString(); } // Set the selected value if available if (widget.selectedItem != null && widget.selectedItem!["class"] != null) { selectedClass = widget.selectedItem!["class"].toString(); } } void loadInitialData() async { String? layoutString = await getLayoutColor(); // setState(() { // layoutColor = // layoutString != null // ? Color(int.parse(layoutString)) // : Colors.redAccent; // }); futureHotels = await apiService.fetchGetHotels(context); print("futureHotels - $futureHotels"); // Extract hotel_name and assign to items allItems = futureHotels! .map((hotel) => hotel['hotel_name'].toString()) .toList(); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; filteredItems = List.from(allItems); print("items - $allItems"); _hotelNameController.addListener(_filterItems); }); } void _filterItems() { print("Filter iteks"); String query = _hotelNameController.text.toLowerCase(); print("Filter iteks - $query"); setState(() { filteredItems = allItems.where((item) => item.toLowerCase().contains(query)).toList(); }); } @override void dispose() { _destinationFocusNode.dispose(); _destinationController.dispose(); _hotelNameController.dispose(); _checkInController.dispose(); _checkInTimeController.dispose(); _checkOutController.dispose(); _checkOutTimeController.dispose(); _commentsController.dispose(); super.dispose(); } void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { errorMessages.remove(field); }); } } DateTime? _parseDateSafe(String? dateStr, DateFormat formatter) { try { if (dateStr == null || dateStr.isEmpty) return null; return formatter.parseStrict(dateStr); } catch (e) { return null; } } Map getFlightTripDateRange( List> flightData, ) { String tt = ""; if (widget.flightData.isNotEmpty) { print("Trip Type - ${widget.flightData[0]['trip_type']}"); tt = widget.flightData[0]['trip_type']; } else { print("No flight data available"); } final allTrips = flightData .expand((flight) => flight['trips'] ?? []) .whereType>() .toList(); if (allTrips.isEmpty) { return {'firstTripDate': null, 'lastTripDate': null}; } final DateFormat dateFormatter = DateFormat('dd-MM-yyyy'); 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; final firstTripToDestination = allTrips.first; final firstTripDate = _parseDateSafe(firstTrip['date'], dateFormatter); print("res1 - ${firstTrip['date']},"); print("res11 - $firstTripDate"); print("res22 - $tt"); // Use `List.where` instead of `firstWhere` so you can handle an empty result final Map nextTrip = allTrips.skip(1).firstWhere((trip) { final DateFormat dateFormatter = DateFormat('dd-MM-yyyy'); final tripDate = dateFormatter.tryParse(trip['date'] ?? ''); return tripDate != null && firstTripDate != null && tripDate.isAfter(firstTripDate); }, orElse: () => {}); print("res2 - $nextTrip"); print("allTrips - $allTrips"); return { 'firstTripDate': firstTrip['date'], 'lastTripDate': nextTrip != null ? nextTrip['date'] : null, // 'lastTripDate': nextTrip['date'], // 'lastTripDate': lastTrip['date'], 'firstToDestination': firstTripToDestination['to_place'], 'tripType': tt, // trip_type: Oneway }; } bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = [ "destination_city", "hotel_name", "class", "checkin_date", "checkin_time", "checkout_date", "checkout_time", ]; // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { errorMessages[field] = "Required"; } } // Additional validation: checkout_date >= checkin_date final checkIn = data["checkin_date"]; final checkInTime = data["checkin_time"]; final checkOut = data["checkout_date"]; final checkOutTime = data["checkout_time"]; if (checkIn != null && checkOut != null && checkIn.toString().isNotEmpty && checkOut.toString().isNotEmpty) { try { final format = DateFormat("dd-MM-yyyy"); final formatTime = DateFormat("dd-MM-yyyy HH:mm"); // final checkInDate = format.parse(checkIn); // final checkOutDate = format.parse(checkOut); final checkInDate = format.parse("$checkIn"); final checkOutDate = format.parse("$checkOut"); if (checkOutDate.isBefore(checkInDate)) { errorMessages["checkout_date"] = "Check-out date cannot be before check-in date"; } else if (checkOutDate.isAtSameMomentAs(checkInDate)) { // If dates are same, check the times if (checkInTime != null && checkOutTime != null && checkInTime.toString().isNotEmpty && checkOutTime.toString().isNotEmpty) { try { final checkInDateTime = formatTime.parse("$checkIn $checkInTime"); final checkOutDateTime = formatTime.parse( "$checkOut $checkOutTime", ); if (!checkOutDateTime.isAfter(checkInDateTime)) { errorMessages["checkout_time"] = "Check-out must be after check-in time"; } else { final difference = checkOutDateTime.difference(checkInDateTime); if (difference.inMinutes < 30) { errorMessages["checkout_time"] = "Check-out must be at least 30 minutes after check-in"; } } } catch (e) { errorMessages["checkout_time"] = "Invalid time format"; } } } } catch (e) { errorMessages["checkout_date"] = "Invalid date format"; } } return errorMessages.isEmpty; // Valid if there are no errors } Future loadCountryList(BuildContext context) async { final result = await apiService.fetchFlightsCountryList( context, widget.tripType, ); 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 (toDestinationCity != '') { _destinationController.text = toDestinationCity; } } void handleSave() { print("Handle Save accomadationData $accomadationData"); Map data = accomadationData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails } else { widget.onSaveAccomadation(accomadationData); // if (hasExceptionalClass) { // widget.onExceptionalClass?.call(true); // or false // } } widget.onClose("Accomodation", 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( // color: Color(0xFFF4F4FB), 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(isDesktop) { return [ isDesktop ? Row(children: _buildFirstRow(isDesktop)) : Column(children: _buildFirstRow(isDesktop)), SizedBox(height: 10), // Spacing between first and second row isDesktop ? Row(children: _buildSecondRow(isDesktop)) : Column(children: _buildSecondRow(isDesktop)), SizedBox(height: 10), isDesktop ? Row(children: _buildThirdRow(isDesktop)) : Column(children: _buildThirdRow(isDesktop)), ]; } List _buildFirstRow(isDesktop) { List purposeList = widget.apiDataForClass?['hotel_class'] ?? []; print("hotel_classhotel_class"); print(purposeList); 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), ), ), ); } // final List items = ['Apple Banana', 'Banana', 'Orange', 'Mango']; // Default selected value // selectedClass ??= // dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ // Column( // children: [ // ValueListenableBuilder( // valueListenable: flightFirstTripDateNotifier, // builder: (context, value, child) => // Text("First Trip Date: ${value ?? 'Not available'}"), // ), // ValueListenableBuilder( // valueListenable: flightLastTripDateNotifier, // builder: (context, value, child) => // Text("Last Trip Date: ${value ?? 'Not available'}"), // ), // ], // ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Destination *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: _destinationFocused, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.35 : null, //MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( focusNode: _destinationFocusNode, controller: _destinationController, style: TextStyle(fontSize: 12), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')), ], decoration: InputDecoration( labelText: "Destination", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["destination_city"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["destination_city"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Hotel Name *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: _isHotelNameFocused, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, // width: // isDesktop // ? MediaQuery.of(context).size.width * 0.34 // : MediaQuery.of(context).size.width * 0.66, child: Focus( focusNode: _hotelNameFocusNode, onFocusChange: (hasFocus) { setState(() { _isHotelNameFocused = hasFocus; }); }, child: SizedBox( height: 40, child: GestureDetector( onTap: () { _hotelNameFocusNode.requestFocus(); }, child: DropdownSearch( items: allItems, selectedItem: _hotelNameController.text, popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), menuProps: const MenuProps(backgroundColor: Colors.white), showSearchBox: true, itemBuilder: (context, item, isSelected) => Padding( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 2, ), child: Text( item, style: GoogleFonts.poppins( fontSize: 11, ), // 👈 smaller font size here ), ), searchFieldProps: TextFieldProps( controller: _hotelNameController, style: TextStyle(fontSize: 11), decoration: InputDecoration( labelText: "Enter/ Search Hotel Name", labelStyle: GoogleFonts.poppins(fontSize: 11), isDense: true, floatingLabelBehavior: FloatingLabelBehavior.never, // contentPadding: EdgeInsets.symmetric( // vertical: 12, // horizontal: 10, // ), border: OutlineInputBorder(), ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( "Select ", style: GoogleFonts.poppins( color: Colors.grey, fontSize: 12, fontWeight: FontWeight.w400, ), ); } return Text( selectedItem ?? '', semanticsLabel: "hotelName", style: GoogleFonts.roboto( fontSize: 11, fontWeight: FontWeight.w400, color: Colors.black, ), ); }, dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( labelText: "Hotel Name", labelStyle: GoogleFonts.poppins( fontSize: 11, color: Colors.grey, ), // border: OutlineInputBorder(), // border: InputBorder.none, isDense: true, floatingLabelBehavior: FloatingLabelBehavior.never, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide( color: (_isHotelNameFocused ?? false) ? layoutColor! : Colors.white, width: 1, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_isHotelNameFocused ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 1, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 1), ), contentPadding: const EdgeInsets.symmetric( horizontal: 10, vertical: 10, ), ), ), onChanged: (value) { _hotelNameController.text = value ?? ''; }, ), ), // TextField( // focusNode: _hotelNameFocusNode, // Assign the correct focus node // controller: _hotelNameController, // style: TextStyle(fontSize: 12), // decoration: InputDecoration( // labelText: "Hotel Name", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 16), // ), // ), ), ), ), if (errorMessages["hotel_name"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["hotel_name"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Category *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: focusStates["_class${index}Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _CategoryFocusNode, onFocusChange: (hasFocus) { setState(() { _CategoryFocused = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps _CategoryFocusNode?.requestFocus(); }, child: DropdownSearch>( // key: // selectedClass == null // ? UniqueKey() // : ValueKey(selectedClass), items: purposeList.cast>(), // selectedItem: purposeList.firstWhere( // (item) => item['dropdown_key'] == selectedClass, // orElse: () => {}, // ), selectedItem: selectedClass != null ? purposeList.firstWhere( (item) => item['dropdown_key'] == selectedClass, orElse: () => {}, ) : null, itemAsString: (item) => item['dropdown_value'] ?? '', popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, menuProps: const MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) { print("categotey - $item "); final bool isNotAllowed = item['is_allowed'] == 'No'; return Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), child: Text( item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: isNotAllowed ? Colors.redAccent : Colors.black, ), ), ); }, ), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide( color: (_CategoryFocused ?? false) ? layoutColor! : Colors.white, width: 1, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_CategoryFocused ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 1, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 1), ), contentPadding: const 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: 11, // color: Colors.red, color: Colors.black, ), ); }, onChanged: purposeList.isNotEmpty ? (Map? newValue) async { if (newValue != null && newValue['is_allowed'] == 'No') { final confirm = await showNotAllowedDialog( context, message: "You are not entitled for this service. If you wish to continue with this service, you may require another approval.", ); if (confirm) { setState(() { exceptionalClass = "1"; selectedClass = newValue['dropdown_key']; }); print("Selected Purpose: ${selectedClass}"); } else { setState(() { exceptionalClass = "0"; selectedClass = null; // ❌ Clear selection if user cancels }); print( "selectedItem resolved to: ${selectedClass}", ); } return; } else if (newValue != null && newValue['is_allowed'] == 'yes') { setState(() { exceptionalClass = "0"; selectedClass = newValue?['dropdown_key']; print("Selected Purpose: ${selectedClass}"); }); } } : null, ), ), ), ), ), if (errorMessages["class"] != null) ...[ SizedBox(height: 5), // Space before error message Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), // if (isDesktop) Spacer() else SizedBox(height: 8), ]; } List _buildSecondRow(bool isDesktop) { DateTime? _selectedCheckInDate; TimeOfDay? _selectedCheckInTime; Future _selectCheckInDate(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 (_selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)) { initialDate = _selectedCheckInDate!; } final pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: initialDate, lastDate: DateTime(2100), initialEntryMode: DatePickerEntryMode.calendarOnly, ); // DateTime? pickedDate = await showDatePicker( // context: context, // initialDate: // _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today) // ? _selectedCheckInDate! // : today, // firstDate: today, // lastDate: DateTime(2100), // ); if (pickedDate != null && pickedDate != _selectedCheckInDate) { setState(() { _selectedCheckInDate = pickedDate; _checkInController.text = DateFormat('dd-MM-yyyy').format(pickedDate); }); } } Future _selectCheckInTime(BuildContext context) async { TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: _selectedCheckInTime ?? TimeOfDay.now(), ); if (pickedTime != null) { final now = DateTime.now(); // Parse the selected date final dateText = _checkInController.text ?? ""; final selectedDate = DateFormat( 'dd-MM-yyyy', ).parse(dateText); // or 'yyyy-MM-dd' depending on your format final selectedDateTime = DateTime( selectedDate.year, selectedDate.month, selectedDate.day, pickedTime.hour, pickedTime.minute, ); // ✅ Only validate past time if date is today final isToday = selectedDate.year == now.year && selectedDate.month == now.month && selectedDate.day == now.day; bool isPastTime = selectedDateTime.isBefore(now); if (isToday && isPastTime) { setState(() { errorMessages["checkin_time"] = "You can't select a past time."; }); return; } // ✅ Valid time selection setState(() { _selectedCheckInTime = pickedTime; final formattedTime = DateFormat('HH:mm').format(selectedDateTime); _checkInTimeController.text = formattedTime; // errorMessages["time_$index"] = ""; // clear previous error errorMessages.remove("checkin_time"); }); } // if (pickedTime != null && pickedTime != _selectedCheckInTime) { // setState(() { // _selectedCheckInTime = pickedTime; // // Formatting time to HH:mm (24-hour format) // final now = DateTime.now(); // final formattedTime = DateFormat('HH:mm').format( // DateTime(now.year, now.month, now.day, pickedTime.hour, // pickedTime.minute), // ); // _checkInTimeController.text = formattedTime; // }); // } } //-------------------------------Check-In End 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; // _checkOutController.text = // DateFormat('yyyy-MM-dd').format(pickedDate); // }); // } // } Future _selectCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); final format = DateFormat("dd-MM-yyyy HH:mm"); // final prevDateTime = DateTime.parse("$prevDateStr $prevTimeStr"); // final currDateTime = DateTime.parse("$currDateStr $currTimeStr"); DateTime? checkInDate; try { checkInDate = format.parse(_checkInController.text); } catch (e) { checkInDate = today; } // // Ensure at least today is used // DateTime firstDate = checkInDate.isAfter(today) ? checkInDate : today; // DateTime initialDate = _selectedCheckOutDate != null && // _selectedCheckOutDate!.isAfter(firstDate) // ? _selectedCheckOutDate! // : firstDate; DateTime firstDate = checkInDate; DateTime initialDate = _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(firstDate) ? _selectedCheckOutDate! : firstDate; final pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: firstDate, lastDate: DateTime(2100), initialEntryMode: DatePickerEntryMode.calendarOnly, ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; _checkOutController.text = DateFormat( 'dd-MM-yyyy', ).format(pickedDate); errorMessages.remove("checkout_date"); }); } } Future _selectCheckOutTime(BuildContext context) async { TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), ); if (pickedTime != null && pickedTime != _selectedCheckOutTime) { setState(() { _selectedCheckOutTime = pickedTime; // Formatting time to HH:mm (24-hour format) final now = DateTime.now(); final formattedTime = DateFormat('HH:mm').format( DateTime( now.year, now.month, now.day, pickedTime.hour, pickedTime.minute, ), ); _checkOutTimeController.text = formattedTime; errorMessages.remove("checkout_time"); }); } } return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Check-in *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: _checkInFocus, isDesktop: isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: () { _checkInFocusNode.requestFocus(); _selectCheckInDate(context); }, child: AbsorbPointer( child: TextField( focusNode: _checkInFocusNode, controller: _checkInController, 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["checkin_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["checkin_date"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Time (Check-in) *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: _checkInTimeFocus, isDesktop: isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: () { _checkInTimeFocusNode.requestFocus(); // _checkInTimeController.text = ""; // pavithra told me. here client reported the issues here thats why. _selectCheckInTime(context); }, child: AbsorbPointer( child: TextField( focusNode: _checkInTimeFocusNode, controller: _checkInTimeController, readOnly: true, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Time", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon( Icons.access_time, size: 16, color: Colors.grey, ), ), ), ), ), ), ), if (errorMessages["checkin_time"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["checkin_time"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Check-out*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: _checkOutFocus, isDesktop: isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: () { _checkOutFocusNode.requestFocus(); _selectCheckOutDate(context); }, child: AbsorbPointer( child: TextField( focusNode: _checkOutFocusNode, controller: _checkOutController, readOnly: true, style: const TextStyle(fontSize: 12, color: Colors.black), 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["checkout_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["checkout_date"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Time (Check-out) *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: _checkOutTimeFocus, isDesktop: isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: () { _checkOutTimeFocusNode.requestFocus(); _selectCheckOutTime(context); }, child: AbsorbPointer( child: TextField( focusNode: _checkOutTimeFocusNode, controller: _checkOutTimeController, readOnly: true, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Time", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon( Icons.access_time, size: 16, color: Colors.grey, ), ), ), ), ), ), ), if (errorMessages["checkout_time"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["checkout_time"]!, 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.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), // CustomTextFieldWrapper CustomTextFieldItnerarySubWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.35 : null, // MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( focusNode: _commentsFocusNode, controller: _commentsController, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')), ], 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("Accomodation", 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: 14), ), ), 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: 14), ), ), ]; } }