From de960412a057f2b36e598c68bc2a15572087b79a Mon Sep 17 00:00:00 2001 From: venbaittech Date: Mon, 9 Jun 2025 12:16:02 +0530 Subject: [PATCH] Current Time Validations --- lib/Screens/itnerary/accomodations.dart | 367 ++++++++++--------- lib/Screens/itnerary/flights.dart | 260 ++++++++------ lib/Screens/itnerary/insurance.dart | 452 ++++++++++++------------ lib/Screens/itnerary/train.dart | 77 +++- lib/Screens/plans/create_plans.dart | 5 +- pubspec.lock | 20 +- pubspec.yaml | 2 +- 7 files changed, 679 insertions(+), 504 deletions(-) diff --git a/lib/Screens/itnerary/accomodations.dart b/lib/Screens/itnerary/accomodations.dart index 061f1bb..1ec7cea 100644 --- a/lib/Screens/itnerary/accomodations.dart +++ b/lib/Screens/itnerary/accomodations.dart @@ -15,13 +15,14 @@ class AccomodationScreen extends StatefulWidget { final String? loginUser; final String? tripType; - AccomodationScreen( - {required this.onClose, - required this.onSaveAccomadation, - required this.selectedItem, - required this.loginUser, - required this.flightData, - this.tripType}); + AccomodationScreen({ + required this.onClose, + required this.onSaveAccomadation, + required this.selectedItem, + required this.loginUser, + required this.flightData, + this.tripType, + }); @override _AccomodationScreenState createState() => _AccomodationScreenState(); @@ -107,15 +108,23 @@ class _AccomodationScreenState extends State { super.initState(); _addFocusListener( - _destinationFocusNode, (focus) => _destinationFocused = focus); + _destinationFocusNode, + (focus) => _destinationFocused = focus, + ); _addFocusListener( - _hotelNameFocusNode, (focus) => _isHotelNameFocused = focus); + _hotelNameFocusNode, + (focus) => _isHotelNameFocused = focus, + ); _addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus); _addFocusListener( - _checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus); + _checkInTimeFocusNode, + (focus) => _checkInTimeFocus = focus, + ); _addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus); _addFocusListener( - _checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus); + _checkOutTimeFocusNode, + (focus) => _checkOutTimeFocus = focus, + ); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _destinationController = initController("destination_city"); @@ -149,25 +158,29 @@ class _AccomodationScreenState extends State { flightFirstToDestinationNotifier.value = result['firstToDestination']; print( - "flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value "); + "flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value ", + ); // ✅ Only set controller after value is updated // final parsedDate = DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); - final parsedDate = DateFormat("dd-MM-yyyy") - .parse(flightFirstTripDateNotifier.value ?? ''); + final parsedDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightFirstTripDateNotifier.value ?? ''); if (parsedDate != null) { _checkInController.text = DateFormat('dd-MM-yyyy').format(parsedDate); } // final parsedEndDate = DateTime.tryParse(flightLastTripDateNotifier.value ?? ''); - final parsedEndDate = DateFormat("dd-MM-yyyy") - .parse(flightLastTripDateNotifier.value ?? ''); + final parsedEndDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightLastTripDateNotifier.value ?? ''); if (parsedEndDate != null) { // _checkOutController.text = DateFormat('dd-MM-yyyy').format(parsedEndDate); - _checkOutController.text = - DateFormat('dd-MM-yyyy').format(parsedEndDate); + _checkOutController.text = DateFormat( + 'dd-MM-yyyy', + ).format(parsedEndDate); } // ✅ Check and set default times if empty @@ -202,17 +215,16 @@ class _AccomodationScreenState extends State { } Map getFlightTripDateRange( - List> flightData) { - final allTrips = flightData - .expand((flight) => flight['trips'] ?? []) - .whereType>() - .toList(); + List> flightData, + ) { + final allTrips = + flightData + .expand((flight) => flight['trips'] ?? []) + .whereType>() + .toList(); if (allTrips.isEmpty) { - return { - 'firstTripDate': null, - 'lastTripDate': null, - }; + return {'firstTripDate': null, 'lastTripDate': null}; } allTrips.sort((a, b) { @@ -230,7 +242,7 @@ class _AccomodationScreenState extends State { return { 'firstTripDate': firstTrip['date'], 'lastTripDate': lastTrip['date'], - 'firstToDestination': firstTripToDestination['to_place'] + 'firstToDestination': firstTripToDestination['to_place'], }; } @@ -244,7 +256,7 @@ class _AccomodationScreenState extends State { "checkin_date", "checkin_time", "checkout_date", - "checkout_time" + "checkout_time", ]; // Check validation for each field @@ -284,8 +296,9 @@ class _AccomodationScreenState extends State { checkOutTime.toString().isNotEmpty) { try { final checkInDateTime = formatTime.parse("$checkIn $checkInTime"); - final checkOutDateTime = - formatTime.parse("$checkOut $checkOutTime"); + final checkOutDateTime = formatTime.parse( + "$checkOut $checkOutTime", + ); if (!checkOutDateTime.isAfter(checkInDateTime)) { errorMessages["checkout_time"] = @@ -359,60 +372,51 @@ class _AccomodationScreenState extends State { @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isMobile = sizingInfo.isMobile; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + 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.all(28.0), - child: Center( - child: Column(children: _buildAccomadtionForm(isDesktop)), + return Container( + // color: Color(0xFFF4F4FB), + child: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(28.0), + child: Center( + child: Column(children: _buildAccomadtionForm(isDesktop)), + ), ), - ) - ], + ], + ), ), ), - ), - ); - }); + ); + }, + ); } List _buildAccomadtionForm(isDesktop) { return [ isDesktop - ? Row( - children: _buildFirstRow(isDesktop), - ) - : Column( - children: _buildFirstRow(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), - ), + ? Row(children: _buildSecondRow(isDesktop)) + : Column(children: _buildSecondRow(isDesktop)), SizedBox(height: 10), isDesktop - ? Row( - children: _buildThirdRow(isDesktop), - ) - : Column( - children: _buildThirdRow(isDesktop), - ), + ? Row(children: _buildThirdRow(isDesktop)) + : Column(children: _buildThirdRow(isDesktop)), ]; } @@ -432,24 +436,25 @@ class _AccomodationScreenState extends State { // ), // ], // ), - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Destination *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _destinationFocused, isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( @@ -475,29 +480,26 @@ class _AccomodationScreenState extends State { ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + 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)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( @@ -583,18 +585,61 @@ class _AccomodationScreenState extends State { initialTime: _selectedCheckInTime ?? TimeOfDay.now(), ); - if (pickedTime != null && pickedTime != _selectedCheckInTime) { + 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; - // 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), - ); + + 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 @@ -647,10 +692,11 @@ class _AccomodationScreenState extends State { // : firstDate; DateTime firstDate = checkInDate; - DateTime initialDate = _selectedCheckOutDate != null && - _selectedCheckOutDate!.isAfter(firstDate) - ? _selectedCheckOutDate! - : firstDate; + DateTime initialDate = + _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(firstDate) + ? _selectedCheckOutDate! + : firstDate; final pickedDate = await showDatePicker( context: context, @@ -662,8 +708,9 @@ class _AccomodationScreenState extends State { if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; - _checkOutController.text = - DateFormat('dd-MM-yyyy').format(pickedDate); + _checkOutController.text = DateFormat( + 'dd-MM-yyyy', + ).format(pickedDate); errorMessages.remove("checkout_date"); }); } @@ -681,8 +728,13 @@ class _AccomodationScreenState extends State { // 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), + DateTime( + now.year, + now.month, + now.day, + pickedTime.hour, + pickedTime.minute, + ), ); _checkOutTimeController.text = formattedTime; errorMessages.remove("checkout_time"); @@ -697,9 +749,10 @@ class _AccomodationScreenState extends State { Text( "Check-in *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -720,8 +773,11 @@ class _AccomodationScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -737,21 +793,17 @@ class _AccomodationScreenState extends State { ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + 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)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -760,7 +812,10 @@ class _AccomodationScreenState extends State { child: SizedBox( height: 40, child: GestureDetector( - onTap: () => _selectCheckInTime(context), + onTap: () { + _checkInTimeController.text = ""; + _selectCheckInTime(context); + }, child: AbsorbPointer( child: TextField( focusNode: _checkInTimeFocusNode, @@ -772,8 +827,11 @@ class _AccomodationScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -789,21 +847,17 @@ class _AccomodationScreenState extends State { ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + 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)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -824,8 +878,11 @@ class _AccomodationScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -841,21 +898,17 @@ class _AccomodationScreenState extends State { ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + 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)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -876,8 +929,11 @@ class _AccomodationScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -904,17 +960,19 @@ class _AccomodationScreenState extends State { Text( "Comments", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + 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.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( @@ -934,9 +992,7 @@ class _AccomodationScreenState extends State { ], ), if (isDesktop) Spacer(), - SizedBox( - height: 5, - ), + SizedBox(height: 5), Column( children: [ Row( @@ -957,9 +1013,7 @@ class _AccomodationScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( @@ -968,7 +1022,6 @@ class _AccomodationScreenState extends State { ), ), SizedBox(width: 10), // Space between buttons - // Save Changes Button ElevatedButton( onPressed: () { @@ -976,9 +1029,7 @@ class _AccomodationScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index 2fe0357..f7c3407 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -568,9 +568,9 @@ class FlightScreenState extends State { } bool validateFields() { - errorMessages.clear(); // Reset errors + // errorMessages.clear(); // Reset errors - rowCount = 1; // Default row count for One-way + rowCount = 1; // Default row count for One-way if (selectedTripType == "Roundtrip") { rowCount = 2; // Fixed for Roundtrip } else if (selectedTripType == "Multitrip") { @@ -594,23 +594,21 @@ class FlightScreenState extends State { // errorMessages["to_place_$i"] = "Change Destination"; // } - if (selectedTo[i] == null) { errorMessages["to_place_$i"] = "Required"; print("Error: to_place_$i -> Required (selectedTo[$i] is null)"); } else if (selectedFrom[i] == selectedTo[i]) { - - if(selectedTripType == "Roundtrip"){ + if (selectedTripType == "Roundtrip") { errorMessages["to_place_1"] = "Change Destination"; - } - else{ + } else { errorMessages["to_place_$i"] = "Change Destination"; } - print("Error: to_place_$i -> Change Destination (selectedFrom[$i] == selectedTo[$i])"); + print( + "Error: to_place_$i -> Change Destination (selectedFrom[$i] == selectedTo[$i])", + ); } - // if (textControllers["_to${i}Controller"]?.text.trim().isEmpty ?? true) { // errorMessages["to_place_$i"] = "Required"; // } @@ -620,13 +618,8 @@ class FlightScreenState extends State { if (textControllers["_time${i}Controller"]?.text.trim().isEmpty ?? true) { errorMessages["time_$i"] = "Required"; } - - } - - - setState(() {}); // Update UI to show error messages return errorMessages @@ -1238,41 +1231,73 @@ class FlightScreenState extends State { initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), ); - if (pickedTime != null && pickedTime != _selectedCheckOutTime) { + if (pickedTime != null) { + final now = DateTime.now(); + + // Parse the selected date + final dateText = textControllers["_date${index}Controller"]?.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["time_$index"] = "You can't select a past time."; + }); + return; + } + + // ✅ Valid time selection 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, - ), - ); - // _timeController.text = formattedTime; - textControllers["_time${index}Controller"]?.text = formattedTime; - onPicked(); + final formattedTime = DateFormat('HH:mm').format(selectedDateTime); + textControllers["_time${index}Controller"]?.text = formattedTime; + // errorMessages["time_$index"] = ""; // clear previous error + errorMessages.remove("time_$index"); + onPicked(); // Trigger callback }); } + + // 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, + // ), + // ); + // + // // _timeController.text = formattedTime; + // textControllers["_time${index}Controller"]?.text = formattedTime; + // + // onPicked(); + // }); + // } } - // late Map countryMap; // Mapping country_code -> country_name - // late List countryCodes; // List of country codes - // countryMap = { - // for (var item in countryList) - // if (item['country_code'] != null && item['country_name'] != null) - // item['country_code'] as String: item['country_name'] as String - // }; - - // // Extract only country codes for processing - // countryCodes = countryMap.keys.toList(); - // - // selectedCountry ??= null; - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1297,7 +1322,8 @@ class FlightScreenState extends State { isCountryLoading ? Center(child: CircularProgressIndicator()) : DropdownSearch( - enabled: !(selectedTripType == "Roundtrip" && index == 2), + enabled: + !(selectedTripType == "Roundtrip" && index == 2), selectedItem: selectedFrom[index] != null ? countryMap[selectedFrom[index]] @@ -1323,25 +1349,37 @@ class FlightScreenState extends State { final airport = parts.length > 1 ? parts[1] : ''; // Extract city and code from "City (CODE)" - final cityMatch = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode); + final cityMatch = RegExp( + r'^(.*)\s+\(([^)]+)\)$', + ).firstMatch(cityAndCode); final city = cityMatch?.group(1) ?? ''; final code = cityMatch?.group(2) ?? ''; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0), + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ Text( city, - style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), Text( code, - style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), ], ), @@ -1380,26 +1418,31 @@ class FlightScreenState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 1), ), ), - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null) { - return Align( - alignment: Alignment.centerLeft,child: const Text("Select", style: TextStyle(fontSize: 12))); - } - - final parts = selectedItem.split('\n'); - final cityAndCode = parts[0]; - final airport = parts.length > 1 ? parts[1] : ''; - - return Align( + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null) { + return Align( alignment: Alignment.centerLeft, - child: Text( - cityAndCode ?? "Select", - style: const TextStyle(fontSize: 12), - ), - ); - }, + child: const Text( + "Select", + style: TextStyle(fontSize: 12), + ), + ); + } - // dropdownBuilder: + final parts = selectedItem.split('\n'); + final cityAndCode = parts[0]; + final airport = parts.length > 1 ? parts[1] : ''; + + return Align( + alignment: Alignment.centerLeft, + child: Text( + cityAndCode ?? "Select", + style: const TextStyle(fontSize: 12), + ), + ); + }, + + // dropdownBuilder: // (context, selectedItem) => Align( // alignment: Alignment.centerLeft, // child: Text( @@ -1409,6 +1452,7 @@ class FlightScreenState extends State { // ), onChanged: (String? newValue) { setState(() { + errorMessages.remove("from_place_$index"); // selectedFrom[index] = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; @@ -1420,13 +1464,11 @@ class FlightScreenState extends State { ) .key; - if(selectedTripType == "Roundtrip"){ - + if (selectedTripType == "Roundtrip") { print('rounfTo${selectedFrom[index]}'); // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; - selectedTo[2]= selectedFrom[index]!; + selectedTo[2] = selectedFrom[index]!; } - }); }, ), @@ -1477,7 +1519,8 @@ class FlightScreenState extends State { isCountryLoading ? Center(child: CircularProgressIndicator()) : DropdownSearch( - enabled: !(selectedTripType == "Roundtrip" && index == 2), + enabled: + !(selectedTripType == "Roundtrip" && index == 2), selectedItem: selectedTo[index] != null ? countryMap[selectedTo[index]] @@ -1502,27 +1545,38 @@ class FlightScreenState extends State { final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; - // Extract city and code from "City (CODE)" - final cityMatch = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode); + final cityMatch = RegExp( + r'^(.*)\s+\(([^)]+)\)$', + ).firstMatch(cityAndCode); final city = cityMatch?.group(1) ?? ''; final code = cityMatch?.group(2) ?? ''; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0), + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ Text( city, - style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), Text( code, - style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), ), ], ), @@ -1560,6 +1614,7 @@ class FlightScreenState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 1), ), ), + // dropdownBuilder: // (context, selectedItem) => Align( // alignment: Alignment.centerLeft, @@ -1568,26 +1623,30 @@ class FlightScreenState extends State { // style: TextStyle(fontSize: 12), // ), // ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null) { + return Align( + alignment: Alignment.centerLeft, + child: const Text( + "Select", + style: TextStyle(fontSize: 12), + ), + ); + } - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null) { - return Align( - alignment: Alignment.centerLeft,child: const Text("Select", style: TextStyle(fontSize: 12))); - } + final parts = selectedItem.split('\n'); + final cityAndCode = parts[0]; + final airport = parts.length > 1 ? parts[1] : ''; - final parts = selectedItem.split('\n'); - final cityAndCode = parts[0]; - final airport = parts.length > 1 ? parts[1] : ''; - - return Align( - alignment: Alignment.centerLeft, - child: Text( - cityAndCode ?? "Select", - style: const TextStyle(fontSize: 12), - ), - ); - }, - onChanged: (String? newValue) { + return Align( + alignment: Alignment.centerLeft, + child: Text( + cityAndCode ?? "Select", + style: const TextStyle(fontSize: 12), + ), + ); + }, + onChanged: (String? newValue) { setState(() { errorMessages.remove("to_place_$index"); selectedTo[index] = @@ -1599,11 +1658,10 @@ class FlightScreenState extends State { print(selectedTo[index]); - if(selectedTripType == "Roundtrip"){ - + if (selectedTripType == "Roundtrip") { print('rounfTo${selectedTo[index]}'); // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; - selectedFrom[2]= selectedTo[index]!; + selectedFrom[2] = selectedTo[index]!; } }); }, @@ -1612,7 +1670,10 @@ class FlightScreenState extends State { ), if (errorMessages["to_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( errorMessages["to_place_$index"]! , style: TextStyle(color: Colors.red, fontSize: 12)), + Text( + errorMessages["to_place_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), ], ], ), @@ -1835,6 +1896,7 @@ class FlightScreenState extends State { height: 40, child: GestureDetector( onTap: () { + textControllers["_time${index}Controller"]?.text = ""; _clearError("time_$index"); // validateTimeDifference(index); // _selectCheckOutTime(context); diff --git a/lib/Screens/itnerary/insurance.dart b/lib/Screens/itnerary/insurance.dart index 0a23644..b5e50e1 100644 --- a/lib/Screens/itnerary/insurance.dart +++ b/lib/Screens/itnerary/insurance.dart @@ -15,13 +15,14 @@ class InsuranceScreen extends StatefulWidget { final Map? selectedItem; final String? loginUser; - InsuranceScreen( - {required this.onClose, - required this.apiData, - required this.onSaveInsurance, - required this.selectedItem, - required this.loginUser, - required this.flightData}); + InsuranceScreen({ + required this.onClose, + required this.apiData, + required this.onSaveInsurance, + required this.selectedItem, + required this.loginUser, + required this.flightData, + }); @override _InsuranceScreenState createState() => _InsuranceScreenState(); @@ -45,7 +46,8 @@ class _InsuranceScreenState extends State { late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _startdateController = TextEditingController(); late TextEditingController _endDateController = TextEditingController(); - late TextEditingController _insuranceCommentsController = TextEditingController(); + late TextEditingController _insuranceCommentsController = + TextEditingController(); late TextEditingController _nomineeController = TextEditingController(); bool _isHotelNameFocused = false; @@ -64,7 +66,7 @@ class _InsuranceScreenState extends State { "start_date": _startdateController.text, "end_date": _endDateController.text, "comments": _insuranceCommentsController.text, - "nominee_name" : _nomineeController.text, + "nominee_name": _nomineeController.text, "created_by": widget.loginUser, "updated_by": widget.loginUser, }; @@ -108,14 +110,18 @@ class _InsuranceScreenState extends State { }); }); - _insuranceCommentsController = - TextEditingController(text: widget.selectedItem?["comments"] ?? ""); - _startdateController = - TextEditingController(text: widget.selectedItem?["start_date"] ?? ""); - _endDateController = - TextEditingController(text: widget.selectedItem?['end_date'] ?? ""); - _nomineeController = - TextEditingController(text: widget.selectedItem?['nominee_name'] ?? ""); + _insuranceCommentsController = TextEditingController( + text: widget.selectedItem?["comments"] ?? "", + ); + _startdateController = TextEditingController( + text: widget.selectedItem?["start_date"] ?? "", + ); + _endDateController = TextEditingController( + text: widget.selectedItem?['end_date'] ?? "", + ); + _nomineeController = TextEditingController( + text: widget.selectedItem?['nominee_name'] ?? "", + ); // Set the selected value if available if (widget.selectedItem != null && @@ -135,17 +141,20 @@ class _InsuranceScreenState extends State { // ✅ Only set controller after value is updated // final parsedDate = // DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); - final parsedDate = DateFormat("dd-MM-yyyy") - .parse(flightFirstTripDateNotifier.value ?? ''); + final parsedDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightFirstTripDateNotifier.value ?? ''); if (parsedDate != null) { _startdateController.text = DateFormat('dd-MM-yyyy').format(parsedDate); } - final parsedEndDate = DateFormat("dd-MM-yyyy") - .parse(flightLastTripDateNotifier.value ?? ''); + final parsedEndDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightLastTripDateNotifier.value ?? ''); if (parsedEndDate != null) { - _endDateController.text = - DateFormat('dd-MM-yyyy').format(parsedEndDate); + _endDateController.text = DateFormat( + 'dd-MM-yyyy', + ).format(parsedEndDate); print("parsedDate"); print(parsedDate); @@ -156,17 +165,16 @@ class _InsuranceScreenState extends State { } Map getFlightTripDateRange( - List> flightData) { - final allTrips = flightData - .expand((flight) => flight['trips'] ?? []) - .whereType>() - .toList(); + List> flightData, + ) { + final allTrips = + flightData + .expand((flight) => flight['trips'] ?? []) + .whereType>() + .toList(); if (allTrips.isEmpty) { - return { - 'firstTripDate': null, - 'lastTripDate': null, - }; + return {'firstTripDate': null, 'lastTripDate': null}; } allTrips.sort((a, b) { @@ -199,7 +207,7 @@ class _InsuranceScreenState extends State { List requiredFields = [ "type_of_insurance", "start_date", - "end_date" + "end_date", ]; // Check validation for each field @@ -267,30 +275,33 @@ class _InsuranceScreenState extends State { @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isMobile = sizingInfo.isMobile; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + 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.all(28.0), - child: Center( - child: Column(children: _buildAccomadtionForm(isDesktop)), + 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(bool isDesktop) { @@ -303,7 +314,7 @@ class _InsuranceScreenState extends State { List> rowBuilders = [ // _builClassType(isDesktop), - _buildSecondRow(isDesktop) + _buildSecondRow(isDesktop), ]; return [ @@ -318,51 +329,52 @@ class _InsuranceScreenState extends State { ]; } - List _buildFirstRow(isDesktop) { - return [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Insurance Type", // not in use - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - isDesktop - ? Row(children: _buildTripType(isDesktop)) - : Column(children: _buildTripType(isDesktop)) - ], - ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), - ]; - } + // List _buildFirstRow(isDesktop) { + // return [ + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Insurance Type", // not in use + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w500, + // 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(); + 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)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -375,9 +387,10 @@ class _InsuranceScreenState extends State { CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownButtonFormField( @@ -386,21 +399,23 @@ class _InsuranceScreenState extends State { style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), // Proper padding ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedInsuranceType = newValue; - if (selectedInsuranceType!.isNotEmpty) { - errorMessages.remove("type_of_insurance"); - } - }); + onChanged: + purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedInsuranceType = newValue; + if (selectedInsuranceType!.isNotEmpty) { + errorMessages.remove("type_of_insurance"); + } + }); - print(selectedInsuranceType); - } - : null, + print(selectedInsuranceType); + } + : null, items: dropdownItems, ), @@ -409,16 +424,17 @@ class _InsuranceScreenState extends State { ]; } - List _buildInsuranceTypeDropdown(bool isDesktop) { // List purposeList = widget.apiData?['insurance_type_of_insurance'] ?? []; - List> purposeList = (widget.apiData?['insurance_type_of_insurance'] as List?) - ?.map((e) => Map.from(e as Map)) - .toList() ?? []; + List> purposeList = + (widget.apiData?['insurance_type_of_insurance'] as List?) + ?.map((e) => Map.from(e as Map)) + .toList() ?? + []; // Find selected item object based on key Map? selectedItem = purposeList.firstWhere( - (item) => item['dropdown_key'] == selectedInsuranceType, + (item) => item['dropdown_key'] == selectedInsuranceType, orElse: () => {}, ); @@ -476,17 +492,20 @@ class _InsuranceScreenState extends State { }, selectedItem: selectedItem!.isNotEmpty ? selectedItem : null, itemAsString: (item) => item['dropdown_value'] ?? '', - onChanged: purposeList.isNotEmpty - ? (Map? newItem) { - if (newItem != null) { - setState(() { - selectedInsuranceType = newItem['dropdown_key']; - errorMessages.remove("type_of_insurance"); - }); - print("Selected Insurance Type: $selectedInsuranceType"); - } - } - : null, + onChanged: + purposeList.isNotEmpty + ? (Map? newItem) { + if (newItem != null) { + setState(() { + selectedInsuranceType = newItem['dropdown_key']; + errorMessages.remove("type_of_insurance"); + }); + print( + "Selected Insurance Type: $selectedInsuranceType", + ); + } + } + : null, items: purposeList, ), ), @@ -494,26 +513,29 @@ class _InsuranceScreenState extends State { ]; } - - List _buildSecondRow(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(); + 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)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -569,8 +591,9 @@ class _InsuranceScreenState extends State { if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; - _startdateController.text = - DateFormat('dd-MM-yyyy').format(pickedDate); + _startdateController.text = DateFormat( + 'dd-MM-yyyy', + ).format(pickedDate); }); } } @@ -594,10 +617,11 @@ class _InsuranceScreenState extends State { // : firstDate; DateTime firstDate = checkInDate; - DateTime initialDate = _selectedCheckOutDate != null && - _selectedCheckOutDate!.isAfter(firstDate) - ? _selectedCheckOutDate! - : firstDate; + DateTime initialDate = + _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(firstDate) + ? _selectedCheckOutDate! + : firstDate; final pickedDate = await showDatePicker( context: context, @@ -624,7 +648,6 @@ class _InsuranceScreenState extends State { } } - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -643,7 +666,7 @@ class _InsuranceScreenState extends State { CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.17 : null, + width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, child: SizedBox( height: 35, width: double.infinity, @@ -653,53 +676,56 @@ class _InsuranceScreenState extends State { style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), decoration: const InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), ), hint: Text( "Select ", style: GoogleFonts.poppins(color: Colors.grey, fontSize: 13), ), - items: purposeList.map>((item) { - return DropdownMenuItem( - value: item['dropdown_key'], - child: Text( - item['dropdown_value'] ?? '', - style: GoogleFonts.poppins(fontSize: 12), - ), - ); - }).toList(), - onChanged: purposeList.isNotEmpty - ? (String? newValue) { - setState(() { - selectedInsuranceType = newValue; - if ((selectedInsuranceType ?? '').isNotEmpty) { - errorMessages.remove("type_of_insurance"); - } - }); - print("Selected Insurance Type: $selectedInsuranceType"); - } - : null, + items: + purposeList.map>((item) { + return DropdownMenuItem( + value: item['dropdown_key'], + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins(fontSize: 12), + ), + ); + }).toList(), + onChanged: + purposeList.isNotEmpty + ? (String? newValue) { + setState(() { + selectedInsuranceType = newValue; + if ((selectedInsuranceType ?? '').isNotEmpty) { + errorMessages.remove("type_of_insurance"); + } + }); + print( + "Selected Insurance Type: $selectedInsuranceType", + ); + } + : null, ), ), ), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Nominee", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( @@ -726,27 +752,23 @@ class _InsuranceScreenState extends State { ), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + 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)), + 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, + width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, child: SizedBox( height: 40, child: GestureDetector( @@ -754,8 +776,9 @@ class _InsuranceScreenState extends State { await _selectCheckOutDate(context); if (_startdateController.text.isNotEmpty) { setState(() { - errorMessages - .remove("start_date"); // Removes the key completely + errorMessages.remove( + "start_date", + ); // Removes the key completely }); } }, @@ -770,8 +793,11 @@ class _InsuranceScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -780,34 +806,27 @@ class _InsuranceScreenState extends State { ), if (errorMessages["start_date"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "End Date *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + 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, + width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, child: SizedBox( height: 40, child: GestureDetector( @@ -843,8 +862,11 @@ class _InsuranceScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -861,12 +883,7 @@ class _InsuranceScreenState extends State { ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), ]; } @@ -878,17 +895,19 @@ class _InsuranceScreenState extends State { Text( "Comments", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + 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 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.31 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( @@ -908,9 +927,7 @@ class _InsuranceScreenState extends State { ], ), if (isDesktop) Spacer(), - SizedBox( - height: 5, - ), + SizedBox(height: 5), Column( children: [ Row( @@ -931,9 +948,7 @@ class _InsuranceScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( @@ -942,7 +957,6 @@ class _InsuranceScreenState extends State { ), ), SizedBox(width: 10), // Space between buttons - // Save Changes Button ElevatedButton( onPressed: () { @@ -950,9 +964,7 @@ class _InsuranceScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index 3c7c755..248938c 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -480,23 +480,66 @@ class _TrainScreenState extends State { initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), ); - if (pickedTime != null && pickedTime != _selectedCheckOutTime) { + if (pickedTime != null) { + final now = DateTime.now(); + + // Parse the selected date + final dateText = _dateController.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["time"] = "You can't select a past time."; + }); + return; + } + + // ✅ Valid time selection 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, - ), - ); + + final formattedTime = DateFormat('HH:mm').format(selectedDateTime); _timeController.text = formattedTime; + // errorMessages["time_$index"] = ""; // clear previous error + errorMessages.remove("time"); }); } + + // 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, + // ), + // ); + // _timeController.text = formattedTime; + // }); + // } } //---------------------------------------------- @@ -831,7 +874,10 @@ class _TrainScreenState extends State { child: SizedBox( height: 40, child: GestureDetector( - onTap: () => _selectCheckOutTime(context), + onTap: () { + _timeController.text = ""; + _selectCheckOutTime(context); + }, child: AbsorbPointer( child: TextField( focusNode: _timeFocusNode, @@ -856,7 +902,10 @@ class _TrainScreenState extends State { ), if (errorMessages["time"] != null) ...[ SizedBox(height: 5), // Space before error message - Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), + Text( + errorMessages["time"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), ], ], ), diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index 63a6f72..5cead2f 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -3008,8 +3008,9 @@ class CreateNewPlansState extends State { labelText: "Trip Name *", labelStyle: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), + fontWeight: FontWeight.w400, + color: Colors.grey, + // color: Color(0xFF575A74), ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, diff --git a/pubspec.lock b/pubspec.lock index 94a6c9d..e4884cf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" bcrypt: dependency: "direct main" description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" ffi: dependency: transitive description: @@ -465,18 +465,18 @@ packages: dependency: "direct main" description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: @@ -1014,10 +1014,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "15.0.0" vsc_quill_delta_to_html: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index cbf239d..3b6d7f9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: responsive_builder: ^0.7.1 shared_preferences: ^2.5.2 easy_stepper: ^0.8.5+1 - intl: ^0.19.0 + intl: ^0.20.2 dropdown_search: ^5.0.6 file_picker: ^10.0.0 bcrypt: ^1.1.3