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, size: 18,
), ),
tooltip: tooltip:
'Download The Trip Details', 'Download The PDF',
onPressed: () { onPressed: () {
Navigator.pop( Navigator.pop(
context, context,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -93,13 +93,15 @@ class _groupState extends State<Group> {
String? bodyStringColor = await getBodyColor(); String? bodyStringColor = await getBodyColor();
setState(() { setState(() {
layoutColor = layoutString != null layoutColor =
? Color(int.parse(layoutString)) layoutString != null
: Colors.redAccent; ? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor = bodyStringColor != null bodyColor =
? Color(int.parse(bodyStringColor)) bodyStringColor != null
: Colors.white; ? Color(int.parse(bodyStringColor))
: Colors.white;
}); });
} }
@ -154,10 +156,7 @@ class _groupState extends State<Group> {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = [ List<String> requiredFields = ["name", "description"];
"name",
"description",
];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -165,6 +164,14 @@ class _groupState extends State<Group> {
errorMessages[field] = "Required"; 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 return errorMessages.isEmpty; // Valid if there are no errors
} }
@ -223,23 +230,24 @@ class _groupState extends State<Group> {
} }
try { try {
final response = await (isEdit final response =
? http.put( await (isEdit
Uri.parse(apiUrlData), ? http.put(
headers: { Uri.parse(apiUrlData),
'Authorization': 'Bearer $token', headers: {
'Content-Type': 'application/json', 'Authorization': 'Bearer $token',
}, 'Content-Type': 'application/json',
body: jsonEncode(groupData), },
) body: jsonEncode(groupData),
: http.post( )
Uri.parse(apiUrlData), : http.post(
headers: { Uri.parse(apiUrlData),
'Authorization': 'Bearer $token', headers: {
'Content-Type': 'application/json', 'Authorization': 'Bearer $token',
}, 'Content-Type': 'application/json',
body: jsonEncode(groupData), },
)); body: jsonEncode(groupData),
));
if (response.statusCode == 200 || response.statusCode == 201) { if (response.statusCode == 200 || response.statusCode == 201) {
print("Group submitted successfully!"); print("Group submitted successfully!");
@ -320,32 +328,38 @@ class _groupState extends State<Group> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; builder: (context, sizingInfo) {
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold( return Scaffold(
// backgroundColor: Colors.white, // backgroundColor: Colors.white,
backgroundColor: Color(0xFFf5f5f5), backgroundColor: Color(0xFFf5f5f5),
appBar: CustomAppBar(isDesktop: isDesktop), appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false), drawer: CustomDrawer(isDesktop: false),
body: Padding( body: Padding(
padding: isDesktop padding:
? EdgeInsets.symmetric( isDesktop
horizontal: MediaQuery.of(context).size.width * ? EdgeInsets.symmetric(
0.1, // 30% of screen width as horizontal padding horizontal:
vertical: MediaQuery.of(context).size.height * MediaQuery.of(context).size.width *
0, // 5% of screen height as vertical padding 0.1, // 30% of screen width as horizontal padding
) vertical:
: EdgeInsets.all(8), MediaQuery.of(context).size.height *
child: Row( 0, // 5% of screen height as vertical padding
children: [ )
// if (isDesktop) CustomDrawer(isDesktop: true), : EdgeInsets.all(8),
Expanded(child: buildData(isDesktop, context)) child: Row(
], children: [
// if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(child: buildData(isDesktop, context)),
],
),
), ),
), );
); },
}); );
} }
Widget buildData(bool isDesktop, context) { Widget buildData(bool isDesktop, context) {
@ -367,15 +381,16 @@ class _groupState extends State<Group> {
Container( Container(
color: Colors.white, color: Colors.white,
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: isDesktop child:
? Row( isDesktop
mainAxisAlignment: MainAxisAlignment.end, ? Row(
children: _buildSubmit(isDesktop), mainAxisAlignment: MainAxisAlignment.end,
) children: _buildSubmit(isDesktop),
: Row( )
mainAxisAlignment: MainAxisAlignment.center, : Row(
children: _buildSubmit(isDesktop), mainAxisAlignment: MainAxisAlignment.center,
), children: _buildSubmit(isDesktop),
),
), ),
], ],
), ),
@ -387,9 +402,10 @@ class _groupState extends State<Group> {
// margin: isDesktop // margin: isDesktop
// ? EdgeInsets.all(10.0) // ? EdgeInsets.all(10.0)
// : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
height: isDesktop height:
? MediaQuery.of(context).size.height * 0.98 isDesktop
: MediaQuery.of(context).size.height, ? MediaQuery.of(context).size.height * 0.98
: MediaQuery.of(context).size.height,
// decoration: BoxDecoration( // decoration: BoxDecoration(
// border: isDesktop // border: isDesktop
// ? Border.all( // ? Border.all(
@ -414,10 +430,7 @@ class _groupState extends State<Group> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text("Create Group", style: GoogleFonts.poppins(fontSize: 18)),
"Create Group",
style: GoogleFonts.poppins(fontSize: 18),
),
// Container( // Container(
// color: Color(0xFFE9EBF6), // color: Color(0xFFE9EBF6),
// child: IconButton( // child: IconButton(
@ -436,53 +449,52 @@ class _groupState extends State<Group> {
// height: 5, // height: 5,
// ), // ),
Container( Container(
margin: const EdgeInsets.all(10), margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
color: Colors.white, color: Colors.white,
height: MediaQuery.of(context).size.height * 0.6, height: MediaQuery.of(context).size.height * 0.6,
child: Column( child: Column(
children: [ children: [
// Row( // Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [ // children: [
// Text( // Text(
// "Mail Settings", // "Mail Settings",
// style: TextStyle(color: Colors.blueAccent), // style: TextStyle(color: Colors.blueAccent),
// ), // ),
// Icon( // Icon(
// Icons.keyboard_arrow_down_outlined, // Icons.keyboard_arrow_down_outlined,
// color: Colors.blueAccent, // color: Colors.blueAccent,
// size: 30, // size: 30,
// ), // ),
// ], // ],
// ), // ),
isDesktop isDesktop
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildFirstRow(isDesktop), children: _buildFirstRow(isDesktop),
) )
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: _buildFirstRow(isDesktop), children: _buildFirstRow(isDesktop),
), ),
SizedBox( SizedBox(height: 10),
height: 10,
),
isDesktop isDesktop
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildSecondRow(isDesktop), children: _buildSecondRow(isDesktop),
) )
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: _buildSecondRow(isDesktop), children: _buildSecondRow(isDesktop),
), ),
// SizedBox(height: 15), // SizedBox(height: 15),
], ],
)), ),
),
], ],
), ),
); );
@ -496,17 +508,19 @@ class _groupState extends State<Group> {
Text( Text(
"Group Name", "Group Name",
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: false, isFocused: false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
? MediaQuery.of(context).size.width * 0.34 isDesktop
: MediaQuery.of(context).size.width * 0.85, ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
@ -540,17 +554,19 @@ class _groupState extends State<Group> {
Text( Text(
"Description", "Description",
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: false, isFocused: false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
? MediaQuery.of(context).size.width * 0.34 isDesktop
: MediaQuery.of(context).size.width * 0.85, ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
@ -589,72 +605,82 @@ class _groupState extends State<Group> {
Text( Text(
"Select Policy For Domestic", "Select Policy For Domestic",
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: false, isFocused: false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
? MediaQuery.of(context).size.width * 0.34 isDesktop
: MediaQuery.of(context).size.width * 0.85, ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: apiForDomestic == null child:
? Center( apiForDomestic == null
child: Transform.scale( ? Center(
scale: 0.5, child: Transform.scale(
child: CircularProgressIndicator(), scale: 0.5,
), child: CircularProgressIndicator(),
) ),
: DropdownSearch<String>( )
selectedItem: selectedDomestic == null : DropdownSearch<String>(
? null selectedItem:
: apiForDomestic! selectedDomestic == null
.firstWhere((policy) => ? null
policy['policy_id'] == : apiForDomestic!
selectedDomestic)['name'] .firstWhere(
.toString(), (policy) =>
popupProps: PopupProps.menu( policy['policy_id'] ==
showSearchBox: true, selectedDomestic,
fit: FlexFit.loose, )['name']
constraints: BoxConstraints(maxHeight: 250), .toString(),
searchFieldProps: TextFieldProps( popupProps: PopupProps.menu(
decoration: InputDecoration( showSearchBox: true,
hintText: "Search Policy...", fit: FlexFit.loose,
contentPadding: constraints: BoxConstraints(maxHeight: 250),
EdgeInsets.symmetric(horizontal: 10), searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Policy...",
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
),
), ),
), ),
), items:
items: apiForDomestic! apiForDomestic!
.map((policy) => policy['name'].toString()) .map((policy) => policy['name'].toString())
.toList(), .toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1), contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
), ),
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
selectedDomestic =
apiForDomestic!.firstWhere(
(policy) => policy['name'] == newValue,
)['policy_id'];
});
},
), ),
dropdownBuilder: (context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
selectedDomestic = apiForDomestic!.firstWhere(
(policy) =>
policy['name'] == newValue)['policy_id'];
});
},
),
), ),
) ),
], ],
), ),
Column( Column(
@ -663,72 +689,82 @@ class _groupState extends State<Group> {
Text( Text(
"Select Policy For International", "Select Policy For International",
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: false, isFocused: false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
? MediaQuery.of(context).size.width * 0.34 isDesktop
: MediaQuery.of(context).size.width * 0.85, ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: apiForInternational == null child:
? Center( apiForInternational == null
child: Transform.scale( ? Center(
scale: 0.5, child: Transform.scale(
child: CircularProgressIndicator(), scale: 0.5,
), child: CircularProgressIndicator(),
) ),
: DropdownSearch<String>( )
selectedItem: selectedInternational == null : DropdownSearch<String>(
? null selectedItem:
: apiForInternational! selectedInternational == null
.firstWhere((policy) => ? null
policy['policy_id'] == : apiForInternational!
selectedInternational)['name'] .firstWhere(
.toString(), (policy) =>
popupProps: PopupProps.menu( policy['policy_id'] ==
showSearchBox: true, selectedInternational,
fit: FlexFit.loose, )['name']
constraints: BoxConstraints(maxHeight: 250), .toString(),
searchFieldProps: TextFieldProps( popupProps: PopupProps.menu(
decoration: InputDecoration( showSearchBox: true,
hintText: "Search Policy...", fit: FlexFit.loose,
contentPadding: constraints: BoxConstraints(maxHeight: 250),
EdgeInsets.symmetric(horizontal: 10), searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Policy...",
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
),
), ),
), ),
), items:
items: apiForInternational! apiForInternational!
.map((policy) => policy['name'].toString()) .map((policy) => policy['name'].toString())
.toList(), .toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1), contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
), ),
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
selectedInternational =
apiForInternational!.firstWhere(
(policy) => policy['name'] == newValue,
)['policy_id'];
});
},
), ),
dropdownBuilder: (context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
selectedInternational = apiForInternational!
.firstWhere((policy) =>
policy['name'] == newValue)['policy_id'];
});
},
),
), ),
) ),
], ],
), ),
]; ];
@ -737,22 +773,21 @@ class _groupState extends State<Group> {
List<Widget> _buildSubmit(isDesktop) { List<Widget> _buildSubmit(isDesktop) {
return [ return [
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.white, backgroundColor: Colors.white,
foregroundColor: layoutColor, foregroundColor: layoutColor,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide(color: layoutColor ?? Colors.green, width: 2), side: BorderSide(color: layoutColor ?? Colors.green, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: () { padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
context.go('/group'); ),
}, onPressed: () {
child: Text("Cancel")), context.go('/group');
SizedBox( },
width: 20, child: Text("Cancel"),
), ),
SizedBox(width: 20),
MouseRegion( MouseRegion(
// cursor: widget.isViewMode // cursor: widget.isViewMode
// ? SystemMouseCursors.forbidden // ? SystemMouseCursors.forbidden
@ -773,7 +808,7 @@ class _groupState extends State<Group> {
onPressed: handleSubmit, // Disable when in view mode onPressed: handleSubmit, // Disable when in view mode
child: Text("Submit"), child: Text("Submit"),
), ),
) ),
]; ];
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,3 +1,4 @@
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
@ -219,7 +220,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
child: Column( child: Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(28.0), padding: const EdgeInsets.only(top: 30.0),
child: Center( child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)), child: Column(children: _buildAccomadtionForm(isDesktop)),
), ),
@ -362,34 +363,109 @@ class _TaxiScreenState extends State<TaxiScreen> {
), ),
), ),
SizedBox(height: 5), 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( CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus, isFocused: _toFocus,
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( width: double.infinity,
focusNode: _toFocusNode, // Assign the correct focus node child: DropdownSearch<Map<String, dynamic>>(
value: selectedCarType, // focusNode: _taxiReqFocusNode,
style: TextStyle(fontSize: 12), items: purposeList.cast<Map<String, dynamic>>(),
decoration: InputDecoration( selectedItem: purposeList.firstWhere(
border: InputBorder.none, (item) => item['dropdown_key'] == selectedCarType,
contentPadding: EdgeInsets.symmetric( orElse: () => {},
horizontal: 10,
), // Proper padding
), ),
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: onChanged:
purposeList.isNotEmpty purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedCarType = newValue; selectedCarType = newValue as String?;
}); });
print( print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}", "Updating form data: -> ${newValue ?? ""}",
); );
} }
: null, : null,
items: dropdownItems,
), ),
), ),
), ),
@ -429,38 +505,117 @@ class _TaxiScreenState extends State<TaxiScreen> {
dropdownItems.isNotEmpty ? dropdownItems.first.value : null; dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [ 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, isFocused: _taxiReqFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: width:
isDesktop isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( width: double.infinity,
focusNode: _taxiReqFocusNode, // Assign the correct focus node child: DropdownSearch<Map<String, dynamic>>(
value: selectedReqTaxi, // focusNode: _taxiReqFocusNode,
style: TextStyle(fontSize: 12), items: purposeList.cast<Map<String, dynamic>>(),
decoration: InputDecoration( selectedItem: purposeList.firstWhere(
border: InputBorder.none, (item) => item['dropdown_key'] == selectedReqTaxi,
contentPadding: EdgeInsets.symmetric( orElse: () => {},
horizontal: 10,
), // Proper padding
), ),
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: onChanged:
purposeList.isNotEmpty purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedReqTaxi = newValue; selectedReqTaxi = newValue as String?;
}); });
print( print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}", "Updating form data: -> ${newValue ?? ""}",
); );
} }
: null, : null,
items: dropdownItems,
), ),
), ),
), ),
@ -484,6 +639,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
: today, : today,
firstDate: today, firstDate: today,
lastDate: DateTime(2100), lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
); );
if (pickedDate != null && pickedDate != _selectedCheckOutDate) { if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
@ -501,30 +657,87 @@ class _TaxiScreenState extends State<TaxiScreen> {
); );
if (pickedTime != null && pickedTime != _selectedCheckOutTime) { if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
setState(() { // Parse the selected date
_selectedCheckOutTime = pickedTime; final dateText = _dateController.text ?? "";
// Formatting time to HH:mm (24-hour format) final selectedDate = DateFormat(
final now = DateTime.now(); 'dd-MM-yyyy',
final formattedTime = DateFormat('HH:mm').format( ).parse(dateText); // or 'yyyy-MM-dd' depending on your format
DateTime( final now = DateTime.now();
now.year,
now.month, final selectedDateTime = DateTime(
now.day, selectedDate.year,
pickedTime.hour, selectedDate.month,
pickedTime.minute, selectedDate.day,
), pickedTime.hour,
); pickedTime.minute,
_timeController.text = formattedTime; );
});
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)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(
now.year,
now.month,
now.day,
pickedTime.hour,
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 [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"City", "City *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -562,7 +775,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Pickup Location", "Pickup Location *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -600,7 +813,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Date", "Date *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -648,7 +861,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Time", "Time *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -662,7 +875,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutTime(context), onTap: () {
_timeController.text = "";
_selectCheckOutTime(context);
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _timeFocusNode, focusNode: _timeFocusNode,
@ -687,7 +903,11 @@ class _TaxiScreenState extends State<TaxiScreen> {
), ),
if (errorMessages["time"] != null) ...[ if (errorMessages["time"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), // Text("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 return errorMessages.isEmpty; // Valid if there are no errors
} }
@ -324,7 +330,7 @@ class _TrainScreenState extends State<TrainScreen> {
// height: 6, // height: 6,
// ), // ),
Padding( Padding(
padding: const EdgeInsets.all(28.0), padding: const EdgeInsets.only(top: 30.0),
child: Center( child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)), child: Column(children: _buildAccomadtionForm(isDesktop)),
), ),
@ -369,7 +375,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Train Number", "Train Number *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -464,6 +470,7 @@ class _TrainScreenState extends State<TrainScreen> {
: today, : today,
firstDate: today, firstDate: today,
lastDate: DateTime(2100), lastDate: DateTime(2100),
initialEntryMode: DatePickerEntryMode.calendarOnly,
); );
if (pickedDate != null && pickedDate != _selectedCheckOutDate) { if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
@ -480,23 +487,66 @@ class _TrainScreenState extends State<TrainScreen> {
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
); );
if (pickedTime != null && pickedTime != _selectedCheckOutTime) { if (pickedTime != null) {
final now = DateTime.now();
// 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(() { setState(() {
_selectedCheckOutTime = pickedTime; _selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now(); final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
final formattedTime = DateFormat('HH:mm').format(
DateTime(
now.year,
now.month,
now.day,
pickedTime.hour,
pickedTime.minute,
),
);
_timeController.text = formattedTime; _timeController.text = formattedTime;
// errorMessages["time_$index"] = ""; // clear previous error
errorMessages.remove("time");
}); });
} }
// if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
// setState(() {
// _selectedCheckOutTime = pickedTime;
// // Formatting time to HH:mm (24-hour format)
// final now = DateTime.now();
// final formattedTime = DateFormat('HH:mm').format(
// DateTime(
// now.year,
// now.month,
// now.day,
// pickedTime.hour,
// pickedTime.minute,
// ),
// );
// _timeController.text = formattedTime;
// });
// }
} }
//---------------------------------------------- //----------------------------------------------
@ -542,34 +592,104 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
), ),
SizedBox(height: 5), 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, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
// width: isDesktop width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
// ? MediaQuery.of(context).size.width * 0.34
// : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( width: double.infinity,
focusNode: _hotelNameFocusNode, // Assign the correct focus node child: DropdownSearch<Map<String, dynamic>>(
// controller: _hotelNameController, items: purposeList.cast<Map<String, dynamic>>(),
value: selectedClass, selectedItem: purposeList.firstWhere(
style: TextStyle(fontSize: 12), (item) => item['dropdown_key'] == selectedClass,
decoration: InputDecoration( orElse: () => {},
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
), ),
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: onChanged:
purposeList.isNotEmpty purposeList.isNotEmpty
? (newValue) { ? (Map<String, dynamic>? newValue) {
setState(() { setState(() {
selectedClass = newValue; selectedClass= newValue?['dropdown_key'];
}); print("Selected Purpose: ${selectedClass}");
} });
: null, }
items: dropdownItems, : null,
), ),
), ),
), ),
@ -584,7 +704,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"From", "From *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -592,96 +712,158 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
), ),
SizedBox(height: 5), 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;
// });
// },
// ),
// ),
// // 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),
// // ),
// // ),
// // ),
// ),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus, isFocused: _fromFocus ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: child: DropdownSearch<String>(
isCountryLoading selectedItem: countryMap[selectedFrom],
? Center(child: CircularProgressIndicator()) popupProps: PopupProps.menu(
: DropdownSearch<String>( showSearchBox: true,
// selectedItem: selectedFrom != null fit: FlexFit.loose,
// ? countryMap[selectedFrom] constraints: BoxConstraints(maxHeight: 250),
// : null, menuProps: const MenuProps(backgroundColor: Colors.white),
selectedItem: itemBuilder:
selectedFrom != null (context, item, isSelected) => Container(
? countryMap[selectedFrom] // get the display value from code color: Colors.white,
: null, padding: EdgeInsets.symmetric(
popupProps: PopupProps.menu( horizontal: 10,
menuProps: MenuProps(backgroundColor: Colors.white), vertical: 6,
constraints: BoxConstraints(maxHeight: 230), ),
showSearchBox: true, child: Text(
searchFieldProps: TextFieldProps( item,
decoration: InputDecoration( style: GoogleFonts.poppins(fontSize: 11.5),
hintText: "Search ...", ),
contentPadding: EdgeInsets.symmetric( ),
horizontal: 10, searchFieldProps: TextFieldProps(
), decoration: InputDecoration(
), hintText: "Search ...",
), contentPadding: EdgeInsets.symmetric(horizontal: 10),
), ),
),
items: countryMap.values.toList(), ),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
contentPadding: EdgeInsets.symmetric( border: InputBorder.none,
horizontal: 10, contentPadding: EdgeInsets.symmetric(horizontal: 1),
vertical: 5, ),
), ),
border: InputBorder.none, dropdownBuilder:
), (context, selectedItem) => Align(
), alignment: Alignment.centerLeft,
dropdownBuilder: child: Text(
(context, selectedItem) => Align( selectedItem ?? "Select ",
alignment: Alignment.centerLeft, style: GoogleFonts.poppins(
child: Text( fontSize: 12,
selectedItem ?? "Select", color: Colors.black,
style: TextStyle(fontSize: 12), ),
), ),
), ),
onChanged: (String? newValue) {
// onChanged: (String? newValue) { setState(() {
// setState(() { selectedFrom =
// // selectedFrom[index] = countryMap.entries countryMap.entries
// // .firstWhere((entry) => entry.value == newValue) .firstWhere(
// // .key; (entry) => entry.value == newValue,
// )
// selectedFrom = countryMap.entries .key;
// .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) ...[ if (errorMessages["from_station"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -694,7 +876,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"To", "To *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -702,63 +884,128 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
), ),
SizedBox(height: 5), 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( CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus, isFocused: _toFocus ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: child: DropdownSearch<String>(
isCountryLoading selectedItem: countryMap[selectedTo],
? Center(child: CircularProgressIndicator()) popupProps: PopupProps.menu(
: DropdownSearch<String>( showSearchBox: true,
selectedItem: fit: FlexFit.loose,
selectedTo != null constraints: BoxConstraints(maxHeight: 250),
? countryMap[selectedTo] // get the display value from code menuProps: const MenuProps(backgroundColor: Colors.white),
: null, itemBuilder:
popupProps: PopupProps.menu( (context, item, isSelected) => Container(
menuProps: MenuProps(backgroundColor: Colors.white), color: Colors.white,
constraints: BoxConstraints(maxHeight: 230), padding: EdgeInsets.symmetric(
showSearchBox: true, horizontal: 10,
searchFieldProps: TextFieldProps( vertical: 6,
decoration: InputDecoration( ),
hintText: "Search ...", child: Text(
contentPadding: EdgeInsets.symmetric( item,
horizontal: 10, style: GoogleFonts.poppins(fontSize: 11.5),
), ),
), ),
), searchFieldProps: TextFieldProps(
), decoration: InputDecoration(
items: countryMap.values.toList(), hintText: "Search ...",
dropdownDecoratorProps: DropDownDecoratorProps( contentPadding: EdgeInsets.symmetric(horizontal: 10),
dropdownSearchDecoration: InputDecoration( ),
border: InputBorder.none, ),
contentPadding: EdgeInsets.symmetric(horizontal: 1), ),
), items: countryMap.values.toList(),
), dropdownDecoratorProps: DropDownDecoratorProps(
dropdownBuilder: dropdownSearchDecoration: InputDecoration(
(context, selectedItem) => Align( border: InputBorder.none,
alignment: Alignment.centerLeft, contentPadding: EdgeInsets.symmetric(horizontal: 1),
child: Text( ),
selectedItem ?? "Select", ),
style: TextStyle(fontSize: 12), dropdownBuilder:
), (context, selectedItem) => Align(
), alignment: Alignment.centerLeft,
onChanged: (String? newValue) { child: Text(
setState(() { selectedItem ?? "Select ",
selectedTo = style: GoogleFonts.poppins(
countryMap.entries fontSize: 12,
.firstWhere( color: Colors.black,
(entry) => entry.value == newValue, ),
) ),
.key; ),
}); onChanged: (String? newValue) {
}, setState(() {
), selectedTo =
countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key;
});
},
),
), ),
), ),
if (errorMessages["to_station"] != null) ...[ if (errorMessages["to_station"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), Text(
errorMessages["to_station"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
], ],
], ],
), ),
@ -767,7 +1014,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Date", "Date *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -816,7 +1063,7 @@ class _TrainScreenState extends State<TrainScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Time", "Time *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -831,7 +1078,10 @@ class _TrainScreenState extends State<TrainScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutTime(context), onTap: () {
_timeController.text = "";
_selectCheckOutTime(context);
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _timeFocusNode, focusNode: _timeFocusNode,
@ -856,7 +1106,10 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
if (errorMessages["time"] != null) ...[ if (errorMessages["time"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), Text(
errorMessages["time"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
], ],
], ],
), ),

