ts-tat/lib/Screens/itnerary/insurance.dart
2025-10-27 17:35:57 +05:30

1414 lines
48 KiB
Dart

import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class InsuranceScreen extends StatefulWidget {
final List<Map<String, dynamic>> flightData;
final Map<String, dynamic>? apiData;
final Function(String, bool) onClose;
final Function(Map<String, dynamic>) onSaveInsurance;
final Map<String, dynamic>? selectedItem;
final String? loginUser;
InsuranceScreen({
required this.onClose,
required this.apiData,
required this.onSaveInsurance,
required this.selectedItem,
required this.loginUser,
required this.flightData,
});
@override
_InsuranceScreenState createState() => _InsuranceScreenState();
}
class _InsuranceScreenState extends State<InsuranceScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
late ValueNotifier<String?> flightFirstTripDateNotifier;
late ValueNotifier<String?> flightLastTripDateNotifier;
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _startDateFocusNode = FocusNode();
final FocusNode _endDateFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
final FocusNode _nomineeFocusNode = FocusNode();
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _startDateController = TextEditingController();
late TextEditingController _dateOfBirthController = TextEditingController();
late TextEditingController _endDateController = TextEditingController();
late TextEditingController _insuranceCommentsController =
TextEditingController();
late TextEditingController _nomineeController = TextEditingController();
late TextEditingController _nomineerelationController =
TextEditingController();
Color layoutColor = Colors.grey;
bool _isHotelNameFocused = false;
bool _startDateFocus = false;
bool _endDateFocus = false;
bool _commentsFocus = false;
bool _nomineeFocus = false;
String? selectedTripType;
String? selectedInsuranceType;
Map<String, String> errorMessages = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
List<String> dataHeader = [
"type_of_insurance",
"start_date",
"end_date",
"comments",
"nominee_name",
"nominee_dob",
"nominee_relationship",
];
Map<String, dynamic> get InsuranceData {
Map<String, dynamic> data = {
"type_of_insurance": selectedInsuranceType,
"start_date": _startDateController.text,
"end_date": _endDateController.text,
"comments": _insuranceCommentsController.text,
"nominee_name": _nomineeController.text,
"nominee_dob": _dateOfBirthController.text,
"nominee_relationship": _nomineerelationController.text,
"created_by": widget.loginUser,
"updated_by": widget.loginUser,
};
if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["insurance_id"] != null &&
widget.selectedItem?["insurance_id"] != 0) {
data["insurance_id"] = widget.selectedItem!["insurance_id"];
}
}
return data;
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
focusNodes.clear();
focusStates.clear();
// Initialize fields dynamically
for (var field in dataHeader) {
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;
});
});
}
print("Focus Nodes Keys: ${focusNodes.keys.toList()}");
print("Focus States Keys: ${focusStates.keys.toList()}");
_insuranceCommentsController = TextEditingController(
text: widget.selectedItem?["comments"] ?? "",
);
_startDateController = TextEditingController(
text: widget.selectedItem?["start_date"] ?? "",
);
_endDateController = TextEditingController(
text: widget.selectedItem?['end_date'] ?? "",
);
_dateOfBirthController = TextEditingController(
text: widget.selectedItem?['nominee_dob'] ?? "",
);
_nomineerelationController = TextEditingController(
text: widget.selectedItem?['nominee_relationship'] ?? "",
);
_nomineeController = TextEditingController(
text: widget.selectedItem?['nominee_name'] ?? "",
);
// Set the selected value if available
if (widget.selectedItem != null &&
widget.selectedItem!["type_of_insurance"] != null) {
selectedInsuranceType =
widget.selectedItem!["type_of_insurance"].toString();
}
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
flightLastTripDateNotifier = ValueNotifier<String?>(null);
WidgetsBinding.instance.addPostFrameCallback((_) {
final result = getFlightTripDateRange(widget.flightData);
flightFirstTripDateNotifier.value = result['firstTripDate'];
flightLastTripDateNotifier.value = result['lastTripDate'];
// ✅ Only set controller after value is updated
// final parsedDate =
// DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat(
"dd-MM-yyyy",
).parse(flightFirstTripDateNotifier.value ?? '');
if (parsedDate != null) {
_startDateController.text = DateFormat('dd-MM-yyyy').format(parsedDate);
}
final parsedEndDate = DateFormat(
"dd-MM-yyyy",
).parse(flightLastTripDateNotifier.value ?? '');
if (parsedEndDate != null) {
_endDateController.text = DateFormat(
'dd-MM-yyyy',
).format(parsedEndDate);
}
print("parsedDate $parsedDate");
print("parsedEndDate $parsedEndDate");
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData,
) {
final allTrips =
flightData
.expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>()
.toList();
if (allTrips.isEmpty) {
return {'firstTripDate': null, 'lastTripDate': null};
}
allTrips.sort((a, b) {
final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900);
final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900);
return aDate.compareTo(bDate);
});
final firstTrip = allTrips.first;
final lastTrip = allTrips.last;
return {
'firstTripDate': firstTrip['date'],
'lastTripDate': lastTrip['date'],
};
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors
// Required fields that must not be empty
List<String> requiredFields = [
"type_of_insurance",
"start_date",
"end_date",
"nominee_name",
"nominee_dob",
"nominee_relationship",
];
// Check validation for each field
for (String field in requiredFields) {
if (data[field] == null || data[field].toString().trim().isEmpty) {
errorMessages[field] = "Required";
}
}
final format = DateFormat("dd-MM-yyyy");
// Additional validation: checkout_date >= checkin_date
final checkIn = data["start_date"];
final checkOut = data["end_date"];
if (checkIn != null &&
checkOut != null &&
checkIn.toString().isNotEmpty &&
checkOut.toString().isNotEmpty) {
try {
final checkInDate = format.parse(checkIn);
final checkOutDate = format.parse(checkOut);
if (checkOutDate.isBefore(checkInDate)) {
errorMessages["end_date"] = "EndDate date cannot be before StartDate";
}
} catch (e) {
errorMessages["end_date"] = "Invalid date format";
}
}
return errorMessages.isEmpty; // Valid if there are no errors
}
void handleSave() {
print("Handle Save InsuranceData $InsuranceData");
Map<String, dynamic> data = InsuranceData;
if (!isValidData(data)) {
print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
} else {
widget.onSaveInsurance(InsuranceData);
}
widget.onClose("Insurance", false);
// widget.onClose(false); // Close screen after saving
}
DateTime? _parseDate(String date) {
try {
return DateFormat("dd-MM-yyyy").parse(date); // Change format if needed
} catch (e) {
return null;
}
}
@override
void dispose() {
// Dispose all dynamically created FocusNodes
for (var node in focusNodes.values) {
node.dispose();
}
_tripTypeFocusNode.dispose();
_tripTypeController.dispose();
_startDateController.dispose();
_endDateController.dispose();
_insuranceCommentsController.dispose();
_nomineeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
// color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
),
],
),
),
),
);
},
);
}
List<Widget> _buildAccomadtionForm(bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop),
_buildSecondSubRow(isDesktop),
];
return [
// ...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
];
}
// List<Widget> _buildFirstRow(isDesktop) {
// return [
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Insurance Type", // not in use
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w500,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// isDesktop
// ? Row(children: _buildTripType(isDesktop))
// : Column(children: _buildTripType(isDesktop)),
// ],
// ),
// if (isDesktop) Spacer() else SizedBox(height: 8),
// ];
// }
List<Widget> _buildTripType(bool isDesktop) {
List<dynamic> purposeList =
widget.apiData?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
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
selectedInsuranceType ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedInsuranceType,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
),
onChanged:
purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedInsuranceType = newValue;
if (selectedInsuranceType!.isNotEmpty) {
errorMessages.remove("type_of_insurance");
}
});
print(selectedInsuranceType);
}
: null,
items: dropdownItems,
),
),
),
];
}
List<Widget> _buildInsuranceTypeDropdown(bool isDesktop) {
// List<dynamic> purposeList = widget.apiData?['insurance_type_of_insurance'] ?? [];
List<Map<String, dynamic>> purposeList =
(widget.apiData?['insurance_type_of_insurance'] as List<dynamic>?)
?.map((e) => Map<String, dynamic>.from(e as Map))
.toList() ??
[];
// Find selected item object based on key
Map<String, dynamic>? selectedItem = purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedInsuranceType,
orElse: () => {},
);
return [
CustomTextFieldWrapper(
isFocused: false,
isDesktop: isDesktop,
child: SizedBox(
height: 35,
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
popupProps: PopupProps.menu(
showSearchBox: false,
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 10),
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'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12, // 👈 Smaller font size
color: Colors.black,
),
),
);
},
),
dropdownDecoratorProps: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, // No underline
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
return Text(
"Select Insurance Type",
style: GoogleFonts.poppins(color: Colors.grey, fontSize: 13),
);
}
return Text(
selectedItem['dropdown_value'] ?? '',
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
);
},
selectedItem: selectedItem!.isNotEmpty ? selectedItem : null,
itemAsString: (item) => item['dropdown_value'] ?? '',
onChanged:
purposeList.isNotEmpty
? (Map<String, dynamic>? newItem) {
if (newItem != null) {
setState(() {
selectedInsuranceType = newItem['dropdown_key'];
errorMessages.remove("type_of_insurance");
});
print(
"Selected Insurance Type: $selectedInsuranceType",
);
}
}
: null,
items: purposeList,
),
),
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
List<dynamic> purposeList =
widget.apiData?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
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
selectedInsuranceType ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
// ----------------------------------------------------------------
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
// Parse date from notifier if available, else use today
DateTime initialDate;
if (flightFirstTripDateNotifier.value != null) {
try {
initialDate = DateTime.parse(flightFirstTripDateNotifier.value!);
} catch (e) {
initialDate = today;
}
} else {
initialDate = today;
}
// Use previously selected date if valid
if (_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)) {
initialDate = _selectedCheckOutDate!;
}
final pickedDate = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: initialDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
// DateTime? pickedDate = await showDatePicker(
// context: context,
// initialDate: _selectedCheckOutDate != null &&
// _selectedCheckOutDate!.isAfter(today)
// ? _selectedCheckOutDate!
// : today,
// firstDate: today,
// lastDate: DateTime(2100),
// );
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_startDateController.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
});
}
}
Future<void> _selectEndCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
// DateTime? checkInDate;
// try {
// checkInDate = DateTime.parse(_startDateController.text);
// } catch (e) {
// checkInDate = today;
// }
// // Ensure at least today is used
// DateTime firstDate = checkInDate.isAfter(today) ? checkInDate : today;
// DateTime initialDate = _selectedCheckOutDate != null &&
// _selectedCheckOutDate!.isAfter(firstDate)
// ? _selectedCheckOutDate!
// : firstDate;
// DateTime firstDate = checkInDate;
// DateTime initialDate =
// _selectedCheckOutDate != null &&
// _selectedCheckOutDate!.isAfter(firstDate)
// ? _selectedCheckOutDate!
// : firstDate;
DateTime? validFromDate;
try {
String fromDateText = _startDateController.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,
);
// final pickedDate = await showDatePicker(
// context: context,
// // initialDate: initialDate,
// initialDate: initialDate,
// firstDate: initialDate,
// lastDate: DateTime(2100),
// initialEntryMode: DatePickerEntryMode.calendarOnly,
// );
// DateTime? pickedDate = await showDatePicker(
// context: context,
// initialDate: _selectedCheckOutDate != null &&
// _selectedCheckOutDate!.isAfter(today)
// ? _selectedCheckOutDate!
// : today,
// firstDate: today,
// lastDate: DateTime(2100),
// );
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_endDateController.text = DateFormat('dd-MM-yyyy').format(pickedDate);
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Insurance Type *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: const Color(0xFF575A74),
),
),
const SizedBox(height: 5),
// ▼ Dropdown Wrapper ▼
// CustomTextFieldWrapper(
// isFocused: _isHotelNameFocused,
// isDesktop: isDesktop,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
// child: SizedBox(
// height: 35,
// width: double.infinity,
// child: DropdownButtonFormField<String>(
// focusNode: _tripTypeFocusNode,
// value: selectedInsuranceType,
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
// decoration: const InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 5,
// ),
// ),
// hint: Text(
// "Select ",
// style: GoogleFonts.poppins(color: Colors.grey, fontSize: 13),
// ),
// items:
// purposeList.map<DropdownMenuItem<String>>((item) {
// return DropdownMenuItem<String>(
// value: item['dropdown_key'],
// child: Text(
// item['dropdown_value'] ?? '',
// style: GoogleFonts.poppins(fontSize: 12),
// ),
// );
// }).toList(),
// onChanged:
// purposeList.isNotEmpty
// ? (String? newValue) {
// setState(() {
// selectedInsuranceType = newValue;
// if ((selectedInsuranceType ?? '').isNotEmpty) {
// errorMessages.remove("type_of_insurance");
// }
// });
// print(
// "Selected Insurance Type: $selectedInsuranceType",
// );
// }
// : null,
// ),
// ),
// ),
// ▼ Dropdown Wrapper ▼
CustomTextFieldWrapper(
// isFocused: _isHotelNameFocused,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
child: SizedBox(
height: 40,
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedInsuranceType,
orElse: () => {},
),
itemAsString: (item) => item['dropdown_value'] ?? '',
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: 5,
),
child: Text(
item['dropdown_value'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
);
},
),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none, // No underline
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["type_of_insuranceFocusNode"] ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["type_of_insuranceFocusNode"] ?? false)
? layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor!, width: 1),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
),
// dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 10,
// ),
// ),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
return Text(
"Select ",
style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 13,
),
);
}
return Text(
selectedItem['dropdown_value'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
);
},
onChanged:
purposeList.isNotEmpty
? (Map<String, dynamic>? newValue) {
setState(() {
selectedInsuranceType = newValue?['dropdown_key'];
print(
"selected Insurance Type: ${selectedInsuranceType}",
);
});
}
: null,
),
),
),
],
),
// if (isDesktop) Spacer() else SizedBox(height: 8),
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Nominee",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// CustomTextFieldWrapper(
// isFocused: _nomineeFocus,
// isDesktop: isDesktop,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
// // width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
// child: SizedBox(
// height: 40,
// child: TextField(
// focusNode: _nomineeFocusNode,
// controller: _nomineeController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Nominee",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// // contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1),
// ),
// ),
// ),
// ),
// ],
// ),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Start Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
// isFocused: _startDateFocus,
isFocused: focusStates["start_dateFocused"] ?? false,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
focusNodes["start_dateFocusNode"]?.requestFocus();
await _selectCheckOutDate(context);
if (_startDateController.text.isNotEmpty) {
setState(() {
errorMessages.remove(
"start_date",
); // Removes the key completely
});
}
},
child: AbsorbPointer(
child: TextField(
// focusNode: _startDateFocusNode,
focusNode: focusNodes["start_dateFocusNode"],
controller: _startDateController,
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select ",
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["start_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"End Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["end_dateFocused"] ?? false,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
focusNodes["end_dateFocusNode"]?.requestFocus();
await _selectEndCheckOutDate(context);
if (_endDateController.text.isNotEmpty) {
DateTime? startDate = _parseDate(_startDateController.text);
DateTime? endDate = _parseDate(_endDateController.text);
if (startDate != null &&
endDate != null &&
endDate.isBefore(startDate)) {
setState(() {
errorMessages["end_date"] =
"End date cannot be earlier than start date";
});
} else {
setState(() {
errorMessages.remove("end_date");
});
}
}
},
child: AbsorbPointer(
child: TextField(
// focusNode: _endDateFocusNode,
focusNode: focusNodes["end_dateFocusNode"],
controller: _endDateController,
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select ",
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["end_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["end_date"]!,
style: const TextStyle(color: Colors.red, fontSize: 12),
maxLines: 2, // Allow it to wrap onto two lines
overflow:
TextOverflow.ellipsis, // Add ellipsis if it still overflows
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
];
}
List<Widget> _buildSecondSubRow(bool isDesktop) {
List<dynamic> purposeList =
widget.apiData?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
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
selectedInsuranceType ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
// ----------------------------------------------------------------
DateTime? _selectedCheckDOBDate;
Future<void> _selectCheckDOBOutDate(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;
initialDate = today;
final pickedDate = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: DateTime(1900),
lastDate: today,
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
// DateTime? pickedDate = await showDatePicker(
// context: context,
// initialDate: _selectedCheckOutDate != null &&
// _selectedCheckOutDate!.isAfter(today)
// ? _selectedCheckOutDate!
// : today,
// firstDate: today,
// lastDate: DateTime(2100),
// );
if (pickedDate != null && pickedDate != _selectedCheckDOBDate) {
setState(() {
_selectedCheckDOBDate = pickedDate;
_dateOfBirthController.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Nominee*",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
// isFocused: _nomineeFocus,
isFocused: focusStates["nominee_nameFocused"] ?? false,
isDesktop: isDesktop,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["nominee_nameFocusNode"],
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')),
],
// focusNode: _nomineeFocusNode,
controller: _nomineeController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Nominee",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
// contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1),
),
),
),
),
if (errorMessages["nominee_name"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date of Birth*",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["nominee_dobFocused"] ?? false,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
focusNodes["nominee_dobFocusNode"]?.requestFocus();
await _selectCheckDOBOutDate(context);
// await _selectDateOfBirth(context);
if (_dateOfBirthController.text.isNotEmpty) {
setState(() {
errorMessages.remove(
"nominee_dob",
); // Removes the key completely
});
}
},
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["nominee_dobFocusNode"],
controller: _dateOfBirthController,
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select ",
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["nominee_dob"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Relationship*",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
//
CustomTextFieldWrapper(
isFocused: focusStates["nominee_relationshipFocused"] ?? false,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["nominee_relationshipFocusNode"],
controller: _nomineerelationController,
style: TextStyle(fontSize: 12),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')),
],
decoration: InputDecoration(
labelText: "Nominee Relationship",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
// contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1),
),
),
),
),
if (errorMessages["nominee_relationship"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused:
focusStates["commentsFocused"] ??
false, // Dropdown doesn't use focus
isDesktop: isDesktop,
width:
isDesktop
? MediaQuery.of(context).size.width * 0.31
: null, // MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
// focusNode: _commentsFocusNode,
focusNode: focusNodes["commentsFocusNode"],
controller: _insuranceCommentsController,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')),
],
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Comments",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop) Spacer(),
SizedBox(height: 5),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
],
),
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
widget.onClose("Insurance", false); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: GoogleFonts.poppins(color: Colors.white, fontSize: 12),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: GoogleFonts.poppins(color: Colors.white, fontSize: 12),
),
),
];
}
}