latest commit 10-06-2025

This commit is contained in:
vadivelJ96 2025-06-11 09:56:12 +05:30
commit 036d0704fd
38 changed files with 5013 additions and 2310 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -913,7 +913,7 @@ class _ListAllPlansState extends State<ListAllPlans> {
size: 18,
),
tooltip:
'Download The Trip Details',
'Download The PDF',
onPressed: () {
Navigator.pop(
context,

View File

@ -931,7 +931,7 @@ class _ApprovalListState extends State<ApprovalList> {
size: 18,
),
tooltip:
'Download The Trip Details',
'Download The PDF',
onPressed: () {
Navigator.pop(
context,
@ -1170,7 +1170,7 @@ class _ApprovalListState extends State<ApprovalList> {
size: 18,
),
tooltip:
'Download The Trip Details',
'Download The PDF',
onPressed: () {
Navigator.pop(context);
apiService

View File

@ -336,7 +336,7 @@ class CostCenterListState extends State<CostCenterList> {
MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Add CostCenter",
"Add Cost Center",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
),

View File

@ -196,23 +196,26 @@ class DepartmentDataState extends State<DepartmentData> {
? await http.put(uri, headers: headers, body: body)
: await http.post(uri, headers: headers, body: body);
switch (response.statusCode) {
case 200:
print("Update - Response: ${response.body}");
if (response.statusCode == 200 || response.statusCode == 201) {
print("successfully!");
print("Response: ${response.body}");
_clearError();
widget.fetchGetDepartment();
// dispose();
Navigator.of(context).pop();
break;
case 201:
print("Save - Response: ${response.body}");
_clearError();
await widget.fetchGetDepartment();
} else if (response.statusCode == 404) {
Navigator.of(context).pop();
break;
final message = jsonDecode(response.body)['message'] ?? 'Unknown error';
default:
print("Failed to submit department. Status: ${response.statusCode}");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.redAccent,
behavior: SnackBarBehavior.floating,
),
);
} else {
print("Failed to submit. Status: ${response.statusCode}");
print("Error: ${response.body}");
}
} catch (e) {

View File

@ -349,7 +349,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
mainAxisSize:
MainAxisSize.min, // Ensures content doesn't expand unnecessarily
children: [
widget.title == "Others"
widget.title == "Others (Non Employee)"
? Text(
"Please Select Other User",
style: GoogleFonts.poppins(fontSize: 14),
@ -367,7 +367,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
setState(() {
_showTravellerForm = false;
});
widget.title == "Others"
widget.title == "Others (Non Employee)"
? _filterTravellers(query)
: _filterUsers(query);
},
@ -395,7 +395,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
SizedBox(height: 10),
if (widget.title == "Others") ...[
if (widget.title == "Others (Non Employee)") ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [

View File

@ -396,7 +396,7 @@ class ForexDataState extends State<ForexData> {
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Country...",
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
@ -414,7 +414,7 @@ class ForexDataState extends State<ForexData> {
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),

View File

@ -746,9 +746,9 @@ class ForexDataListState extends State<ForexDataList> {
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
// color: forex['is_active'] == "1"
// ? Colors.green
// : Colors.grey,
color: forex['is_active'] == "1"
? Colors.green
: Colors.grey,
),
softWrap: true,
overflow: TextOverflow.ellipsis,

View File

@ -93,11 +93,13 @@ class _groupState extends State<Group> {
String? bodyStringColor = await getBodyColor();
setState(() {
layoutColor = layoutString != null
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor = bodyStringColor != null
bodyColor =
bodyStringColor != null
? Color(int.parse(bodyStringColor))
: Colors.white;
});
@ -154,10 +156,7 @@ class _groupState extends State<Group> {
errorMessages.clear(); // Reset errors
// Required fields that must not be empty
List<String> requiredFields = [
"name",
"description",
];
List<String> requiredFields = ["name", "description"];
// Check validation for each field
for (String field in requiredFields) {
@ -165,6 +164,14 @@ class _groupState extends State<Group> {
errorMessages[field] = "Required";
}
}
// At least one of the two policies must be selected
if ((selectedDomestic == null || selectedDomestic!.isEmpty) &&
(selectedInternational == null || selectedInternational!.isEmpty)) {
errorMessages["domestic_policy_id"] = "Select at least one policy";
errorMessages["international_policy_id"] = "Select at least one policy";
}
return errorMessages.isEmpty;
return errorMessages.isEmpty; // Valid if there are no errors
}
@ -223,7 +230,8 @@ class _groupState extends State<Group> {
}
try {
final response = await (isEdit
final response =
await (isEdit
? http.put(
Uri.parse(apiUrlData),
headers: {
@ -320,8 +328,10 @@ class _groupState extends State<Group> {
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
// backgroundColor: Colors.white,
@ -329,23 +339,27 @@ class _groupState extends State<Group> {
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding: isDesktop
padding:
isDesktop
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width *
horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height *
vertical:
MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(8),
child: Row(
children: [
// if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(child: buildData(isDesktop, context))
Expanded(child: buildData(isDesktop, context)),
],
),
),
);
});
},
);
}
Widget buildData(bool isDesktop, context) {
@ -367,7 +381,8 @@ class _groupState extends State<Group> {
Container(
color: Colors.white,
padding: const EdgeInsets.all(8.0),
child: isDesktop
child:
isDesktop
? Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _buildSubmit(isDesktop),
@ -387,7 +402,8 @@ class _groupState extends State<Group> {
// margin: isDesktop
// ? EdgeInsets.all(10.0)
// : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
height: isDesktop
height:
isDesktop
? MediaQuery.of(context).size.height * 0.98
: MediaQuery.of(context).size.height,
// decoration: BoxDecoration(
@ -414,10 +430,7 @@ class _groupState extends State<Group> {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Create Group",
style: GoogleFonts.poppins(fontSize: 18),
),
Text("Create Group", style: GoogleFonts.poppins(fontSize: 18)),
// Container(
// color: Color(0xFFE9EBF6),
// child: IconButton(
@ -466,9 +479,7 @@ class _groupState extends State<Group> {
children: _buildFirstRow(isDesktop),
),
SizedBox(
height: 10,
),
SizedBox(height: 10),
isDesktop
? Row(
@ -482,7 +493,8 @@ class _groupState extends State<Group> {
// SizedBox(height: 15),
],
)),
),
),
],
),
);
@ -498,13 +510,15 @@ class _groupState extends State<Group> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
@ -542,13 +556,15 @@ class _groupState extends State<Group> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
@ -591,18 +607,21 @@ class _groupState extends State<Group> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
height: 40,
child: apiForDomestic == null
child:
apiForDomestic == null
? Center(
child: Transform.scale(
scale: 0.5,
@ -610,12 +629,15 @@ class _groupState extends State<Group> {
),
)
: DropdownSearch<String>(
selectedItem: selectedDomestic == null
selectedItem:
selectedDomestic == null
? null
: apiForDomestic!
.firstWhere((policy) =>
.firstWhere(
(policy) =>
policy['policy_id'] ==
selectedDomestic)['name']
selectedDomestic,
)['name']
.toString(),
popupProps: PopupProps.menu(
showSearchBox: true,
@ -624,12 +646,14 @@ class _groupState extends State<Group> {
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Policy...",
contentPadding:
EdgeInsets.symmetric(horizontal: 10),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
),
),
items: apiForDomestic!
),
items:
apiForDomestic!
.map((policy) => policy['name'].toString())
.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
@ -638,7 +662,8 @@ class _groupState extends State<Group> {
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder: (context, selectedItem) => Align(
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
@ -647,14 +672,15 @@ class _groupState extends State<Group> {
),
onChanged: (String? newValue) {
setState(() {
selectedDomestic = apiForDomestic!.firstWhere(
(policy) =>
policy['name'] == newValue)['policy_id'];
selectedDomestic =
apiForDomestic!.firstWhere(
(policy) => policy['name'] == newValue,
)['policy_id'];
});
},
),
),
)
),
],
),
Column(
@ -665,18 +691,21 @@ class _groupState extends State<Group> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
height: 40,
child: apiForInternational == null
child:
apiForInternational == null
? Center(
child: Transform.scale(
scale: 0.5,
@ -684,12 +713,15 @@ class _groupState extends State<Group> {
),
)
: DropdownSearch<String>(
selectedItem: selectedInternational == null
selectedItem:
selectedInternational == null
? null
: apiForInternational!
.firstWhere((policy) =>
.firstWhere(
(policy) =>
policy['policy_id'] ==
selectedInternational)['name']
selectedInternational,
)['name']
.toString(),
popupProps: PopupProps.menu(
showSearchBox: true,
@ -698,12 +730,14 @@ class _groupState extends State<Group> {
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Policy...",
contentPadding:
EdgeInsets.symmetric(horizontal: 10),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
),
),
items: apiForInternational!
),
items:
apiForInternational!
.map((policy) => policy['name'].toString())
.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
@ -712,7 +746,8 @@ class _groupState extends State<Group> {
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder: (context, selectedItem) => Align(
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
@ -721,14 +756,15 @@ class _groupState extends State<Group> {
),
onChanged: (String? newValue) {
setState(() {
selectedInternational = apiForInternational!
.firstWhere((policy) =>
policy['name'] == newValue)['policy_id'];
selectedInternational =
apiForInternational!.firstWhere(
(policy) => policy['name'] == newValue,
)['policy_id'];
});
},
),
),
)
),
],
),
];
@ -749,10 +785,9 @@ class _groupState extends State<Group> {
onPressed: () {
context.go('/group');
},
child: Text("Cancel")),
SizedBox(
width: 20,
child: Text("Cancel"),
),
SizedBox(width: 20),
MouseRegion(
// cursor: widget.isViewMode
// ? SystemMouseCursors.forbidden
@ -773,7 +808,7 @@ class _groupState extends State<Group> {
onPressed: handleSubmit, // Disable when in view mode
child: Text("Submit"),
),
)
),
];
}
}

View File