View File

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

View File

@ -398,13 +398,34 @@ class _FlightListWidgetState extends State<FlightListWidget> {
), ),
Expanded( Expanded(
flex: 4, flex: 4,
child: Text( child: Column(
"$fromPlaceCountry (From) - (To) $toPlaceCountry", crossAxisAlignment: CrossAxisAlignment.start,
// "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", children: [
style: GoogleFonts.poppins( Text(
fontSize: 12, "$fromPlaceCountry",
fontWeight: FontWeight.w700, // "${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( Expanded(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,14 +42,14 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
final GlobalKey<PersonalDetailsState> personalDetailsKey = final GlobalKey<PersonalDetailsState> personalDetailsKey =
GlobalKey<PersonalDetailsState>(); GlobalKey<PersonalDetailsState>();
final GlobalKey<TravellerDetailsState> travellerDetailsKey = final GlobalKey<TravellerDetailsState> travellerDetailsKey =
GlobalKey<TravellerDetailsState>(); GlobalKey<TravellerDetailsState>();
// late List<Map<String, dynamic>?> travelDetailsData; // late List<Map<String, dynamic>?> travelDetailsData;
Map<String, dynamic>? travelDetailsData; Map<String, dynamic>? travelDetailsData;
Map<String, dynamic>? travelDetailsDataFromAPI; Map<String, dynamic>? travelDetailsDataFromAPI;
Map<String, String> errorMessagesTravel = {};
late TabController _tabController; late TabController _tabController;
String? userId; String? userId;
@ -299,7 +299,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
// Fix the invalid JSON (dangerous if the format changes) // Fix the invalid JSON (dangerous if the format changes)
final fixedJson = raw.replaceAllMapped( final fixedJson = raw.replaceAllMapped(
RegExp(r'(\w+):'), // matches `service_id:` RegExp(r'(\w+):'), // matches `service_id:`
(match) => '"${match.group(1)}":', (match) => '"${match.group(1)}":',
); );
List<dynamic> decodedList = jsonDecode(fixedJson); List<dynamic> decodedList = jsonDecode(fixedJson);
@ -363,7 +363,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
// } // }
final extraData = final extraData =
GoRouterState.of(context).extra as Map<String, dynamic>?; GoRouterState.of(context).extra as Map<String, dynamic>?;
if (extraData != null) { if (extraData != null) {
print("extraData: ${extraData['selectedUser']}"); print("extraData: ${extraData['selectedUser']}");
@ -381,8 +381,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
// Handle selectedUser as a Map (not a List) // Handle selectedUser as a Map (not a List)
apiselectedUser = apiselectedUser =
extraData['selectedUser'] extraData['selectedUser']
as Map<String, dynamic>?; // Cast it as a Map as Map<String, dynamic>?; // Cast it as a Map
isViewMode = extraData['isViewMode'] ?? false; isViewMode = extraData['isViewMode'] ?? false;
isEditProfile = extraData['isEditProfile'] ?? false; isEditProfile = extraData['isEditProfile'] ?? false;
}); });
@ -444,7 +444,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
userMap = { userMap = {
for (var user in userList) for (var user in userList)
user['user_id'].toString(): user['user_id'].toString():
"${user['first_name']} ${user['last_name']}", "${user['first_name']} ${user['last_name']}",
}; };
userIdsApi = userMap.keys.toList(); userIdsApi = userMap.keys.toList();
}); });
@ -483,14 +483,14 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
setState(() { setState(() {
layoutColor = layoutColor =
layoutString != null layoutString != null
? Color(int.parse(layoutString)) ? Color(int.parse(layoutString))
: Colors.redAccent; : Colors.redAccent;
bodyColor = bodyColor =
bodyStringColor != null bodyStringColor != null
? Color(int.parse(bodyStringColor)) ? Color(int.parse(bodyStringColor))
: Colors.white; : Colors.white;
}); });
} }
@ -546,6 +546,33 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
printFormData(); 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 { void handleNext() async {
print("USR Detail Next"); print("USR Detail Next");
printFormData(); printFormData();
@ -553,6 +580,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
// travelDetailsData = [travellerDetailsKey.currentState?.travel_Detials]; // travelDetailsData = [travellerDetailsKey.currentState?.travel_Detials];
travelDetailsData = travellerDetailsKey.currentState?.travel_Detials; travelDetailsData = travellerDetailsKey.currentState?.travel_Detials;
// errorMessagesTravel = travellerDetailsKey.currentState!.errorMessages;
print("TRAVEL DETAILS FROM CHILD: $travelDetailsData"); print("TRAVEL DETAILS FROM CHILD: $travelDetailsData");
@ -626,31 +654,106 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
void handleSubmit() async { void handleSubmit() async {
print("USR Detail Submit"); print("USR Detail Submit");
// printFormData(); // printFormData();
travelDetailsData = travellerDetailsKey.currentState?.travel_Detials;
print("TRAVEL DETAILS FROM CHILD"); bool isValid = travellerDetailsKey.currentState?.boolValidation() ?? false;
// print("TRAVEL DETAILS FROM CHILD: $travelDetailsData"); if (selectedTab == "travel" ||
selectedRole == "5" ||
setSelectesUserType == true) {
print("NO validation");
passportFile = travellerDetailsKey.currentState?.passportFile; Map<String, dynamic> data = userDetials;
print("passportFile : $passportFile"); 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();
Map<String, dynamic> data = userDetials; createUserData(userDetials);
}
print("USERDETAILS : $data");
// Map<String, dynamic> data = userDetials;
if (!isValidData(data) && isValidDataTwo(data)) {
print("USERDETAILS : $userDetials");
print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
} else { } else {
print("USERDETAILS : $userDetials"); print("isValid- $isValid");
orgId = await getOrgId(); if (!isValid) {
print("Validation failed. Please check the inputs.");
setState(() {});
return; // STOP execution here if not valid
}
createUserData(userDetials); 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");
passportFile = travellerDetailsKey.currentState?.passportFile;
print("passportFile : $passportFile");
Map<String, dynamic> data = userDetials;
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)) {
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);
}
} }
} }
@ -682,7 +785,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
if (data["mobile_no"] != null && data["mobile_no"].toString().isNotEmpty) { if (data["mobile_no"] != null && data["mobile_no"].toString().isNotEmpty) {
if (!RegExp(r"^\d{10}$").hasMatch(data["mobile_no"].toString())) { if (!RegExp(r"^\d{10}$").hasMatch(data["mobile_no"].toString())) {
errorMessages["mobile_no"] = errorMessages["mobile_no"] =
"Enter 10 digits"; // Invalid mobile number format "Enter 10 digits"; // Invalid mobile number format
} }
} }
@ -692,7 +795,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
r"^\d{10}$", r"^\d{10}$",
).hasMatch(data["alternate_mobile_no"].toString())) { ).hasMatch(data["alternate_mobile_no"].toString())) {
errorMessages["alternate_mobile_no"] = errorMessages["alternate_mobile_no"] =
"Enter 10 digits"; // Invalid mobile number format "Enter 10 digits"; // Invalid mobile number format
} }
} }
@ -882,7 +985,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
// Ensure UI updates // Ensure UI updates
if (isMatch) { if (isMatch) {
errorMessages["password"] = errorMessages["password"] =
"New password is not similar to old password"; "New password is not similar to old password";
print(" Password match!"); print(" Password match!");
} else { } else {
print(" Password NOT match!"); print(" Password NOT match!");
@ -911,16 +1014,16 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
drawer: CustomDrawer(isDesktop: false), drawer: CustomDrawer(isDesktop: false),
body: Padding( body: Padding(
padding: padding:
isDesktop isDesktop
? EdgeInsets.symmetric( ? EdgeInsets.symmetric(
horizontal: horizontal:
MediaQuery.of(context).size.width * MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding 0.1, // 30% of screen width as horizontal padding
vertical: vertical:
MediaQuery.of(context).size.height * MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding 0, // 5% of screen height as vertical padding
) )
: EdgeInsets.all(0), : EdgeInsets.all(0),
child: Row( child: Row(
children: [Expanded(child: buildData(isDesktop, context))], children: [Expanded(child: buildData(isDesktop, context))],
), ),
@ -942,9 +1045,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
Expanded( Expanded(
child: Container( child: Container(
height: height:
isDesktop isDesktop
? MediaQuery.of(context).size.height * 0.98 ? MediaQuery.of(context).size.height * 0.98
: MediaQuery.of(context).size.height, : MediaQuery.of(context).size.height,
child: Padding( child: Padding(
padding: EdgeInsets.all(0.0), padding: EdgeInsets.all(0.0),
child: _buildUserDetails(isDesktop), child: _buildUserDetails(isDesktop),
@ -956,28 +1059,39 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: child:
isDesktop isDesktop
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: children: [
(selectedTab == "travel" || if (selectedTab != "personal")
selectedRole == "5" || ..._buildBack(isDesktop, layoutColor!),
setSelectesUserType == true) Spacer(), // spacing between buttons
? _buildSubmit(isDesktop, layoutColor!) // Next or Submit based on role or user type
: _buildNext( if (selectedTab == "travel" ||
isDesktop, selectedRole == "5" ||
layoutColor!, setSelectesUserType == true)
), // _buildGoBack(isDesktop, layoutColor!), ..._buildSubmit(isDesktop, layoutColor!)
) else
: Row( ..._buildNext(isDesktop, layoutColor!),
mainAxisAlignment: MainAxisAlignment.end, // (selectedTab == "travel" ||
children: // selectedRole == "5" ||
(selectedTab == "travel" || // setSelectesUserType == true)
selectedRole == "5" || // ? _buildSubmit(isDesktop, layoutColor!)
setSelectesUserType == true) // : _buildNext(
? _buildSubmit(isDesktop, layoutColor!) // isDesktop,
: _buildNext(isDesktop, layoutColor!), // layoutColor!,
), // ), // _buildGoBack(isDesktop, layoutColor!),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.end,
children:
(selectedTab == "travel" ||
selectedRole == "5" ||
setSelectesUserType == true)
? _buildSubmit(isDesktop, layoutColor!)
: _buildNext(isDesktop, layoutColor!),
),
), ),
), ),
], ],
@ -1022,9 +1136,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
isDesktop isDesktop
? buildTabsForUser() ? buildTabsForUser()
: SingleChildScrollView( : SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: buildTabsForUser(), child: buildTabsForUser(),
), ),
Container( Container(
// color: Colors.yellow.shade50, // color: Colors.yellow.shade50,
height: MediaQuery.of(context).size.height * 0.64, height: MediaQuery.of(context).size.height * 0.64,
@ -1127,8 +1241,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
); );
case "travel": case "travel":
final fullName = final fullName =
"${controllers["Fname"]?.text ?? ""} ${controllers["Lname"]?.text ?? ""}" "${controllers["Fname"]?.text ?? ""} ${controllers["Lname"]?.text ?? ""}"
.trim(); .trim();
return TravellerDetails( return TravellerDetails(
key: travellerDetailsKey, key: travellerDetailsKey,
isDesktop: isDesktop, isDesktop: isDesktop,
@ -1187,69 +1301,69 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.end, // important crossAxisAlignment: CrossAxisAlignment.end, // important
children: children:
tabs.entries.map((entry) { tabs.entries.map((entry) {
final targetTab = entry.key; final targetTab = entry.key;
print("TargetsTAb: $targetTab"); print("TargetsTAb: $targetTab");
final isSelected = selectedTab == entry.key; final isSelected = selectedTab == entry.key;
print("isSelected: $isSelected"); print("isSelected: $isSelected");
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
bool isValid = false; bool isValid = false;
final currentTab = selectedTab; final currentTab = selectedTab;
if (currentTab == "personal") { if (currentTab == "personal") {
isValid = isValidData(userDetials); isValid = isValidData(userDetials);
if (isValid) { if (isValid) {
selectedTab = entry.key; selectedTab = entry.key;
} }
} else if (currentTab == "office" && } else if (currentTab == "office" &&
targetTab == "personal") { targetTab == "personal") {
selectedTab = entry.key; selectedTab = entry.key;
} else if (currentTab == "office") { } else if (currentTab == "office") {
isValid = isValidDataTwo(userDetials); isValid = isValidDataTwo(userDetials);
if (isValid) { if (isValid) {
selectedTab = entry.key; selectedTab = entry.key;
} }
} else { } else {
isValid = isValid =
true; // Travel tab might not need validation at this point true; // Travel tab might not need validation at this point
selectedTab = entry.key; selectedTab = entry.key;
} }
}); });
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
right: 24.0, right: 24.0,
), // space between tabs ), // space between tabs
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
entry.value, entry.value,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: color:
isSelected ? Color(0xFF114D8B) : Color(0xFF475569), isSelected ? Color(0xFF114D8B) : Color(0xFF475569),
), ),
),
const SizedBox(height: 8),
AnimatedContainer(
duration: Duration(milliseconds: 300),
height: 2,
width: isSelected ? 50 : 0, // small line
color: Color(0xFF114D8B),
),
],
), ),
), const SizedBox(height: 8),
); AnimatedContainer(
}).toList(), duration: Duration(milliseconds: 300),
height: 2,
width: isSelected ? 50 : 0, // small line
color: Color(0xFF114D8B),
),
],
),
),
);
}).toList(),
); );
} }
@ -1259,17 +1373,17 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
return [ return [
MouseRegion( MouseRegion(
cursor: cursor:
isViewMode isViewMode
? SystemMouseCursors.forbidden ? SystemMouseCursors.forbidden
: SystemMouseCursors.click, : SystemMouseCursors.click,
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor:
isViewMode ? layoutColor : layoutColor, // Keep original color isViewMode ? layoutColor : layoutColor, // Keep original color
foregroundColor: foregroundColor:
isViewMode ? Colors.white : Colors.white, // Keep original color isViewMode ? Colors.white : Colors.white, // Keep original color
disabledBackgroundColor: disabledBackgroundColor:
layoutColor, // Ensure color remains when disabled layoutColor, // Ensure color remains when disabled
disabledForegroundColor: Colors.white, disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@ -1288,17 +1402,17 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
return [ return [
MouseRegion( MouseRegion(
cursor: cursor:
isViewMode isViewMode
? SystemMouseCursors.forbidden ? SystemMouseCursors.forbidden
: SystemMouseCursors.click, : SystemMouseCursors.click,
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor:
isViewMode ? layoutColor : layoutColor, // Keep original color isViewMode ? layoutColor : layoutColor, // Keep original color
foregroundColor: foregroundColor:
isViewMode ? Colors.white : Colors.white, // Keep original color isViewMode ? Colors.white : Colors.white, // Keep original color
disabledBackgroundColor: disabledBackgroundColor:
layoutColor, // Ensure color remains when disabled layoutColor, // Ensure color remains when disabled
disabledForegroundColor: Colors.white, disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@ -1319,28 +1433,28 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
return [ return [
MouseRegion( MouseRegion(
cursor: cursor:
isViewMode isViewMode
? SystemMouseCursors.forbidden ? SystemMouseCursors.forbidden
: SystemMouseCursors.click, : SystemMouseCursors.click,
child: TextButton( child: TextButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor:
isViewMode ? layoutColor : layoutColor, // Keep original color isViewMode ? Colors.white : Colors.white, // Keep original color
foregroundColor: foregroundColor:
isViewMode ? Colors.white : Colors.red, // Keep original color isViewMode ? layoutColor : layoutColor, // Keep original color
disabledBackgroundColor: disabledBackgroundColor:
layoutColor, // Ensure color remains when disabled layoutColor, // Ensure color remains when disabled
disabledForegroundColor: Colors.white, disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), 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: // onPressed:
// isViewMode ? null : handleNext, // Disable when in view mode // isViewMode ? null : handleNext, // Disable when in view mode
child: Text("Next"), child: Text("Back"),
), ),
), ),
]; ];
@ -1367,19 +1481,19 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
if (!isViewMode) if (!isViewMode)
MouseRegion( MouseRegion(
cursor: cursor:
isViewMode isViewMode
? SystemMouseCursors.forbidden ? SystemMouseCursors.forbidden
: SystemMouseCursors.click, : SystemMouseCursors.click,
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor:
isViewMode ? layoutColor : layoutColor, // Keep original color isViewMode ? layoutColor : layoutColor, // Keep original color
foregroundColor: foregroundColor:
isViewMode isViewMode
? Colors.white ? Colors.white
: Colors.white, // Keep original color : Colors.white, // Keep original color
disabledBackgroundColor: disabledBackgroundColor:
layoutColor, // Ensure color remains when disabled layoutColor, // Ensure color remains when disabled
disabledForegroundColor: Colors.white, disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@ -1388,7 +1502,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: onPressed:
isViewMode ? null : handleSubmit, // Disable when in view mode isViewMode ? null : handleSubmit, // Disable when in view mode
child: Text("Submit"), child: Text("Submit"),
), ),
), ),

View File

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

View File

@ -639,7 +639,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
children: [ children: [
if (!widget.apiselectedUser) ...[ if (!widget.apiselectedUser) ...[
buildPassword(), buildPassword(),
SizedBox(width: 15), SizedBox(
width: MediaQuery.of(context).size.width * 0.015,
),
buildRole(), buildRole(),
] else ...[ ] else ...[
// TextButton( // TextButton(
@ -736,9 +738,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
buildAddress(), buildAddress(),
SizedBox(width: 15), Spacer(),
// SizedBox(width: 15),
buildCountryField(), buildCountryField(),
SizedBox(width: 15), Spacer(),
// SizedBox(width: 15),
buildPostalCodeField(), buildPostalCodeField(),
], ],
) )
@ -1043,7 +1047,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
? _selectedDateOfBirth! ? _selectedDateOfBirth!
: today, : today,
firstDate: DateTime(1900), firstDate: DateTime(1900),
lastDate: DateTime(2100), // lastDate: DateTime(2100),
lastDate: today,
initialEntryMode: DatePickerEntryMode.calendarOnly,
); );
if (pickedDate != null && pickedDate != _selectedDateOfBirth) { if (pickedDate != null && pickedDate != _selectedDateOfBirth) {
@ -1583,6 +1589,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
widget.isViewMode widget.isViewMode
? null ? null
: (String? newValue) { : (String? newValue) {
widget.errorMessages.remove("role_id");
if (newValue == null) return; if (newValue == null) return;
final selectedKey = final selectedKey =
roleMap.entries 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( child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
// Expanded(
// child:
// isDesktop
// ? SingleChildScrollView(
// scrollDirection: Axis.vertical,
// child: table, // <-- your existing table
// )
// : buildMobileCardView(paginatedUser),
// ),
Expanded( Expanded(
child: child:
isDesktop isDesktop
? SingleChildScrollView( ? (searchController.text.isNotEmpty &&
scrollDirection: Axis.vertical, filteredUsers.isEmpty
child: table, // <-- your existing table ? Center(
) child: Text(
: buildMobileCardView(paginatedUser), "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( PaginationControls(
currentPage: currentPage, currentPage: currentPage,
itemsPerPage: itemsPerPage, itemsPerPage: itemsPerPage,

View File

@ -284,297 +284,298 @@ class _CustomAppBarState extends State<CustomAppBar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return return AppBar(
FocusTraversalGroup( backgroundColor: Colors.white,
descendantsAreFocusable: false, surfaceTintColor: Colors.white,
child: // elevation: 3,
AppBar( automaticallyImplyLeading: !widget.isDesktop,
backgroundColor: Colors.white, iconTheme: IconThemeData(
surfaceTintColor: Colors.white, color: layoutColor, // 👈 Set your desired icon color here
// elevation: 3, ),
automaticallyImplyLeading: !widget.isDesktop,
iconTheme: IconThemeData(
color: layoutColor, // 👈 Set your desired icon color here
),
titleSpacing: 0, titleSpacing: 0,
title: title:
!widget.isDesktop !widget.isDesktop
? Text('') ? Text('')
: Padding( : Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.05,
),
child: Row(
children: [
Padding(
padding: const EdgeInsets.all(10),
// padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 10),
child:
selectedOrg?['logo'] != null
? SizedBox(
height: 50,
child: ClipRect(
child: Image.network(
selectedOrg!['logo'],
width: 130, //130
height: 80, //80
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return const CircleAvatar(
radius: 20,
backgroundColor: Colors.redAccent,
child: Icon(Icons.error, size: 10),
);
},
),
),
)
: const CircleAvatar(
radius: 20,
// backgroundColor: Colors.white,
child: Icon(
Icons.add_a_photo,
size: 10,
color: Colors.grey,
),
),
),
SizedBox(width: MediaQuery.of(context).size.width * 0.18),
Container(
width: MediaQuery.of(context).size.width * 0.35,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
buildNavItem(
"Dashboard",
() => handleTabChange(
TabSelection.dashboard,
'/StatusDashboard',
),
layoutColor!,
isSelected: selectedTab == TabSelection.dashboard,
icon: Icons.dashboard,
// icon: Icons.insights_outlined,
),
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
const SizedBox(width: 20),
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
buildNavItem(
"All Trips",
() => handleTabChange(
TabSelection.allTrips,
'/listAllPlan',
),
layoutColor!,
isSelected: selectedTab == TabSelection.allTrips,
icon: Icons.format_list_bulleted_rounded,
// icon: Icons.insights_outlined,
),
if (userDetails["role"] != "Travel Agent")
const SizedBox(width: 20),
if (userData?["role"] == "Travel Agent")
buildNavItem(
"Trips",
() => handleTabChange(
TabSelection.myTrips,
'/listTravelAgentPlan',
),
layoutColor!,
// () => context.go('/listTravelAgentPlan'),
isSelected: selectedTab == TabSelection.myTrips,
icon: Icons.shopping_bag_outlined,
// icon: Icons.request_page_outlined,
),
if (userData?["role"] != "Travel Agent") // for others
buildNavItem(
"My Trips",
() => handleTabChange(
TabSelection.myTrips,
'/listPlan',
),
layoutColor!,
// () => context.go('/listPlan'),
isSelected: selectedTab == TabSelection.myTrips,
icon: Icons.shopping_bag_outlined,
),
const SizedBox(width: 20),
if (userData?["role"] != "Travel Agent")
buildNavItem(
"My Approvals",
() => handleTabChange(
TabSelection.myApprovals,
'/ApprovalList',
),
layoutColor!,
// () => context.go('/ApprovalList'),
isSelected:
selectedTab == TabSelection.myApprovals,
icon: Icons.verified_outlined,
),
],
),
),
Spacer(),
],
),
),
actions: [
Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.05, horizontal: MediaQuery.of(context).size.width * 0.05,
), ),
child: Row( child: Row(
children: [ children: [
// if (userData?["role"] != "User") Padding(
Builder( padding: const EdgeInsets.all(10),
builder:
(context) => PopupMenuButton<String>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: const Offset(0, 50), // 👈 shift it 50 pixels down
onSelected: (String value) {
switch (value) {
case '/OrganizationSettings':
context.go('/OrganizationSettings');
break;
// case '/OrganizationSetup':
// context.go('/OrganizationSetup');
// break;
case '/listUser':
context.go('/listUser');
break;
// case '/group':
// context.go('/group');
// break;
// case '/department':
// context.go('/department');
// break;
// case '/PolicyList':
// context.go('/PolicyList');
// case '/getPerdiem':
// context.go('/getPerdiem');
// case '/templateList':
// context.go('/templateList');
// case '/template':
// context.go('/template');
case '/CreateUserDetails': // padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 10),
context.go( child:
"/CreateUserDetails", selectedOrg?['logo'] != null
extra: { ? SizedBox(
"selectedUser": profileUserDetails, height: 50,
"isEditProfile": true, child: ClipRect(
"isViewMode": false, child: Image.network(
}, selectedOrg!['logo'],
); width: 130, //130
case '/logout': height: 80, //80
context.go('/'); fit: BoxFit.contain,
break; errorBuilder: (context, error, stackTrace) {
} return const CircleAvatar(
}, radius: 20,
child: Icon(
// itemBuilder: (BuildContext context) => Icons.add_a_photo,
// menuItems.map(buildMenuItem).toList(), size: 10,
itemBuilder: (BuildContext context) { color: Colors.grey,
// final isUser = userData?["role"] == "User";
final role = userData?["role"];
List<Map<String, dynamic>> filteredItems;
if (role == "User") {
filteredItems =
menuItems
.where(
(item) =>
item['value'] == '/CreateUserDetails' ||
item['value'] == '/logout',
)
.toList();
} else if (role == "Travel Agent") {
filteredItems =
menuItems
.where((item) => item['value'] == '/logout')
.toList();
} else {
filteredItems = menuItems;
}
// Create a new list starting with role display and divider
return [
PopupMenuItem<String>(
enabled: false, // Not clickable
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userData?["role"] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.black,
), ),
), // backgroundColor: Colors.redAccent,
const Divider(), // 👈 Divider after role // child: Icon(Icons.error, size: 10),
], );
},
), ),
), ),
...filteredItems )
.map(buildMenuItem) : const CircleAvatar(
.toList(), // 👈 then normal items radius: 20,
]; // backgroundColor: Colors.white,
}, child: Icon(
Icons.add_a_photo,
child: MouseRegion( size: 10,
cursor: SystemMouseCursors.click, color: Colors.grey,
child: Row( ),
mainAxisSize: MainAxisSize.min,
children: [
Text(
userData?["name"] ?? "N/A",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black,
),
// style: const TextStyle(
// fontSize: 14,
// fontWeight: FontWeight.w500,
// fontFamily: "Roboto",
// color: Colors.black,
// ),
),
const Icon(
Icons.arrow_drop_down,
size: 20,
color: Colors.black87,
),
],
), ),
),
),
), ),
const SizedBox(width: 8), SizedBox(width: MediaQuery.of(context).size.width * 0.18),
// Spacer(),
Container(
width: MediaQuery.of(context).size.width * 0.35,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
buildNavItem(
"Dashboard",
() => handleTabChange(
TabSelection.dashboard,
'/StatusDashboard',
),
layoutColor!,
isSelected: selectedTab == TabSelection.dashboard,
icon: Icons.dashboard,
// icon: Icons.insights_outlined,
),
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
const SizedBox(width: 20),
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
buildNavItem(
"All Trips",
() => handleTabChange(
TabSelection.allTrips,
'/listAllPlan',
),
layoutColor!,
isSelected: selectedTab == TabSelection.allTrips,
icon: Icons.format_list_bulleted_rounded,
// icon: Icons.insights_outlined,
),
if (userDetails["role"] != "Travel Agent")
const SizedBox(width: 20),
if (userData?["role"] == "Travel Agent")
buildNavItem(
"Trips",
() => handleTabChange(
TabSelection.myTrips,
'/listTravelAgentPlan',
),
layoutColor!,
// () => context.go('/listTravelAgentPlan'),
isSelected: selectedTab == TabSelection.myTrips,
icon: Icons.shopping_bag_outlined,
// icon: Icons.request_page_outlined,
),
if (userData?["role"] != "Travel Agent") // for others
buildNavItem(
"My Trips",
() => handleTabChange(
TabSelection.myTrips,
'/listPlan',
),
layoutColor!,
// () => context.go('/listPlan'),
isSelected: selectedTab == TabSelection.myTrips,
icon: Icons.shopping_bag_outlined,
),
const SizedBox(width: 20),
if (userData?["role"] != "Travel Agent")
buildNavItem(
"My Approvals",
() => handleTabChange(
TabSelection.myApprovals,
'/ApprovalList',
),
layoutColor!,
// () => context.go('/ApprovalList'),
isSelected:
selectedTab == TabSelection.myApprovals,
icon: Icons.verified_outlined,
),
],
),
),
Spacer(),
], ],
), ),
), ),
], actions: [
bottom: PreferredSize( Padding(
preferredSize: Size.fromHeight(1), padding: EdgeInsets.symmetric(
child: Container( horizontal: MediaQuery.of(context).size.width * 0.05,
height: 1, ),
// color: Colors.grey.shade200, // Set the color of the bottom border child: Row(
color: layoutColor, // Set the color of the bottom border children: [
// if (userData?["role"] != "User")
Builder(
builder:
(context) => PopupMenuButton<String>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: const Offset(0, 50), // 👈 shift it 50 pixels down
onSelected: (String value) {
switch (value) {
case '/OrganizationSettings':
context.go('/OrganizationSettings');
break;
// case '/OrganizationSetup':
// context.go('/OrganizationSetup');
// break;
case '/listUser':
context.go('/listUser');
break;
// case '/group':
// context.go('/group');
// break;
// case '/department':
// context.go('/department');
// break;
// case '/PolicyList':
// context.go('/PolicyList');
// case '/getPerdiem':
// context.go('/getPerdiem');
// case '/templateList':
// context.go('/templateList');
// case '/template':
// context.go('/template');
case '/CreateUserDetails':
context.go(
"/CreateUserDetails",
extra: {
"selectedUser": profileUserDetails,
"isEditProfile": true,
"isViewMode": false,
},
);
case '/logout':
context.go('/');
break;
}
},
// itemBuilder: (BuildContext context) =>
// menuItems.map(buildMenuItem).toList(),
itemBuilder: (BuildContext context) {
// final isUser = userData?["role"] == "User";
final role = userData?["role"];
List<Map<String, dynamic>> filteredItems;
if (role == "User") {
filteredItems =
menuItems
.where(
(item) =>
item['value'] == '/CreateUserDetails' ||
item['value'] == '/logout',
)
.toList();
} else if (role == "Travel Agent") {
filteredItems =
menuItems
.where((item) => item['value'] == '/logout')
.toList();
} else {
filteredItems = menuItems;
}
// Create a new list starting with role display and divider
return [
PopupMenuItem<String>(
enabled: false, // Not clickable
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userData?["role"] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
const Divider(), // 👈 Divider after role
],
),
),
...filteredItems
.map(buildMenuItem)
.toList(), // 👈 then normal items
];
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
userData?["name"] ?? "N/A",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black,
),
// style: const TextStyle(
// fontSize: 14,
// fontWeight: FontWeight.w500,
// fontFamily: "Roboto",
// color: Colors.black,
// ),
),
const Icon(
Icons.arrow_drop_down,
size: 20,
color: Colors.black87,
),
],
),
),
),
), ),
), const SizedBox(width: 8),
) ],
); ),
),
],
bottom: PreferredSize(
preferredSize: Size.fromHeight(1),
child: Container(
height: 1,
// color: Colors.grey.shade200, // Set the color of the bottom border
color: layoutColor, // Set the color of the bottom border
),
),
);
} }
@override @override

