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