@ -407,7 +407,7 @@ class GroupDataState extends State<GroupData> {
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Select Policy For International",
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
@ -425,7 +425,7 @@ class GroupDataState extends State<GroupData> {
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Policy For International",
selectedItem ?? "Select",
style: GoogleFonts.poppins(fontSize: 11),
),
),
@ -481,7 +481,7 @@ class GroupDataState extends State<GroupData> {
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Select Policy For Domestic...",
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
@ -499,7 +499,7 @@ class GroupDataState extends State<GroupData> {
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Policy For Domestic",
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),

View File

@ -353,7 +353,7 @@ class _GroupListState extends State<GroupList> {
MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Add New Group",
"Add Group",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
),

View File

@ -418,7 +418,7 @@ class HotelsDataState extends State<HotelsData> {
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Country...",
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
@ -436,7 +436,7 @@ class HotelsDataState extends State<HotelsData> {
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),

View File

@ -15,13 +15,14 @@ class AccomodationScreen extends StatefulWidget {
final String? loginUser;
final String? tripType;
AccomodationScreen(
{required this.onClose,
AccomodationScreen({
required this.onClose,
required this.onSaveAccomadation,
required this.selectedItem,
required this.loginUser,
required this.flightData,
this.tripType});
this.tripType,
});
@override
_AccomodationScreenState createState() => _AccomodationScreenState();
@ -107,15 +108,23 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
super.initState();
_addFocusListener(
_destinationFocusNode, (focus) => _destinationFocused = focus);
_destinationFocusNode,
(focus) => _destinationFocused = focus,
);
_addFocusListener(
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_hotelNameFocusNode,
(focus) => _isHotelNameFocused = focus,
);
_addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus);
_addFocusListener(
_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus);
_checkInTimeFocusNode,
(focus) => _checkInTimeFocus = focus,
);
_addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus);
_addFocusListener(
_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
_checkOutTimeFocusNode,
(focus) => _checkOutTimeFocus = focus,
);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
_destinationController = initController("destination_city");
@ -149,25 +158,29 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
flightFirstToDestinationNotifier.value = result['firstToDestination'];
print(
"flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value ");
"flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value ",
);
// Only set controller after value is updated
// final parsedDate = DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat("dd-MM-yyyy")
.parse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat(
"dd-MM-yyyy",
).parse(flightFirstTripDateNotifier.value ?? '');
if (parsedDate != null) {
_checkInController.text = DateFormat('dd-MM-yyyy').format(parsedDate);
}
// final parsedEndDate = DateTime.tryParse(flightLastTripDateNotifier.value ?? '');
final parsedEndDate = DateFormat("dd-MM-yyyy")
.parse(flightLastTripDateNotifier.value ?? '');
final parsedEndDate = DateFormat(
"dd-MM-yyyy",
).parse(flightLastTripDateNotifier.value ?? '');
if (parsedEndDate != null) {
// _checkOutController.text = DateFormat('dd-MM-yyyy').format(parsedEndDate);
_checkOutController.text =
DateFormat('dd-MM-yyyy').format(parsedEndDate);
_checkOutController.text = DateFormat(
'dd-MM-yyyy',
).format(parsedEndDate);
}
// Check and set default times if empty
@ -202,17 +215,16 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
}
Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) {
final allTrips = flightData
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,
};
return {'firstTripDate': null, 'lastTripDate': null};
}
allTrips.sort((a, b) {
@ -230,7 +242,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
return {
'firstTripDate': firstTrip['date'],
'lastTripDate': lastTrip['date'],
'firstToDestination': firstTripToDestination['to_place']
'firstToDestination': firstTripToDestination['to_place'],
};
}
@ -244,7 +256,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
"checkin_date",
"checkin_time",
"checkout_date",
"checkout_time"
"checkout_time",
];
// Check validation for each field
@ -284,8 +296,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
checkOutTime.toString().isNotEmpty) {
try {
final checkInDateTime = formatTime.parse("$checkIn $checkInTime");
final checkOutDateTime =
formatTime.parse("$checkOut $checkOutTime");
final checkOutDateTime = formatTime.parse(
"$checkOut $checkOutTime",
);
if (!checkOutDateTime.isAfter(checkInDateTime)) {
errorMessages["checkout_time"] =
@ -359,9 +372,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
// color: Color(0xFFF4F4FB),
@ -372,47 +387,36 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(28.0),
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
),
],
),
),
),
);
});
},
);
}
List<Widget> _buildAccomadtionForm(isDesktop) {
return [
isDesktop
? Row(
children: _buildFirstRow(isDesktop),
)
: Column(
children: _buildFirstRow(isDesktop),
),
? Row(children: _buildFirstRow(isDesktop))
: Column(children: _buildFirstRow(isDesktop)),
SizedBox(height: 10), // Spacing between first and second row
isDesktop
? Row(
children: _buildSecondRow(isDesktop),
)
: Column(
children: _buildSecondRow(isDesktop),
),
? Row(children: _buildSecondRow(isDesktop))
: Column(children: _buildSecondRow(isDesktop)),
SizedBox(height: 10),
isDesktop
? Row(
children: _buildThirdRow(isDesktop),
)
: Column(
children: _buildThirdRow(isDesktop),
),
? Row(children: _buildThirdRow(isDesktop))
: Column(children: _buildThirdRow(isDesktop)),
];
}
@ -432,22 +436,23 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// ),
// ],
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Destination",
"Destination *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _destinationFocused,
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
@ -475,27 +480,24 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Hotel Name",
"Hotel Name *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
@ -557,6 +559,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
initialDate: initialDate,
firstDate: initialDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
// DateTime? pickedDate = await showDatePicker(
@ -583,18 +586,61 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
initialTime: _selectedCheckInTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckInTime) {
if (pickedTime != null) {
final now = DateTime.now();
// Parse the selected date
final dateText = _checkInController.text ?? "";
final selectedDate = DateFormat(
'dd-MM-yyyy',
).parse(dateText); // or 'yyyy-MM-dd' depending on your format
final selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
pickedTime.hour,
pickedTime.minute,
);
// Only validate past time if date is today
final isToday =
selectedDate.year == now.year &&
selectedDate.month == now.month &&
selectedDate.day == now.day;
bool isPastTime = selectedDateTime.isBefore(now);
if (isToday && isPastTime) {
setState(() {
errorMessages["checkin_time"] = "You can't select a past time.";
});
return;
}
// Valid time selection
setState(() {
_selectedCheckInTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
);
final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
_checkInTimeController.text = formattedTime;
// errorMessages["time_$index"] = ""; // clear previous error
errorMessages.remove("checkin_time");
});
}
// if (pickedTime != null && pickedTime != _selectedCheckInTime) {
// setState(() {
// _selectedCheckInTime = pickedTime;
// // Formatting time to HH:mm (24-hour format)
// final now = DateTime.now();
// final formattedTime = DateFormat('HH:mm').format(
// DateTime(now.year, now.month, now.day, pickedTime.hour,
// pickedTime.minute),
// );
// _checkInTimeController.text = formattedTime;
// });
// }
}
//-------------------------------Check-In End
@ -647,7 +693,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// : firstDate;
DateTime firstDate = checkInDate;
DateTime initialDate = _selectedCheckOutDate != null &&
DateTime initialDate =
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(firstDate)
? _selectedCheckOutDate!
: firstDate;
@ -657,13 +704,15 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
initialDate: initialDate,
firstDate: firstDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_checkOutController.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
_checkOutController.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
errorMessages.remove("checkout_date");
});
}
@ -681,8 +730,13 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
DateTime(
now.year,
now.month,
now.day,
pickedTime.hour,
pickedTime.minute,
),
);
_checkOutTimeController.text = formattedTime;
errorMessages.remove("checkout_time");
@ -695,11 +749,12 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Check-in*",
"Check-in *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -720,8 +775,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
@ -737,21 +795,17 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time (Check-in)",
"Time (Check-in) *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -760,7 +814,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckInTime(context),
onTap: () {
_checkInTimeController.text = "";
_selectCheckInTime(context);
},
child: AbsorbPointer(
child: TextField(
focusNode: _checkInTimeFocusNode,
@ -772,8 +829,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
suffixIcon: Icon(
Icons.access_time,
size: 16,
color: Colors.grey,
),
),
),
),
@ -789,12 +849,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -803,7 +858,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -817,15 +873,18 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: TextField(
focusNode: _checkOutFocusNode,
controller: _checkOutController,
style: const TextStyle(fontSize: 12),
style: const TextStyle(fontSize: 12, color: Colors.black),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
@ -841,21 +900,17 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time (Check-out)",
"Time (Check-out) *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -876,8 +931,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
suffixIcon: Icon(
Icons.access_time,
size: 16,
color: Colors.grey,
),
),
),
),
@ -906,13 +964,15 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
@ -934,9 +994,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
),
if (isDesktop) Spacer(),
SizedBox(
height: 5,
),
SizedBox(height: 5),
Column(
children: [
Row(
@ -957,9 +1015,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
@ -968,7 +1024,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
@ -976,9 +1031,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(

View File

@ -13,12 +13,13 @@ class BusScreen extends StatefulWidget {
final Map<String, dynamic>? selectedItem;
final String? loginUser;
BusScreen(
{required this.onClose,
BusScreen({
required this.onClose,
this.apiData,
required this.onSaveBus,
required this.selectedItem,
required this.loginUser});
required this.loginUser,
});
@override
_BusScreenState createState() => _BusScreenState();
@ -195,9 +196,11 @@ class _BusScreenState extends State<BusScreen> {
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
// color: Colors.white,
@ -229,17 +232,18 @@ class _BusScreenState extends State<BusScreen> {
// height: 6,
// ),
Padding(
padding: const EdgeInsets.all(28.0),
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
),
],
),
),
),
);
});
},
);
}
List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -252,7 +256,7 @@ class _BusScreenState extends State<BusScreen> {
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop)
_buildSecondRow(isDesktop),
];
return [
@ -277,39 +281,40 @@ class _BusScreenState extends State<BusScreen> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
isDesktop
? Row(children: _buildTripType(isDesktop))
: Column(children: _buildTripType(isDesktop))
: Column(children: _buildTripType(isDesktop)),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
];
}
List<Widget> _buildTripType(bool isDesktop) {
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item) => DropdownMenuItem<String>(
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)),
child: Text(
"No options available",
style: TextStyle(color: Colors.grey),
),
),
);
}
@ -327,19 +332,23 @@ class _BusScreenState extends State<BusScreen> {
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose,
style: TextStyle(fontSize: 12),
// style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 10), // Proper padding
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
),
onChanged: purposeList.isNotEmpty
onChanged:
purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
);
}
: null,
@ -360,12 +369,14 @@ class _BusScreenState extends State<BusScreen> {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null &&
initialDate:
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
@ -383,29 +394,64 @@ class _BusScreenState extends State<BusScreen> {
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
// Parse the selected date
final dateText = _dateController.text ?? "";
final selectedDate = DateFormat(
'dd-MM-yyyy',
).parse(dateText); // or 'yyyy-MM-dd' depending on your format
final now = DateTime.now();
final selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
pickedTime.hour,
pickedTime.minute,
);
final isToday =
selectedDate.year == now.year &&
selectedDate.month == now.month &&
selectedDate.day == now.day;
bool isPastTime = selectedDateTime.isBefore(now);
if (isToday && isPastTime) {
setState(() {
errorMessages["time"] = "You can't select a past time.";
});
return;
} else {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
DateTime(
now.year,
now.month,
now.day,
pickedTime.hour,
pickedTime.minute,
),
);
_timeController.text = formattedTime;
});
}
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"From",
"From *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -429,28 +475,21 @@ class _BusScreenState extends State<BusScreen> {
),
if (errorMessages["from"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"To",
"To *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -474,28 +513,21 @@ class _BusScreenState extends State<BusScreen> {
),
if (errorMessages["to"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date",
"Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -516,8 +548,11 @@ class _BusScreenState extends State<BusScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
@ -526,28 +561,21 @@ class _BusScreenState extends State<BusScreen> {
),
if (errorMessages["date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time",
"Time *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -556,20 +584,27 @@ class _BusScreenState extends State<BusScreen> {
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
onTap: () {
// _selectedCheckOutTime = null; // Reset time variable
_timeController.text = ""; // Clear text field
_selectCheckOutTime(context);
},
child: AbsorbPointer(
child: TextField(
focusNode: _timeFocusNode,
controller: _timeController,
style: const TextStyle(fontSize: 12),
style: const TextStyle(fontSize: 12, color: Colors.black),
decoration: const InputDecoration(
labelText: "Select Time",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
suffixIcon: Icon(
Icons.access_time,
size: 16,
color: Colors.grey,
),
),
),
),
@ -579,7 +614,7 @@ class _BusScreenState extends State<BusScreen> {
if (errorMessages["time"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
errorMessages["time"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
@ -598,7 +633,8 @@ class _BusScreenState extends State<BusScreen> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
@ -644,9 +680,7 @@ class _BusScreenState extends State<BusScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
@ -655,7 +689,6 @@ class _BusScreenState extends State<BusScreen> {
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
@ -663,9 +696,7 @@ class _BusScreenState extends State<BusScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(

View File

@ -55,6 +55,8 @@ class FlightScreenState extends State<FlightScreen> {
Map<String, String?> selectedValues = {};
int rowCount = 1;
String? selectedTripType;
Map<int, String?> selectedClasses = {}; // Store class selection for each trip
Map<int, String?> selectedFrom = {}; // Store class selection for each trip
@ -251,9 +253,14 @@ class FlightScreenState extends State<FlightScreen> {
Map<String, String> tempCountryMap = {};
for (var country in result) {
String city = country['City'] ?? '';
String airport = country['Airport'] ?? '';
String displayName = '${country['City']} - ${country['Airport']}';
// String city = country['City'] ?? '';
// String airport = country['Airport'] ?? '';
// String displayName = '${country['City']} - ${country['Airport']}';
final code = country['Code'] ?? '';
final city = country['City'] ?? '';
final airport = country['Airport'] ?? '';
final displayName = '$city ($code)\n$airport';
tempCountryMap[country['Code']] = displayName;
}
@ -561,9 +568,9 @@ class FlightScreenState extends State<FlightScreen> {
}
bool validateFields() {
errorMessages.clear(); // Reset errors
// errorMessages.clear(); // Reset errors
int rowCount = 1; // Default row count for One-way
rowCount = 1; // Default row count for One-way
if (selectedTripType == "Roundtrip") {
rowCount = 2; // Fixed for Roundtrip
} else if (selectedTripType == "Multitrip") {
@ -579,19 +586,131 @@ class FlightScreenState extends State<FlightScreen> {
if (selectedFrom[i] == null) {
errorMessages["from_place_$i"] = "Required";
}
// if (selectedTo[i] == null) {
// errorMessages["to_place_$i"] = "Required";
// }else if (
// selectedFrom[i] == selectedTo[i]) {
// errorMessages["to_place_$i"] = "Change Destination";
// }
if (selectedTo[i] == null) {
errorMessages["to_place_$i"] = "Required";
print("Error: to_place_$i -> Required (selectedTo[$i] is null)");
} else if (selectedFrom[i] == selectedTo[i]) {
if (selectedTripType == "Roundtrip") {
errorMessages["to_place_1"] = "Change Destination";
} else {
errorMessages["to_place_$i"] = "Change Destination";
}
print(
"Error: to_place_$i -> Change Destination (selectedFrom[$i] == selectedTo[$i])",
);
}
// if (textControllers["_to${i}Controller"]?.text.trim().isEmpty ?? true) {
// errorMessages["to_place_$i"] = "Required";
// }
// if (textControllers["_date${i}Controller"]?.text.trim().isEmpty ?? true) {
// errorMessages["date_$i"] = "Required";
// }
if (textControllers["_date${i}Controller"]?.text.trim().isEmpty ?? true) {
errorMessages["date_$i"] = "Required";
}
if (textControllers["_time${i}Controller"]?.text.trim().isEmpty ?? true) {
errorMessages["time_$i"] = "Required";
}
}
//selectedFrom For every trips should n't same
Set<String?> seenFrom = {};
Set<String?> seenTo = {};
for (int i = 1; i <= rowCount; i++) {
final fromValue = selectedFrom[i];
final toValue = selectedTo[i];
if (fromValue != null) {
if (seenFrom.contains(fromValue)) {
errorMessages["from_place_$i"] = "Duplicate Departure ";
print("Error: from_place_$i duplicates a previous departure");
} else {
seenFrom.add(fromValue);
}
}
if (toValue != null) {
if (seenTo.contains(toValue)) {
errorMessages["to_place_$i"] = "Duplicate Destination ";
print("Error: to_place_$i duplicates a previous departure");
} else {
seenTo.add(toValue);
}
}
}
// Step 3: Sequential Date Comparison
DateFormat format = DateFormat("dd-MM-yyyy"); // Assumes "12 Jun" format
DateTime now = DateTime.now();
List<DateTime> parsedDates = [];
for (int i = 1; i <= rowCount; i++) {
String? dateStr = textControllers["_date${i}Controller"]?.text.trim();
String? timeStr = textControllers["_time${i}Controller"]?.text.trim();
print("VAlidationDATE - $dateStr ");
if (dateStr != null && dateStr.isNotEmpty) {
try {
DateTime date = format.parseStrict(dateStr);
date = DateTime(
now.year,
date.month,
date.day,
); // Assume current year
parsedDates.add(date);
} catch (e) {
errorMessages["date_$i"] = "Invalid format";
}
}
}
for (int i = 1; i < parsedDates.length; i++) {
if (parsedDates[i].isAtSameMomentAs(parsedDates[i - 1])) {
//Here check time[i] and time[i-1] if same error 30 mins gap reeuired
print("Ckecking Same DAte");
// Same date - check time gap
// Combine parsedDates and times into DateTime objects
String prevDateStr =
textControllers["_date${i}Controller"]!.text.trim();
String prevTimeStr =
textControllers["_time${i}Controller"]!.text.trim();
String currDateStr =
textControllers["_date${i + 1}Controller"]!.text.trim();
String currTimeStr =
textControllers["_time${i + 1}Controller"]!.text.trim();
final dtFormat = DateFormat("dd-MM-yyyy HH:mm");
final prevDT = dtFormat.parse("$prevDateStr $prevTimeStr");
final currDT = dtFormat.parse("$currDateStr $currTimeStr");
int diffMins = currDT.difference(prevDT).inMinutes;
print("Time difference between row ${i} and ${i + 1}: $diffMins mins");
// If same day (diff >= 0 & < 1440 minutes), enforce 30min minimum gap
if (diffMins < 0) {
errorMessages["time_${i + 1}"] = "Must be after previous";
} else if (diffMins < 30) {
errorMessages["time_${i + 1}"] = "30 mins gap required";
} else {
errorMessages.remove("time_${i + 1}");
}
print("Ckecking Same DAte...1");
} else if (!parsedDates[i].isAfter(parsedDates[i - 1])) {
errorMessages["date_${i + 1}"] = "Must be after date_${i}";
print("Error: date_${i + 1} is not after date_${i}");
}
}
setState(() {}); // Update UI to show error messages
@ -686,7 +805,7 @@ class FlightScreenState extends State<FlightScreen> {
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(20.0),
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
@ -882,6 +1001,15 @@ class FlightScreenState extends State<FlightScreen> {
multiTripRowCount = 1;
}
errorMessages.clear();
for (int i = 1; i <= rowCount; i++) {
textControllers["_date${i}Controller"]?.clear();
textControllers["_time${i}Controller"]?.clear();
// Also clear the actual selected data (not just the text in controllers)
selectedFrom[i] = null; // Assuming null means no selection
selectedTo[i] = null;
}
});
print(
"Updating form data: Flight -> trip_type -> $selectedTripType",
@ -1172,6 +1300,7 @@ class FlightScreenState extends State<FlightScreen> {
initialDate: initialDate,
firstDate: firstDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
@ -1195,40 +1324,72 @@ class FlightScreenState extends State<FlightScreen> {
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
if (pickedTime != null) {
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(
now.year,
now.month,
now.day,
// Parse the selected date
final dateText = textControllers["_date${index}Controller"]?.text ?? "";
final selectedDate = DateFormat(
'dd-MM-yyyy',
).parse(dateText); // or 'yyyy-MM-dd' depending on your format
final selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
pickedTime.hour,
pickedTime.minute,
),
);
// _timeController.text = formattedTime;
textControllers["_time${index}Controller"]?.text = formattedTime;
onPicked();
// Only validate past time if date is today
final isToday =
selectedDate.year == now.year &&
selectedDate.month == now.month &&
selectedDate.day == now.day;
bool isPastTime = selectedDateTime.isBefore(now);
if (isToday && isPastTime) {
setState(() {
errorMessages["time_$index"] = "You can't select a past time.";
});
return;
}
// Valid time selection
setState(() {
_selectedCheckOutTime = pickedTime;
final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
textControllers["_time${index}Controller"]?.text = formattedTime;
// errorMessages["time_$index"] = ""; // clear previous error
errorMessages.remove("time_$index");
onPicked(); // Trigger callback
});
}
}
// late Map<String, String> countryMap; // Mapping country_code -> country_name
// late List<String> countryCodes; // List of country codes
// countryMap = {
// for (var item in countryList)
// if (item['country_code'] != null && item['country_name'] != null)
// item['country_code'] as String: item['country_name'] as String
// };
// // Extract only country codes for processing
// countryCodes = countryMap.keys.toList();
// if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
// setState(() {
// _selectedCheckOutTime = pickedTime;
// // Formatting time to HH:mm (24-hour format)
// final now = DateTime.now();
// final formattedTime = DateFormat('HH:mm').format(
// DateTime(
// now.year,
// now.month,
// now.day,
// pickedTime.hour,
// pickedTime.minute,
// ),
// );
//
// selectedCountry ??= null;
// // _timeController.text = formattedTime;
// textControllers["_time${index}Controller"]?.text = formattedTime;
//
// onPicked();
// });
// }
}
return [
Column(
@ -1254,6 +1415,8 @@ class FlightScreenState extends State<FlightScreen> {
isCountryLoading
? Center(child: CircularProgressIndicator())
: DropdownSearch<String>(
enabled:
!(selectedTripType == "Roundtrip" && index == 2),
selectedItem:
selectedFrom[index] != null
? countryMap[selectedFrom[index]]
@ -1273,19 +1436,73 @@ class FlightScreenState extends State<FlightScreen> {
style: TextStyle(fontSize: 12),
),
menuProps: MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Padding(
itemBuilder: (context, item, isSelected) {
final parts = item.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)"
final cityMatch = RegExp(
r'^(.*)\s+\(([^)]+)\)$',
).firstMatch(cityAndCode);
final city = cityMatch?.group(1) ?? '';
final code = cityMatch?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text(
item,
style: TextStyle(
fontSize: 13,
), // 👈 Set your desired text size here
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
city,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
Text(
code,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 1),
Text(
airport,
style: const TextStyle(
fontSize: 11,
color: Colors.grey,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
},
// itemBuilder:
// (context, item, isSelected) => Padding(
// padding: const EdgeInsets.symmetric(
// horizontal: 8.0,
// vertical: 6.0,
// ),
// child: Text(
// item,
// style: TextStyle(
// fontSize: 13,
// ), // 👈 Set your desired text size here
// ),
// ),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
@ -1294,16 +1511,41 @@ class FlightScreenState extends State<FlightScreen> {
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null) {
return Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
child: const Text(
"Select",
style: TextStyle(fontSize: 12),
),
);
}
final parts = selectedItem.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
return Align(
alignment: Alignment.centerLeft,
child: Text(
cityAndCode ?? "Select",
style: const TextStyle(fontSize: 12),
),
);
},
// dropdownBuilder:
// (context, selectedItem) => Align(
// alignment: Alignment.centerLeft,
// child: Text(
// selectedItem ?? "Select",
// style: TextStyle(fontSize: 12),
// ),
// ),
onChanged: (String? newValue) {
setState(() {
errorMessages.remove("from_place_$index");
// selectedFrom[index] = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
@ -1315,7 +1557,11 @@ class FlightScreenState extends State<FlightScreen> {
)
.key;
print(selectedFrom[index]);
if (selectedTripType == "Roundtrip") {
print('rounfTo${selectedFrom[index]}');
// textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!;
selectedTo[2] = selectedFrom[index]!;
}
});
},
),
@ -1340,7 +1586,10 @@ class FlightScreenState extends State<FlightScreen> {
),
if (errorMessages["from_place_$index"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
Text(
errorMessages["from_place_$index"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
@ -1366,6 +1615,8 @@ class FlightScreenState extends State<FlightScreen> {
isCountryLoading
? Center(child: CircularProgressIndicator())
: DropdownSearch<String>(
enabled:
!(selectedTripType == "Roundtrip" && index == 2),
selectedItem:
selectedTo[index] != null
? countryMap[selectedTo[index]]
@ -1385,19 +1636,72 @@ class FlightScreenState extends State<FlightScreen> {
style: TextStyle(fontSize: 12),
),
menuProps: MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Padding(
itemBuilder: (context, item, isSelected) {
final parts = item.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)"
final cityMatch = RegExp(
r'^(.*)\s+\(([^)]+)\)$',
).firstMatch(cityAndCode);
final city = cityMatch?.group(1) ?? '';
final code = cityMatch?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text(
item,
style: TextStyle(
fontSize: 13,
), // 👈 Set your desired text size here
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
city,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
Text(
code,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 1),
Text(
airport,
style: const TextStyle(
fontSize: 11,
color: Colors.grey,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
},
// itemBuilder:
// (context, item, isSelected) => Padding(
// padding: const EdgeInsets.symmetric(
// horizontal: 8.0,
// vertical: 6.0,
// ),
// child: Text(
// item,
// style: TextStyle(
// fontSize: 13,
// ), // 👈 Set your desired text size here
// ),
// ),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
@ -1406,16 +1710,41 @@ class FlightScreenState extends State<FlightScreen> {
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// dropdownBuilder:
// (context, selectedItem) => Align(
// alignment: Alignment.centerLeft,
// child: Text(
// selectedItem ?? "Select Country",
// style: TextStyle(fontSize: 12),
// ),
// ),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null) {
return Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
child: const Text(
"Select",
style: TextStyle(fontSize: 12),
),
);
}
final parts = selectedItem.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
return Align(
alignment: Alignment.centerLeft,
child: Text(
cityAndCode ?? "Select",
style: const TextStyle(fontSize: 12),
),
);
},
onChanged: (String? newValue) {
setState(() {
errorMessages.remove("to_place_$index");
selectedTo[index] =
countryMap.entries
.firstWhere(
@ -1424,6 +1753,12 @@ class FlightScreenState extends State<FlightScreen> {
.key;
print(selectedTo[index]);
if (selectedTripType == "Roundtrip") {
print('rounfTo${selectedTo[index]}');
// textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!;
selectedFrom[2] = selectedTo[index]!;
}
});
},
),
@ -1431,11 +1766,67 @@ class FlightScreenState extends State<FlightScreen> {
),
if (errorMessages["to_place_$index"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
Text(
errorMessages["to_place_$index"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Class *",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// CustomTextFieldItnerarySubWrapper(
// isFocused: focusStates["_class${index}Focused"] ?? false,
// isDesktop: isDesktop,
// // width: isDesktop
// // ? MediaQuery.of(context).size.width * 0.34
// // : MediaQuery.of(context).size.width * 0.66,
// child: SizedBox(
// height: 40,
// child:
// isFlightClassLoading
// ? const Center(child: CircularProgressIndicator())
// : DropdownButtonFormField<String>(
// focusNode: focusNodes["_class${index}FocusNode"],
// // focusNode: _tripTypeFocusNode, // Assign the correct focus node
// // controller: _hotelNameController,
// value: selectedClasses[index],
//
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedClasses[index] = newValue;
// });
//
// print(selectedClasses[index]);
// }
// : null,
// items: dropdownItems,
// ),
// ),
// ),
// ],
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1451,41 +1842,83 @@ class FlightScreenState extends State<FlightScreen> {
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_class${index}Focused"] ?? false,
isDesktop: isDesktop,
// width: isDesktop
// ? MediaQuery.of(context).size.width * 0.34
// : MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child:
isFlightClassLoading
? const Center(child: CircularProgressIndicator())
: DropdownButtonFormField<String>(
focusNode: focusNodes["_class${index}FocusNode"],
// focusNode: _tripTypeFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedClasses[index],
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
height: 35,
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedClasses[index],
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: 8,
),
child: Text(
item['dropdown_value'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
);
},
),
dropdownDecoratorProps: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
vertical: 5,
),
),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
return Text(
"Select Class",
style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 13,
),
);
}
return Text(
selectedItem['dropdown_value'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
);
},
onChanged:
purposeList.isNotEmpty
? (newValue) {
? (Map<String, dynamic>? newValue) {
setState(() {
selectedClasses[index] = newValue;
selectedClasses[index] = newValue?['dropdown_key'];
print("Selected Class: ${selectedClasses[index]}");
});
print(selectedClasses[index]);
}
: null,
items: dropdownItems,
),
),
),
if (errorMessages["class_$index"] != null)
Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
errorMessages["class_$index"]!,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.red),
),
),
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
@ -1534,7 +1967,10 @@ class FlightScreenState extends State<FlightScreen> {
),
if (errorMessages["date_$index"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
Text(
errorMessages["date_$index"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
@ -1559,6 +1995,7 @@ class FlightScreenState extends State<FlightScreen> {
height: 40,
child: GestureDetector(
onTap: () {
textControllers["_time${index}Controller"]?.text = "";
_clearError("time_$index");
// validateTimeDifference(index);
// _selectCheckOutTime(context);
@ -1647,6 +2084,61 @@ class FlightScreenState extends State<FlightScreen> {
);
}
return [
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Visa Required",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// CustomTextFieldItnerarySubWrapper(
// // isFocused: _tripTypeFocused,
// isFocused: focusStates["_visa1Focused"] ?? false,
// 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
// focusNode: focusNodes["_visa1FocusNode"],
// value: selectedvisa_available,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// visa_available.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedvisa_available = newValue;
// // selectedTripType = "Oneway";
// // Reset `multiTripRowCount` when switching away from Multitrip
// });
// print(
// "Updating form data: Flight -> trip_type -> $selectedvisa_available",
// );
//
// // _initializeRows();
// }
// : null,
//
// items: dropdownItems,
// ),
// ),
// ),
// ],
// ),
//********//
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1660,42 +2152,75 @@ class FlightScreenState extends State<FlightScreen> {
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
// isFocused: _tripTypeFocused,
// use same wrapper as class
isFocused: focusStates["_visa1Focused"] ?? false,
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
focusNode: focusNodes["_visa1FocusNode"],
value: selectedvisa_available,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
height: 35,
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
items: visa_available.cast<Map<String, dynamic>>(),
selectedItem: visa_available.firstWhere(
(item) => item['dropdown_key'] == selectedvisa_available,
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: 8,
),
child: Text(
item['dropdown_value'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
);
},
),
dropdownDecoratorProps: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
vertical: 5,
),
),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
return Text(
"Select Option",
style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 13,
),
);
}
return Text(
selectedItem['dropdown_value'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
);
},
onChanged:
visa_available.isNotEmpty
? (newValue) {
? (Map<String, dynamic>? newValue) {
setState(() {
selectedvisa_available = newValue;
// selectedTripType = "Oneway";
// Reset `multiTripRowCount` when switching away from Multitrip
selectedvisa_available = newValue?['dropdown_key'];
print("Selected Visa: $selectedvisa_available");
});
print(
"Updating form data: Flight -> trip_type -> $selectedvisa_available",
);
// _initializeRows();
}
: null,
items: dropdownItems,
),
),
),
@ -1793,14 +2318,14 @@ class FlightScreenState extends State<FlightScreen> {
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
CustomTextFieldItnerarySubWrapper(
// isFocused: _tripTypeFocused,
isFocused: focusStates["_visa1Focused"] ?? false,
isDesktop: isDesktop,
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
// width:
// isDesktop
// ? MediaQuery.of(context).size.width * 0.34
// : MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(

View File

@ -53,6 +53,7 @@ class _ForexScreenState extends State<ForexScreen> {
int fifteenPercent = 0;
int remainingAmount = 0;
bool isWidget = false;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
@ -248,6 +249,31 @@ class _ForexScreenState extends State<ForexScreen> {
}
}
// Additional validation starts
final start_Date = data["start_date"];
final end_Date = data["end_date"];
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)) {
errorMessages["end_date"] =
"End date cannot be earlier than start date";
;
}
} catch (e) {
errorMessages["end_date"] = "Invalid date format";
}
return errorMessages.isEmpty;
}
// Additional validation ends
return errorMessages.values.every((msg) => msg.trim().isEmpty);
// if (hasErrors) {
@ -290,7 +316,13 @@ class _ForexScreenState extends State<ForexScreen> {
Map<String, dynamic> data = forexData;
if (!isValidForexData(data) && errorMessages.isNotEmpty) {
final card = data["deposit_on_card"];
final cash = data["deposit_on_cash"];
print("card - $card");
print("cash - $cash");
if (!isValidForexData(data)) {
// && errorMessages.isNotEmpty) {
print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
@ -419,12 +451,16 @@ class _ForexScreenState extends State<ForexScreen> {
selectedCashPercent = int.tryParse(
widget.selectedItem!["cash_percentage"]?.toString() ?? "",
);
print("selectedCashPercentUPdae - $selectedCashPercent");
selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?;
isChecked =
widget.selectedItem!["have_card"] == "1"; // Convert string to bool
textControllers["_cardNumber"]?.text =
widget.selectedItem!["card_number"]?.toString() ?? "";
isWidget = true;
// if (textControllers["_cardNumber"] != null) {
// print("userCardNumber11 - $userCardNumber");
// textControllers["_cardNumber"]!.text = userCardNumber ?? '';
@ -548,6 +584,13 @@ class _ForexScreenState extends State<ForexScreen> {
selectedQuotedAmount =
((perdiemAmount + calclateVal).toString() ?? 0) as String?;
if (isWidget) {
print("IsWidget Update");
int? quotedAmount = int.tryParse(selectedQuotedAmount!);
fifteenPercent = (quotedAmount! * selectedCashPercent!) ~/ 100;
}
});
if (userEdited) {
@ -612,7 +655,8 @@ class _ForexScreenState extends State<ForexScreen> {
);
if (enteredAmount == null || calculateAmnt > qouteAmount!) {
errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount";
errorMessages["deposit_on_card"] =
"Sum of cash card cannot exceed $qouteAmount";
} else if (checkValidAmount < qouteAmount) {
errorMessages["deposit_on_card"] =
"Enter Valid Amount"; // Clear error if valid
@ -627,6 +671,7 @@ class _ForexScreenState extends State<ForexScreen> {
void _validateCashAmount(String value) {
// errorMessages["deposit_on_card"] = " ";
errorMessages.remove("deposit_on_cash");
print("_validateCashAmount - $value - $fifteenPercent");
int? enteredAmount = int.tryParse(value);
@ -652,6 +697,7 @@ class _ForexScreenState extends State<ForexScreen> {
errorMessages["deposit_on_card"] =
"Enter Valid Amount"; // Clear error if valid
}
print("CASHfifteenPercent - $fifteenPercent");
errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent";
} else if (checkValidAmount == quotedAmount) {
errorMessages.remove("deposit_on_cash");
@ -819,6 +865,7 @@ class _ForexScreenState extends State<ForexScreen> {
initialDate: initialDate,
firstDate: initialDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
// DateTime? pickedDate = await showDatePicker(
@ -870,6 +917,7 @@ class _ForexScreenState extends State<ForexScreen> {
initialDate: initialDate,
firstDate: initialDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
// DateTime? pickedDate = await showDatePicker(
@ -1048,8 +1096,9 @@ class _ForexScreenState extends State<ForexScreen> {
SizedBox(height: 5), // Space before error message
Text(
errorMessages["end_date"]!,
// "Select End Date",
style: TextStyle(color: Colors.red, fontSize: 12),
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
),
],
],
@ -1143,6 +1192,51 @@ class _ForexScreenState extends State<ForexScreen> {
),
),
SizedBox(height: 5),
// CustomTextFieldForexWrapper(
// isFocused: focusStates["_countries"] ?? false,
// isDesktop: isDesktop,
// child: SizedBox(
// height: 40,
// child: DropdownSearch<String>(
// selectedItem: countryMap[selectedCountry],
// popupProps: PopupProps.menu(
// showSearchBox: true, // Enables search functionality
// searchFieldProps: TextFieldProps(
// decoration: InputDecoration(
// hintText: "Search ...",
// 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 ",
// 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;
// _onCountryChanged(selectedCountry);
// });
// },
// ),
// ),
// ),
CustomTextFieldForexWrapper(
isFocused: focusStates["_countries"] ?? false,
isDesktop: isDesktop,
@ -1151,10 +1245,25 @@ class _ForexScreenState extends State<ForexScreen> {
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
showSearchBox: true,
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 250),
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Country...",
hintText: "Search ...",
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
@ -1168,11 +1277,13 @@ class _ForexScreenState extends State<ForexScreen> {
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
style: TextStyle(fontSize: 12),
selectedItem ?? "Select ",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
),
onChanged: (String? newValue) {
@ -1203,7 +1314,8 @@ class _ForexScreenState extends State<ForexScreen> {
// height: 8,
// ),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.048)
Spacer()
// SizedBox(width: MediaQuery.of(context).size.width * 0.048)
else
SizedBox(height: 8),
Column(
@ -1620,6 +1732,12 @@ class _ForexScreenState extends State<ForexScreen> {
// keyboardType: TextInputType.number,
onChanged: (value) {
// errorMessages["deposit_on_cash"] = "";
errorMessages.remove("deposit_on_cash");
errorMessages.remove("deposit_on_card");
// errorMessages["deposit_on_card"] = "";
setState(() {
userEdited = true;
});
_validateCashAmount(
value,
); // Call validation when text changes
@ -1673,6 +1791,9 @@ class _ForexScreenState extends State<ForexScreen> {
FilteringTextInputFormatter.digitsOnly, // Only allow digits
],
onChanged: (value) {
errorMessages.remove("deposit_on_cash");
errorMessages.remove("deposit_on_card");
userEdited = true;
_validateCardAmount(
value,
); // Call validation when text changes

View File

@ -1,3 +1,4 @@
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
@ -14,13 +15,14 @@ class InsuranceScreen extends StatefulWidget {
final Map<String, dynamic>? selectedItem;
final String? loginUser;
InsuranceScreen(
{required this.onClose,
InsuranceScreen({
required this.onClose,
required this.apiData,
required this.onSaveInsurance,
required this.selectedItem,
required this.loginUser,
required this.flightData});
required this.flightData,
});
@override
_InsuranceScreenState createState() => _InsuranceScreenState();
@ -36,19 +38,23 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _dateFocusNode = 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 _startDateController = TextEditingController();
late TextEditingController _endDateController = TextEditingController();
late TextEditingController _insuranceCommentsController =
TextEditingController();
late TextEditingController _nomineeController = TextEditingController();
bool _isHotelNameFocused = false;
bool _dateFocus = false;
bool _startDateFocus = false;
bool _endDateFocus = false;
bool _commentsFocus = false;
bool _nomineeFocus = false;
String? selectedTripType;
String? selectedInsuranceType;
@ -58,9 +64,10 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
Map<String, dynamic> get InsuranceData {
Map<String, dynamic> data = {
"type_of_insurance": selectedInsuranceType,
"start_date": _startdateController.text,
"start_date": _startDateController.text,
"end_date": _endDateController.text,
"comments": _insuranceCommentsController.text,
"nominee_name": _nomineeController.text,
"created_by": widget.loginUser,
"updated_by": widget.loginUser,
};
@ -87,9 +94,14 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
});
_dateFocusNode.addListener(() {
_endDateFocusNode.addListener(() {
setState(() {
_dateFocus = _fromFocusNode.hasFocus;
_endDateFocus = _endDateFocusNode.hasFocus;
});
});
_startDateFocusNode.addListener(() {
setState(() {
_startDateFocus = _startDateFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
@ -98,12 +110,24 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
});
});
_insuranceCommentsController =
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
_startdateController =
TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
_endDateController =
TextEditingController(text: widget.selectedItem?['end_date'] ?? "");
_nomineeFocusNode.addListener(() {
setState(() {
_nomineeFocus = _nomineeFocusNode.hasFocus;
});
});
_insuranceCommentsController = TextEditingController(
text: widget.selectedItem?["comments"] ?? "",
);
_startDateController = TextEditingController(
text: widget.selectedItem?["start_date"] ?? "",
);
_endDateController = TextEditingController(
text: widget.selectedItem?['end_date'] ?? "",
);
_nomineeController = TextEditingController(
text: widget.selectedItem?['nominee_name'] ?? "",
);
// Set the selected value if available
if (widget.selectedItem != null &&
@ -123,38 +147,29 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// 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 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);
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");
print("parsedDate");
print(parsedDate);
print("parsedEndDate");
print(parsedEndDate);
}
});
}
Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) {
final allTrips = flightData
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,
};
return {'firstTripDate': null, 'lastTripDate': null};
}
allTrips.sort((a, b) {
@ -187,7 +202,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
List<String> requiredFields = [
"type_of_insurance",
"start_date",
"end_date"
"end_date",
];
// Check validation for each field
@ -247,16 +262,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
void dispose() {
_tripTypeFocusNode.dispose();
_tripTypeController.dispose();
_startdateController.dispose();
_startDateController.dispose();
_endDateController.dispose();
_insuranceCommentsController.dispose();
_nomineeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
// color: Color(0xFFF4F4FB),
@ -267,17 +286,18 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(28.0),
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
),
],
),
),
),
);
});
},
);
}
List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -290,7 +310,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop)
_buildSecondRow(isDesktop),
];
return [
@ -305,51 +325,52 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Insurance Type",
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> _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>(
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)),
child: Text(
"No options available",
style: TextStyle(color: Colors.grey),
),
),
);
}
@ -362,7 +383,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
@ -370,13 +392,15 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedInsuranceType,
style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 10), // Proper padding
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
),
onChanged: purposeList.isNotEmpty
onChanged:
purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedInsuranceType = newValue;
@ -396,24 +420,118 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
];
}
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>(
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)),
child: Text(
"No options available",
style: TextStyle(color: Colors.grey),
),
),
);
}
@ -454,6 +572,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
initialDate: initialDate,
firstDate: initialDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
// DateTime? pickedDate = await showDatePicker(
@ -469,8 +588,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_startdateController.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
_startDateController.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
});
}
}
@ -479,12 +599,12 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
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;
}
// 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;
@ -493,19 +613,52 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// ? _selectedCheckOutDate!
// : firstDate;
DateTime firstDate = checkInDate;
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,
// 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 &&
@ -529,93 +682,223 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Insurance Type",
"Insurance Type *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: const Color(0xFF575A74),
),
SizedBox(height: 5),
),
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,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedInsuranceType,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 10), // Proper padding
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedInsuranceType,
orElse: () => {},
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedInsuranceType = newValue;
if (selectedInsuranceType!.isNotEmpty) {
errorMessages.remove("type_of_insurance");
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: const DropDownDecoratorProps(
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}");
});
print(selectedInsuranceType);
}
: null,
),
),
),
items: dropdownItems,
],
),
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,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Start Date",
"Start Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isFocused: _startDateFocus,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
await _selectCheckOutDate(context);
if (_startdateController.text.isNotEmpty) {
if (_startDateController.text.isNotEmpty) {
setState(() {
errorMessages
.remove("start_date"); // Removes the key completely
errorMessages.remove(
"start_date",
); // Removes the key completely
});
}
},
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _startdateController,
focusNode: _startDateFocusNode,
controller: _startDateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
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),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
@ -624,34 +907,27 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
),
if (errorMessages["start_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"End Date",
"End Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isFocused: _endDateFocus,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
@ -659,7 +935,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
await _selectEndCheckOutDate(context);
if (_endDateController.text.isNotEmpty) {
DateTime? startDate = _parseDate(_startdateController.text);
DateTime? startDate = _parseDate(_startDateController.text);
DateTime? endDate = _parseDate(_endDateController.text);
if (startDate != null &&
@ -678,17 +954,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
},
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
focusNode: _endDateFocusNode,
controller: _endDateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
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),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
@ -698,19 +977,15 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
if (errorMessages["end_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
// "Required",
errorMessages["end_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
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,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
];
}
@ -724,14 +999,16 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
width:
isDesktop
? MediaQuery.of(context).size.width * 0.31
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
@ -752,9 +1029,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
],
),
if (isDesktop) Spacer(),
SizedBox(
height: 5,
),
SizedBox(height: 5),
Column(
children: [
Row(
@ -775,9 +1050,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
@ -786,7 +1059,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
@ -794,9 +1066,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(

View File

@ -1,3 +1,4 @@
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
@ -14,13 +15,14 @@ class MiscellaneousScreen extends StatefulWidget {
final int? selectedIndex;
final String? loginUser;
MiscellaneousScreen(
{required this.onClose,
MiscellaneousScreen({
required this.onClose,
required this.apiData,
required this.onSaveMiscellaneous,
this.selectedItem,
this.selectedIndex,
required this.loginUser});
required this.loginUser,
});
@override
_MiscellaneousScreenState createState() => _MiscellaneousScreenState();
@ -87,8 +89,9 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
});
});
_commentsController =
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
_commentsController = TextEditingController(
text: widget.selectedItem?["comments"] ?? "",
);
// Set the selected value if available
if (widget.selectedItem != null &&
@ -114,7 +117,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
// Check validation for each field
for (String field in requiredFields) {
if (data[field] == null || data[field].toString().trim().isEmpty) {
errorMessages[field] = "This field is required";
errorMessages[field] = "Required";
}
}
@ -143,9 +146,11 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
child: Form(
@ -155,17 +160,18 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(28.0),
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
),
],
),
),
),
);
});
},
);
}
List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -191,16 +197,17 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Special Type",
"Special Type *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
isDesktop
? Row(children: _buildTripType(isDesktop))
: Column(children: _buildTripType(isDesktop))
: Column(children: _buildTripType(isDesktop)),
],
),
];
@ -211,19 +218,24 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
widget.apiData?['miscellaneous_special_request'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item) => DropdownMenuItem<String>(
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)),
child: Text(
"No options available",
style: TextStyle(color: Colors.grey),
),
),
);
}
@ -233,7 +245,40 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options";
return [
CustomTextFieldWrapper(
// 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: selectedSpecialType,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedSpecialType = newValue;
// });
//
// print(selectedSpecialType);
// }
// : null,
// items: dropdownItems,
// ),
// ),
// ),
CustomTextFieldItnerarySubWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
width: isDesktop
@ -241,34 +286,79 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedSpecialType,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 10), // Proper padding
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
// focusNode: _taxiReqFocusNode,
items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedSpecialType,
orElse: () => {},
),
onChanged: purposeList.isNotEmpty
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: const DropDownDecoratorProps(
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
? (newValue) {
setState(() {
selectedSpecialType = newValue;
selectedSpecialType = newValue as String?;
});
print(selectedSpecialType);
print(
"Updating form data: -> ${newValue ?? ""}",
);
}
: null,
items: dropdownItems,
),
),
),
if (errorMessages["special_request"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Select Type",
style: TextStyle(color: Colors.red, fontSize: 12),
),
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
];
}
@ -279,17 +369,19 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
"Comments *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
@ -310,17 +402,12 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
),
if (errorMessages["comments"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop) Spacer(),
SizedBox(
height: 5,
),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
@ -338,9 +425,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
@ -349,7 +434,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
@ -357,9 +441,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(

View File

@ -1,3 +1,4 @@
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
@ -219,7 +220,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(28.0),
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
@ -362,34 +363,109 @@ class _TaxiScreenState extends State<TaxiScreen> {
),
),
SizedBox(height: 5),
// CustomTextFieldItnerarySubWrapper(
// isFocused: _toFocus,
// isDesktop: isDesktop,
// child: SizedBox(
// height: 40,
// child: DropdownButtonFormField<String>(
// focusNode: _toFocusNode, // Assign the correct focus node
// value: selectedCarType,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedCarType = newValue;
// });
// print(
// "Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
// );
// }
// : null,
//
// items: dropdownItems,
// ),
// ),
// ),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _toFocusNode, // Assign the correct focus node
value: selectedCarType,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
// focusNode: _taxiReqFocusNode,
items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedCarType,
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: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
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
? (newValue) {
setState(() {
selectedCarType = newValue;
selectedCarType = newValue as String?;
});
print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
"Updating form data: -> ${newValue ?? ""}",
);
}
: null,
items: dropdownItems,
),
),
),
@ -429,7 +505,42 @@ class _TaxiScreenState extends State<TaxiScreen> {
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldWrapper(
// CustomTextFieldWrapper(
// isFocused: _taxiReqFocused,
// 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: _taxiReqFocusNode, // Assign the correct focus node
// value: selectedReqTaxi,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedReqTaxi = newValue;
// });
// print(
// "Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
// );
// }
// : null,
//
// items: dropdownItems,
// ),
// ),
// ),
CustomTextFieldItnerarySubWrapper(
isFocused: _taxiReqFocused,
isDesktop: isDesktop,
width:
@ -438,29 +549,73 @@ class _TaxiScreenState extends State<TaxiScreen> {
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _taxiReqFocusNode, // Assign the correct focus node
value: selectedReqTaxi,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
// focusNode: _taxiReqFocusNode,
items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedReqTaxi,
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: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
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
? (newValue) {
setState(() {
selectedReqTaxi = newValue;
selectedReqTaxi = newValue as String?;
});
print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
"Updating form data: -> ${newValue ?? ""}",
);
}
: null,
items: dropdownItems,
),
),
),
@ -484,6 +639,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
: today,
firstDate: today,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
@ -501,6 +657,36 @@ class _TaxiScreenState extends State<TaxiScreen> {
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
// Parse the selected date
final dateText = _dateController.text ?? "";
final selectedDate = DateFormat(
'dd-MM-yyyy',
).parse(dateText); // or 'yyyy-MM-dd' depending on your format
final now = DateTime.now();
final selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
pickedTime.hour,
pickedTime.minute,
);
final isToday =
selectedDate.year == now.year &&
selectedDate.month == now.month &&
selectedDate.day == now.day;
bool isPastTime = selectedDateTime.isBefore(now);
if (isToday && isPastTime) {
setState(() {
errorMessages["time"] = "You can't select a past time.";
_selectedCheckOutTime = null; // Reset time variable
_timeController.text = ""; // Clear text field
});
return;
} else {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
@ -514,17 +700,44 @@ class _TaxiScreenState extends State<TaxiScreen> {
pickedTime.minute,
),
);
errorMessages["time"] = "";
_timeController.text = formattedTime;
});
}
}
}
// Future<void> _selectCheckOutTime(BuildContext context) async {
// TimeOfDay? pickedTime = await showTimePicker(
// context: context,
// initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
// );
//
// if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
// setState(() {
// _selectedCheckOutTime = pickedTime;
// // Formatting time to HH:mm (24-hour format)
// final now = DateTime.now();
// final formattedTime = DateFormat('HH:mm').format(
// DateTime(
// now.year,
// now.month,
// now.day,
// pickedTime.hour,
// pickedTime.minute,
// ),
// );
// _timeController.text = formattedTime;
// });
// }
// }
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"City",
"City *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
@ -562,7 +775,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Pickup Location",
"Pickup Location *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
@ -600,7 +813,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date",
"Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
@ -648,7 +861,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time",
"Time *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
@ -662,7 +875,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
onTap: () {
_timeController.text = "";
_selectCheckOutTime(context);
},
child: AbsorbPointer(
child: TextField(
focusNode: _timeFocusNode,
@ -687,7 +903,11 @@ class _TaxiScreenState extends State<TaxiScreen> {
),
if (errorMessages["time"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
// Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
Text(
errorMessages["time"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),

View File

@ -243,6 +243,12 @@ class _TrainScreenState extends State<TrainScreen> {
}
}
if (selectedFrom == selectedTo) {
errorMessages["to_station"] = "Change Destination";
}
print("Error: to_place -> Change Destination (selectedFrom == selectedTo)");
return errorMessages.isEmpty; // Valid if there are no errors
}
@ -324,7 +330,7 @@ class _TrainScreenState extends State<TrainScreen> {
// height: 6,
// ),
Padding(
padding: const EdgeInsets.all(28.0),
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
@ -369,7 +375,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Train Number",
"Train Number *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
@ -464,6 +470,7 @@ class _TrainScreenState extends State<TrainScreen> {
: today,
firstDate: today,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
@ -480,23 +487,66 @@ class _TrainScreenState extends State<TrainScreen> {
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
if (pickedTime != null) {
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(
now.year,
now.month,
now.day,
// Parse the selected date
final dateText = _dateController.text ?? "";
final selectedDate = DateFormat(
'dd-MM-yyyy',
).parse(dateText); // or 'yyyy-MM-dd' depending on your format
final selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
pickedTime.hour,
pickedTime.minute,
),
);
// Only validate past time if date is today
final isToday =
selectedDate.year == now.year &&
selectedDate.month == now.month &&
selectedDate.day == now.day;
bool isPastTime = selectedDateTime.isBefore(now);
if (isToday && isPastTime) {
setState(() {
errorMessages["time"] = "You can't select a past time.";
});
return;
}
// Valid time selection
setState(() {
_selectedCheckOutTime = pickedTime;
final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
_timeController.text = formattedTime;
// errorMessages["time_$index"] = ""; // clear previous error
errorMessages.remove("time");
});
}
// if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
// setState(() {
// _selectedCheckOutTime = pickedTime;
// // Formatting time to HH:mm (24-hour format)
// final now = DateTime.now();
// final formattedTime = DateFormat('HH:mm').format(
// DateTime(
// now.year,
// now.month,
// now.day,
// pickedTime.hour,
// pickedTime.minute,
// ),
// );
// _timeController.text = formattedTime;
// });
// }
}
//----------------------------------------------
@ -542,34 +592,104 @@ class _TrainScreenState extends State<TrainScreen> {
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
// CustomTextFieldItnerarySubWrapper(
// isFocused: _isHotelNameFocused,
// isDesktop: isDesktop,
// // width: isDesktop
// // ? MediaQuery.of(context).size.width * 0.34
// // : MediaQuery.of(context).size.width * 0.66,
// child: SizedBox(
// height: 40,
// child: DropdownButtonFormField<String>(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// // controller: _hotelNameController,
// value: selectedClass,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedClass = newValue;
// });
// }
// : null,
// items: dropdownItems,
// ),
// ),
// ),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
// width: isDesktop
// ? MediaQuery.of(context).size.width * 0.34
// : MediaQuery.of(context).size.width * 0.66,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedClass,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
width: double.infinity,
child: DropdownSearch<Map<String, dynamic>>(
items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedClass,
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: const DropDownDecoratorProps(
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
? (newValue) {
? (Map<String, dynamic>? newValue) {
setState(() {
selectedClass = newValue;
selectedClass= newValue?['dropdown_key'];
print("Selected Purpose: ${selectedClass}");
});
}
: null,
items: dropdownItems,
),
),
),
@ -584,7 +704,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"From",
"From *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
@ -592,139 +712,125 @@ class _TrainScreenState extends State<TrainScreen> {
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child:
isCountryLoading
? Center(child: CircularProgressIndicator())
: DropdownSearch<String>(
// selectedItem: selectedFrom != null
// ? countryMap[selectedFrom]
// : null,
selectedItem:
selectedFrom != null
? countryMap[selectedFrom] // get the display value from code
: null,
popupProps: PopupProps.menu(
menuProps: MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 230),
showSearchBox: true,
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
border: InputBorder.none,
),
),
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
style: TextStyle(fontSize: 12),
),
),
// onChanged: (String? newValue) {
// setState(() {
// // selectedFrom[index] = countryMap.entries
// // .firstWhere((entry) => entry.value == newValue)
// // .key;
//
// selectedFrom = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
//
// print(selectedFrom);
// });
// },
onChanged: (String? newValue) {
setState(() {
selectedFrom =
countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key;
});
},
),
),
// CustomTextFieldItnerarySubWrapper(
// isFocused: _fromFocus,
// isDesktop: isDesktop,
// child: SizedBox(
// height: 40,
// child: TextField(
// focusNode: _fromFocusNode,
// controller: _fromController,
// style: const TextStyle(fontSize: 12),
// decoration: const InputDecoration(
// labelText: "From",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// child:
// isCountryLoading
// ? Center(child: CircularProgressIndicator())
// : DropdownSearch<String>(
// // selectedItem: selectedFrom != null
// // ? countryMap[selectedFrom]
// // : null,
// selectedItem:
// selectedFrom != null
// ? countryMap[selectedFrom] // get the display value from code
// : null,
// popupProps: PopupProps.menu(
// menuProps: MenuProps(backgroundColor: Colors.white),
// constraints: BoxConstraints(maxHeight: 230),
// showSearchBox: true,
// searchFieldProps: TextFieldProps(
// decoration: InputDecoration(
// hintText: "Search ...",
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ),
// ),
// ),
// ),
//
// items: countryMap.values.toList(),
//
// dropdownDecoratorProps: DropDownDecoratorProps(
// dropdownSearchDecoration: InputDecoration(
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 5,
// ),
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
// dropdownBuilder:
// (context, selectedItem) => Align(
// alignment: Alignment.centerLeft,
// child: Text(
// selectedItem ?? "Select",
// style: TextStyle(fontSize: 12),
// ),
// ),
//
// // onChanged: (String? newValue) {
// // setState(() {
// // // selectedFrom[index] = countryMap.entries
// // // .firstWhere((entry) => entry.value == newValue)
// // // .key;
// //
// // selectedFrom = countryMap.entries
// // .firstWhere((entry) => entry.value == newValue)
// // .key;
// //
// // print(selectedFrom);
// // });
// // },
// onChanged: (String? newValue) {
// setState(() {
// selectedFrom =
// countryMap.entries
// .firstWhere(
// (entry) => entry.value == newValue,
// )
// .key;
// });
// },
// ),
// ),
// // child: SizedBox(
// // height: 40,
// // child: TextField(
// // focusNode: _fromFocusNode,
// // controller: _fromController,
// // style: const TextStyle(fontSize: 12),
// // decoration: const InputDecoration(
// // labelText: "From",
// // labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// // floatingLabelBehavior: FloatingLabelBehavior.never,
// // border: InputBorder.none,
// // contentPadding: EdgeInsets.symmetric(vertical: 16),
// // ),
// // ),
// // ),
// ),
),
if (errorMessages["from_station"] != 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(
"To",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isFocused: _fromFocus ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child:
isCountryLoading
? Center(child: CircularProgressIndicator())
: DropdownSearch<String>(
selectedItem:
selectedTo != null
? countryMap[selectedTo] // get the display value from code
: null,
child: DropdownSearch<String>(
selectedItem: countryMap[selectedFrom],
popupProps: PopupProps.menu(
menuProps: MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 230),
showSearchBox: true,
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 250),
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
@ -739,8 +845,146 @@ class _TrainScreenState extends State<TrainScreen> {
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
style: TextStyle(fontSize: 12),
selectedItem ?? "Select ",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
),
onChanged: (String? newValue) {
setState(() {
selectedFrom =
countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key;
});
},
),
),
),
if (errorMessages["from_station"] != 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(
"To *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
// CustomTextFieldItnerarySubWrapper(
// isFocused: _toFocus,
// isDesktop: isDesktop,
// child: SizedBox(
// height: 40,
// child:
// isCountryLoading
// ? Center(child: CircularProgressIndicator())
// : DropdownSearch<String>(
// selectedItem:
// selectedTo != null
// ? countryMap[selectedTo] // get the display value from code
// : null,
// popupProps: PopupProps.menu(
// menuProps: MenuProps(backgroundColor: Colors.white),
// constraints: BoxConstraints(maxHeight: 230),
// showSearchBox: true,
// searchFieldProps: TextFieldProps(
// decoration: InputDecoration(
// hintText: "Search ...",
// 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(
// alignment: Alignment.centerLeft,
// child: Text(
// selectedItem ?? "Select",
// style: TextStyle(fontSize: 12),
// ),
// ),
// onChanged: (String? newValue) {
// setState(() {
// selectedTo =
// countryMap.entries
// .firstWhere(
// (entry) => entry.value == newValue,
// )
// .key;
// });
// },
// ),
// ),
// ),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownSearch<String>(
selectedItem: countryMap[selectedTo],
popupProps: PopupProps.menu(
showSearchBox: true,
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 250),
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
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(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
),
onChanged: (String? newValue) {
@ -758,7 +1002,10 @@ class _TrainScreenState extends State<TrainScreen> {
),
if (errorMessages["to_station"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
Text(
errorMessages["to_station"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
@ -767,7 +1014,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date",
"Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
@ -816,7 +1063,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time",
"Time *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
@ -831,7 +1078,10 @@ class _TrainScreenState extends State<TrainScreen> {
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
onTap: () {
_timeController.text = "";
_selectCheckOutTime(context);
},
child: AbsorbPointer(
child: TextField(
focusNode: _timeFocusNode,
@ -856,7 +1106,10 @@ class _TrainScreenState extends State<TrainScreen> {
),
if (errorMessages["time"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
Text(
errorMessages["time"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),

View File

@ -18,14 +18,15 @@ class VisaScreen extends StatefulWidget {
final Map<String, dynamic>? selectedItem;
final String? loginUser;
VisaScreen(
{required this.onClose,
VisaScreen({
required this.onClose,
required this.onSaveVisa,
this.apiData,
required this.selectedItem,
required this.apiCountryData,
required this.loginUser,
required this.flightData});
required this.flightData,
});
@override
_VisaScreenState createState() => _VisaScreenState();
@ -99,14 +100,18 @@ class _VisaScreenState extends State<VisaScreen> {
_addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
_addFocusListener(
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_hotelNameFocusNode,
(focus) => _isHotelNameFocused = focus,
);
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
_visaCommentsController =
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
_dateController =
TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
_visaCommentsController = TextEditingController(
text: widget.selectedItem?["comments"] ?? "",
);
_dateController = TextEditingController(
text: widget.selectedItem?["start_date"] ?? "",
);
// Set the selected value if available
if (widget.selectedItem != null &&
@ -130,8 +135,9 @@ class _VisaScreenState extends State<VisaScreen> {
// Only set controller after value is updated
// final parsedDate =
// DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat("dd-MM-yyyy")
.parse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat(
"dd-MM-yyyy",
).parse(flightFirstTripDateNotifier.value ?? '');
if (parsedDate != null) {
_dateController.text = DateFormat('dd-MM-yyyy').format(parsedDate);
}
@ -181,17 +187,16 @@ class _VisaScreenState extends State<VisaScreen> {
}
Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) {
final allTrips = flightData
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,
};
return {'firstTripDate': null, 'lastTripDate': null};
}
allTrips.sort((a, b) {
@ -229,7 +234,7 @@ class _VisaScreenState extends State<VisaScreen> {
List<String> requiredFields = [
"type_of_visa",
"country_code",
"start_date"
"start_date",
];
// Check validation for each field
@ -260,9 +265,11 @@ class _VisaScreenState extends State<VisaScreen> {
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
child: Form(
@ -272,17 +279,18 @@ class _VisaScreenState extends State<VisaScreen> {
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(28.0),
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
),
],
),
),
),
);
});
},
);
}
List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -308,14 +316,7 @@ class _VisaScreenState extends State<VisaScreen> {
}
List<Widget> _buildFirstRow(isDesktop) {
return [
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
return [if (isDesktop) Spacer() else SizedBox(height: 8)];
}
List<Widget> _buildTripType(bool isDesktop) {
@ -354,7 +355,7 @@ class _VisaScreenState extends State<VisaScreen> {
// Map country codes to country names
countryMap = {
for (var item in countryList)
item['country_code'] as String: item['country_name'] as String
item['country_code'] as String: item['country_name'] as String,
};
// Extract only country codes for processing
@ -393,6 +394,7 @@ class _VisaScreenState extends State<VisaScreen> {
initialDate: initialDate,
firstDate: initialDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
// DateTime? pickedDate = await showDatePicker(
@ -417,19 +419,24 @@ class _VisaScreenState extends State<VisaScreen> {
List<dynamic> purposeList = widget.apiData?['visa_type_of_visa'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item) => DropdownMenuItem<String>(
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)),
child: Text(
"No options available",
style: TextStyle(color: Colors.grey),
),
),
);
}
@ -442,114 +449,115 @@ class _VisaScreenState extends State<VisaScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Type of Visa",
"Country *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _tripTypeFocused,
// CustomTextFieldWrapper(
// isFocused: _isHotelNameFocused,
// isDesktop: isDesktop,
// // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
// width:
// isDesktop
// ? MediaQuery.of(context).size.width * 0.34
// : MediaQuery.of(context).size.width * 0.66,
// child: SizedBox(
// height: 40,
// child: DropdownSearch<String>(
// selectedItem: countryMap[selectedCountry],
// popupProps: PopupProps.menu(
// showSearchBox: true, // Enables search functionality
// searchFieldProps: TextFieldProps(
// decoration: InputDecoration(
// hintText: "Search ...",
// 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 ",
// 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;
//
// if (selectedCountry!.isNotEmpty) {
// errorMessages.remove("country_code");
// }
// });
// },
// ),
// ),
// ),
CustomTextFieldItnerarySubWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
}
: null,
items: dropdownItems,
),
),
),
if (errorMessages["type_of_visa"] != 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(
"Country",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
child: SizedBox(
height: 40,
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
showSearchBox: true,
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Country...",
hintText: "Search ...",
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
itemBuilder: (context, item, isSelected) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: Text(
item,
style: TextStyle(fontSize: 12,color: Colors.black,),
),
);
},
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 1,
),
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder: (context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
selectedItem ?? "Select",
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;
if (selectedCountry!.isNotEmpty) {
errorMessages.remove("country_code");
}
});
},
),
@ -557,28 +565,154 @@ class _VisaScreenState extends State<VisaScreen> {
),
if (errorMessages["country_code"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Start Date",
"Type of Visa",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
// CustomTextFieldWrapper(
// isFocused: _tripTypeFocused,
// isDesktop: isDesktop,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
// // 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: selectedPurpose,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedPurpose = newValue;
// });
// print(
// "Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
// );
// }
// : null,
//
// items: dropdownItems,
// ),
// ),
// ),
CustomTextFieldWrapper(
isFocused: _tripTypeFocused,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : 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'] == selectedPurpose,
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: const DropDownDecoratorProps(
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,
),
);
},
// focusNode: _tripTypeFocusNode, // Assign the correct focus node
// value: selectedPurpose,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding:
// EdgeInsets.symmetric(horizontal: 10), // Proper padding
// ),
onChanged:
purposeList.isNotEmpty
? (Map<String, dynamic>? newValue) {
setState(() {
selectedPurpose= newValue?['dropdown_key'];
print("Selected Purpose: ${selectedPurpose}");
});
}
: null,
),
),
),
if (errorMessages["type_of_visa"] != 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(
"Start Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
@ -607,8 +741,11 @@ class _VisaScreenState extends State<VisaScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
@ -617,10 +754,7 @@ class _VisaScreenState extends State<VisaScreen> {
),
if (errorMessages["start_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
@ -637,13 +771,15 @@ class _VisaScreenState extends State<VisaScreen> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
@ -665,9 +801,7 @@ class _VisaScreenState extends State<VisaScreen> {
],
),
if (isDesktop) Spacer(),
SizedBox(
height: 5,
),
SizedBox(height: 5),
Column(
children: [
Row(
@ -688,9 +822,7 @@ class _VisaScreenState extends State<VisaScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
@ -699,7 +831,6 @@ class _VisaScreenState extends State<VisaScreen> {
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
@ -707,9 +838,7 @@ class _VisaScreenState extends State<VisaScreen> {
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(

View File

@ -398,14 +398,35 @@ class _FlightListWidgetState extends State<FlightListWidget> {
),
Expanded(
flex: 4,
child: Text(
"$fromPlaceCountry (From) - (To) $toPlaceCountry",
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"$fromPlaceCountry",
// "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w700,
),
),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Image.asset(
'assets/images/IconsImg/alternate.png',
width: 20,
height: 20,
),
),
Text(
"$toPlaceCountry",
// "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w700,
),
),
],
),
),
Expanded(
flex: 2,

View File

@ -334,7 +334,7 @@ class VisaListWidget extends StatelessWidget {
Expanded(
flex: 2,
child: Text(
" Visa Type",
" Country",
style: GoogleFonts.poppins(
fontSize: 11,
),
@ -342,7 +342,7 @@ class VisaListWidget extends StatelessWidget {
Expanded(
flex: 2,
child: Text(
" Country",
" Visa Type",
style: GoogleFonts.poppins(
fontSize: 11,
),
@ -372,8 +372,8 @@ class VisaListWidget extends StatelessWidget {
Expanded(
flex: 2,
child: Text(
getRequestForVisa(
item["type_of_visa"]!.toString()),
getRequestForCountry(
item["country_code"]!.toString()),
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -384,8 +384,8 @@ class VisaListWidget extends StatelessWidget {
Expanded(
flex: 2,
child: Text(
getRequestForCountry(
item["country_code"]!.toString()),
getRequestForVisa(
item["type_of_visa"]!.toString()),
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,

View File

@ -903,10 +903,15 @@ class CreateNewPlansState extends State<CreateNewPlan> {
setState(() {
apiCostData = plansJson;
print("apiCostData...1");
costCenterMap = {
for (var item in apiCostData!)
// item['department_id'].toString(): item['name'].toString(),
item['cost_center_id'].toString(): item['name'].toString(),
};
print("apiCostData...122");
costCenterIds = costCenterMap.keys.toList();
// Optionally auto-select the first item if not already selected
@ -1030,8 +1035,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// Handle Submit
bool validateForm() {
print("validateForm");
validationErrors.clear(); // Clear previous errors
print("validateForm.....1");
// Ensure either "user_id" or "traveller_id" is provided
if ((planUsrId == null || planUsrId!.isEmpty) &&
(planTravlrId == null || planTravlrId!.isEmpty)) {
@ -1040,7 +1046,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
validationErrors["traveller_id"] =
"Either User ID or Traveller ID is required";
}
print("validateForm.....2");
final requiredFields = {
if (TripPlanAction != "Plan Creation Not Allowed") //
"trip_type": _selectedTripType,
@ -1049,14 +1055,14 @@ class CreateNewPlansState extends State<CreateNewPlan> {
"functional_department": selectedFuncDept,
"purpose_of_travel": selectedPurpose,
};
print("validateForm.....3");
for (var entry in requiredFields.entries) {
if (entry.value == null || entry.value!.isEmpty) {
validationErrors[entry.key] = "Required";
// "${entry.key.replaceAll('_', ' ').toUpperCase()} Required";
}
}
print("validateForm.....1");
// Validate at least one service is selected
final serviceLists = [
flightList,
@ -1179,7 +1185,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
void handleSubmit() {
setState(() async {
print("Hansle Submit....11");
if (validateForm() && temporaryMessage == null) {
print("Hansle Submit....11222");
// if (selectedPlanId != null && selectedPlanId!.isNotEmpty) {
// planData['plan_id'] = selectedPlanId; // Add plan_id for update
// }
@ -1251,6 +1259,26 @@ class CreateNewPlansState extends State<CreateNewPlan> {
} else {
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Trip Creation Failed"),
content: Text(
"There was a problem submitting your plan. Please try again.",
),
actions: [
TextButton(
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
} catch (e) {
print(" Error submitting plan: $e");
@ -2592,7 +2620,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
List<Map<String, String>> options = [
{"title": "Self", "value": "Option 1"},
{"title": "Other Employee", "value": "Option 2"},
{"title": "Others", "value": "Option 3"},
{"title": "Others (Non Employee)", "value": "Option 3"},
];
print(" layoutColor: ${widget.layoutColor}");
@ -2908,8 +2936,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// ),
// ),
CustomTextFieldWrapper(
isFocused: false,
// isFocused: _isdescriptionFocused,
// isFocused: false,
isFocused: _isdescriptionFocused,
// width: isDesktop
// ? MediaQuery.of(context).size.width * 0.38
// : MediaQuery.of(context).size.width * 0.85,
@ -3017,8 +3045,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
labelText: "Trip Name *",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
fontWeight: FontWeight.w400,
color: Colors.grey,
// color: Color(0xFF575A74),
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,

View File

@ -101,6 +101,13 @@ class DynamicItineraryState extends State<DynamicItinerary> {
super.didChangeDependencies();
if (widget.tripType != _tripType) {
// setState(() {
// selectedOption = "";
// isSelected = false;
// selectedIndex = null;
// selectedItem = null;
// });
updateTripType(widget.tripType);
}
}
@ -160,6 +167,20 @@ class DynamicItineraryState extends State<DynamicItinerary> {
}
List<String> getAllowedServiceNames() {
print(
"DID Updatee changee - $selectedOption $selectedListOption $isSelected $selectedItem $selectedIndex",
);
setState(() {
selectedOption = "";
selectedListOption = "";
isSelected = false;
selectedIndex = null;
selectedItem = null;
});
print(
"DID Updatee changee - $selectedOption $isSelected $selectedItem $selectedIndex",
);
if (widget.tripType == "1") {
return ["flight", "accomodation", "train", "bus", "taxi"];
} else if (widget.tripType == "2") {

View File

@ -968,7 +968,7 @@ class _ListPlansState extends State<ListPlans> {
height: 15,
),
tooltip:
'Edit Trip/Plans Details',
'Edit Trip Details',
onPressed: () {
Navigator.pop(
context,
@ -1009,7 +1009,7 @@ class _ListPlansState extends State<ListPlans> {
size: 18,
),
tooltip:
'Download The Trip Comments',
'Download The PDF',
onPressed: () {
Navigator.pop(
context,
@ -1222,7 +1222,7 @@ class _ListPlansState extends State<ListPlans> {
height: 15,
),
tooltip:
'Edit Trip/plan Details',
'Edit Trip Details',
onPressed: () {
Navigator.pop(
context,
@ -1256,7 +1256,7 @@ class _ListPlansState extends State<ListPlans> {
size: 18,
),
tooltip:
'Download The Trip Comments',
'Download The PDF',
onPressed: () {
Navigator.pop(context);
apiService

View File

@ -322,7 +322,8 @@ class _PolicyListState extends State<PolicyList> {
controller: searchController,
onChanged: filterPolicy,
decoration: InputDecoration(
hintText: "Search for a Policy",
hintText: "Search ...",
hintStyle: TextStyle(
fontSize: 12,
color: Color(0xFF9E9DBD),
@ -383,7 +384,7 @@ class _PolicyListState extends State<PolicyList> {
MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Add New Policy",
"Add Policy",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
),
@ -413,7 +414,7 @@ class _PolicyListState extends State<PolicyList> {
controller: searchController,
onChanged: filterPolicy,
decoration: InputDecoration(
hintText: "Search for a Policy",
hintText: "Search ...",
hintStyle: TextStyle(
fontSize: 12,
color: Color(0xFF9E9DBD),

View File

@ -49,7 +49,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
// late List<Map<String, dynamic>?> travelDetailsData;
Map<String, dynamic>? travelDetailsData;
Map<String, dynamic>? travelDetailsDataFromAPI;
Map<String, String> errorMessagesTravel = {};
late TabController _tabController;
String? userId;
@ -546,6 +546,33 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
printFormData();
}
void handleBack() async {
print("USR Detail back");
final tabs = {
"personal": "Personal Details",
"office": "Office Details",
"travel": "Travel Details",
};
final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"]
final currentIndex = tabKeys.indexOf(selectedTab ?? "travel");
print("currentIndex - $currentIndex");
if (currentIndex > 0) {
// Move to next tab
setState(() {
selectedTab = tabKeys[currentIndex - 1];
});
} else {
// Final step submit or show done
print("All tabs completed!");
// Submit the full form here
}
}
void handleNext() async {
print("USR Detail Next");
printFormData();
@ -553,6 +580,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
// travelDetailsData = [travellerDetailsKey.currentState?.travel_Detials];
travelDetailsData = travellerDetailsKey.currentState?.travel_Detials;
// errorMessagesTravel = travellerDetailsKey.currentState!.errorMessages;
print("TRAVEL DETAILS FROM CHILD: $travelDetailsData");
@ -626,8 +654,41 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
void handleSubmit() async {
print("USR Detail Submit");
// printFormData();
bool isValid = travellerDetailsKey.currentState?.boolValidation() ?? false;
if (selectedTab == "travel" ||
selectedRole == "5" ||
setSelectesUserType == true) {
print("NO validation");
Map<String, dynamic> data = userDetials;
if (!isValidData(data)) {
print("USERDETAILS : $userDetials");
print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
} else {
print("USERDETAILS : $userDetials");
orgId = await getOrgId();
createUserData(userDetials);
}
} else {
print("isValid- $isValid");
if (!isValid) {
print("Validation failed. Please check the inputs.");
setState(() {});
return; // STOP execution here if not valid
}
travelDetailsData = travellerDetailsKey.currentState?.travel_Detials;
print("travelDetailsData - $travelDetailsData");
print("travelDetailsData - $travelDetailsData");
// errorMessagesTravel = travellerDetailsKey.currentState!.errorMessages;
print("TRAVEL DETAILS FROM CHILD");
// print("TRAVEL DETAILS FROM CHILD: $travelDetailsData");
@ -639,6 +700,47 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
print("USERDETAILS : $data");
// block-submit-here
// 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";
}
}
// valid_from: 20-06-2025, valid_upto: 19-06-2025
DateTime? valid_from = data?['valid_from'];
DateTime? valid_upto = data?['valid_upto'];
if (valid_from != null && valid_upto != null && valid_from.toString().isNotEmpty && valid_upto.toString().isNotEmpty) {
try {
final format = DateFormat("dd-MM-yyyy");
final checkValidFrom = format.parse("$valid_from");
final checkValidUpto = format.parse("$valid_upto");
if (checkValidUpto.isBefore(checkValidFrom)) {
// return "valid upto cannot be earlier than valid from";
return ;
}
} catch (e) { // return "valid upto cannot be earlier than valid from";
return ;
// errorMessages["end_date"] = "Invalid date format";
}
}
// Map<String, dynamic> data = userDetials;
if (!isValidData(data) && isValidDataTwo(data)) {
@ -653,6 +755,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
createUserData(userDetials);
}
}
}
bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors
@ -959,15 +1062,26 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
isDesktop
? Row(
mainAxisAlignment: MainAxisAlignment.end,
children:
(selectedTab == "travel" ||
children: [
if (selectedTab != "personal")
..._buildBack(isDesktop, layoutColor!),
Spacer(), // spacing between buttons
// Next or Submit based on role or user type
if (selectedTab == "travel" ||
selectedRole == "5" ||
setSelectesUserType == true)
? _buildSubmit(isDesktop, layoutColor!)
: _buildNext(
isDesktop,
layoutColor!,
), // _buildGoBack(isDesktop, layoutColor!),
..._buildSubmit(isDesktop, layoutColor!)
else
..._buildNext(isDesktop, layoutColor!),
// (selectedTab == "travel" ||
// selectedRole == "5" ||
// setSelectesUserType == true)
// ? _buildSubmit(isDesktop, layoutColor!)
// : _buildNext(
// isDesktop,
// layoutColor!,
// ), // _buildGoBack(isDesktop, layoutColor!),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.end,
@ -1325,22 +1439,22 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
child: TextButton(
style: ElevatedButton.styleFrom(
backgroundColor:
isViewMode ? layoutColor : layoutColor, // Keep original color
isViewMode ? Colors.white : Colors.white, // Keep original color
foregroundColor:
isViewMode ? Colors.white : Colors.red, // Keep original color
isViewMode ? layoutColor : layoutColor, // Keep original color
disabledBackgroundColor:
layoutColor, // Ensure color remains when disabled
disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: layoutColor, width: 2),
side: BorderSide(color: Colors.white, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 18),
),
onPressed: handleNext,
onPressed: handleBack,
// onPressed:
// isViewMode ? null : handleNext, // Disable when in view mode
child: Text("Next"),
child: Text("Back"),
),
),
];

View File

@ -391,7 +391,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
buildDelegationStartDateField(isDesktop),
Spacer(), // Space after Last Name
buildDelegationEndDateField(isDesktop),
SizedBox(width: 15),
SizedBox(width: 16),
buildReset(isDesktop),
],
)
@ -692,6 +692,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
enabled: !widget.isViewMode,
popupProps: PopupProps.menu(
menuProps: const MenuProps(
backgroundColor: Colors.white,
),
showSearchBox: true,
fit: FlexFit.loose, // Allows flexible height
itemBuilder:
@ -1342,6 +1345,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
: today,
firstDate: today,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
@ -1450,6 +1454,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
: minDate,
firstDate: minDate,
lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedEndDate) {
@ -1475,7 +1480,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
width: isDesktop ? MediaQuery.of(context).size.width * 0.18 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.2 : null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(

View File

@ -639,7 +639,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
children: [
if (!widget.apiselectedUser) ...[
buildPassword(),
SizedBox(width: 15),
SizedBox(
width: MediaQuery.of(context).size.width * 0.015,
),
buildRole(),
] else ...[
// TextButton(
@ -736,9 +738,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildAddress(),
SizedBox(width: 15),
Spacer(),
// SizedBox(width: 15),
buildCountryField(),
SizedBox(width: 15),
Spacer(),
// SizedBox(width: 15),
buildPostalCodeField(),
],
)
@ -1043,7 +1047,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
? _selectedDateOfBirth!
: today,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
// lastDate: DateTime(2100),
lastDate: today,
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedDateOfBirth) {
@ -1583,6 +1589,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
widget.isViewMode
? null
: (String? newValue) {
widget.errorMessages.remove("role_id");
if (newValue == null) return;
final selectedKey =
roleMap.entries

File diff suppressed because it is too large Load Diff

View File

@ -1137,15 +1137,54 @@ class _UserListScreenState extends State<UserListScreen> {
child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Expanded(
// child:
// isDesktop
// ? SingleChildScrollView(
// scrollDirection: Axis.vertical,
// child: table, // <-- your existing table
// )
// : buildMobileCardView(paginatedUser),
// ),
Expanded(
child:
isDesktop
? SingleChildScrollView(
scrollDirection: Axis.vertical,
child: table, // <-- your existing table
)
: buildMobileCardView(paginatedUser),
? (searchController.text.isNotEmpty &&
filteredUsers.isEmpty
? Center(
child: Text(
"No users found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
),
)
: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: table,
))
: (searchController.text.isNotEmpty &&
filteredUsers.isEmpty
? Center(
child: Text(
"No users found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
),
)
: buildMobileCardView(paginatedUser)),
// child: isDesktop
// ? SingleChildScrollView(
// scrollDirection: Axis.vertical,
// child: table, // <-- your existing table
// )
// : buildMobileCardView(paginatedPlans),
),
PaginationControls(
currentPage: currentPage,
itemsPerPage: itemsPerPage,

View File

@ -284,11 +284,7 @@ class _CustomAppBarState extends State<CustomAppBar> {
@override
Widget build(BuildContext context) {
return
FocusTraversalGroup(
descendantsAreFocusable: false,
child:
AppBar(
return AppBar(
backgroundColor: Colors.white,
surfaceTintColor: Colors.white,
// elevation: 3,
@ -325,8 +321,13 @@ class _CustomAppBarState extends State<CustomAppBar> {
errorBuilder: (context, error, stackTrace) {
return const CircleAvatar(
radius: 20,
backgroundColor: Colors.redAccent,
child: Icon(Icons.error, size: 10),
child: Icon(
Icons.add_a_photo,
size: 10,
color: Colors.grey,
),
// backgroundColor: Colors.redAccent,
// child: Icon(Icons.error, size: 10),
);
},
),
@ -343,6 +344,7 @@ class _CustomAppBarState extends State<CustomAppBar> {
),
),
SizedBox(width: MediaQuery.of(context).size.width * 0.18),
// Spacer(),
Container(
width: MediaQuery.of(context).size.width * 0.35,
child: Row(
@ -573,7 +575,6 @@ class _CustomAppBarState extends State<CustomAppBar> {
color: layoutColor, // Set the color of the bottom border
),
),
)
);
}

View File

@ -112,9 +112,9 @@ class OrganizationSettingState extends State<OrganizationSetting> {
},
{
'value': '/forexTexmplate',
'icon': Icons.attach_money,
'label': 'Forex Template',
'description': 'Edit Template',
'icon': Icons.credit_card,
'label': 'Forex Documentation',
'description': 'Edit Document',
},
{
'value': '/templateList',

View File

@ -102,6 +102,41 @@ class ApiService {
throw Exception('Failed to load country list');
}
}
Future<List<dynamic>> fetchAirlineList() async {
final String apiUrldata = '$apiUrl/api/getAirlineMaster';
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
try {
final data = json.decode(response.body);
print("Airline - $data");
if (!data.containsKey('data') || data['data'] is! List) {
throw Exception(
"Invalid response format: 'data' field is missing or not a List",
);
}
return data['data'];
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
throw Exception('Failed to load country list');
}
}
Future<List<dynamic>> fetchUsers() async {
String? ordId = await getOrgId();

View File

@ -42,7 +42,7 @@ class UserActionsMenu extends StatelessWidget {
children: [
GestureDetector(
child: Tooltip(
message: 'View User Details',
message: 'View Details',
child: Icon(Icons.remove_red_eye,
color: Color(0xFF475569), size: 18)
),
@ -61,7 +61,7 @@ class UserActionsMenu extends StatelessWidget {
SizedBox(width: 8),
GestureDetector(
child: Tooltip(
message: 'Edit User Details',
message: 'Edit Details',
child: Image.asset(
'assets/images/IconsImg/edit.png',
width: 20,

View File

@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
# sdk: ^3.6.1
# sdk: ^3.6.1
sdk: ^3.7.2
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
@ -99,6 +99,8 @@ flutter:
- assets/images/login/VectorG.png
- assets/images/login/logoNew.jpg
- assets/images/login/microsoft.png
- assets/images/IconsImg/path.png
- assets/images/IconsImg/alternate.png
- assets/images/IconsImg/delete.png
- assets/images/IconsImg/edit.png
- assets/images/IconsImg/planPdf_icon.png
@ -134,4 +136,4 @@ flutter:
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
# see https://flutter.dev/to/font-from-packagej