Current Time Validations

This commit is contained in:
venbaittech 2025-06-09 12:16:02 +05:30
parent 5ccb201b7a
commit de960412a0
7 changed files with 679 additions and 504 deletions

View File

@ -15,13 +15,14 @@ class AccomodationScreen extends StatefulWidget {
final String? loginUser; final String? loginUser;
final String? tripType; final String? tripType;
AccomodationScreen( AccomodationScreen({
{required this.onClose, required this.onClose,
required this.onSaveAccomadation, required this.onSaveAccomadation,
required this.selectedItem, required this.selectedItem,
required this.loginUser, required this.loginUser,
required this.flightData, required this.flightData,
this.tripType}); this.tripType,
});
@override @override
_AccomodationScreenState createState() => _AccomodationScreenState(); _AccomodationScreenState createState() => _AccomodationScreenState();
@ -107,15 +108,23 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
super.initState(); super.initState();
_addFocusListener( _addFocusListener(
_destinationFocusNode, (focus) => _destinationFocused = focus); _destinationFocusNode,
(focus) => _destinationFocused = focus,
);
_addFocusListener( _addFocusListener(
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus); _hotelNameFocusNode,
(focus) => _isHotelNameFocused = focus,
);
_addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus); _addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus);
_addFocusListener( _addFocusListener(
_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus); _checkInTimeFocusNode,
(focus) => _checkInTimeFocus = focus,
);
_addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus); _addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus);
_addFocusListener( _addFocusListener(
_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus); _checkOutTimeFocusNode,
(focus) => _checkOutTimeFocus = focus,
);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
_destinationController = initController("destination_city"); _destinationController = initController("destination_city");
@ -149,25 +158,29 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
flightFirstToDestinationNotifier.value = result['firstToDestination']; flightFirstToDestinationNotifier.value = result['firstToDestination'];
print( print(
"flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value "); "flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value ",
);
// Only set controller after value is updated // Only set controller after value is updated
// final parsedDate = DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); // final parsedDate = DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat("dd-MM-yyyy") final parsedDate = DateFormat(
.parse(flightFirstTripDateNotifier.value ?? ''); "dd-MM-yyyy",
).parse(flightFirstTripDateNotifier.value ?? '');
if (parsedDate != null) { if (parsedDate != null) {
_checkInController.text = DateFormat('dd-MM-yyyy').format(parsedDate); _checkInController.text = DateFormat('dd-MM-yyyy').format(parsedDate);
} }
// final parsedEndDate = DateTime.tryParse(flightLastTripDateNotifier.value ?? ''); // final parsedEndDate = DateTime.tryParse(flightLastTripDateNotifier.value ?? '');
final parsedEndDate = DateFormat("dd-MM-yyyy") final parsedEndDate = DateFormat(
.parse(flightLastTripDateNotifier.value ?? ''); "dd-MM-yyyy",
).parse(flightLastTripDateNotifier.value ?? '');
if (parsedEndDate != null) { if (parsedEndDate != null) {
// _checkOutController.text = DateFormat('dd-MM-yyyy').format(parsedEndDate); // _checkOutController.text = DateFormat('dd-MM-yyyy').format(parsedEndDate);
_checkOutController.text = _checkOutController.text = DateFormat(
DateFormat('dd-MM-yyyy').format(parsedEndDate); 'dd-MM-yyyy',
).format(parsedEndDate);
} }
// Check and set default times if empty // Check and set default times if empty
@ -202,17 +215,16 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
} }
Map<String, String?> getFlightTripDateRange( Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) { List<Map<String, dynamic>> flightData,
final allTrips = flightData ) {
final allTrips =
flightData
.expand((flight) => flight['trips'] ?? []) .expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>() .whereType<Map<String, dynamic>>()
.toList(); .toList();
if (allTrips.isEmpty) { if (allTrips.isEmpty) {
return { return {'firstTripDate': null, 'lastTripDate': null};
'firstTripDate': null,
'lastTripDate': null,
};
} }
allTrips.sort((a, b) { allTrips.sort((a, b) {
@ -230,7 +242,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
return { return {
'firstTripDate': firstTrip['date'], 'firstTripDate': firstTrip['date'],
'lastTripDate': lastTrip['date'], 'lastTripDate': lastTrip['date'],
'firstToDestination': firstTripToDestination['to_place'] 'firstToDestination': firstTripToDestination['to_place'],
}; };
} }
@ -244,7 +256,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
"checkin_date", "checkin_date",
"checkin_time", "checkin_time",
"checkout_date", "checkout_date",
"checkout_time" "checkout_time",
]; ];
// Check validation for each field // Check validation for each field
@ -284,8 +296,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
checkOutTime.toString().isNotEmpty) { checkOutTime.toString().isNotEmpty) {
try { try {
final checkInDateTime = formatTime.parse("$checkIn $checkInTime"); final checkInDateTime = formatTime.parse("$checkIn $checkInTime");
final checkOutDateTime = final checkOutDateTime = formatTime.parse(
formatTime.parse("$checkOut $checkOutTime"); "$checkOut $checkOutTime",
);
if (!checkOutDateTime.isAfter(checkInDateTime)) { if (!checkOutDateTime.isAfter(checkInDateTime)) {
errorMessages["checkout_time"] = errorMessages["checkout_time"] =
@ -359,9 +372,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile; bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container( return Container(
// color: Color(0xFFF4F4FB), // color: Color(0xFFF4F4FB),
@ -376,43 +391,32 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: Center( child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)), child: Column(children: _buildAccomadtionForm(isDesktop)),
), ),
) ),
], ],
), ),
), ),
), ),
); );
}); },
);
} }
List<Widget> _buildAccomadtionForm(isDesktop) { List<Widget> _buildAccomadtionForm(isDesktop) {
return [ return [
isDesktop isDesktop
? Row( ? Row(children: _buildFirstRow(isDesktop))
children: _buildFirstRow(isDesktop), : Column(children: _buildFirstRow(isDesktop)),
)
: Column(
children: _buildFirstRow(isDesktop),
),
SizedBox(height: 10), // Spacing between first and second row SizedBox(height: 10), // Spacing between first and second row
isDesktop isDesktop
? Row( ? Row(children: _buildSecondRow(isDesktop))
children: _buildSecondRow(isDesktop), : Column(children: _buildSecondRow(isDesktop)),
)
: Column(
children: _buildSecondRow(isDesktop),
),
SizedBox(height: 10), SizedBox(height: 10),
isDesktop isDesktop
? Row( ? Row(children: _buildThirdRow(isDesktop))
children: _buildThirdRow(isDesktop), : Column(children: _buildThirdRow(isDesktop)),
)
: Column(
children: _buildThirdRow(isDesktop),
),
]; ];
} }
@ -432,7 +436,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// ), // ),
// ], // ],
// ), // ),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -441,13 +444,15 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _destinationFocused, isFocused: _destinationFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
@ -475,12 +480,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
], ],
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -489,13 +489,15 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
@ -583,18 +585,61 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
initialTime: _selectedCheckInTime ?? TimeOfDay.now(), 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(() { setState(() {
_selectedCheckInTime = pickedTime; _selectedCheckInTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now(); final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
);
_checkInTimeController.text = formattedTime; _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 //-------------------------------Check-In End
@ -647,7 +692,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// : firstDate; // : firstDate;
DateTime firstDate = checkInDate; DateTime firstDate = checkInDate;
DateTime initialDate = _selectedCheckOutDate != null && DateTime initialDate =
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(firstDate) _selectedCheckOutDate!.isAfter(firstDate)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: firstDate; : firstDate;
@ -662,8 +708,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) { if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() { setState(() {
_selectedCheckOutDate = pickedDate; _selectedCheckOutDate = pickedDate;
_checkOutController.text = _checkOutController.text = DateFormat(
DateFormat('dd-MM-yyyy').format(pickedDate); 'dd-MM-yyyy',
).format(pickedDate);
errorMessages.remove("checkout_date"); errorMessages.remove("checkout_date");
}); });
} }
@ -681,8 +728,13 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// Formatting time to HH:mm (24-hour format) // Formatting time to HH:mm (24-hour format)
final now = DateTime.now(); final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format( final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour, DateTime(
pickedTime.minute), now.year,
now.month,
now.day,
pickedTime.hour,
pickedTime.minute,
),
); );
_checkOutTimeController.text = formattedTime; _checkOutTimeController.text = formattedTime;
errorMessages.remove("checkout_time"); errorMessages.remove("checkout_time");
@ -699,7 +751,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
@ -720,8 +773,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today, suffixIcon: Icon(
size: 16, color: Colors.grey), Icons.calendar_today,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -737,12 +793,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
], ],
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -751,7 +802,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
@ -760,7 +812,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckInTime(context), onTap: () {
_checkInTimeController.text = "";
_selectCheckInTime(context);
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _checkInTimeFocusNode, focusNode: _checkInTimeFocusNode,
@ -772,8 +827,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: suffixIcon: Icon(
Icon(Icons.access_time, size: 16, color: Colors.grey), Icons.access_time,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -789,12 +847,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
], ],
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -803,7 +856,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
@ -824,8 +878,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today, suffixIcon: Icon(
size: 16, color: Colors.grey), Icons.calendar_today,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -841,12 +898,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
], ],
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -855,7 +907,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
@ -876,8 +929,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: suffixIcon: Icon(
Icon(Icons.access_time, size: 16, color: Colors.grey), Icons.access_time,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -906,13 +962,15 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
@ -934,9 +992,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
], ],
), ),
if (isDesktop) Spacer(), if (isDesktop) Spacer(),
SizedBox( SizedBox(height: 5),
height: 5,
),
Column( Column(
children: [ children: [
Row( Row(
@ -957,9 +1013,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
child: Text( child: Text(
@ -968,7 +1022,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
), ),
), ),
SizedBox(width: 10), // Space between buttons SizedBox(width: 10), // Space between buttons
// Save Changes Button // Save Changes Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
@ -976,9 +1029,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
child: Text( child: Text(

View File

@ -568,7 +568,7 @@ class FlightScreenState extends State<FlightScreen> {
} }
bool validateFields() { 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") { if (selectedTripType == "Roundtrip") {
@ -594,23 +594,21 @@ class FlightScreenState extends State<FlightScreen> {
// errorMessages["to_place_$i"] = "Change Destination"; // errorMessages["to_place_$i"] = "Change Destination";
// } // }
if (selectedTo[i] == null) { if (selectedTo[i] == null) {
errorMessages["to_place_$i"] = "Required"; errorMessages["to_place_$i"] = "Required";
print("Error: to_place_$i -> Required (selectedTo[$i] is null)"); print("Error: to_place_$i -> Required (selectedTo[$i] is null)");
} else if (selectedFrom[i] == selectedTo[i]) { } else if (selectedFrom[i] == selectedTo[i]) {
if (selectedTripType == "Roundtrip") {
if(selectedTripType == "Roundtrip"){
errorMessages["to_place_1"] = "Change Destination"; errorMessages["to_place_1"] = "Change Destination";
} } else {
else{
errorMessages["to_place_$i"] = "Change Destination"; 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) { // if (textControllers["_to${i}Controller"]?.text.trim().isEmpty ?? true) {
// errorMessages["to_place_$i"] = "Required"; // errorMessages["to_place_$i"] = "Required";
// } // }
@ -620,13 +618,8 @@ class FlightScreenState extends State<FlightScreen> {
if (textControllers["_time${i}Controller"]?.text.trim().isEmpty ?? true) { if (textControllers["_time${i}Controller"]?.text.trim().isEmpty ?? true) {
errorMessages["time_$i"] = "Required"; errorMessages["time_$i"] = "Required";
} }
} }
setState(() {}); // Update UI to show error messages setState(() {}); // Update UI to show error messages
return errorMessages return errorMessages
@ -1238,40 +1231,72 @@ class FlightScreenState extends State<FlightScreen> {
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
); );
if (pickedTime != null && pickedTime != _selectedCheckOutTime) { if (pickedTime != null) {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now(); final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime( // Parse the selected date
now.year, final dateText = textControllers["_date${index}Controller"]?.text ?? "";
now.month, final selectedDate = DateFormat(
now.day, '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.hour,
pickedTime.minute, pickedTime.minute,
),
); );
// _timeController.text = formattedTime;
textControllers["_time${index}Controller"]?.text = formattedTime;
onPicked(); // 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;
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
}); });
} }
}
// late Map<String, String> countryMap; // Mapping country_code -> country_name // if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
// late List<String> countryCodes; // List of country codes // setState(() {
// countryMap = { // _selectedCheckOutTime = pickedTime;
// for (var item in countryList) // // Formatting time to HH:mm (24-hour format)
// if (item['country_code'] != null && item['country_name'] != null) // final now = DateTime.now();
// item['country_code'] as String: item['country_name'] as String // final formattedTime = DateFormat('HH:mm').format(
// }; // DateTime(
// now.year,
// // Extract only country codes for processing // now.month,
// countryCodes = countryMap.keys.toList(); // now.day,
// pickedTime.hour,
// pickedTime.minute,
// ),
// );
// //
// selectedCountry ??= null; // // _timeController.text = formattedTime;
// textControllers["_time${index}Controller"]?.text = formattedTime;
//
// onPicked();
// });
// }
}
return [ return [
Column( Column(
@ -1297,7 +1322,8 @@ class FlightScreenState extends State<FlightScreen> {
isCountryLoading isCountryLoading
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: DropdownSearch<String>( : DropdownSearch<String>(
enabled: !(selectedTripType == "Roundtrip" && index == 2), enabled:
!(selectedTripType == "Roundtrip" && index == 2),
selectedItem: selectedItem:
selectedFrom[index] != null selectedFrom[index] != null
? countryMap[selectedFrom[index]] ? countryMap[selectedFrom[index]]
@ -1323,25 +1349,37 @@ class FlightScreenState extends State<FlightScreen> {
final airport = parts.length > 1 ? parts[1] : ''; final airport = parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)" // 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 city = cityMatch?.group(1) ?? '';
final code = cityMatch?.group(2) ?? ''; final code = cityMatch?.group(2) ?? '';
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0), padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
city, city,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
), ),
Text( Text(
code, code,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
), ),
], ],
), ),
@ -1383,7 +1421,12 @@ class FlightScreenState extends State<FlightScreen> {
dropdownBuilder: (context, selectedItem) { dropdownBuilder: (context, selectedItem) {
if (selectedItem == null) { if (selectedItem == null) {
return Align( return Align(
alignment: Alignment.centerLeft,child: const Text("Select", style: TextStyle(fontSize: 12))); alignment: Alignment.centerLeft,
child: const Text(
"Select",
style: TextStyle(fontSize: 12),
),
);
} }
final parts = selectedItem.split('\n'); final parts = selectedItem.split('\n');
@ -1409,6 +1452,7 @@ class FlightScreenState extends State<FlightScreen> {
// ), // ),
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
errorMessages.remove("from_place_$index");
// selectedFrom[index] = countryMap.entries // selectedFrom[index] = countryMap.entries
// .firstWhere((entry) => entry.value == newValue) // .firstWhere((entry) => entry.value == newValue)
// .key; // .key;
@ -1420,13 +1464,11 @@ class FlightScreenState extends State<FlightScreen> {
) )
.key; .key;
if(selectedTripType == "Roundtrip"){ if (selectedTripType == "Roundtrip") {
print('rounfTo${selectedFrom[index]}'); print('rounfTo${selectedFrom[index]}');
// textControllers["_to${index+1}Controller"]?.text = 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<FlightScreen> {
isCountryLoading isCountryLoading
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: DropdownSearch<String>( : DropdownSearch<String>(
enabled: !(selectedTripType == "Roundtrip" && index == 2), enabled:
!(selectedTripType == "Roundtrip" && index == 2),
selectedItem: selectedItem:
selectedTo[index] != null selectedTo[index] != null
? countryMap[selectedTo[index]] ? countryMap[selectedTo[index]]
@ -1502,27 +1545,38 @@ class FlightScreenState extends State<FlightScreen> {
final cityAndCode = parts[0]; final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : ''; final airport = parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)" // 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 city = cityMatch?.group(1) ?? '';
final code = cityMatch?.group(2) ?? ''; final code = cityMatch?.group(2) ?? '';
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0), padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
city, city,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
), ),
Text( Text(
code, 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<FlightScreen> {
contentPadding: EdgeInsets.symmetric(horizontal: 1), contentPadding: EdgeInsets.symmetric(horizontal: 1),
), ),
), ),
// dropdownBuilder: // dropdownBuilder:
// (context, selectedItem) => Align( // (context, selectedItem) => Align(
// alignment: Alignment.centerLeft, // alignment: Alignment.centerLeft,
@ -1568,11 +1623,15 @@ class FlightScreenState extends State<FlightScreen> {
// style: TextStyle(fontSize: 12), // style: TextStyle(fontSize: 12),
// ), // ),
// ), // ),
dropdownBuilder: (context, selectedItem) { dropdownBuilder: (context, selectedItem) {
if (selectedItem == null) { if (selectedItem == null) {
return Align( return Align(
alignment: Alignment.centerLeft,child: const Text("Select", style: TextStyle(fontSize: 12))); alignment: Alignment.centerLeft,
child: const Text(
"Select",
style: TextStyle(fontSize: 12),
),
);
} }
final parts = selectedItem.split('\n'); final parts = selectedItem.split('\n');
@ -1599,11 +1658,10 @@ class FlightScreenState extends State<FlightScreen> {
print(selectedTo[index]); print(selectedTo[index]);
if(selectedTripType == "Roundtrip"){ if (selectedTripType == "Roundtrip") {
print('rounfTo${selectedTo[index]}'); print('rounfTo${selectedTo[index]}');
// textControllers["_to${index+1}Controller"]?.text = selectedFrom[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<FlightScreen> {
), ),
if (errorMessages["to_place_$index"] != null) ...[ if (errorMessages["to_place_$index"] != null) ...[
SizedBox(height: 5), // Space before error message 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<FlightScreen> {
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
textControllers["_time${index}Controller"]?.text = "";
_clearError("time_$index"); _clearError("time_$index");
// validateTimeDifference(index); // validateTimeDifference(index);
// _selectCheckOutTime(context); // _selectCheckOutTime(context);

View File

@ -15,13 +15,14 @@ class InsuranceScreen extends StatefulWidget {
final Map<String, dynamic>? selectedItem; final Map<String, dynamic>? selectedItem;
final String? loginUser; final String? loginUser;
InsuranceScreen( InsuranceScreen({
{required this.onClose, required this.onClose,
required this.apiData, required this.apiData,
required this.onSaveInsurance, required this.onSaveInsurance,
required this.selectedItem, required this.selectedItem,
required this.loginUser, required this.loginUser,
required this.flightData}); required this.flightData,
});
@override @override
_InsuranceScreenState createState() => _InsuranceScreenState(); _InsuranceScreenState createState() => _InsuranceScreenState();
@ -45,7 +46,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _startdateController = TextEditingController(); late TextEditingController _startdateController = TextEditingController();
late TextEditingController _endDateController = TextEditingController(); late TextEditingController _endDateController = TextEditingController();
late TextEditingController _insuranceCommentsController = TextEditingController(); late TextEditingController _insuranceCommentsController =
TextEditingController();
late TextEditingController _nomineeController = TextEditingController(); late TextEditingController _nomineeController = TextEditingController();
bool _isHotelNameFocused = false; bool _isHotelNameFocused = false;
@ -64,7 +66,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
"start_date": _startdateController.text, "start_date": _startdateController.text,
"end_date": _endDateController.text, "end_date": _endDateController.text,
"comments": _insuranceCommentsController.text, "comments": _insuranceCommentsController.text,
"nominee_name" : _nomineeController.text, "nominee_name": _nomineeController.text,
"created_by": widget.loginUser, "created_by": widget.loginUser,
"updated_by": widget.loginUser, "updated_by": widget.loginUser,
}; };
@ -108,14 +110,18 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}); });
}); });
_insuranceCommentsController = _insuranceCommentsController = TextEditingController(
TextEditingController(text: widget.selectedItem?["comments"] ?? ""); text: widget.selectedItem?["comments"] ?? "",
_startdateController = );
TextEditingController(text: widget.selectedItem?["start_date"] ?? ""); _startdateController = TextEditingController(
_endDateController = text: widget.selectedItem?["start_date"] ?? "",
TextEditingController(text: widget.selectedItem?['end_date'] ?? ""); );
_nomineeController = _endDateController = TextEditingController(
TextEditingController(text: widget.selectedItem?['nominee_name'] ?? ""); text: widget.selectedItem?['end_date'] ?? "",
);
_nomineeController = TextEditingController(
text: widget.selectedItem?['nominee_name'] ?? "",
);
// Set the selected value if available // Set the selected value if available
if (widget.selectedItem != null && if (widget.selectedItem != null &&
@ -135,17 +141,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// Only set controller after value is updated // Only set controller after value is updated
// final parsedDate = // final parsedDate =
// DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); // DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat("dd-MM-yyyy") final parsedDate = DateFormat(
.parse(flightFirstTripDateNotifier.value ?? ''); "dd-MM-yyyy",
).parse(flightFirstTripDateNotifier.value ?? '');
if (parsedDate != null) { if (parsedDate != null) {
_startdateController.text = DateFormat('dd-MM-yyyy').format(parsedDate); _startdateController.text = DateFormat('dd-MM-yyyy').format(parsedDate);
} }
final parsedEndDate = DateFormat("dd-MM-yyyy") final parsedEndDate = DateFormat(
.parse(flightLastTripDateNotifier.value ?? ''); "dd-MM-yyyy",
).parse(flightLastTripDateNotifier.value ?? '');
if (parsedEndDate != null) { if (parsedEndDate != null) {
_endDateController.text = _endDateController.text = DateFormat(
DateFormat('dd-MM-yyyy').format(parsedEndDate); 'dd-MM-yyyy',
).format(parsedEndDate);
print("parsedDate"); print("parsedDate");
print(parsedDate); print(parsedDate);
@ -156,17 +165,16 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
} }
Map<String, String?> getFlightTripDateRange( Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) { List<Map<String, dynamic>> flightData,
final allTrips = flightData ) {
final allTrips =
flightData
.expand((flight) => flight['trips'] ?? []) .expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>() .whereType<Map<String, dynamic>>()
.toList(); .toList();
if (allTrips.isEmpty) { if (allTrips.isEmpty) {
return { return {'firstTripDate': null, 'lastTripDate': null};
'firstTripDate': null,
'lastTripDate': null,
};
} }
allTrips.sort((a, b) { allTrips.sort((a, b) {
@ -199,7 +207,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
List<String> requiredFields = [ List<String> requiredFields = [
"type_of_insurance", "type_of_insurance",
"start_date", "start_date",
"end_date" "end_date",
]; ];
// Check validation for each field // Check validation for each field
@ -267,9 +275,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile; bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container( return Container(
// color: Color(0xFFF4F4FB), // color: Color(0xFFF4F4FB),
@ -280,17 +290,18 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
child: Column( child: Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(28.0), padding: const EdgeInsets.only(top: 30.0),
child: Center( child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)), child: Column(children: _buildAccomadtionForm(isDesktop)),
), ),
) ),
], ],
), ),
), ),
), ),
); );
}); },
);
} }
List<Widget> _buildAccomadtionForm(bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -303,7 +314,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
List<List<Widget>> rowBuilders = [ List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop), // _builClassType(isDesktop),
_buildSecondRow(isDesktop) _buildSecondRow(isDesktop),
]; ];
return [ return [
@ -318,51 +329,52 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
]; ];
} }
List<Widget> _buildFirstRow(isDesktop) { // List<Widget> _buildFirstRow(isDesktop) {
return [ // return [
Column( // Column(
crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
children: [ // children: [
Text( // Text(
"Insurance Type", // not in use // "Insurance Type", // not in use
style: GoogleFonts.poppins( // style: GoogleFonts.poppins(
fontSize: 12, // fontSize: 12,
fontWeight: FontWeight.w500, // fontWeight: FontWeight.w500,
color: Color(0xFF575A74)), // color: Color(0xFF575A74),
), // ),
SizedBox(height: 5), // ),
isDesktop // SizedBox(height: 5),
? Row(children: _buildTripType(isDesktop)) // isDesktop
: Column(children: _buildTripType(isDesktop)) // ? Row(children: _buildTripType(isDesktop))
], // : Column(children: _buildTripType(isDesktop)),
), // ],
if (isDesktop) // ),
Spacer() // if (isDesktop) Spacer() else SizedBox(height: 8),
else // ];
SizedBox( // }
height: 8,
),
];
}
List<Widget> _buildTripType(bool isDesktop) { List<Widget> _buildTripType(bool isDesktop) {
List<dynamic> purposeList = List<dynamic> purposeList =
widget.apiData?['insurance_type_of_insurance'] ?? []; widget.apiData?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems =
.map((item) => DropdownMenuItem<String>( purposeList
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", child: Text(
style: TextStyle(color: Colors.grey)), "No options available",
style: TextStyle(color: Colors.grey),
),
), ),
); );
} }
@ -375,7 +387,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
@ -386,10 +399,12 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: contentPadding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 10), // Proper padding horizontal: 10,
), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged:
purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedInsuranceType = newValue; selectedInsuranceType = newValue;
@ -409,12 +424,13 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
]; ];
} }
List<Widget> _buildInsuranceTypeDropdown(bool isDesktop) { List<Widget> _buildInsuranceTypeDropdown(bool isDesktop) {
// List<dynamic> purposeList = widget.apiData?['insurance_type_of_insurance'] ?? []; // List<dynamic> purposeList = widget.apiData?['insurance_type_of_insurance'] ?? [];
List<Map<String, dynamic>> purposeList = (widget.apiData?['insurance_type_of_insurance'] as List<dynamic>?) List<Map<String, dynamic>> purposeList =
(widget.apiData?['insurance_type_of_insurance'] as List<dynamic>?)
?.map((e) => Map<String, dynamic>.from(e as Map)) ?.map((e) => Map<String, dynamic>.from(e as Map))
.toList() ?? []; .toList() ??
[];
// Find selected item object based on key // Find selected item object based on key
Map<String, dynamic>? selectedItem = purposeList.firstWhere( Map<String, dynamic>? selectedItem = purposeList.firstWhere(
@ -476,14 +492,17 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}, },
selectedItem: selectedItem!.isNotEmpty ? selectedItem : null, selectedItem: selectedItem!.isNotEmpty ? selectedItem : null,
itemAsString: (item) => item['dropdown_value'] ?? '', itemAsString: (item) => item['dropdown_value'] ?? '',
onChanged: purposeList.isNotEmpty onChanged:
purposeList.isNotEmpty
? (Map<String, dynamic>? newItem) { ? (Map<String, dynamic>? newItem) {
if (newItem != null) { if (newItem != null) {
setState(() { setState(() {
selectedInsuranceType = newItem['dropdown_key']; selectedInsuranceType = newItem['dropdown_key'];
errorMessages.remove("type_of_insurance"); errorMessages.remove("type_of_insurance");
}); });
print("Selected Insurance Type: $selectedInsuranceType"); print(
"Selected Insurance Type: $selectedInsuranceType",
);
} }
} }
: null, : null,
@ -494,26 +513,29 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
]; ];
} }
List<Widget> _buildSecondRow(bool isDesktop) { List<Widget> _buildSecondRow(bool isDesktop) {
List<dynamic> purposeList = List<dynamic> purposeList =
widget.apiData?['insurance_type_of_insurance'] ?? []; widget.apiData?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems =
.map((item) => DropdownMenuItem<String>( purposeList
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", child: Text(
style: TextStyle(color: Colors.grey)), "No options available",
style: TextStyle(color: Colors.grey),
),
), ),
); );
} }
@ -569,8 +591,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) { if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() { setState(() {
_selectedCheckOutDate = pickedDate; _selectedCheckOutDate = pickedDate;
_startdateController.text = _startdateController.text = DateFormat(
DateFormat('dd-MM-yyyy').format(pickedDate); 'dd-MM-yyyy',
).format(pickedDate);
}); });
} }
} }
@ -594,7 +617,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// : firstDate; // : firstDate;
DateTime firstDate = checkInDate; DateTime firstDate = checkInDate;
DateTime initialDate = _selectedCheckOutDate != null && DateTime initialDate =
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(firstDate) _selectedCheckOutDate!.isAfter(firstDate)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: firstDate; : firstDate;
@ -624,7 +648,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
} }
} }
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -643,7 +666,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.17 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
width: double.infinity, width: double.infinity,
@ -653,13 +676,17 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: const InputDecoration( decoration: const InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
), ),
hint: Text( hint: Text(
"Select ", "Select ",
style: GoogleFonts.poppins(color: Colors.grey, fontSize: 13), style: GoogleFonts.poppins(color: Colors.grey, fontSize: 13),
), ),
items: purposeList.map<DropdownMenuItem<String>>((item) { items:
purposeList.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text( child: Text(
@ -668,7 +695,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
); );
}).toList(), }).toList(),
onChanged: purposeList.isNotEmpty onChanged:
purposeList.isNotEmpty
? (String? newValue) { ? (String? newValue) {
setState(() { setState(() {
selectedInsuranceType = newValue; selectedInsuranceType = newValue;
@ -676,7 +704,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
errorMessages.remove("type_of_insurance"); errorMessages.remove("type_of_insurance");
} }
}); });
print("Selected Insurance Type: $selectedInsuranceType"); print(
"Selected Insurance Type: $selectedInsuranceType",
);
} }
: null, : null,
), ),
@ -685,12 +715,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -699,7 +724,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
@ -726,12 +752,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -740,13 +761,14 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _dateFocus, isFocused: _dateFocus,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
@ -754,8 +776,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
await _selectCheckOutDate(context); await _selectCheckOutDate(context);
if (_startdateController.text.isNotEmpty) { if (_startdateController.text.isNotEmpty) {
setState(() { setState(() {
errorMessages errorMessages.remove(
.remove("start_date"); // Removes the key completely "start_date",
); // Removes the key completely
}); });
} }
}, },
@ -770,8 +793,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today, suffixIcon: Icon(
size: 16, color: Colors.grey), Icons.calendar_today,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -780,19 +806,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
if (errorMessages["start_date"] != null) ...[ if (errorMessages["start_date"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
], ],
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -801,13 +819,14 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _dateFocus, isFocused: _dateFocus,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
@ -843,8 +862,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today, suffixIcon: Icon(
size: 16, color: Colors.grey), Icons.calendar_today,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -861,12 +883,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
], ],
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
]; ];
} }
@ -880,14 +897,16 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
? MediaQuery.of(context).size.width * 0.34 isDesktop
? MediaQuery.of(context).size.width * 0.31
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
@ -908,9 +927,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
], ],
), ),
if (isDesktop) Spacer(), if (isDesktop) Spacer(),
SizedBox( SizedBox(height: 5),
height: 5,
),
Column( Column(
children: [ children: [
Row( Row(
@ -931,9 +948,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
child: Text( child: Text(
@ -942,7 +957,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
), ),
SizedBox(width: 10), // Space between buttons SizedBox(width: 10), // Space between buttons
// Save Changes Button // Save Changes Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
@ -950,9 +964,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
child: Text( child: Text(

View File

@ -480,23 +480,66 @@ class _TrainScreenState extends State<TrainScreen> {
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
); );
if (pickedTime != null && pickedTime != _selectedCheckOutTime) { if (pickedTime != null) {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now(); final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime( // Parse the selected date
now.year, final dateText = _dateController.text ?? "";
now.month, final selectedDate = DateFormat(
now.day, '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.hour,
pickedTime.minute, 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;
final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
_timeController.text = formattedTime; _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<TrainScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutTime(context), onTap: () {
_timeController.text = "";
_selectCheckOutTime(context);
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _timeFocusNode, focusNode: _timeFocusNode,
@ -856,7 +902,10 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
if (errorMessages["time"] != null) ...[ if (errorMessages["time"] != null) ...[
SizedBox(height: 5), // Space before error message 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),
),
], ],
], ],
), ),

View File

@ -3008,8 +3008,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
labelText: "Trip Name *", labelText: "Trip Name *",
labelStyle: GoogleFonts.poppins( labelStyle: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w400,
color: Color(0xFF575A74), color: Colors.grey,
// color: Color(0xFF575A74),
), ),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,

View File

@ -21,10 +21,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.12.0" version: "2.13.0"
bcrypt: bcrypt:
dependency: "direct main" dependency: "direct main"
description: description:
@ -149,10 +149,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.3"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
@ -465,18 +465,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.19.0" version: "0.20.2"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.8" version: "10.0.9"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
@ -1014,10 +1014,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.3.1" version: "15.0.0"
vsc_quill_delta_to_html: vsc_quill_delta_to_html:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -44,7 +44,7 @@ dependencies:
responsive_builder: ^0.7.1 responsive_builder: ^0.7.1
shared_preferences: ^2.5.2 shared_preferences: ^2.5.2
easy_stepper: ^0.8.5+1 easy_stepper: ^0.8.5+1
intl: ^0.19.0 intl: ^0.20.2
dropdown_search: ^5.0.6 dropdown_search: ^5.0.6
file_picker: ^10.0.0 file_picker: ^10.0.0
bcrypt: ^1.1.3 bcrypt: ^1.1.3