View File

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

View File

@ -102,6 +102,41 @@ class ApiService {
throw Exception('Failed to load country list'); 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 { Future<List<dynamic>> fetchUsers() async {
String? ordId = await getOrgId(); String? ordId = await getOrgId();

View File

@ -42,7 +42,7 @@ class UserActionsMenu extends StatelessWidget {
children: [ children: [
GestureDetector( GestureDetector(
child: Tooltip( child: Tooltip(
message: 'View User Details', message: 'View Details',
child: Icon(Icons.remove_red_eye, child: Icon(Icons.remove_red_eye,
color: Color(0xFF475569), size: 18) color: Color(0xFF475569), size: 18)
), ),
@ -61,7 +61,7 @@ class UserActionsMenu extends StatelessWidget {
SizedBox(width: 8), SizedBox(width: 8),
GestureDetector( GestureDetector(
child: Tooltip( child: Tooltip(
message: 'Edit User Details', message: 'Edit Details',
child: Image.asset( child: Image.asset(
'assets/images/IconsImg/edit.png', 'assets/images/IconsImg/edit.png',
width: 20, 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 version: 1.0.0+1
environment: environment:
# sdk: ^3.6.1 # sdk: ^3.6.1
sdk: ^3.7.2 sdk: ^3.7.2
# Dependencies specify other packages that your package needs in order to work. # Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions # To automatically upgrade your package dependencies to the latest versions
@ -99,6 +99,8 @@ flutter:
- assets/images/login/VectorG.png - assets/images/login/VectorG.png
- assets/images/login/logoNew.jpg - assets/images/login/logoNew.jpg
- assets/images/login/microsoft.png - assets/images/login/microsoft.png
- assets/images/IconsImg/path.png
- assets/images/IconsImg/alternate.png
- assets/images/IconsImg/delete.png - assets/images/IconsImg/delete.png
- assets/images/IconsImg/edit.png - assets/images/IconsImg/edit.png
- assets/images/IconsImg/planPdf_icon.png - assets/images/IconsImg/planPdf_icon.png
@ -134,4 +136,4 @@ flutter:
# weight: 700 # weight: 700
# #
# For details regarding fonts from package dependencies, # For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package # see https://flutter.dev/to/font-from-packagej