ts-tat/lib/Screens/userManagement/create_user/traveller_details.dart
2025-10-16 09:24:18 +05:30

4901 lines
166 KiB
Dart

// import 'dart:io' as html;
import 'dart:io' as io;
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:frontend/utils/auth_utils.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:universal_html/html.dart' as html;
import 'dart:async';
import 'dart:typed_data'; // Import for Uint8List
import 'package:flutter/foundation.dart';
import '../../../config/apiUrl.dart';
import '../../../services/apiService.dart';
import '../../../widgets/custom_user_form.dart';
import '../../../widgets/custom_user_form.dart';
import '../../../widgets/custom_user_travel.dart';
class TravellerDetails extends StatefulWidget {
final bool isDesktop;
final Map<String, TextEditingController> controllers;
final Map<String, String> errorMessages;
final bool isViewMode;
final Map<String, dynamic>? travelDetails;
final String? passportFileUrlFromApi;
final String? userIdApi;
final String fullName;
const TravellerDetails({
Key? key,
required this.controllers,
required this.errorMessages,
required this.isDesktop,
required this.isViewMode,
this.travelDetails,
this.passportFileUrlFromApi,
this.userIdApi,
required this.fullName,
}) : super(key: key);
@override
TravellerDetailsState createState() => TravellerDetailsState();
}
class TravellerDetailsState extends State<TravellerDetails> {
final ApiService apiService = ApiService();
html.File? passportFile;
late List<bool> isExpandedList;
int? expandedIndex;
bool isCountryLoading = true;
Map<String, String> errorMessages = {};
late final userId;
String? selectedFileNames;
Uint8List? passportDocumentBytes;
String? passportFileUrlFromApi;
String? _selectedTripType;
// String? selectedPurpose;
// String? selectedCountry;
String? selectedLocalIdType;
List<dynamic> countryList = [];
String? selectedDomesticSeat;
String? selectedIntenationalSeat;
String? SelectedIdType;
Map<int, TextEditingController> airlineControllers = {};
Map<int, TextEditingController> flierNumberControllers = {};
List<Map<String, dynamic>> hotelLoyaltyEntries = [];
int _rowhotelCounter = 1;
List<Map<String, dynamic>> frequentFlierEntries = [];
int _rowFlierCounter = 1;
List<Map<String, dynamic>> visaEntries = [];
int _rowVisaCounter = 1;
Map<String, String> countryMap = {};
late List<dynamic>? apiCountryData;
late List<dynamic>? apiHotelsData;
late List<dynamic>? apiAirlineCountryData;
Map<String, dynamic>? apiData;
final Map<String, TextEditingController> controllers = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
bool _iSeatPrefFocus = false;
bool _dSeatPrefFocus = false;
final FocusNode _iSeatPrefFocusNode = FocusNode();
final FocusNode _dSeatPrefFocusNode = FocusNode();
final List<String> headers = [
"Passport Details",
// "Local ID Details",
"Visa Details",
"Frequent Flyer Information",
"Hotel Loyalty Membership",
"Preferences Domestic",
"Preferences International",
"Other Details",
"Forex Details",
];
//To Create Controllers
List<String> dataHeader = [
"Fname",
"Mname",
"Lname",
"nationality",
"d_meal_pref",
"i_meal_pref",
"d_additonal_Info",
"i_additonal_Info",
"passportNumber",
"emergency_contact",
"placeOfIssue",
"passportDoc",
"dateOfIssue",
"dateOfExpiry",
"forex_card_num",
"full_name_as_id",
"local_id_num",
"forex_expiry_date",
];
// Color? layoutColor;
Color layoutColor = Colors.grey;
Color? bodyColor;
@override
void initState() {
super.initState();
// userId = getUserId();
apiCountryData = null;
apiHotelsData = [];
apiAirlineCountryData = [];
apiData = null;
for (var field in dataHeader) {
controllers[field] = TextEditingController();
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
}
fetchCountries();
fetchHotels();
loadCountryList();
fetchApiData();
// Delay adding the row until after the first frame
WidgetsBinding.instance.addPostFrameCallback((_) {
// focusNodes["FnameFocusNode"]?.requestFocus();
setState(() {
addHotelLoyaltyEntry();
addFrequentFlierEntry();
addVisaEntry();
loadInitialData();
});
updateTravel();
print("Trying to focus Fname...");
final node = focusNodes["FnameFocusNode"];
if (node != null) {
print("Node exists, requesting focus");
node.requestFocus();
}
});
_addFocusListener(_iSeatPrefFocusNode, (focus) => _iSeatPrefFocus = focus);
_addFocusListener(_dSeatPrefFocusNode, (focus) => _dSeatPrefFocus = focus);
// _addFocusListener(_groupFocusNode, (focus) => _groupFocus = focus);
// _addFocusListener(_firstApprovalFocusNode, (focus) => _firstApprovalFocus = focus);
// _addFocusListener(_secondApprovalFocusNode, (focus) => _secondApprovalFocus = focus);
// _addFocusListener(_thirdApprovalFocusNode, (focus) => _thirdApprovalFocus = focus);
// _addFocusListener(_exceptionalApprovalFocusNode, (focus) => _exceptionalApprovalFocus = focus);
// _addFocusListener(_delegateFocusNode, (focus) => _delegateFocus = focus);
// _addFocusListener(_startDateFocusNode, (focus) => _startDateFocus = focus);
// _addFocusListener(_endDateFocusNode, (focus) => _endDateFocus = focus);
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor =
bodyStringColor != null
? Color(int.parse(bodyStringColor))
: Colors.white;
});
}
bool boolValidation() {
print("travel validation start");
print("visaEntries - $visaEntries");
final passportText = controllers["passportNumber"]?.text ?? "";
if (_selectedTripType == "Indian") {
// Match exactly 8 alphanumeric characters
final isValid = RegExp(r'^[a-zA-Z0-9]{8}$').hasMatch(passportText);
if (!isValid) {
setState(() {
errorMessages["passport_number"] = "Enter 8 characters";
});
return false;
} else {
setState(() {
errorMessages.remove("passport_number");
});
}
}
final format = DateFormat("dd-MM-yyyy");
DateTime? start_Date;
DateTime? end_Date;
try {
if (controllers["dateOfIssue"]?.text != null &&
controllers["dateOfIssue"]!.text.isNotEmpty) {
start_Date = format.parse(controllers["dateOfIssue"]!.text);
}
if (controllers["dateOfExpiry"]?.text != null &&
controllers["dateOfExpiry"]!.text.isNotEmpty) {
end_Date = format.parse(controllers["dateOfExpiry"]!.text);
}
if (start_Date != null &&
end_Date != null &&
end_Date.isBefore(start_Date)) {
setState(() {
errorMessages["date_of_expiry"] = "Invalid Date Range";
});
print("End date cannot be earlier than start date");
}
} catch (e) {
print("Invalid passport date format");
}
// Additional validation starts - travelDetailsData passport
// DateTime? start_Date = travelDetailsData?['date_of_issue'];
// DateTime? end_Date = travelDetailsData?['date_of_expiry'];
//
// if (start_Date != null &&
// end_Date != null &&
// start_Date.toString().isNotEmpty &&
// end_Date.toString().isNotEmpty) {
// try {
// final format = DateFormat("dd-MM-yyyy");
// final checkStartDate = format.parse("$start_Date");
// final checkEndDate = format.parse("$end_Date");
//
// if (checkEndDate.isBefore(checkStartDate)) {
// // return "End date cannot be earlier than start date";;
// return;
// }
// } catch (e) {
// // return "End date cannot be earlier than start date";
// return;
// // errorMessages["end_date"] = "Invalid date format";
// }
// }
// ✅ Visa entries: Validate valid_from < valid_upto for each entry
for (var entry in visaEntries) {
final localId = entry["local_id"];
final fromText = entry["controller_valid_from"]?.text.trim();
final toText = entry["controller_valid_upto"]?.text.trim();
if (fromText != null &&
toText != null &&
fromText.isNotEmpty &&
toText.isNotEmpty) {
try {
final fromDate = format.parseStrict(fromText);
final toDate = format.parseStrict(toText);
if (toDate.isBefore(fromDate)) {
print("VisaErroer1");
errorMessages["valid_upto${entry['local_id']}"] =
"Invalid date range";
// errorMessages["valid_upto_$localId"] =
// "Valid Upto can't be before Valid From";
} else {
print("VisaErroer2");
// errorMessages.remove("valid_upto$localId");
errorMessages.remove("valid_upto${entry['local_id']}");
}
} catch (e) {
print("VisaErroer3");
errorMessages["valid_upto${entry['local_id']}"] =
"Invalid date format";
}
}
}
if (errorMessages.isNotEmpty) {
print("Validation failed with errors: $errorMessages");
setState(() {}); // Refresh UI to show errors
return false;
}
print("travel validation end");
return true;
}
Future<void> apiCheckDuplicate(
String label,
String field,
String value,
String? userId,
) async {
try {
final response = await apiService.CheckDuplicate(
context,
label,
field,
value,
userId,
);
if (response.isNotEmpty) {
_clearError(field);
errorMessages[field] = response['message'] ?? "$label Already Exists";
print("Duplicate found: ${response['message']}");
return;
} else {
_clearError(field);
print("No duplicates found.");
}
} catch (e) {
print("Error in checkDuplicate: $e");
}
}
// Future<void> apiCheckDuplicate(
// String label,
// String field,
// String value,
// String? userId,
// ) async {
// var newField = "";
// try {
// final response = await apiService.CheckDuplicate(
// label,
// newField,
// value,
// userId,
// );
// if (response.isNotEmpty) {
// _clearError(field);
// errorMessages[field] = response['message'] ?? "$label already exists";
// print("Duplicate found: ${response['message']}");
// return;
// } else {
// _clearError(field);
// print("No duplicates found.");
// }
// } catch (e) {
// print("Error in checkDuplicate: $e");
// }
// }
void _clearError(String field) {
setState(() {
errorMessages.remove(field);
});
}
//-------------------------Hotel Loyalty --------------------------------
void addHotelLoyaltyEntry() {
final localId = _rowhotelCounter++;
hotelLoyaltyEntries.add({
"local_id": localId,
"id": null, // to be set when backend responds
"hotel_name": null,
// "controller_hotel": TextEditingController(),
"controller_membership": TextEditingController(),
"is_active": "1",
});
setState(() {});
}
void removeHotelLoyaltyEntry(Map<String, dynamic> entry) {
print("removeHotelLoyaltyEntry- $entry");
setState(() {
entry["is_active"] = "0";
});
}
//-------------------------Frequent Flier--------------------------------
void addFrequentFlierEntry() {
final localId = _rowFlierCounter++;
frequentFlierEntries.add({
"local_id": localId,
"id": null, // to be set when backend responds
// "controller_airline": null,
"airline": null,
"controller_flier_number": TextEditingController(),
"is_active": "1",
});
setState(() {});
}
void removeFrequentFlierEntry(Map<String, dynamic> entry) {
print("removeFrequentFlierEntrydd- $entry");
setState(() {
entry["is_active"] = "0";
});
}
//-------------------------Visa --------------------------------
void addVisaEntry() {
final localId = _rowVisaCounter++;
visaEntries.add({
"local_id": localId,
"id": null, // to be set when backend responds
"country_code": null,
"visa_type_id": null,
"controller_valid_from": TextEditingController(),
"controller_valid_upto": TextEditingController(),
"is_active": "1",
});
setState(() {});
}
void removeVisaEntry(Map<String, dynamic> entry) {
print("removeFrequentFlierEntrydd- $entry");
setState(() {
entry["is_active"] = "0";
});
}
@override
void dispose() {
for (var entry in hotelLoyaltyEntries) {
// entry["controller_hotel"].dispose();
entry["controller_membership"].dispose();
}
for (var entry in frequentFlierEntries) {
// entry["controller_airline"].dispose();
entry["controller_flier_number"].dispose();
}
for (var entry in visaEntries) {
entry["controller_valid_from"].dispose();
entry["controller_valid_upto"].dispose();
}
super.dispose();
}
void disposeController(dynamic controller) {
if (controller is TextEditingController) {
controller.dispose();
}
}
Map<String, dynamic> get travel_Detials {
Map<String, dynamic> data = {
"passport_firstname": controllers["Fname"]?.text,
"passport_middlename": controllers["Mname"]?.text,
"passport_lastname": controllers["Lname"]?.text,
"nationality": _selectedTripType,
"passport_number": controllers["passportNumber"]?.text ?? '',
"place_of_issue": controllers["placeOfIssue"]?.text,
"date_of_issue": controllers["dateOfIssue"]?.text,
"date_of_expiry": controllers["dateOfExpiry"]?.text,
"d_meal_preference": controllers["d_meal_pref"]?.text,
"d_seat_preference": selectedDomesticSeat,
"d_additonalInfo": controllers["d_additonal_Info"]?.text,
"i_meal_preference": controllers["i_meal_pref"]?.text,
"i_seat_preference": selectedIntenationalSeat,
"i_additonalInfo": controllers["i_additonal_Info"]?.text,
"emergency_contact_number": controllers["emergency_contact"]?.text,
"forex_pre_paid_card_number": controllers["forex_card_num"]?.text,
"forex_expiry_date": controllers["forex_expiry_date"]?.text,
// "frequent_flier_information": [],
"frequent_flier_information": frequentFlierList,
"hotel_membership": hotelMembershipList,
"visa_details": visaDetailsList,
// "local_id_type": selectedLocalIdType,
// "local_id_num": controllers["local_id_num"]?.text,
// "full_name_as_id": controllers["full_name_as_id"]?.text,
};
return data;
}
// List<Map<String, dynamic>> get hotelMembershipList {
// final mappedList = hotelLoyaltyEntries.map((entry) {
// return {
// "id": entry["id"],
// "hotel_name": entry["controller_hotel"].text ?? '',
// "membership_number": entry["controller_membership"].text ?? '',
// "created_by": null,
// "updated_by": null,
// "is_active": entry["is_active"] ?? "1",
// };
// }).toList();
//
// final allEntriesEmpty = mappedList.every((entry) =>
// (entry["hotel_name"] as String).trim().isEmpty &&
// (entry["membership_number"] as String).trim().isEmpty);
//
// return allEntriesEmpty ? [] : mappedList;
// }
List<Map<String, dynamic>> get hotelMembershipList {
final mappedList =
hotelLoyaltyEntries.map((entry) {
final membership_number = entry["controller_membership"];
// final hotelName = entry["controller_hotel"];
print("membership_number- ${membership_number}");
return {
"id": entry["id"],
"hotel_id": entry["hotel_id"] ?? "",
// "hotel_name": entry["hotel_id"] ?? "",
"hotel_name": entry["hotel_name"] ?? "",
// "hotel_name": hotelName is TextEditingController ? hotelName.text : "",
"membership_number":
membership_number is TextEditingController
? membership_number.text
: "",
"created_by": null,
"updated_by": null,
"is_active": entry["is_active"] ?? "1",
};
}).toList();
final allEntriesEmpty = mappedList.every(
(entry) =>
(entry["hotel_id"] as String).trim().isEmpty &&
(entry["membership_number"] as String).trim().isEmpty,
);
return allEntriesEmpty ? [] : mappedList;
}
// List<Map<String, dynamic>> get frequentFlierList {
// return frequentFlierEntries
// // .where((entry) => entry["is_active"] != "0") // ✅ Only active entries
// .map((entry) {
// return {
// "id": entry["id"],
// "airline": entry["airline"],
// "frequent_flier_number": entry["controller_flier_number"].text,
// "created_by": null,
// "updated_by": null,
// "is_active": entry["is_active"] ?? "1",
// };
// }).toList();
// }
List<Map<String, dynamic>> get frequentFlierList {
final mappedList =
frequentFlierEntries.map((entry) {
final frequent_flier_number = entry["controller_flier_number"];
print("frequent_flier_number- $frequent_flier_number");
return {
"id": entry["id"],
"airline": entry["airline"] ?? "",
"frequent_flier_number":
frequent_flier_number is TextEditingController
? frequent_flier_number.text
: "",
"created_by": null,
"updated_by": null,
"is_active": entry["is_active"] ?? "1",
};
}).toList();
final allEntriesEmpty = mappedList.every(
(entry) =>
(entry["airline"] as String).trim().isEmpty &&
(entry["frequent_flier_number"] as String).trim().isEmpty,
);
return allEntriesEmpty ? [] : mappedList;
}
// List<Map<String, dynamic>> get visaDetailsList {
// return visaEntries
// // .where((entry) => entry["is_active"] != "0") // ✅ Only active entries
// .map((entry) {
// return {
// "id": entry["id"],
// "country_code": entry["country_code"],
// "visa_type_id": entry["visa_type_id"],
// "valid_from": entry["controller_valid_from"].text,
// "valid_upto": entry["controller_valid_upto"].text,
// // "valid_from": entry["controller_valid_from"] != null
// // ? entry["controller_valid_from"].text
// // : "",
// // "valid_upto": entry["controller_valid_upto"] != null
// // ? entry["controller_valid_upto"].text
// // : "",
// "created_by": null,
// "updated_by": null,
// "is_active": entry["is_active"] ?? "1",
// };
// }).toList();
// }
List<Map<String, dynamic>> get visaDetailsList {
final mappedList =
visaEntries.map((entry) {
final fromController = entry["controller_valid_from"];
final uptoController = entry["controller_valid_upto"];
return {
"id": entry["id"],
"country_code": entry["country_code"], // ✅ string value
"visa_type_id": entry["visa_type_id"], // ✅ string value
"valid_from":
fromController is TextEditingController
? fromController.text
: "",
"valid_upto":
uptoController is TextEditingController
? uptoController.text
: "",
"created_by": null,
"updated_by": null,
"is_active": entry["is_active"] ?? "1",
};
}).toList();
final allEntriesEmpty = mappedList.every(
(entry) =>
(entry["country_code"] == null ||
entry["country_code"].toString().trim().isEmpty) &&
(entry["visa_type_id"] == null ||
entry["visa_type_id"].toString().trim().isEmpty) &&
(entry["valid_from"] == null ||
entry["valid_from"].toString().trim().isEmpty) &&
(entry["valid_upto"] == null ||
entry["valid_upto"].toString().trim().isEmpty),
);
return allEntriesEmpty ? [] : mappedList;
}
void updateTravel() {
print("widget.travelDetails");
print(widget.travelDetails);
if (widget.travelDetails != null) {
print("Updatewidget.travelDetails");
print(widget.travelDetails);
controllers["Fname"]?.text =
widget.travelDetails?["passport_firstname"] ?? "";
controllers["Mname"]?.text =
widget.travelDetails?["passport_middlename"] ?? "";
controllers["Lname"]?.text =
widget.travelDetails?["passport_lastname"] ?? "";
_selectedTripType = widget.travelDetails?["nationality"] ?? "";
controllers["passportNumber"]?.text =
widget.travelDetails?["passport_number"] ?? "";
controllers["placeOfIssue"]?.text =
widget.travelDetails?["place_of_issue"] ?? "";
controllers["dateOfIssue"]?.text =
widget.travelDetails?["date_of_issue"] ?? "";
controllers["dateOfExpiry"]?.text =
widget.travelDetails?["date_of_expiry"] ?? "";
controllers["d_meal_pref"]?.text =
widget.travelDetails?["d_meal_preference"] ?? "";
controllers["i_meal_pref"]?.text =
widget.travelDetails?["i_meal_preference"] ?? "";
controllers["d_additonal_Info"]?.text =
widget.travelDetails?["d_additonalInfo"] ?? "";
controllers["i_additonal_Info"]?.text =
widget.travelDetails?["i_additonalInfo"] ?? "";
selectedIntenationalSeat =
widget.travelDetails?["i_seat_preference"] ?? "";
selectedDomesticSeat = widget.travelDetails?["d_seat_preference"] ?? "";
controllers["emergency_contact"]?.text =
widget.travelDetails?["emergency_contact_number"] ?? "";
controllers["forex_card_num"]?.text =
widget.travelDetails?["forex_pre_paid_card_number"] ?? "";
controllers["forex_expiry_date"]?.text =
widget.travelDetails?["forex_expiry_date"] ?? "";
String? apiDocPath = widget.travelDetails?["passport_document"];
if (apiDocPath != null && apiDocPath.isNotEmpty) {
passportFileUrlFromApi = apiDocPath;
selectedFileNames = apiDocPath.split('/').last;
passportFile = null;
} else {
passportFileUrlFromApi = null;
selectedFileNames = null;
passportFile = null;
}
//----------------------------------------------------
// 🚨 Hotel Membership Logic (pre-fill for edit)
// for (var entry in hotelLoyaltyEntries) {
// // entry["controller_hotel"].dispose();
// entry["controller_membership"].dispose();
// }
// hotelLoyaltyEntries.clear();
for (var entry in hotelLoyaltyEntries) {
disposeController(entry["controller_membership"]);
// (entry["controller_membership"] as TextEditingController?)?.dispose();
}
if (!mounted) return; // ✅ avoids context errors
print(MediaQuery.of(context).size);
//
// for (var entry in hotelLoyaltyEntries) {
// (entry["controller_membership"] as TextEditingController?)?.dispose();
// }
hotelLoyaltyEntries.clear();
final hotelMembershipList = widget.travelDetails?['hotel_membership'];
if (hotelMembershipList != null && hotelMembershipList is List) {
for (var item in hotelMembershipList) {
if (item["is_active"]?.toString() == "0") continue;
final localId = _rowhotelCounter++;
final hotelDataId = item["hotel_id"];
hotelLoyaltyEntries.add({
"local_id": localId,
"id": item["id"],
// "controller_hotel":
// TextEditingController(text: item["hotel_name"] ?? ""),
"hotel_name": item["hotel_name"] ?? "",
"hotel_id": hotelDataId ?? "",
// "controller_membership":
// TextEditingController(text: item["membership_number"] ?? ""),
"controller_membership": TextEditingController(
text: item["membership_number"] ?? "",
),
});
}
if (hotelLoyaltyEntries.isEmpty) {
addHotelLoyaltyEntry();
}
}
//----------------------------------------------------
// 🚨 Visa Logic (pre-fill for edit)
for (var entry in visaEntries) {
entry["controller_valid_from"]?.dispose();
entry["controller_valid_upto"]?.dispose();
}
visaEntries.clear();
final visaDetailsList = widget.travelDetails?['visa_details'];
if (visaDetailsList != null && visaDetailsList is List) {
for (var item in visaDetailsList) {
if (item["is_active"]?.toString() == "0") continue;
final localId = _rowVisaCounter++;
visaEntries.add({
"local_id": localId,
"id": item["id"],
"country_code": item["country_code"],
"visa_type_id": item["visa_type_id"],
"controller_valid_from": TextEditingController(
text: item["valid_from"] ?? "",
),
"controller_valid_upto": TextEditingController(
text: item["valid_upto"] ?? "",
),
});
}
if (visaEntries.isEmpty) {
addVisaEntry();
}
}
// ----------------------------------------------------
// 🚨 Frequent Flier Logic (pre-fill for edit)
// for (var entry in frequentFlierEntries) {
// // entry["controller_airline"].dispose();
// // entry["controller_flier_number"].dispose();
// (entry["controller_flier_number"] as TextEditingController?)?.dispose();
// }
for (var entry in frequentFlierEntries) {
entry["controller_flier_number"]?.dispose();
}
frequentFlierEntries.clear();
print(MediaQuery.of(context).size);
final frequentFlierList =
widget.travelDetails?['frequent_flier_information'];
if (frequentFlierList != null && frequentFlierList is List) {
for (var item in frequentFlierList) {
if (item["is_active"]?.toString() == "0") continue;
final localId = _rowFlierCounter++;
final airlineCode = item["airline"];
frequentFlierEntries.add({
"local_id": localId,
"id": item["id"],
"airline": airlineCode ?? "",
"controller_flier_number": TextEditingController(
text: item["frequent_flier_number"] ?? "",
),
});
}
if (frequentFlierEntries.isEmpty) {
addFrequentFlierEntry();
}
}
//----------------------------------------------------
setState(() {});
}
}
Future<void> fetchCountries() async {
try {
List<dynamic> countries = await apiService.fetchCountryList(context);
setState(() {
apiCountryData = countries;
});
} catch (e) {
print('Error fetching country list: $e');
}
}
Future<void> fetchHotels() async {
try {
List<dynamic> countries = await apiService.fetchHotelsList(context);
setState(() {
apiHotelsData = countries;
});
} catch (e) {
print('Error fetching country list: $e');
}
}
Future<void> loadCountryList() async {
final newTripType = "2";
final result = await apiService.fetchAirlineList(context);
// final result = await apiService.fetchFlightsCountryList(newTripType);
print("resultFlight: $result ");
setState(() {
apiAirlineCountryData = result;
});
// 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']} - ${country['Airport']}';
//
// tempCountryMap[country['Code']] = displayName;
// }
//
// setState(() {
// countryMap = tempCountryMap; // Update the map
// isCountryLoading = false;
// });
}
Future<void> fetchApiData() async {
try {
final data = await apiService.fetchMasterDropdown(context);
print("fetchApiData - $data");
if (data is! Map) {
throw Exception("Invalid response format: expected a Map");
}
if (!mounted) return;
setState(() {
apiData = data;
});
} catch (e) {
print('Error fetching role list: $e');
}
}
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
padding: const EdgeInsets.only(top: 8),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: Colors.blueGrey.shade300, // Set your desired color
width: 0.09, // Set border thickness
),
),
),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...List.generate(headers.length, (index) {
final isExpanded = expandedIndex == index;
return Card(
color: isExpanded ? Color(0xFFFFFDF3) : Colors.white,
elevation: isExpanded ? 0.9 : 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
margin: const EdgeInsets.symmetric(vertical: 5),
child: InkWell(
focusColor: Color(0xFFFFFDF3),
hoverColor: Color(0xFFFFFDF3),
highlightColor: Color(0xFFFFFDF3), // removes grey highlight
splashColor: Colors.white,
borderRadius: BorderRadius.circular(12),
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
headers[index],
style: GoogleFonts.poppins(
fontSize: widget.isDesktop ? 12 : 11,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
Icon(
isExpanded
? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down,
color:
isExpanded
? Colors.black87
: Colors.grey[600],
),
],
),
AnimatedSize(
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
child: ConstrainedBox(
constraints:
isExpanded
? const BoxConstraints()
: const BoxConstraints(maxHeight: 0),
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: _buildExpandedContent(index),
),
),
),
),
],
),
),
),
);
}),
],
),
),
);
}
Widget _buildExpandedContent(int index) {
switch (headers[index]) {
case "Passport Details":
return _buildPassportDatas(widget.isDesktop);
// case "Local ID Details":
// return _buildLocalIdDetails(widget.isDesktop);
case "Visa Details":
return _buildVisaDetails(widget.isDesktop);
case "Frequent Flyer Information":
return _buildFrequentFlierInfo(widget.isDesktop);
case "Hotel Loyalty Membership":
return _buildHotelLoyalty(widget.isDesktop);
case "Preferences Domestic":
return _buildPreferencesDomestic(widget.isDesktop);
//
case "Preferences International":
return _buildPreferencesInternational(widget.isDesktop);
case "Other Details":
return _buildOtherDetails(widget.isDesktop);
case "Forex Details":
return _buildForexDetails(widget.isDesktop);
default:
return Text(
"Form fields for ${headers[index]} go here",
style: GoogleFonts.poppins(fontSize: 12),
);
}
}
//------------------------------------Passport Details-----------------------------------
Widget _buildPassportDatas(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// SizedBox(
// height: 10,
// ),
_buildPassportDataRow1(widget.isDesktop),
SizedBox(height: 10),
_buildPassportDataRow2(widget.isDesktop),
SizedBox(height: 10),
_buildPassportDataRow3(widget.isDesktop),
],
);
}
Widget _buildPassportDataRow1(bool isDesktop) {
return Container(
color: Colors.white,
// child: Row(
// children: [
// Text(
// "Name as per passport : ",
// style: GoogleFonts.poppins(
// fontSize: 11,
// fontStyle: FontStyle.italic,
// letterSpacing: 0.5,
// ),
// ),
// Text(
// "${widget.fullName} ",
// style: GoogleFonts.poppins(
// fontSize: 10,
// fontWeight: FontWeight.w600,
// letterSpacing: 0.5,
// color: Colors.black87,
// // fontStyle: FontStyle.italic,
// ),
// ),
// ],
// ),
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildFirstNameField(),
Spacer(), buildMiddleNameField(),
Spacer(),
buildLastNameField(),
SizedBox(width: 25), // Space after Last Name
buildNationality(),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildFirstNameField(),
SizedBox(height: 8), buildMiddleNameField(),
SizedBox(height: 8), // Vertical space
buildLastNameField(),
SizedBox(height: 8),
buildNationality(),
],
),
);
}
Widget _buildPassportDataRow2(bool isDesktop) {
return Container(
color: Colors.white,
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildPassportNumber(),
Spacer(),
buildPlaceOfIssue(),
Spacer(),
/// Space after Last Name
buildDateOfIssue(),
SizedBox(width: 25),
buildDateOfExpiry(),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildPassportNumber(),
SizedBox(height: 8), // Vertical space
buildPlaceOfIssue(),
SizedBox(height: 8),
buildDateOfIssue(),
SizedBox(height: 8),
buildDateOfExpiry(),
],
),
);
}
Widget _buildPassportDataRow3(bool isDesktop) {
return Container(
color: Colors.white,
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [buildPassportDocument()],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [buildPassportDocument()],
),
);
}
Widget buildFirstNameField() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"First Name",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
// isFocused: false,
isFocused: focusStates["FnameFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["FnameFocusNode"],
controller: controllers["Fname"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("first_name");
},
decoration: InputDecoration(
labelText: "FirstName",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildMiddleNameField() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Middle Name",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: focusStates["MnameFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["MnameFocusNode"],
controller: controllers["Mname"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("middle_name");
},
decoration: InputDecoration(
labelText: "MiddleName",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildLastNameField() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Last Name",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: focusStates["LnameFocused"] ?? false,
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.13
: null,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["LnameFocusNode"],
controller: controllers["Lname"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("last_name");
},
decoration: InputDecoration(
labelText: "LastName",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildNationality() {
List<String> nationalityOptions = [
"Indian",
"International",
"other nationality",
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Nationality",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
// isFocused: false,
isFocused: focusStates["nationality"] ?? false,
padding: const EdgeInsets.symmetric(horizontal: 0),
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.13
: null,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: Focus(
focusNode: focusNodes["nationalityFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["nationalityFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["nationalityFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: _selectedTripType,
enabled: !widget.isViewMode,
popupProps: PopupProps.menu(
showSearchBox: false, // Set true if you want search
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 200),
itemBuilder:
(context, item, isSelected) => Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
),
items: nationalityOptions,
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(1),
borderSide: BorderSide(
color:
(focusStates["nationalityFocused"] ?? false)
? (layoutColor)
: Colors.white,
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["nationalityFocused"] ?? false)
? (layoutColor)
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 1,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: (layoutColor),
width: 0.5,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 1,
),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
),
onChanged:
widget.isViewMode
? null
: (String? newValue) {
setState(() {
_selectedTripType = newValue;
controllers["passportNumber"]?.clear();
print("_selectedTripType - $_selectedTripType");
});
},
),
),
),
),
),
],
);
}
Widget buildPassportNumber() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Passport Number",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: focusStates["passportNumberFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["passportNumberFocusNode"],
controller: controllers["passportNumber"],
enabled: !widget.isViewMode,
inputFormatters: [
// limit to 8 characters
FilteringTextInputFormatter.allow(
RegExp(r'[a-zA-Z0-9]'),
), // allow only letters and digits
if (_selectedTripType == "Indian")
LengthLimitingTextInputFormatter(8),
],
onChanged: (value) {
_clearError("passport_number");
apiCheckDuplicate(
"Passport Number",
"passport_number",
value,
widget.userIdApi,
);
},
decoration: InputDecoration(
labelText: "Passport Number",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
if (errorMessages["passport_number"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["passport_number"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
),
],
],
);
}
Widget buildPlaceOfIssue() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Place of Issue",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: focusStates["placeOfIssueFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["placeOfIssueFocusNode"],
controller: controllers["placeOfIssue"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("placeOfIssue");
},
decoration: InputDecoration(
labelText: "Place of Issue",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
DateTime? _selectedDateOfIssue;
DateTime? _selectedDateOfExpiry;
Widget buildDateOfIssue() {
Future<void> _selectCheckDateOfIssue(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate:
_selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today)
? _selectedDateOfIssue!
: today,
firstDate: DateTime(1900),
// lastDate: DateTime(2100),
lastDate: today,
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedDateOfIssue) {
setState(() {
_selectedDateOfIssue = pickedDate;
controllers["dateOfIssue"]?.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
// Revalidate expiry
if (_selectedDateOfExpiry != null &&
_selectedDateOfExpiry!.isBefore(_selectedDateOfIssue!)) {
errorMessages["date_of_expiry"] = "Invalid Date Range";
} else {
errorMessages.remove("date_of_expiry");
}
});
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date of Issue",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.13
: null,
isFocused: focusStates["dateOfIssueFocused"] ?? false,
// isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap:
widget.isViewMode
? null
: () async {
await _selectCheckDateOfIssue(context);
if (controllers["dateOfIssue"]!.text.isNotEmpty) {
setState(() {
// errorMessages.remove("start_date");
});
}
focusNodes["dateOfIssueFocusNode"]?.requestFocus();
},
child: AbsorbPointer(
child: TextField(
controller: controllers["dateOfIssue"],
focusNode: focusNodes["dateOfIssueFocusNode"],
readOnly: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
color: Color(0xFF8B8FB2),
),
),
),
),
),
),
),
],
);
}
Widget buildDateOfExpiry() {
Future<void> _selectCheckDateOfExpiry(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
// DateTime firstDate = today;
DateTime? validFromDate;
try {
String? fromDateText = controllers["dateOfIssue"]?.text.trim();
print("dateOfIssue From Text: $fromDateText");
if (fromDateText != null && fromDateText.isNotEmpty) {
validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText);
print("dateOfIssue Valid From: $validFromDate");
}
} catch (e) {
print("Error parsing valid from date: $e");
}
// Use max(today, validFromDate) as firstDate
// DateTime firstDate = today;
DateTime firstDate = validFromDate ?? today;
// Parse dateOfIssue safely
// DateTime firstDate;
// try {
// firstDate = DateFormat(
// "yyyy-MM-dd",
// ).parse(controllers["dateOfIssue"]!.text);
// } catch (e) {
// firstDate = today; // fallback if parsing fails
// }
DateTime? pickedDate = await showDatePicker(
context: context,
// initialDate:
// _selectedDateOfExpiry != null &&
// _selectedDateOfExpiry!.isAfter(firstDate)
// ? _selectedDateOfExpiry!
// : today,
initialDate: firstDate,
firstDate: firstDate,
// firstDate: DateTime(1900),
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null) {
setState(() {
_selectedDateOfExpiry = pickedDate;
controllers["dateOfExpiry"]?.text = DateFormat(
"dd-MM-yyyy",
).format(pickedDate);
if (_selectedDateOfIssue != null &&
_selectedDateOfExpiry!.isBefore(_selectedDateOfIssue!)) {
errorMessages["date_of_expiry"] = "Invalid Date Range";
} else {
errorMessages.remove("date_of_expiry");
}
});
}
// if (pickedDate != null && pickedDate != _selectedDateOfExpiry) {
// setState(() {
// _selectedDateOfExpiry = pickedDate;
// controllers["dateOfExpiry"]?.text = DateFormat(
// 'dd-MM-yyyy',
// ).format(pickedDate);
//
// // controllers["dateOfExpiry"]?.text =
// // DateFormat('dd-MM-yyyy').format(pickedDate);
// });
// }
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date of Expiry",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
// isFocused: false,
isFocused: focusStates["dateOfExpiryFocused"] ?? false,
isDesktop: widget.isDesktop,
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.13
: null,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap:
widget.isViewMode
? null
: () async {
await _selectCheckDateOfExpiry(context);
if (controllers["dateOfExpiry"]!.text.isNotEmpty) {
setState(() {
// errorMessages.remove("start_date");
});
}
focusNodes["dateOfExpiryFocusNode"]?.requestFocus();
},
child: AbsorbPointer(
child: TextField(
controller: controllers["dateOfExpiry"],
focusNode: focusNodes["dateOfExpiryFocusNode"],
readOnly: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
color: Color(0xFF8B8FB2),
),
),
),
),
),
),
),
if (errorMessages["date_of_expiry"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["date_of_expiry"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 10),
maxLines: 2, // Allow it to wrap onto two lines
overflow:
TextOverflow.ellipsis, // Add ellipsis if it still overflows
),
],
],
);
}
// Widget buildDateOfIssue() {
// DateTime? _selectedDateOfIssue;
//
// Future<void> _selectCheckDateOfIssue(BuildContext context) async {
// DateTime now = DateTime.now();
// DateTime today = DateTime(now.year, now.month, now.day);
//
// DateTime? pickedDate = await showDatePicker(
// context: context,
// initialDate:
// _selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today)
// ? _selectedDateOfIssue!
// : today,
// // firstDate: today,
// firstDate: DateTime(1900),
// lastDate: DateTime(2100),
// initialEntryMode: DatePickerEntryMode.calendarOnly,
// );
//
// if (pickedDate != null && pickedDate != _selectedDateOfIssue) {
// setState(() {
// _selectedDateOfIssue = pickedDate;
// controllers["dateOfIssue"]?.text = DateFormat(
// 'dd-MM-yyyy',
// ).format(pickedDate);
// });
// }
// }
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Date of Issue",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// CustomTextFieldUserTravellerWrapper(
// width:
// widget.isDesktop
// ? MediaQuery.of(context).size.width * 0.13
// : null,
// isFocused: false,
// isDesktop: widget.isDesktop,
// child: SizedBox(
// height: 40,
// child: GestureDetector(
// onTap:
// widget.isViewMode
// ? null
// : () async {
// await _selectCheckDateOfIssue(context);
// if (controllers["dateOfIssue"]!.text.isNotEmpty) {
// setState(() {
// // errorMessages.remove("start_date");
// });
// }
// },
// child: AbsorbPointer(
// child: TextField(
// controller: controllers["dateOfIssue"],
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
// decoration: InputDecoration(
// labelText: "Select Date",
// labelStyle: GoogleFonts.poppins(
// fontSize: 12,
// color: Colors.grey,
// ),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: const EdgeInsets.symmetric(vertical: 16),
// suffixIcon: const Icon(
// Icons.calendar_today,
// size: 16,
// color: Color(0xFF8B8FB2),
// ),
// ),
// ),
// ),
// ),
// ),
// ),
// ],
// );
// }
//
// Widget buildDateOfExpiry() {
// DateTime? _selectedDateOfExpiry;
//
// Future<void> _selectCheckDateOfExpiry(BuildContext context) async {
// DateTime now = DateTime.now();
// DateTime today = DateTime(now.year, now.month, now.day);
// // DateTime today = DateTime(now.year now.day, now.month,);
//
// DateTime? validFromDate;
//
// try {
// String? fromDateText = controllers["dateOfIssue"]?.text.trim();
// print("dateOfIssue From Text: $fromDateText");
// if (fromDateText!.isNotEmpty) {
// validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText);
// print("dateOfIssue Valid From: $validFromDate");
// }
// } catch (e) {
// print("Error parsing valid from date: $e");
// }
//
// // Use max(today, validFromDate) as firstDate
// // DateTime firstDate = today;
// DateTime firstDate = validFromDate ?? today;
//
// // Parse dateOfIssue safely
// // DateTime firstDate;
// // try {
// // firstDate = DateFormat(
// // "yyyy-MM-dd",
// // ).parse(controllers["dateOfIssue"]!.text);
// // } catch (e) {
// // firstDate = today; // fallback if parsing fails
// // }
//
// DateTime? pickedDate = await showDatePicker(
// context: context,
//
// // initialDate:
// // _selectedDateOfExpiry != null &&
// // _selectedDateOfExpiry!.isAfter(firstDate)
// // ? _selectedDateOfExpiry!
// // : today,
// initialDate: firstDate,
// firstDate: firstDate,
// // firstDate: DateTime(1900),
// lastDate: DateTime(2100),
// initialEntryMode: DatePickerEntryMode.calendarOnly,
// );
//
// if (pickedDate != null) {
// _selectedDateOfExpiry = pickedDate;
// controllers["dateOfExpiry"]?.text = DateFormat(
// "dd-MM-yyyy",
// ).format(pickedDate);
// }
//
// // if (pickedDate != null && pickedDate != _selectedDateOfExpiry) {
// // setState(() {
// // _selectedDateOfExpiry = pickedDate;
// // controllers["dateOfExpiry"]?.text = DateFormat(
// // 'dd-MM-yyyy',
// // ).format(pickedDate);
// //
// // // controllers["dateOfExpiry"]?.text =
// // // DateFormat('dd-MM-yyyy').format(pickedDate);
// // });
// // }
// }
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Date of Expiry",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// CustomTextFieldUserTravellerWrapper(
// isFocused: false,
// isDesktop: widget.isDesktop,
// width:
// widget.isDesktop
// ? MediaQuery.of(context).size.width * 0.13
// : null,
// child: SizedBox(
// height: 40,
// child: GestureDetector(
// onTap:
// widget.isViewMode
// ? null
// : () async {
// await _selectCheckDateOfExpiry(context);
// if (controllers["dateOfExpiry"]!.text.isNotEmpty) {
// setState(() {
// // errorMessages.remove("start_date");
// });
// }
// },
// child: AbsorbPointer(
// child: TextField(
// controller: controllers["dateOfExpiry"],
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
// decoration: InputDecoration(
// labelText: "Select Date",
// labelStyle: GoogleFonts.poppins(
// fontSize: 12,
// color: Colors.grey,
// ),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: const EdgeInsets.symmetric(vertical: 16),
// suffixIcon: const Icon(
// Icons.calendar_today,
// size: 16,
// color: Color(0xFF8B8FB2),
// ),
// ),
// ),
// ),
// ),
// ),
// ),
// ],
// );
// }
Widget buildPassportDocument() {
// void pickPDFWeb() {
// html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
// uploadInput.accept = '.pdf';
// uploadInput.click();
//
// uploadInput.onChange.listen((e) {
// final file = uploadInput.files!.first;
//
// // Ensure the file is a PDF
// if (!file.type.contains("pdf")) {
// print("Error: Not a PDF file");
// return;
// }
//
// // 🔹 File size check: Ensure it does not exceed 3MB
// int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
// if (file.size > maxFileSize) {
// print('Error: File size exceeds 3MB');
// return;
// }
//
// setState(() {
// selectedFileNames = file.name;
// passportFile = file;
// passportFileUrlFromApi = null;
// });
//
// print('PDF File selected: ${file.name}');
// });
// }
void pickFileWeb() {
html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
// Accept PDFs and common image formats
uploadInput.accept = '.pdf,.jpg,.jpeg,.png';
uploadInput.click();
uploadInput.onChange.listen((e) {
final file = uploadInput.files!.first;
// Ensure the file is PDF or image
if (!(file.type.contains("pdf") || file.type.contains("image"))) {
print("Error: Not a PDF or image file");
return;
}
// File size check: Ensure it does not exceed 3MB
int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
if (file.size > maxFileSize) {
print('Error: File size exceeds 3MB');
return;
}
setState(() {
selectedFileNames = file.name;
passportFile = file;
passportFileUrlFromApi = null;
});
print('File selected: ${file.name}');
});
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Passport Document",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: pickFileWeb,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.centerLeft,
child: Text(
selectedFileNames ?? "Select Document",
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
overflow: TextOverflow.ellipsis, // Prevents overflow issues
),
),
),
),
),
SizedBox(height: 5),
Center(
child: Text(
" * Allow types pdf, jpg, jpeg, png and max size 3MB ",
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w400,
color: Colors.grey,
),
),
),
SizedBox(height: 10),
if (passportFile != null || passportFileUrlFromApi != null)
// Centers the text
Container(
color: Colors.white,
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.25
: MediaQuery.of(context).size.width * 0.8,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
print('DOWNLOAD - $passportFile - ${widget.userIdApi} ');
if (passportFile != null) {
try {
// ✅ Step 1: Create a Blob directly from the file
// final blob =
// html.Blob([passportFile!], 'application/pdf');
//
// // ✅ Step 2: Generate a download URL from the Blob
// final url = html.Url.createObjectUrlFromBlob(blob);
//
// // ✅ Step 3: Create an invisible anchor to trigger download
// final anchor = html.AnchorElement(href: url)
// ..setAttribute(
// "download", selectedFileNames ?? "document.pdf")
// ..style.display = "none";
//
// // ✅ Step 4: Add anchor to DOM and click it
// html.document.body!.append(anchor);
// anchor.click();
//
// // ✅ Step 5: Clean up
// anchor.remove();
// html.Url.revokeObjectUrl(url);
// Create a blob from the response body
final blob = html.Blob([passportFile!]);
// Generate a download URL for the blob
final url = html.Url.createObjectUrlFromBlob(blob);
// Create a link element to trigger the download
final anchor =
html.AnchorElement(href: url)
..setAttribute(
'download',
selectedFileNames ?? "document.pdf",
)
..click();
// Revoke the download URL to free up resources
html.Url.revokeObjectUrl(url);
print("Download triggered successfully!");
} catch (e) {
print("Error during download: $e");
}
} else if (passportFileUrlFromApi != null) {
print("Raw file path: $passportFileUrlFromApi");
apiService.getPassportDocDownload(
context,
widget.userIdApi,
);
// // Extract public part of the path starting from "/assets"
// String cleanedPath = passportFileUrlFromApi!;
// final index = passportFileUrlFromApi!.indexOf("/assets");
// if (index != -1) {
// cleanedPath = passportFileUrlFromApi!.substring(index);
// }
//
// final fullUrl =
// 'https://apitest.tripapprovaltool.com$cleanedPath';
// print("Final download URL: $fullUrl");
//
// final anchor =
// html.AnchorElement(href: fullUrl)
// ..target = '_blank'
// ..download = selectedFileNames ?? "document.pdf"
// ..click();
} else {
print("No file available to download.");
}
},
child: Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.green.shade300,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"Download",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w200,
color: Colors.white,
),
overflow: TextOverflow.ellipsis,
textAlign:
TextAlign
.end, // Ensures text is centered within the Text widget
),
SizedBox(width: 5),
Icon(Icons.download, size: 13, color: Colors.white),
],
),
),
),
],
),
),
],
);
}
//------------------------Local Details----------------------------------------
Widget _buildLocalIdDetails(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
_buildLocalDetailDataRow1(widget.isDesktop),
],
);
}
Widget _buildLocalDetailDataRow1(bool isDesktop) {
return Container(
color: Colors.white,
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildLocalIdType(),
Spacer(),
buildIdNumber(),
Spacer(),
buildFullNameAsId(),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildLocalIdType(),
SizedBox(height: 8),
buildIdNumber(),
SizedBox(height: 8), // Vertical space
buildFullNameAsId(),
],
),
);
}
Widget buildIdNumber() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Id Number",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["local_id_num"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("local_id_num");
},
decoration: InputDecoration(
labelText: "Id Number",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildLocalIdType() {
final List<Map<String, dynamic>> localIdOptions = [
{"dropdown_value": "Licence"},
{"dropdown_value": "PAN"},
{"dropdown_value": "Voter Card"},
{"dropdown_value": "Aadhar Card"},
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Id Type",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
popupProps: PopupProps.menu(
showSearchBox: false,
fit: FlexFit.loose,
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder: (context, item, isSelected) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
child: Text(
item['dropdown_value'] ?? 'Select',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
);
},
),
dropdownDecoratorProps: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
hintText: "Select",
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
),
),
dropdownButtonProps: const DropdownButtonProps(
icon: Icon(Icons.arrow_drop_down),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
return Text(
"Select ", // fallback text
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
);
}
return Text(
selectedItem['dropdown_value'] ?? "Select ",
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
);
},
items: localIdOptions,
selectedItem:
selectedLocalIdType == null
? null
: {"dropdown_value": selectedLocalIdType},
onChanged:
widget.isViewMode
? null
: (Map<String, dynamic>? newItem) {
if (newItem != null) {
setState(() {
selectedLocalIdType = newItem['dropdown_value'];
});
}
},
),
),
),
],
);
}
Widget buildFullNameAsId() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Full Name As ID",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["full_name_as_id"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("full_name_as_id");
},
decoration: InputDecoration(
labelText: "FullName As ID",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
//------------------------Preferences Domestic ---------------------------------------
Widget _buildPreferencesDomestic(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
_buildDomesticDetailDataRow1(widget.isDesktop),
],
);
}
Widget _buildDomesticDetailDataRow1(bool isDesktop) {
return Container(
color: Colors.white,
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildDomesticSeatType(),
SizedBox(width: 15),
buildDomesticMealPref(),
SizedBox(width: 15),
buildDomesticAddtnlPref(),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildDomesticSeatType(),
SizedBox(height: 8),
buildDomesticMealPref(),
SizedBox(height: 8), // Vertical space
buildDomesticAddtnlPref(),
],
),
);
}
Widget buildDomesticSeatType() {
final List<Map<String, dynamic>> seatOptions = [
{"dropdown_value": "Aisle"},
{"dropdown_value": "Window"},
{"dropdown_value": "Middle"},
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Seat Preference",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.15
: null,
isFocused: _dSeatPrefFocus,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
width: double.infinity,
child: Focus(
focusNode: _dSeatPrefFocusNode,
onFocusChange: (hasFocus) {
setState(() {
_dSeatPrefFocus = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
_dSeatPrefFocusNode.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
popupProps: PopupProps.menu(
showSearchBox: false,
fit: FlexFit.loose,
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder: (context, item, isSelected) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
child: Text(
item['dropdown_value'] ?? "Select Seat",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
);
},
),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(_dSeatPrefFocus) ? (layoutColor) : Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(_dSeatPrefFocus) ? (layoutColor) : Colors.white,
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: (layoutColor),
width: 0.5,
),
),
hintText: "Select Seat",
hintStyle: GoogleFonts.poppins(
fontSize: 11.5,
color: Colors.grey,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
),
),
dropdownButtonProps: const DropdownButtonProps(
icon: Icon(Icons.arrow_drop_down),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null ||
selectedItem.isEmpty ||
selectedItem['dropdown_value'].toString().isEmpty) {
return Text(
"Select Seat", // fallback text
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
);
}
return Text(
selectedItem['dropdown_value'] ?? 'Select Seat',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
);
},
items: seatOptions,
selectedItem:
selectedDomesticSeat == null
? null
: {"dropdown_value": selectedDomesticSeat},
onChanged:
widget.isViewMode
? null
: (Map<String, dynamic>? newItem) {
if (newItem != null) {
setState(() {
selectedDomesticSeat =
newItem['dropdown_value'];
});
}
},
),
),
),
),
),
],
);
}
Widget buildDomesticMealPref() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Meal Preference",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.15
: null,
isFocused: focusStates["d_meal_prefFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["d_meal_prefFocusNode"],
controller: controllers["d_meal_pref"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("d_meal_pref");
},
decoration: InputDecoration(
labelText: "Meal Preference",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildDomesticAddtnlPref() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Additional Information",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop ? MediaQuery.of(context).size.width * 0.4 : null,
isFocused: focusStates["d_additonal_InfoFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["d_additonal_Info"],
focusNode: focusNodes["d_additonal_InfoFocusNode"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("d_additonal_Info");
},
decoration: InputDecoration(
labelText: "Please Enter",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
//------------------------Preferences International ---------------------------------------
Widget _buildPreferencesInternational(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
_buildInternationalDetailDataRow1(widget.isDesktop),
],
);
}
Widget _buildInternationalDetailDataRow1(bool isDesktop) {
return Container(
color: Colors.white,
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildInternationalSeatType(),
SizedBox(width: 15),
buildInternationalMealPref(),
SizedBox(width: 15),
buildInternationalAddtnlPref(),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildInternationalSeatType(),
SizedBox(height: 8),
buildInternationalMealPref(),
SizedBox(height: 8), // Vertical space
buildInternationalAddtnlPref(),
],
),
);
}
Widget buildInternationalSeatType() {
final List<Map<String, dynamic>> seatOptionsInt = [
{"dropdown_value": "Aisle"},
{"dropdown_value": "Window"},
{"dropdown_value": "Middle"},
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Seat Preference",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.15
: null,
isFocused: _iSeatPrefFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
width: double.infinity,
child: Focus(
focusNode: _iSeatPrefFocusNode,
onFocusChange: (hasFocus) {
setState(() {
_iSeatPrefFocus = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
_iSeatPrefFocusNode.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
popupProps: PopupProps.menu(
showSearchBox: false,
fit: FlexFit.loose,
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder: (context, item, isSelected) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
child: Text(
item['dropdown_value'] ?? 'Select Seat',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
);
},
),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(_iSeatPrefFocus) ? (layoutColor) : Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(_iSeatPrefFocus) ? (layoutColor) : Colors.white,
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: (layoutColor),
width: 0.5,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 1,
),
),
),
// selectedItem: purposeList.cast<Map<String, dynamic>>().firstWhere(
// (item) => item['dropdown_key'] == _selectedIsBillable,
// orElse: () => {},
// ),
dropdownButtonProps: const DropdownButtonProps(
icon: Icon(Icons.arrow_drop_down),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null ||
selectedItem.isEmpty ||
selectedItem['dropdown_value'].toString().isEmpty) {
return Text(
"Select Seat", // fallback text
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
);
}
return Text(
selectedItem['dropdown_value'] ?? 'Select Seat',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
);
},
items: seatOptionsInt,
selectedItem:
selectedIntenationalSeat == null
? null
: {"dropdown_value": selectedIntenationalSeat},
onChanged:
widget.isViewMode
? null
: (Map<String, dynamic>? newItem) {
if (newItem != null) {
setState(() {
selectedIntenationalSeat =
newItem['dropdown_value'];
});
}
},
),
),
),
),
),
],
);
}
Widget buildInternationalMealPref() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Meal Preference",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.15
: null,
isFocused: focusStates["i_meal_prefFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["i_meal_pref"],
focusNode: focusNodes["i_meal_prefFocusNode"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("i_meal_pref");
},
decoration: InputDecoration(
labelText: "Meal Preference",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildInternationalAddtnlPref() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Additional Information",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop ? MediaQuery.of(context).size.width * 0.4 : null,
isFocused: focusStates["i_additonal_InfoFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["i_additonal_Info"],
focusNode: focusNodes["i_additonal_InfoFocusNode"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("i_additonal_Info");
},
decoration: InputDecoration(
labelText: "Please Enter",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
//--------------------------Other Details-------------------------------
Widget _buildOtherDetails(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [SizedBox(height: 10), buildEmergencyContactNum()],
);
}
Widget buildEmergencyContactNum() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Emergency Contact Number",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
// isFocused: false,
isFocused: focusStates["emergency_contactFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["emergency_contact"],
focusNode: focusNodes["emergency_contactFocusNode"],
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9\s]')),
],
// maxLength: 10,
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("emergency_contact");
},
decoration: InputDecoration(
// counterText: '',
labelText: "Contact Number",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
//-------------------------Forex Prepaid Card Number------------------------
Widget _buildForexDetails(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [SizedBox(height: 10), _buildForexDetailDataRow1(isDesktop)],
);
}
Widget _buildForexDetailDataRow1(bool isDesktop) {
return Container(
color: Colors.white,
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildForexCardNumber(),
SizedBox(width: 15),
buildForexExpiryDate(),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildForexCardNumber(),
SizedBox(height: 8),
buildForexExpiryDate(),
],
),
);
}
Widget buildForexCardNumber() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Forex Pre-Paid Card Number",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: focusStates["forex_card_numFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["forex_card_num"],
focusNode: focusNodes["forex_card_numFocusNode"],
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9\s]')),
LengthLimitingTextInputFormatter(16),
],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("forex_pre_paid_card_number");
apiCheckDuplicate(
"Forex Pre-Paid Card Number",
"forex_pre_paid_card_number",
value,
widget.userIdApi,
);
},
decoration: InputDecoration(
labelText: "Card Number",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
if (errorMessages["forex_pre_paid_card_number"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["forex_pre_paid_card_number"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
),
],
],
);
}
Widget buildForexExpiryDate() {
DateTime? _selectedExpiryDate;
Future<void> _selectExpiryDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate:
_selectedExpiryDate != null && _selectedExpiryDate!.isAfter(today)
? _selectedExpiryDate!
: today,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedExpiryDate) {
setState(() {
_selectedExpiryDate = pickedDate;
// controllers["forex_expiry_date"]?.text =
// DateFormat('yyyy-MM-dd').format(pickedDate);
controllers["forex_expiry_date"]?.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
});
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Forex Expiry Date",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: focusStates["forex_expiry_dateFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap:
widget.isViewMode
? null
: () async {
focusNodes["forex_expiry_dateFocusNode"]
?.requestFocus();
await _selectExpiryDate(context);
},
child: AbsorbPointer(
child: TextField(
controller: controllers["forex_expiry_date"],
focusNode: focusNodes["forex_expiry_dateFocusNode"],
readOnly: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Color(0xFF8B8FB2),
),
),
),
),
),
),
),
],
);
}
//-------------------------Frequent Flier Information -------------------------------
Widget _buildFrequentFlierInfo(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...frequentFlierEntries
.where((e) => e["is_active"] != "0")
.map((e) => _buildFrequentAirlineDataRow1(e, isDesktop)),
IconForAirlineMem(),
],
);
}
Widget _buildFrequentAirlineDataRow1(
Map<String, dynamic> entry,
bool isDesktop,
) {
int localId = entry["local_id"];
return Container(
color: Colors.white,
padding: const EdgeInsets.only(top: 8.0),
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildAirline(entry),
SizedBox(width: 25),
buildFrequentFlierInformation(entry),
SizedBox(width: 15),
SizedBox(
height: 60,
child: Center(
child: Padding(
padding: EdgeInsets.only(
top: 20,
left: 8,
), // 👈 Add top padding here
child: IconButton(
icon: Icon(
Icons.remove_circle_sharp,
color: Colors.red,
size: 15,
),
tooltip: 'Cancel the Details',
onPressed: () {
setState(() {
removeFrequentFlierEntry(entry);
});
},
),
),
),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildAirline(entry),
SizedBox(height: 8),
buildFrequentFlierInformation(entry),
SizedBox(width: 15),
IconButton(
icon: Icon(
Icons.remove_circle_sharp,
color: Colors.red,
size: 15,
),
tooltip: 'Cancel the Details',
onPressed: () {
setState(() {
removeFrequentFlierEntry(entry);
});
},
),
],
),
);
}
Widget buildAirline(Map<String, dynamic> entry) {
late Map<String, String> countryMap; // Mapping country_code -> country_name
late List<String> countryCodes; // List of country codes
// countryList = [];
bool hasAirlineCountryData =
apiAirlineCountryData == null || apiAirlineCountryData!.isEmpty;
countryList = apiAirlineCountryData!;
print("TestcountryList2 - $countryList");
// countryMap = {
// for (var country in countryList)
// country['Code'] as String: '${country['City']} - ${country['Airport']}',
// };
countryMap = {
for (var country in countryList)
country['airline'] as String:
'${country['airline_name']} (${country['airline']})',
// country['airline'] as String: '${country['airline_name']}',
};
// airline
countryCodes = countryMap.keys.toList();
// setState(() {
// countryMap = tempCountryMap; // Update the map
// isCountryLoading = false;
// });
// Map country codes to country names
// countryMap = {
//
//
// for (var item in countryList)
// item['country_code'] as String: item['country_name'] as String
// };
// Extract only country codes for processing
//--------------------------
// String? selectedCountry = entry['airline'];
String? selectedCode = entry['airline'];
String? selectedText =
selectedCode != null ? countryMap[selectedCode] : null;
print("selectedCode: $selectedCode");
print("selectedText: $selectedText");
print(
"items.contains(selectedText): ${countryMap.values.contains(selectedText)}",
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Airline",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
// width:
// widget.isDesktop
// ? MediaQuery.of(context).size.width * 0.17
// : null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child:
hasAirlineCountryData
? CircularProgressIndicator()
: DropdownSearch<String>(
// selectedItem: countryMap[selectedCountry],
// selectedItem: entry['airline'] != null
// ? countryMap[entry['airline']]
// : null,
// selectedItem: countryMap.containsKey(entry['airline'])
// ? countryMap[entry['airline']]
// : null,
// selectedItem: countryMap[selectedCode],
// selectedItem: countryMap.containsKey(selectedCode)
// ? countryMap[selectedCode]
// : null,
selectedItem:
(entry["airline"] != null &&
countryMap.containsKey(entry["airline"]))
? countryMap[entry["airline"]]
: null,
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(
backgroundColor: Colors.white,
),
constraints: BoxConstraints(maxHeight: 250),
itemBuilder: (context, item, isSelected) {
// Extract airline name and code from "AirlineName (CODE)"
final match = RegExp(
r'^(.*)\s+\(([^)]+)\)$',
).firstMatch(item);
final name = match?.group(1) ?? '';
final code = match?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
name,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
),
),
Text(
code,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.grey,
),
),
],
),
);
},
// itemBuilder:
// (context, item, isSelected) => Padding(
// padding: const EdgeInsets.symmetric(
// horizontal: 8.0,
// vertical: 6.0,
// ),
// child: Text(
// item,
// style: GoogleFonts.poppins(fontSize: 11.5),
// ),
// ),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Airline...",
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Airline",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
// selectedCountry = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
final selectedCode =
countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key;
entry['airline'] = selectedCode;
// final selectedCode = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
// entry['airline'] = selectedCode;
});
},
),
),
),
// CustomTextFieldUserTravellerWrapper(
// width: widget.isDesktop
// ? MediaQuery.of(context).size.width * 0.15
// : null,
// isFocused: false,
// isDesktop: widget.isDesktop,
// child: SizedBox(
// height: 40,
// child: TextField(
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
// controller: entry["controller_airline"],
// enabled: !widget.isViewMode,
// onChanged: (value) {
// _clearError("first_name");
// },
// decoration: InputDecoration(
// labelText: "Airline",
// labelStyle:
// GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
// ),
// ),
],
);
}
Widget buildFrequentFlierInformation(Map<String, dynamic> entry) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Frequent Flyer Information",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: entry["controller_flier_number"],
enabled: !widget.isViewMode,
onChanged: (value) {
// _clearError("first_name");
},
decoration: InputDecoration(
labelText: "Frequent Flyer Information",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget IconForAirlineMem() {
return Container(
// height: 60, // must define height
alignment: Alignment.bottomRight,
child: IconButton(
icon: Icon(Icons.add_circle, color: Color(0xFF114D8B), size: 30),
onPressed: () => addFrequentFlierEntry(),
tooltip: "Frequent Flyer Information",
),
);
}
//-------------------------Frequent Loyalty MemberShip Hotels -------------------------------
Widget _buildHotelLoyalty(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...hotelLoyaltyEntries.map((entry) {
// return _buildFrequentLoyaltyDataRow1(entry, isDesktop);
// }),
...hotelLoyaltyEntries
.where((e) => e["is_active"] != "0")
.map((e) => _buildFrequentLoyaltyDataRow1(e, isDesktop)),
IconForLoyaltyMem(),
],
);
}
Widget _buildFrequentLoyaltyDataRow1(
Map<String, dynamic> entry,
bool isDesktop,
) {
int localId = entry["local_id"];
return Container(
padding: const EdgeInsets.only(top: 8.0),
color: Colors.white,
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildHotel(entry),
SizedBox(width: 25),
buildHotelMembershipNum(entry),
SizedBox(
height: 60,
child: Center(
child: Padding(
padding: EdgeInsets.only(
top: 20,
left: 8,
), // 👈 Add top padding here
child: IconButton(
icon: Icon(
Icons.remove_circle_sharp,
color: Colors.red,
size: 15,
),
tooltip: 'Cancel the Details',
onPressed: () {
setState(() {
removeHotelLoyaltyEntry(entry);
});
},
),
),
),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildHotel(entry),
SizedBox(height: 8),
buildHotelMembershipNum(entry),
IconButton(
icon: Icon(
Icons.remove_circle_sharp,
color: Colors.red,
size: 15,
),
tooltip: 'Cancel the Details',
onPressed: () {
setState(() {
// removeHotelLoyaltyEntry(localId);
removeHotelLoyaltyEntry(entry);
});
},
),
],
),
);
}
Widget buildHotel(Map<String, dynamic> entry) {
late Map<String, String> countryMap; // Mapping country_code -> country_name
late List<String> countryCodes; // List of country codes
bool hasAirlineCountryData =
apiHotelsData == null || apiHotelsData!.isEmpty;
countryList = apiHotelsData!;
print("TestcountryList2 - $countryList");
countryMap = {
for (var country in countryList)
country['hotel_id'] as String: '${country['hotel_name']}',
};
countryCodes = countryMap.keys.toList();
String? selectedCode = entry['hotel_id'];
// String? selectedCode = entry['hotel_id'];
String? selectedText =
selectedCode != null ? countryMap[selectedCode] : null;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Hotel",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
// width: widget.isDesktop
// ? MediaQuery.of(context).size.width * 0.15
// : null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child:
hasAirlineCountryData
? CircularProgressIndicator()
: DropdownSearch<String>(
selectedItem:
(entry["hotel_id"] != null &&
countryMap.containsKey(entry["hotel_id"]))
? countryMap[entry["hotel_id"]]
: null,
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(
backgroundColor: Colors.white,
),
constraints: BoxConstraints(maxHeight: 250),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Hotels...",
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Hotels",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
// selectedCountry = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
final selectedCode =
countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key;
print("SelectedHotelId - $selectedCode");
entry['hotel_id'] = selectedCode;
// entry["controller_hotel"] = newValue;
entry['hotel_name'] = newValue;
// final selectedCode = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
// entry['airline'] = selectedCode;
});
},
),
),
),
],
);
}
Widget buildHotelMembershipNum(Map<String, dynamic> entry) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Hotel Membership Number",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: entry["controller_membership"],
enabled: !widget.isViewMode,
onChanged: (value) {
// _clearError("first_name");
},
decoration: InputDecoration(
labelText: "Hotel Membership Number",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget IconForLoyaltyMem() {
return Container(
height: 60, // must define height
alignment: Alignment.bottomRight,
child: IconButton(
icon: Icon(Icons.add_circle, color: Color(0xFF114D8B), size: 30),
onPressed: () => addHotelLoyaltyEntry(),
tooltip: "Add Hotel Loyalty Membership",
),
);
}
//-------------------------Visa Details -----------------------------------------
Widget _buildVisaDetails(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
...visaEntries
.where((e) => e["is_active"] != "0")
.map((e) => _buildVisaDataRow1(e, isDesktop)),
// _buildVisaDataRow1(isDesktop),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
" * confirm your passport is valid for next 6 months",
style: GoogleFonts.poppins(fontSize: 10),
),
IconForVisa(),
],
),
],
);
}
Widget _buildVisaDataRow1(Map<String, dynamic> entry, bool isDesktop) {
return Container(
padding: const EdgeInsets.only(top: 8),
color: Colors.white,
child:
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildVisaCountry(entry),
SizedBox(width: 25),
buildVisaType(entry),
SizedBox(width: 15),
buildVisaValidFrom(entry),
SizedBox(width: 15),
buildVisaValidUpTo(entry),
SizedBox(
height: 60,
child: Center(
child: Padding(
padding: EdgeInsets.only(
top: 20,
left: 8,
), // 👈 Add top padding here
child: IconButton(
icon: Icon(
Icons.remove_circle_sharp,
color: Colors.red,
size: 15,
),
tooltip: 'Cancel the Details',
onPressed: () {
setState(() {
removeVisaEntry(entry);
});
},
),
),
),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildVisaCountry(entry),
SizedBox(height: 8),
buildVisaType(entry),
SizedBox(height: 8),
buildVisaValidFrom(entry),
SizedBox(height: 8),
buildVisaValidUpTo(entry),
IconButton(
icon: Icon(
Icons.remove_circle_sharp,
color: Colors.red,
size: 15,
),
tooltip: 'Cancel the Details',
onPressed: () {
setState(() {
// visaRowIds.remove(id);
removeVisaEntry(entry);
});
},
),
],
),
);
}
Widget buildVisaCountry(entry) {
late Map<String, String> countryMap; // Mapping country_code -> country_name
late List<String> countryCodes; // List of country codes
// countryList = [];
countryList = apiCountryData ?? [];
print("TestcountryList1 - $countryList");
// Map country codes to country names
countryMap = {
for (var item in countryList)
item['country_code'] as String: item['country_name'] as String,
};
// Extract only country codes for processing
countryCodes = countryMap.keys.toList();
// selectedCountry ??= null;
String? selectedCountry = entry['country_code'];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Country",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.17
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(backgroundColor: Colors.white),
// constraints: BoxConstraints(maxHeight: 250),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Country...",
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
// selectedCountry = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
final selectedCode =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
entry['country_code'] = selectedCode;
});
},
),
),
),
],
);
}
Widget buildVisaType(entry) {
if (apiData == null || apiData?['visa_type_of_visa'] == null) {
return Center(
child: Transform.scale(scale: 0.5, child: CircularProgressIndicator()),
);
}
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
// Map for id => name
Map<String, String> visaTypeMap = {
for (var item in purposeList)
item['dropdown_key'].toString(): item['dropdown_value'].toString(),
};
// Selected value from entry
String? selectedPurpose = entry['visa_type_id']?.toString();
if (!visaTypeMap.containsKey(selectedPurpose)) {
selectedPurpose = null;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Visa Type",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.17
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: DropdownSearch<String>(
selectedItem: visaTypeMap[selectedPurpose],
enabled: purposeList.isNotEmpty,
popupProps: PopupProps.menu(
showSearchBox: true,
fit: FlexFit.loose,
menuProps: const MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 200),
itemBuilder:
(context, item, isSelected) => Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Visa Type...",
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
items: visaTypeMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Visa Type",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
),
onChanged: (newValue) {
if (newValue == null) return;
final selectedId =
visaTypeMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
setState(() {
entry['visa_type_id'] = selectedId;
});
print("Updating form data: visa_type_id -> $selectedId");
},
),
),
),
],
);
}
Widget buildVisaType3(entry) {
if (apiData == null || apiData?['visa_type_of_visa'] == null) {
return Center(
child: Transform.scale(scale: 0.5, child: CircularProgressIndicator()),
);
}
// List<dynamic> purposeList = apiData?['visa_type_of_visa'] ?? [];
// List<dynamic> purposeList = [];
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
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
// selectedPurpose ??=
// dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
// String? selectedPurpose = entry['visa_type_id'];
// 3. Selected value
String? selectedPurpose = entry['visa_type_id']?.toString();
// 4. Avoid crash if selected value doesn't match any item
if (!dropdownItems.any((item) => item.value == selectedPurpose)) {
selectedPurpose = null;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Visa Type",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.17
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
),
hint: Text("Select"),
onChanged:
purposeList.isNotEmpty
? (newValue) {
setState(() {
// selectedPurpose = newValue;
entry['visa_type_id'] = newValue;
});
print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
);
}
: null,
items: dropdownItems,
),
),
),
],
);
}
Widget buildVisaType2(entry) {
late Map<String, String>
visaTypeMap; // Mapping country_code -> country_name
late List<String> visaTypeCodes; // List of country codes
// List<dynamic> purposeList = apiData?['visa_type_of_visa'];
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
print("purposeList - $purposeList");
visaTypeMap = {
for (var item in purposeList)
item['visa_type_id'] as String: item['visa_type_of_visa'] as String,
};
// Extract only country codes for processing
visaTypeCodes = visaTypeMap.keys.toList();
// selectedPurpose ??= null;
String? selectedPurpose = entry['visa_type_of_visa'];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Visa Type",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.17
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: DropdownSearch<String>(
selectedItem: visaTypeMap[selectedPurpose],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(backgroundColor: Colors.white),
// constraints: BoxConstraints(maxHeight: 250),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Visa Type...",
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
items: visaTypeMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Visa Type",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
// selectedCountry = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
final selectedPurpose =
visaTypeMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
entry['visa_type_of_visa'] = selectedPurpose;
});
},
),
),
),
],
);
}
DateTime? _selectedCheckOutDate;
DateTime? _selectedCheckInDate;
Widget buildVisaValidFrom(Map<String, dynamic> entry) {
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectValidFromDate(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 = today;
// Use previously selected date if valid
if (_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)) {
initialDate = _selectedCheckOutDate!;
}
final pickedDate = await showDatePicker(
context: context,
// initialDate: initialDate,
// firstDate: initialDate,
firstDate: DateTime(1900),
// lastDate: DateTime(2100),
lastDate: today,
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
print("CheckOut - $_selectedCheckOutDate");
print("CheckIn - $_selectedCheckInDate");
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckInDate = pickedDate;
entry["controller_valid_from"]?.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
// Revalidate expiry
if (_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isBefore(_selectedCheckInDate!)) {
print("Entry - $entry");
print("errorMessages - $errorMessages");
errorMessages["valid_upto${entry['local_id']}"] =
"Invalid Date Range";
// errorMessages["valid_upto"] = "Invalid Date Range";
} else {
errorMessages.remove("valid_upto${entry['local_id']}");
// errorMessages.remove("valid_upto");
}
});
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Valid From",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.15
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
await _selectValidFromDate(context);
},
child: AbsorbPointer(
child: TextField(
// focusNode: _dateFocusNode,
controller: entry["controller_valid_from"],
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,
),
),
),
),
),
),
),
],
);
}
Widget buildVisaValidUpTo(Map<String, dynamic> entry) {
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectValidUpToDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
print("buildVisaValidUpTo ");
print("buildVisaValidUpTo - ${entry["controller_valid_from"].text}");
// Parse date from notifier if available, else use today
DateTime initialDate = today;
// Use previously selected date if valid
// if (_selectedCheckOutDate != null &&
// _selectedCheckOutDate!.isAfter(today)) {
// initialDate = _selectedCheckOutDate!;
// }
DateTime? validFromDate;
try {
String fromDateText = entry["controller_valid_from"].text.trim();
print("Valid From Text: $fromDateText");
if (fromDateText.isNotEmpty) {
validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText);
print("Parsed Valid From: $validFromDate");
}
} catch (e) {
print("Error parsing valid from date: $e");
}
// Use max(today, validFromDate) as firstDate
// DateTime firstDate = today;
DateTime firstDate = validFromDate ?? today;
if (validFromDate != null && validFromDate.isAfter(today)) {
firstDate = validFromDate;
}
final pickedDate = await showDatePicker(
context: context,
// initialDate: initialDate,
// firstDate: initialDate,
// firstDate: DateTime(1900),
initialDate: firstDate,
firstDate: firstDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
entry["controller_valid_upto"].text = DateFormat(
"dd-MM-yyyy",
).format(pickedDate);
if (_selectedCheckInDate != null &&
_selectedCheckOutDate!.isBefore(_selectedCheckInDate!)) {
errorMessages["valid_upto${entry['local_id']}"] =
"Invalid Date Range";
} else {
errorMessages.remove("valid_upto${entry['local_id']}");
}
});
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Valid To",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.15
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
errorMessages.remove("valid_upto${entry['local_id']}");
await _selectValidUpToDate(context);
},
child: AbsorbPointer(
child: TextField(
// focusNode: _dateFocusNode,
// controller: _dateController,
controller: entry["controller_valid_upto"],
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["valid_upto${entry['local_id']}"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["valid_upto${entry['local_id']}"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 10),
maxLines: 2, // Allow it to wrap onto two lines
overflow:
TextOverflow.ellipsis, // Add ellipsis if it still overflows
),
],
],
);
}
// Widget buildVisaValidFrom(Map<String, dynamic> entry) {
// DateTime? _selectedCheckOutDate;
// TimeOfDay? _selectedCheckOutTime;
//
// Future<void> _selectValidFromDate(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 = today;
//
// // Use previously selected date if valid
// if (_selectedCheckOutDate != null &&
// _selectedCheckOutDate!.isAfter(today)) {
// initialDate = _selectedCheckOutDate!;
// }
//
// final pickedDate = await showDatePicker(
// context: context,
// // initialDate: initialDate,
// // firstDate: initialDate,
// firstDate: DateTime(1900),
// lastDate: DateTime(2100),
// initialEntryMode: DatePickerEntryMode.calendarOnly,
// );
//
// if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
// setState(() {
// _selectedCheckOutDate = pickedDate;
// // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
//
// entry["controller_valid_from"].text = DateFormat(
// 'dd-MM-yyyy',
// ).format(pickedDate);
// });
// }
// }
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Valid From",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// CustomTextFieldUserTravellerWrapper(
// width:
// widget.isDesktop
// ? MediaQuery.of(context).size.width * 0.15
// : null,
// isFocused: false,
// isDesktop: widget.isDesktop,
// child: SizedBox(
// height: 40,
// child: GestureDetector(
// onTap: () async {
// await _selectValidFromDate(context);
// },
// child: AbsorbPointer(
// child: TextField(
// // focusNode: _dateFocusNode,
// controller: entry["controller_valid_from"],
//
// 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,
// ),
// ),
// ),
// ),
// ),
// ),
// ),
// ],
// );
// }
//
// Widget buildVisaValidUpTo(Map<String, dynamic> entry) {
// DateTime? _selectedCheckOutDate;
// TimeOfDay? _selectedCheckOutTime;
//
// Future<void> _selectValidUpToDate(BuildContext context) async {
// DateTime now = DateTime.now();
// DateTime today = DateTime(now.year, now.month, now.day);
// print("buildVisaValidUpTo ");
// print("buildVisaValidUpTo - ${entry["controller_valid_from"].text}");
//
// // Parse date from notifier if available, else use today
// DateTime initialDate = today;
//
// // Use previously selected date if valid
// // if (_selectedCheckOutDate != null &&
// // _selectedCheckOutDate!.isAfter(today)) {
// // initialDate = _selectedCheckOutDate!;
// // }
//
// DateTime? validFromDate;
//
// try {
// String fromDateText = entry["controller_valid_from"].text.trim();
// print("Valid From Text: $fromDateText");
// if (fromDateText.isNotEmpty) {
// validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText);
// print("Parsed Valid From: $validFromDate");
// }
// } catch (e) {
// print("Error parsing valid from date: $e");
// }
//
// // Use max(today, validFromDate) as firstDate
// // DateTime firstDate = today;
// DateTime firstDate = validFromDate ?? today;
// if (validFromDate != null && validFromDate.isAfter(today)) {
// firstDate = validFromDate;
// }
// final pickedDate = await showDatePicker(
// context: context,
// // initialDate: initialDate,
// // firstDate: initialDate,
// // firstDate: DateTime(1900),
// initialDate: firstDate,
// firstDate: firstDate,
// lastDate: DateTime(2100),
// initialEntryMode: DatePickerEntryMode.calendarOnly,
// );
//
// if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
// setState(() {
// _selectedCheckOutDate = pickedDate;
// // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
//
// entry["controller_valid_upto"].text = DateFormat(
// 'dd-MM-yyyy',
// ).format(pickedDate);
// });
// }
// }
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Valid To",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// CustomTextFieldUserTravellerWrapper(
// width:
// widget.isDesktop
// ? MediaQuery.of(context).size.width * 0.15
// : null,
// isFocused: false,
// isDesktop: widget.isDesktop,
// child: SizedBox(
// height: 40,
// child: GestureDetector(
// onTap: () async {
// await _selectValidUpToDate(context);
// },
// child: AbsorbPointer(
// child: TextField(
// // focusNode: _dateFocusNode,
// // controller: _dateController,
// controller: entry["controller_valid_upto"],
// 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,
// ),
// ),
// ),
// ),
// ),
// ),
// ),
// ],
// );
// }
Widget IconForVisa() {
return Container(
height: 60, // must define height
alignment: Alignment.bottomRight,
child: IconButton(
icon: Icon(Icons.add_circle, color: Color(0xFF114D8B), size: 30),
onPressed: () => addVisaEntry(),
tooltip: "Add Visa Details",
),
);
}
}