This commit is contained in:
venbaittech 2025-06-05 20:12:05 +05:30
parent cf15771518
commit 335fbacede
29 changed files with 3730 additions and 3179 deletions

View File

@ -287,7 +287,9 @@ class _ApprovalListState extends State<ApprovalList> {
} }
void viewPlanforApprover( void viewPlanforApprover(
String planId, { String planId,
String? approverId,
String? delegaterId, {
bool isViewMode = false, bool isViewMode = false,
bool isApprover = true, bool isApprover = true,
}) async { }) async {
@ -295,14 +297,26 @@ class _ApprovalListState extends State<ApprovalList> {
Map<String, dynamic> planData = await getViewPlan(planId); Map<String, dynamic> planData = await getViewPlan(planId);
print("ViewAAA - $planData"); print("ViewAAA - $planData");
context.go(
'/createPlan', context.replace(
'/approver/plans',
extra: { extra: {
'planData': planData, 'planData': planData,
'approverId': approverId,
'delegaterId': delegaterId,
'isViewMode': isViewMode, 'isViewMode': isViewMode,
'isApprover': isApprover, 'isApprover': isApprover,
}, },
); );
// context.go(
// '/createPlan',
// extra: {
// 'planData': planData,
// 'isViewMode': isViewMode,
// 'isApprover': isApprover,
// },
// );
} catch (e) { } catch (e) {
print("Error fetching plan: $e"); print("Error fetching plan: $e");
} }
@ -854,6 +868,8 @@ class _ApprovalListState extends State<ApprovalList> {
); // Close popup manually ); // Close popup manually
viewPlanforApprover( viewPlanforApprover(
plan.planId, plan.planId,
plan.approverId,
plan.delegaterId,
isViewMode: isViewMode:
true, true,
isApprover: isApprover:
@ -1108,6 +1124,8 @@ class _ApprovalListState extends State<ApprovalList> {
); // Close popup manually ); // Close popup manually
viewPlanforApprover( viewPlanforApprover(
plan.planId, plan.planId,
plan.approverId,
plan.delegaterId,
isViewMode: true, isViewMode: true,
isApprover: true, isApprover: true,
); );

View File

@ -472,6 +472,40 @@ class _LoginWidgetState extends State<LoginWidget> {
/// **Password Field** /// **Password Field**
_buildLabel("Password"), _buildLabel("Password"),
// TextFormField(
// controller: _passwordController,
// style: GoogleFonts.poppins(
// fontWeight: FontWeight.w600,
// fontSize: 11,
// ),
// obscureText: _obscureText,
//
// decoration: _inputDecoration(
// "Enter your password",
// ).copyWith(
// prefixIcon: Icon(Icons.key, size: 16),
// suffixIcon: IconButton(
// icon: Icon(
// _obscureText
// ? Icons.visibility_off
// : Icons.visibility,
// color: Color(0xFF12B24B),
// size: 16,
// ),
//
// onPressed:
// () => setState(
// () => _obscureText = !_obscureText,
// ),
// ),
// ),
//
// validator:
// (value) =>
// value == null || value.isEmpty
// ? 'Required Password'
// : null,
// ),
TextFormField( TextFormField(
controller: _passwordController, controller: _passwordController,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
@ -479,6 +513,7 @@ class _LoginWidgetState extends State<LoginWidget> {
fontSize: 11, fontSize: 11,
), ),
obscureText: _obscureText, obscureText: _obscureText,
textInputAction: TextInputAction.done,
decoration: _inputDecoration( decoration: _inputDecoration(
"Enter your password", "Enter your password",
).copyWith( ).copyWith(
@ -497,13 +532,17 @@ class _LoginWidgetState extends State<LoginWidget> {
), ),
), ),
), ),
onFieldSubmitted: (_) {
if (_formKey.currentState!.validate()) {
_login(context);
}
},
validator: validator:
(value) => (value) =>
value == null || value.isEmpty value == null || value.isEmpty
? 'Required Password' ? 'Required Password'
: null, : null,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
/// **Login Button** /// **Login Button**

View File

@ -19,13 +19,14 @@ class CostCenterData extends StatefulWidget {
final int? costcenterId; // <-- Add this final int? costcenterId; // <-- Add this
final Map<String, dynamic>? costcenterData; final Map<String, dynamic>? costcenterData;
const CostCenterData( const CostCenterData({
{super.key, super.key,
required this.isDesktop, required this.isDesktop,
this.layoutColor, this.layoutColor,
required this.fetchGetCostCenter, required this.fetchGetCostCenter,
this.costcenterId, this.costcenterId,
this.costcenterData}); this.costcenterData,
});
@override @override
CostCenterDataState createState() => CostCenterDataState(); CostCenterDataState createState() => CostCenterDataState();
@ -49,10 +50,7 @@ class CostCenterDataState extends State<CostCenterData> {
int? costcenterDataId; int? costcenterDataId;
late String isActive = "1"; late String isActive = "1";
List<String> dataHeader = [ List<String> dataHeader = ["name", "description"];
"name",
"description",
];
Map<String, dynamic> costcenterDetails() { Map<String, dynamic> costcenterDetails() {
final data = { final data = {
@ -69,7 +67,6 @@ class CostCenterDataState extends State<CostCenterData> {
void initState() { void initState() {
super.initState(); super.initState();
apiData = null; apiData = null;
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
@ -110,7 +107,6 @@ class CostCenterDataState extends State<CostCenterData> {
}); });
} }
void toggleStatus() { void toggleStatus() {
setState(() { setState(() {
isActive = isActive == "1" ? "0" : "1"; isActive = isActive == "1" ? "0" : "1";
@ -171,7 +167,9 @@ class CostCenterDataState extends State<CostCenterData> {
apiUrldata = '$apiUrl/api/updateCostCenter/$costcenterDataId'; apiUrldata = '$apiUrl/api/updateCostCenter/$costcenterDataId';
costcenterData["cost_center_id"] = costcenterDataId.toString(); costcenterData["cost_center_id"] = costcenterDataId.toString();
costcenterData["updated_by"] = userId; costcenterData["updated_by"] = userId;
(costcenterData.containsKey("created_by")) ? costcenterData.remove("created_by") : '' ; (costcenterData.containsKey("created_by"))
? costcenterData.remove("created_by")
: '';
} else { } else {
print("for add CostCenter id - null"); print("for add CostCenter id - null");
apiUrldata = '$apiUrl/api/createCostCenter'; apiUrldata = '$apiUrl/api/createCostCenter';
@ -193,11 +191,11 @@ class CostCenterDataState extends State<CostCenterData> {
}; };
final body = jsonEncode(costcenterData); final body = jsonEncode(costcenterData);
final response = costcenterDataId != null final response =
costcenterDataId != null
? 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) { switch (response.statusCode) {
case 200: case 200:
print("Update - Response: ${response.body}"); print("Update - Response: ${response.body}");
@ -217,7 +215,6 @@ class CostCenterDataState extends State<CostCenterData> {
print("Failed to submit costcenter. Status: ${response.statusCode}"); print("Failed to submit costcenter. 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");
} }
@ -225,7 +222,6 @@ class CostCenterDataState extends State<CostCenterData> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30), contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30),
@ -238,27 +234,27 @@ class CostCenterDataState extends State<CostCenterData> {
Row( Row(
children: [ children: [
Text( Text(
(costcenterDataId != null) ? 'Edit CostCenter' : 'Create CostCenter', (costcenterDataId != null)
? 'Edit CostCenter'
: 'Create CostCenter',
style: GoogleFonts.poppins(fontSize: 15, color: Colors.black), style: GoogleFonts.poppins(fontSize: 15, color: Colors.black),
), ),
const Spacer(), const Spacer(),
], ],
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Divider( Divider(thickness: 0.2, color: Colors.blueGrey.shade100),
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
const SizedBox(height: 5), const SizedBox(height: 5),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Name", "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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -278,7 +274,8 @@ class CostCenterDataState extends State<CostCenterData> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)), ),
),
), ),
if (errorMessages["name"] != null) ...[ if (errorMessages["name"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -289,18 +286,17 @@ class CostCenterDataState extends State<CostCenterData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Description", "Description *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -334,9 +330,7 @@ class CostCenterDataState extends State<CostCenterData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
if (costcenterDataId != null) if (costcenterDataId != null)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -346,7 +340,8 @@ class CostCenterDataState extends State<CostCenterData> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
Tooltip( Tooltip(
message: message:
@ -358,17 +353,14 @@ class CostCenterDataState extends State<CostCenterData> {
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
color: isActive == "1" ? Colors.green : Colors.red, color: isActive == "1" ? Colors.green : Colors.grey,
),
), ),
), ),
), ),
)
], ],
), ),
if (costcenterDataId != null) if (costcenterDataId != null) SizedBox(height: 15),
SizedBox(
height: 15,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -405,13 +397,17 @@ class CostCenterDataState extends State<CostCenterData> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
child: Text('Save', child: Text(
'Save',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)), fontSize: 11,
color: Colors.white,
),
),
), ),
), ),
], ],
) ),
// : SizedBox.shrink(), // : SizedBox.shrink(),
], ],
), ),

View File

@ -99,7 +99,7 @@ class CostCenterListState extends State<CostCenterList> {
} }
Future<List<dynamic>> fetchGetCostCenter() async { Future<List<dynamic>> fetchGetCostCenter() async {
final String apiUrlData = '$apiUrl/api/getCostCenterMaster'; final String apiUrlData = '$apiUrl/api/getCostCenterMaster?for=table_view';
final String? token = await getToken(); final String? token = await getToken();
@ -566,7 +566,7 @@ class CostCenterListState extends State<CostCenterList> {
color: color:
tableObject['is_active'] == "1" tableObject['is_active'] == "1"
? Colors.green ? Colors.green
: Colors.red, : Colors.grey,
), ),
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,

View File

@ -19,13 +19,14 @@ class DepartmentData extends StatefulWidget {
final int? departmentId; // <-- Add this final int? departmentId; // <-- Add this
final Map<String, dynamic>? departmentData; final Map<String, dynamic>? departmentData;
const DepartmentData( const DepartmentData({
{super.key, super.key,
required this.isDesktop, required this.isDesktop,
this.layoutColor, this.layoutColor,
required this.fetchGetDepartment, required this.fetchGetDepartment,
this.departmentId, this.departmentId,
this.departmentData}); this.departmentData,
});
@override @override
DepartmentDataState createState() => DepartmentDataState(); DepartmentDataState createState() => DepartmentDataState();
@ -49,10 +50,7 @@ class DepartmentDataState extends State<DepartmentData> {
int? departmentDataId; int? departmentDataId;
late String isActive = "1"; late String isActive = "1";
List<String> dataHeader = [ List<String> dataHeader = ["name", "description"];
"name",
"description",
];
Map<String, dynamic> departmentDetails() { Map<String, dynamic> departmentDetails() {
final data = { final data = {
@ -69,7 +67,6 @@ class DepartmentDataState extends State<DepartmentData> {
void initState() { void initState() {
super.initState(); super.initState();
apiData = null; apiData = null;
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
@ -110,7 +107,6 @@ class DepartmentDataState extends State<DepartmentData> {
}); });
} }
void toggleStatus() { void toggleStatus() {
setState(() { setState(() {
isActive = isActive == "1" ? "0" : "1"; isActive = isActive == "1" ? "0" : "1";
@ -171,7 +167,9 @@ class DepartmentDataState extends State<DepartmentData> {
apiUrldata = '$apiUrl/api/updateDepartment/$departmentDataId'; apiUrldata = '$apiUrl/api/updateDepartment/$departmentDataId';
departmentData["department_id"] = departmentDataId.toString(); departmentData["department_id"] = departmentDataId.toString();
departmentData["updated_by"] = userId; departmentData["updated_by"] = userId;
(departmentData.containsKey("created_by")) ? departmentData.remove("created_by") : '' ; (departmentData.containsKey("created_by"))
? departmentData.remove("created_by")
: '';
} else { } else {
print("for add Department id - null"); print("for add Department id - null");
apiUrldata = '$apiUrl/api/createDepartment'; apiUrldata = '$apiUrl/api/createDepartment';
@ -193,11 +191,11 @@ class DepartmentDataState extends State<DepartmentData> {
}; };
final body = jsonEncode(departmentData); final body = jsonEncode(departmentData);
final response = departmentDataId != null final response =
departmentDataId != null
? 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) { switch (response.statusCode) {
case 200: case 200:
print("Update - Response: ${response.body}"); print("Update - Response: ${response.body}");
@ -217,7 +215,6 @@ class DepartmentDataState extends State<DepartmentData> {
print("Failed to submit department. Status: ${response.statusCode}"); print("Failed to submit department. 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");
} }
@ -225,7 +222,6 @@ class DepartmentDataState extends State<DepartmentData> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30), contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30),
@ -238,27 +234,27 @@ class DepartmentDataState extends State<DepartmentData> {
Row( Row(
children: [ children: [
Text( Text(
(departmentDataId != null) ? 'Edit Department' : 'Create Department', (departmentDataId != null)
? 'Edit Department'
: 'Create Department',
style: GoogleFonts.poppins(fontSize: 15, color: Colors.black), style: GoogleFonts.poppins(fontSize: 15, color: Colors.black),
), ),
const Spacer(), const Spacer(),
], ],
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Divider( Divider(thickness: 0.2, color: Colors.blueGrey.shade100),
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
const SizedBox(height: 5), const SizedBox(height: 5),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Name", "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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -278,7 +274,8 @@ class DepartmentDataState extends State<DepartmentData> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)), ),
),
), ),
if (errorMessages["name"] != null) ...[ if (errorMessages["name"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -289,18 +286,17 @@ class DepartmentDataState extends State<DepartmentData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Description", "Description *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -334,9 +330,7 @@ class DepartmentDataState extends State<DepartmentData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
if (departmentDataId != null) if (departmentDataId != null)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -346,7 +340,8 @@ class DepartmentDataState extends State<DepartmentData> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
Tooltip( Tooltip(
message: message:
@ -362,13 +357,10 @@ class DepartmentDataState extends State<DepartmentData> {
), ),
), ),
), ),
) ),
], ],
), ),
if (departmentDataId != null) if (departmentDataId != null) SizedBox(height: 15),
SizedBox(
height: 15,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -405,13 +397,17 @@ class DepartmentDataState extends State<DepartmentData> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
child: Text('Save', child: Text(
'Save',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)), fontSize: 11,
color: Colors.white,
),
),
), ),
), ),
], ],
) ),
// : SizedBox.shrink(), // : SizedBox.shrink(),
], ],
), ),

View File

@ -99,7 +99,7 @@ class DepartmentListState extends State<DepartmentList> {
} }
Future<List<dynamic>> fetchGetDepartment() async { Future<List<dynamic>> fetchGetDepartment() async {
final String apiUrlData = '$apiUrl/api/getDepartmentList'; final String apiUrlData = '$apiUrl/api/getDepartmentList?for=table_view';
final String? token = await getToken(); final String? token = await getToken();
@ -565,7 +565,7 @@ class DepartmentListState extends State<DepartmentList> {
color: color:
tableObject['is_active'] == "1" tableObject['is_active'] == "1"
? Colors.green ? Colors.green
: Colors.red, : Colors.grey,
), ),
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,

View File

@ -100,11 +100,13 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
} }
} else { } else {
throw Exception( throw Exception(
"Unexpected response format: Expected a List but got ${responseBody.runtimeType}"); "Unexpected response format: Expected a List but got ${responseBody.runtimeType}",
);
} }
} else { } else {
throw Exception( throw Exception(
'Failed to load users. Status Code: ${response.statusCode}'); 'Failed to load users. Status Code: ${response.statusCode}',
);
} }
} catch (e) { } catch (e) {
print("Error fetching users: $e"); print("Error fetching users: $e");
@ -138,7 +140,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
List<dynamic> travellerList = responseBody['data']; List<dynamic> travellerList = responseBody['data'];
setState(() { setState(() {
_traveller = travellerList _traveller =
travellerList
.map((user) => SearchTraveler.fromJson(user)) .map((user) => SearchTraveler.fromJson(user))
.toList(); .toList();
_filteredTraveller = List.from(_traveller); _filteredTraveller = List.from(_traveller);
@ -147,47 +150,53 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
print("Users fetched: ${_users.length}"); print("Users fetched: ${_users.length}");
for (var travvelr in _traveller) { for (var travvelr in _traveller) {
print( print(
"${travvelr.firstName} ${travvelr.lastName} ${travvelr.mobileNo}"); "${travvelr.firstName} ${travvelr.lastName} ${travvelr.mobileNo}",
);
} }
} else { } else {
throw Exception( throw Exception(
"Unexpected response format: Expected a List but got ${responseBody.runtimeType}"); "Unexpected response format: Expected a List but got ${responseBody.runtimeType}",
);
} }
} else { } else {
throw Exception( throw Exception(
'Failed to load users. Status Code: ${response.statusCode}'); 'Failed to load users. Status Code: ${response.statusCode}',
);
} }
} catch (e) { } catch (e) {
print("Error fetching traveller: $e"); print("Error fetching traveller: $e");
} }
} }
void _filterUsers1(String query) { // void _filterUsers1(String query) {
print("Filtering users..."); // print("Filtering users...");
setState(() { // setState(() {
if (query.isEmpty) { // if (query.isEmpty) {
_filteredUsers = List.from(_users); // _filteredUsers = List.from(_users);
} else { // } else {
_filteredUsers = _users.where((user) { // _filteredUsers =
List<String> searchFields = [ // _users.where((user) {
"${user.firstName} ${user.lastName}".toLowerCase(), // List<String> searchFields = [
user.email.toLowerCase() ?? "", // "${user.firstName} ${user.lastName}".toLowerCase(),
user.userId.toLowerCase() ?? "", // user.email.toLowerCase() ?? "",
user.mobileNo ?? "", // user.empCode?.toLowerCase() ?? "",
user.alternateMobileNo ?? "" // user.userId.toLowerCase() ?? "",
]; // user.mobileNo ?? "",
// user.alternateMobileNo ?? "",
return searchFields // ];
.any((field) => field.contains(query.toLowerCase())); //
}).toList(); // return searchFields.any(
} // (field) => field.contains(query.toLowerCase()),
}); // );
// }).toList();
print("Filtered Users:"); // }
for (var user in _filteredUsers) { // });
print("${user.firstName} ${user.lastName}"); //
} // print("Filtered Users:");
} // for (var user in _filteredUsers) {
// print("${user.firstName} ${user.lastName}");
// }
// }
void _filterUsers(String query) { void _filterUsers(String query) {
print("Filtering _filterUsersTravellers..."); print("Filtering _filterUsersTravellers...");
@ -200,7 +209,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
]; ];
} else { } else {
_filteredList = [ _filteredList = [
..._users.where((user) { ..._users
.where((user) {
print("usersLLL : ${user}"); print("usersLLL : ${user}");
List<String> searchFields = [ List<String> searchFields = [
@ -208,11 +218,14 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
user.email.toLowerCase() ?? "", user.email.toLowerCase() ?? "",
user.userId.toLowerCase() ?? "", user.userId.toLowerCase() ?? "",
user.mobileNo ?? "", user.mobileNo ?? "",
user.alternateMobileNo ?? "" user.alternateMobileNo ?? "",
user.empCode?.toLowerCase() ?? "",
]; ];
return searchFields return searchFields.any(
.any((field) => field.contains(query.toLowerCase())); (field) => field.contains(query.toLowerCase()),
}).map((user) => {"type": "user", "data": user}), );
})
.map((user) => {"type": "user", "data": user}),
]; ];
} }
}); });
@ -221,7 +234,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
for (var item in _filteredList) { for (var item in _filteredList) {
var user = item["data"]; var user = item["data"];
print( print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}",
);
} }
} }
@ -236,16 +250,19 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
]; ];
} else { } else {
_filteredList = [ _filteredList = [
..._traveller.where((traveller) { ..._traveller
.where((traveller) {
List<String> searchFields = [ List<String> searchFields = [
"${traveller.firstName} ${traveller.lastName}".toLowerCase(), "${traveller.firstName} ${traveller.lastName}".toLowerCase(),
traveller.email.toLowerCase() ?? "", traveller.email.toLowerCase() ?? "",
traveller.travellerId.toLowerCase() ?? "", traveller.travellerId.toLowerCase() ?? "",
traveller.mobileNo ?? "", traveller.mobileNo ?? "",
]; ];
return searchFields return searchFields.any(
.any((field) => field.contains(query.toLowerCase())); (field) => field.contains(query.toLowerCase()),
}).map((traveller) => {"type": "traveller", "data": traveller}), );
})
.map((traveller) => {"type": "traveller", "data": traveller}),
]; ];
} }
}); });
@ -254,7 +271,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
for (var item in _filteredList) { for (var item in _filteredList) {
var user = item["data"]; var user = item["data"];
print( print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}",
);
} }
} }
@ -266,32 +284,39 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
if (query.isEmpty) { if (query.isEmpty) {
_filteredList = [ _filteredList = [
..._users.map((user) => {"type": "user", "data": user}), ..._users.map((user) => {"type": "user", "data": user}),
..._traveller ..._traveller.map(
.map((traveller) => {"type": "traveller", "data": traveller}), (traveller) => {"type": "traveller", "data": traveller},
),
]; ];
} else { } else {
_filteredList = [ _filteredList = [
..._users.where((user) { ..._users
.where((user) {
List<String> searchFields = [ List<String> searchFields = [
"${user.firstName} ${user.lastName}".toLowerCase(), "${user.firstName} ${user.lastName}".toLowerCase(),
user.email.toLowerCase() ?? "", user.email.toLowerCase() ?? "",
user.userId.toLowerCase() ?? "", user.userId.toLowerCase() ?? "",
user.mobileNo ?? "", user.mobileNo ?? "",
user.alternateMobileNo ?? "" user.alternateMobileNo ?? "",
]; ];
return searchFields return searchFields.any(
.any((field) => field.contains(query.toLowerCase())); (field) => field.contains(query.toLowerCase()),
}).map((user) => {"type": "user", "data": user}), );
..._traveller.where((traveller) { })
.map((user) => {"type": "user", "data": user}),
..._traveller
.where((traveller) {
List<String> searchFields = [ List<String> searchFields = [
"${traveller.firstName} ${traveller.lastName}".toLowerCase(), "${traveller.firstName} ${traveller.lastName}".toLowerCase(),
traveller.email.toLowerCase() ?? "", traveller.email.toLowerCase() ?? "",
traveller.travellerId.toLowerCase() ?? "", traveller.travellerId.toLowerCase() ?? "",
traveller.mobileNo ?? "", traveller.mobileNo ?? "",
]; ];
return searchFields return searchFields.any(
.any((field) => field.contains(query.toLowerCase())); (field) => field.contains(query.toLowerCase()),
}).map((traveller) => {"type": "traveller", "data": traveller}), );
})
.map((traveller) => {"type": "traveller", "data": traveller}),
]; ];
} }
}); });
@ -300,7 +325,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
for (var item in _filteredList) { for (var item in _filteredList) {
var user = item["data"]; var user = item["data"];
print( print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}",
);
} }
} }
@ -324,10 +350,14 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
MainAxisSize.min, // Ensures content doesn't expand unnecessarily MainAxisSize.min, // Ensures content doesn't expand unnecessarily
children: [ children: [
widget.title == "Others" widget.title == "Others"
? Text("Please Select Other User", ? Text(
style: GoogleFonts.poppins(fontSize: 14)) "Please Select Other User",
: Text("Please Select Other Employee", style: GoogleFonts.poppins(fontSize: 14),
style: GoogleFonts.poppins(fontSize: 14)), )
: Text(
"Please Select Other Employee",
style: GoogleFonts.poppins(fontSize: 14),
),
SizedBox(height: 10), SizedBox(height: 10),
// Search Field // Search Field
@ -344,11 +374,14 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
style: GoogleFonts.poppins(fontSize: 12), style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search for a user", hintText: "Search for a user",
hintStyle: hintStyle: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 14, color: Colors.grey), fontSize: 14,
color: Colors.grey,
),
prefixIcon: Icon(Icons.search), prefixIcon: Icon(Icons.search),
border: border: OutlineInputBorder(
OutlineInputBorder(borderRadius: BorderRadius.circular(8)), borderRadius: BorderRadius.circular(8),
),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade200, width: 1), borderSide: BorderSide(color: Colors.grey.shade200, width: 1),
// borderSide: BorderSide(color: Color(0xFFF5F5F5), width: 2), // borderSide: BorderSide(color: Color(0xFFF5F5F5), width: 2),
@ -366,9 +399,13 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text("or create a new traveler", Text(
"or create a new traveler",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, color: Color(0xFF575A74))), fontSize: 14,
color: Color(0xFF575A74),
),
),
TextButton( TextButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
@ -376,9 +413,13 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
_searchController.clear(); _searchController.clear();
}); });
}, },
child: Text("Create", child: Text(
"Create",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, color: widget.layoutColorForUser)), fontSize: 14,
color: widget.layoutColorForUser,
),
),
), ),
], ],
), ),
@ -391,12 +432,15 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
? SizedBox( ? SizedBox(
height: 300, // Limit height to avoid overflow height: 300, // Limit height to avoid overflow
// child: _filteredUsers.isEmpty // child: _filteredUsers.isEmpty
child: _filteredList.isEmpty child:
_filteredList.isEmpty
? Center( ? Center(
child: Text( child: Text(
"No users found", "No users found",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, color: Colors.grey), fontSize: 14,
color: Colors.grey,
),
), ),
) )
: ListView.builder( : ListView.builder(
@ -411,7 +455,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
item["type"]; // "user" or "traveller" item["type"]; // "user" or "traveller"
if (user is Map<String, dynamic>) { if (user is Map<String, dynamic>) {
print( print(
"userLsirer - ${jsonEncode(user)}"); // pretty JSON-like string "userLsirer - ${jsonEncode(user)}",
); // pretty JSON-like string
} else { } else {
print("userLsirer - $user"); // fallback print("userLsirer - $user"); // fallback
} }
@ -420,32 +465,37 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}", "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}",
style: GoogleFonts.poppins(fontSize: 11), style: GoogleFonts.poppins(fontSize: 11),
), ),
subtitle: userType == "user" subtitle:
userType == "user"
? Text( ? Text(
"Employee ID: ${user.empCode ?? ""} ", "Employee ID: ${user.empCode ?? ""} ",
// "Employee ID: ${userType == "user" ? user.userId : user.travellerId}", // "Employee ID: ${userType == "user" ? user.userId : user.travellerId}",
style: style: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 10), fontSize: 10,
),
) )
: Text( : Text(
"Mobile : ${user.mobileNo ?? ""} ", "Mobile : ${user.mobileNo ?? ""} ",
// "Employee ID: ${userType == "user" ? user.userId : user.travellerId}", // "Employee ID: ${userType == "user" ? user.userId : user.travellerId}",
style: style: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 10), fontSize: 10,
),
), ),
onTap: () { onTap: () {
String selectedUser = String selectedUser =
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"; "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
setState(() { setState(() {
_searchController.text = selectedUser; _searchController.text = selectedUser;
userIdSelected = userType == "user" userIdSelected =
userType == "user"
? user.userId ? user.userId
: user.travellerId; : user.travellerId;
isTraveller = userType == "traveller"; isTraveller = userType == "traveller";
}); });
print( print(
"Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId}," "Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
" isTraveller: $userIdSelected"); " isTraveller: $userIdSelected",
);
}, },
); );
}, },
@ -462,10 +512,16 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: TravelerForm( child: TravelerForm(
formKey: _formKey, formKey: _formKey,
onSubmit: (String fullName, String travellerId, onSubmit: (
bool isTraveller) { String fullName,
widget.onSubmit(fullName, travellerId, String travellerId,
isTraveller); // Pass the data up bool isTraveller,
) {
widget.onSubmit(
fullName,
travellerId,
isTraveller,
); // Pass the data up
}, },
firstNameController: TextEditingController(), firstNameController: TextEditingController(),
lastNameController: TextEditingController(), lastNameController: TextEditingController(),
@ -487,7 +543,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide( side: BorderSide(
color: widget.layoutColorForUser, width: 2), color: widget.layoutColorForUser,
width: 2,
),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
@ -508,15 +566,21 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide( side: BorderSide(
color: widget.layoutColorForUser, width: 2), color: widget.layoutColorForUser,
width: 2,
),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: () { onPressed: () {
print( print(
"Submitting: ${_searchController.text}, ID: $userIdSelected"); "Submitting: ${_searchController.text}, ID: $userIdSelected",
);
widget.onSubmit( widget.onSubmit(
_searchController.text, userIdSelected, isTraveller); _searchController.text,
userIdSelected,
isTraveller,
);
Navigator.pop(context); Navigator.pop(context);
}, },
child: Text( child: Text(
@ -542,14 +606,15 @@ class TravelerForm extends StatefulWidget {
final GlobalKey<FormState> formKey; final GlobalKey<FormState> formKey;
final void Function(String, String, bool) onSubmit; final void Function(String, String, bool) onSubmit;
TravelerForm( TravelerForm({
{required this.formKey, required this.formKey,
required this.orgId, required this.orgId,
required this.firstNameController, required this.firstNameController,
required this.lastNameController, required this.lastNameController,
required this.emailController, required this.emailController,
required this.mobileController, required this.mobileController,
required this.onSubmit}); required this.onSubmit,
});
@override @override
_TravelerFormState createState() => _TravelerFormState(); _TravelerFormState createState() => _TravelerFormState();
@ -582,8 +647,9 @@ class _TravelerFormState extends State<TravelerForm> {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Email is required'; return 'Email is required';
} }
if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$') if (!RegExp(
.hasMatch(value)) { r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
).hasMatch(value)) {
return 'Enter a valid email address'; return 'Enter a valid email address';
} }
return null; return null;
@ -642,7 +708,8 @@ class _TravelerFormState extends State<TravelerForm> {
String lastName = travellerData["last_name"]; String lastName = travellerData["last_name"];
print( print(
"Traveller Added: ID: $travellerId, Name: $firstName $lastName"); "Traveller Added: ID: $travellerId, Name: $firstName $lastName",
);
// // Pass data to callback // // Pass data to callback
// widget.onSubmit("$firstName $lastName", travellerId, true); // widget.onSubmit("$firstName $lastName", travellerId, true);
@ -658,21 +725,22 @@ class _TravelerFormState extends State<TravelerForm> {
SnackBar( SnackBar(
content: Text( content: Text(
"Traveller added successfully!", "Traveller added successfully!",
style: style: GoogleFonts.poppins(
GoogleFonts.poppins(color: Colors.white), // Set text color color: Colors.white,
), // Set text color
), ),
backgroundColor: Colors.green, backgroundColor: Colors.green,
), ),
); );
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text("Error: ${response.body}")), context,
); ).showSnackBar(SnackBar(content: Text("Error: ${response.body}")));
} }
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text("Failed to connect to server.")), context,
); ).showSnackBar(SnackBar(content: Text("Failed to connect to server.")));
} }
} }
@ -697,8 +765,10 @@ class _TravelerFormState extends State<TravelerForm> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text("Create Traveler", Text(
style: GoogleFonts.poppins(color: Colors.black54)), "Create Traveler",
style: GoogleFonts.poppins(color: Colors.black54),
),
SizedBox(height: 7), SizedBox(height: 7),
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
@ -714,13 +784,17 @@ class _TravelerFormState extends State<TravelerForm> {
onPressed: () { onPressed: () {
widget.formKey.currentState?.reset(); widget.formKey.currentState?.reset();
}, },
child: Text("Clear", child: Text(
style: GoogleFonts.poppins(color: Colors.grey)), "Clear",
style: GoogleFonts.poppins(color: Colors.grey),
),
), ),
TextButton( TextButton(
onPressed: () => _onSubmit(context), onPressed: () => _onSubmit(context),
child: Text("Add", child: Text(
style: GoogleFonts.poppins(color: Color(0xFF114D8B))), "Add",
style: GoogleFonts.poppins(color: Color(0xFF114D8B)),
),
), ),
], ],
), ),

View File

@ -21,13 +21,14 @@ class ForexData extends StatefulWidget {
final int? forexId; // <-- Add this final int? forexId; // <-- Add this
final Map<String, dynamic>? forexData; final Map<String, dynamic>? forexData;
const ForexData( const ForexData({
{super.key, super.key,
required this.isDesktop, required this.isDesktop,
this.layoutColor, this.layoutColor,
required this.fetchGetForex, required this.fetchGetForex,
this.forexId, this.forexId,
this.forexData}); this.forexData,
});
@override @override
ForexDataState createState() => ForexDataState(); ForexDataState createState() => ForexDataState();
@ -61,7 +62,7 @@ class ForexDataState extends State<ForexData> {
"currency", "currency",
"perdiemAmount", "perdiemAmount",
"cash", "cash",
"card" "card",
]; ];
Map<String, dynamic> forex_Detials() { Map<String, dynamic> forex_Detials() {
@ -197,7 +198,7 @@ class ForexDataState extends State<ForexData> {
"currency", "currency",
"perdiemAmount", "perdiemAmount",
"cash_percentage", "cash_percentage",
"card_percentage" "card_percentage",
]; ];
// Check validation for each field // Check validation for each field
@ -273,7 +274,8 @@ class ForexDataState extends State<ForexData> {
}; };
final body = jsonEncode(forexData); final body = jsonEncode(forexData);
final response = forexDataId != null final response =
forexDataId != null
? 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);
@ -326,7 +328,7 @@ class ForexDataState extends State<ForexData> {
// 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
@ -355,21 +357,19 @@ class ForexDataState extends State<ForexData> {
], ],
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Divider( Divider(thickness: 0.2, color: Colors.blueGrey.shade100),
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
const SizedBox(height: 5), const SizedBox(height: 5),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Country", "Country *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -381,13 +381,14 @@ class ForexDataState extends State<ForexData> {
selectedItem: countryMap[selectedCountry], selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality showSearchBox: true, // Enables search functionality
menuProps: const MenuProps( menuProps: const MenuProps(backgroundColor: Colors.white),
backgroundColor: Colors.white,
),
constraints: BoxConstraints(maxHeight: 250), constraints: BoxConstraints(maxHeight: 250),
itemBuilder: (context, item, isSelected) => Padding( itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0), horizontal: 8.0,
vertical: 6.0,
),
child: Text( child: Text(
item, item,
style: GoogleFonts.poppins(fontSize: 11.5), style: GoogleFonts.poppins(fontSize: 11.5),
@ -405,12 +406,11 @@ class ForexDataState extends State<ForexData> {
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:
dropdownBuilder: (context, selectedItem) => Align( (context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
@ -421,7 +421,8 @@ class ForexDataState extends State<ForexData> {
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
// Find the country_code based on selected country_name // 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;
selectedCountryName = newValue; selectedCountryName = newValue;
@ -444,11 +445,12 @@ class ForexDataState extends State<ForexData> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Currency", "Currency *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -470,7 +472,8 @@ class ForexDataState extends State<ForexData> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)), ),
),
), ),
if (errorMessages["currency"] != null) ...[ if (errorMessages["currency"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -481,11 +484,9 @@ class ForexDataState extends State<ForexData> {
], ],
], ],
), ),
// if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,),
SizedBox( // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,),
height: 10, SizedBox(height: 10),
),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -493,18 +494,20 @@ class ForexDataState extends State<ForexData> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Cash (%)", "Cash (%) *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
width: widget.isDesktop width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.09 ? MediaQuery.of(context).size.width * 0.09
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
@ -518,13 +521,16 @@ class ForexDataState extends State<ForexData> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Cash", labelText: "Cash",
labelStyle: labelStyle: TextStyle(
TextStyle(fontSize: 11, color: Colors.grey), fontSize: 11,
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),
), ),
)), ),
),
), ),
if (errorMessages["cash_percentage"] != null) ...[ if (errorMessages["cash_percentage"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -540,18 +546,20 @@ class ForexDataState extends State<ForexData> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Card (%)", "Card (%) *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
width: widget.isDesktop width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.09 ? MediaQuery.of(context).size.width * 0.09
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
@ -565,13 +573,16 @@ class ForexDataState extends State<ForexData> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Card", labelText: "Card",
labelStyle: labelStyle: TextStyle(
TextStyle(fontSize: 11, color: Colors.grey), fontSize: 11,
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),
), ),
)), ),
),
), ),
if (errorMessages["card_percentage"] != null) ...[ if (errorMessages["card_percentage"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -581,21 +592,20 @@ class ForexDataState extends State<ForexData> {
), ),
], ],
], ],
) ),
], ],
), ),
SizedBox( SizedBox(height: 10),
height: 10,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Perdiem Amount", "Perdiem Amount *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -614,7 +624,8 @@ class ForexDataState extends State<ForexData> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)), ),
),
), ),
if (errorMessages["perdiemAmount"] != null) ...[ if (errorMessages["perdiemAmount"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -625,9 +636,7 @@ class ForexDataState extends State<ForexData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
if (forexDataId != null) if (forexDataId != null)
Row( Row(
@ -638,7 +647,8 @@ class ForexDataState extends State<ForexData> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
Tooltip( Tooltip(
message: message:
@ -654,13 +664,10 @@ class ForexDataState extends State<ForexData> {
), ),
), ),
), ),
) ),
], ],
), ),
if (forexDataId != null) if (forexDataId != null) SizedBox(height: 15),
SizedBox(
height: 15,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -697,13 +704,17 @@ class ForexDataState extends State<ForexData> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
child: Text('Save', child: Text(
'Save',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)), fontSize: 11,
color: Colors.white,
),
),
), ),
), ),
], ],
) ),
// : SizedBox.shrink(), // : SizedBox.shrink(),
], ],
), ),

View File

@ -102,7 +102,7 @@ class ForexDataListState extends State<ForexDataList> {
Future<List<dynamic>> fetchGetForex() async { Future<List<dynamic>> fetchGetForex() async {
orgId = await getOrgId(); orgId = await getOrgId();
final String apiUrlData = '$apiUrl/api/getForexPerdiemList'; final String apiUrlData = '$apiUrl/api/getForexPerdiemList?for=table_view';
final String? token = await getToken(); final String? token = await getToken();

View File

@ -21,13 +21,14 @@ class GroupData extends StatefulWidget {
final int? groupId; // <-- Add this final int? groupId; // <-- Add this
final Map<String, dynamic>? groupData; final Map<String, dynamic>? groupData;
const GroupData( const GroupData({
{super.key, super.key,
required this.isDesktop, required this.isDesktop,
this.layoutColor, this.layoutColor,
required this.fetchGetGroup, required this.fetchGetGroup,
this.groupId, this.groupId,
this.groupData}); this.groupData,
});
@override @override
GroupDataState createState() => GroupDataState(); GroupDataState createState() => GroupDataState();
@ -46,7 +47,6 @@ class GroupDataState extends State<GroupData> {
final Map<String, TextEditingController> controllers = {}; final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
List<dynamic> domesticList = []; List<dynamic> domesticList = [];
List<dynamic> internationalList = []; List<dynamic> internationalList = [];
@ -166,7 +166,6 @@ class GroupDataState extends State<GroupData> {
}); });
} }
bool validateData() { bool validateData() {
errorMessages.clear(); errorMessages.clear();
@ -179,10 +178,7 @@ class GroupDataState extends State<GroupData> {
"international_policy_name": selectedInternationalPolicyName, "international_policy_name": selectedInternationalPolicyName,
}; };
final requiredFields = [ final 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) {
@ -240,7 +236,8 @@ class GroupDataState extends State<GroupData> {
}; };
final body = jsonEncode(groupData); final body = jsonEncode(groupData);
final response = groupDataId != null final response =
groupDataId != null
? 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);
@ -288,7 +285,7 @@ class GroupDataState extends State<GroupData> {
// Map id to names // Map id to names
DomesticMap = { DomesticMap = {
for (var object in domesticList) for (var object in domesticList)
object['policy_id'] as String: object['name'] as String object['policy_id'] as String: object['name'] as String,
}; };
// print("domestic -- map--$DomesticMap"); // print("domestic -- map--$DomesticMap");
@ -302,7 +299,7 @@ class GroupDataState extends State<GroupData> {
InternationalMap = { InternationalMap = {
for (var item in internationalList) for (var item in internationalList)
item['policy_id'] as String: item['name'] as String item['policy_id'] as String: item['name'] as String,
}; };
// Extract only id for processing // Extract only id for processing
@ -330,20 +327,18 @@ class GroupDataState extends State<GroupData> {
], ],
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Divider( Divider(thickness: 0.2, color: Colors.blueGrey.shade100),
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
const SizedBox(height: 5), const SizedBox(height: 5),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Name", "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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -362,7 +357,8 @@ class GroupDataState extends State<GroupData> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)), ),
),
), ),
if (errorMessages["name"] != null) ...[ if (errorMessages["name"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -382,7 +378,8 @@ class GroupDataState extends State<GroupData> {
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -391,16 +388,18 @@ class GroupDataState extends State<GroupData> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownSearch<String>( child: DropdownSearch<String>(
selectedItem: InternationalMap[selectedInternationalPolicyID], selectedItem:
InternationalMap[selectedInternationalPolicyID],
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality showSearchBox: true, // Enables search functionality
menuProps: const MenuProps( menuProps: const MenuProps(backgroundColor: Colors.white),
backgroundColor: Colors.white,
),
constraints: BoxConstraints(maxHeight: 250), constraints: BoxConstraints(maxHeight: 250),
itemBuilder: (context, item, isSelected) => Padding( itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0), horizontal: 8.0,
vertical: 6.0,
),
child: Text( child: Text(
item, item,
style: GoogleFonts.poppins(fontSize: 11.5), style: GoogleFonts.poppins(fontSize: 11.5),
@ -418,12 +417,11 @@ class GroupDataState extends State<GroupData> {
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:
dropdownBuilder: (context, selectedItem) => Align( (context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
@ -434,7 +432,8 @@ class GroupDataState extends State<GroupData> {
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
// Find the country_code based on selected country_name // Find the country_code based on selected country_name
selectedInternationalPolicyID = InternationalMap.entries selectedInternationalPolicyID =
InternationalMap.entries
.firstWhere((entry) => entry.value == newValue) .firstWhere((entry) => entry.value == newValue)
.key; .key;
selectedInternationalPolicyName = newValue; selectedInternationalPolicyName = newValue;
@ -454,7 +453,8 @@ class GroupDataState extends State<GroupData> {
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -466,13 +466,14 @@ class GroupDataState extends State<GroupData> {
selectedItem: DomesticMap[selectedDomesticPolicyID], selectedItem: DomesticMap[selectedDomesticPolicyID],
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality showSearchBox: true, // Enables search functionality
menuProps: const MenuProps( menuProps: const MenuProps(backgroundColor: Colors.white),
backgroundColor: Colors.white,
),
constraints: BoxConstraints(maxHeight: 250), constraints: BoxConstraints(maxHeight: 250),
itemBuilder: (context, object, isSelected) => Padding( itemBuilder:
(context, object, isSelected) => Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0), horizontal: 8.0,
vertical: 6.0,
),
child: Text( child: Text(
object, object,
style: GoogleFonts.poppins(fontSize: 11.5), style: GoogleFonts.poppins(fontSize: 11.5),
@ -490,12 +491,11 @@ class GroupDataState extends State<GroupData> {
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:
dropdownBuilder: (context, selectedItem) => Align( (context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
@ -506,7 +506,8 @@ class GroupDataState extends State<GroupData> {
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
// Find the country_code based on selected country_name // Find the country_code based on selected country_name
selectedDomesticPolicyID = DomesticMap.entries selectedDomesticPolicyID =
DomesticMap.entries
.firstWhere((entry) => entry.value == newValue) .firstWhere((entry) => entry.value == newValue)
.key; .key;
selectedDomesticPolicyName = newValue; selectedDomesticPolicyName = newValue;
@ -522,11 +523,12 @@ class GroupDataState extends State<GroupData> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Description", "Description *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -559,9 +561,7 @@ class GroupDataState extends State<GroupData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
if (groupDataId != null) if (groupDataId != null)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -571,7 +571,8 @@ class GroupDataState extends State<GroupData> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
Tooltip( Tooltip(
message: message:
@ -587,13 +588,10 @@ class GroupDataState extends State<GroupData> {
), ),
), ),
), ),
) ),
], ],
), ),
if (groupDataId != null) if (groupDataId != null) SizedBox(height: 15),
SizedBox(
height: 15,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -630,13 +628,17 @@ class GroupDataState extends State<GroupData> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
child: Text('Save', child: Text(
'Save',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)), fontSize: 11,
color: Colors.white,
),
),
), ),
), ),
], ],
) ),
// : SizedBox.shrink(), // : SizedBox.shrink(),
], ],
), ),

View File

@ -102,32 +102,30 @@ class _GroupListState extends State<GroupList> {
} }
void filterGroups(String query) { void filterGroups(String query) {
print("allGroups before filtering: $query");
final lowerQuery = query.toLowerCase(); final lowerQuery = query.toLowerCase();
setState(() { setState(() {
filteredGroups = filteredGroups =
allGroups.where((group) { allGroups.where((object) {
return (group['name']?.toLowerCase().contains(lowerQuery) ?? final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive";
return (object['group_id']?.toLowerCase().contains(lowerQuery) ??
false) || false) ||
(group['domestic_policy_name']?.toLowerCase().contains( (object['name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['domestic_policy_name']?.toLowerCase().contains(
lowerQuery, lowerQuery,
) ?? ) ??
false)( false) ||
group['international_policy_name']?.toLowerCase().contains( (object['international_policy_name']?.toLowerCase().contains(
lowerQuery, lowerQuery,
) ?? ) ??
false,
) ||
(group['description']?.toLowerCase().contains(lowerQuery) ??
false) || false) ||
(group['is_active']?.toLowerCase().contains(lowerQuery) ?? (object['description']?.toLowerCase().contains(lowerQuery) ??
false); false) ||
(isActiveStatus.contains(lowerQuery));
}).toList(); }).toList();
currentPage = 0; currentPage = 0;
}); });
print("filteredGroups: $filteredGroups");
print("filtered: $filteredGroups");
} }
void handleActiveStatus( void handleActiveStatus(
@ -287,7 +285,7 @@ class _GroupListState extends State<GroupList> {
controller: searchController, controller: searchController,
onChanged: filterGroups, onChanged: filterGroups,
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search for a Group", hintText: "Search ...",
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 12, fontSize: 12,
color: Color(0xFF9E9DBD), color: Color(0xFF9E9DBD),
@ -385,7 +383,7 @@ class _GroupListState extends State<GroupList> {
controller: searchController, controller: searchController,
onChanged: filterGroups, onChanged: filterGroups,
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search for a Group", hintText: "Search ...",
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 12, fontSize: 12,
color: Color(0xFF9E9DBD), color: Color(0xFF9E9DBD),
@ -924,12 +922,35 @@ class _GroupListState extends State<GroupList> {
Expanded( Expanded(
child: child:
isDesktop isDesktop
? SingleChildScrollView( ? (searchController.text.isNotEmpty &&
scrollDirection: Axis.vertical, filteredGroups.isEmpty
child: table, // <-- your existing table ? Center(
) child: Text(
: buildMobileCardView(paginatedGroup), "No matches found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
), ),
),
)
: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: table,
))
: (searchController.text.isNotEmpty &&
paginatedGroup.isEmpty
? Center(
child: Text(
"No matches found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
),
)
: buildMobileCardView(paginatedGroup)),
),
PaginationControls( PaginationControls(
currentPage: currentPage, currentPage: currentPage,
itemsPerPage: itemsPerPage, itemsPerPage: itemsPerPage,

View File

@ -20,13 +20,14 @@ class HotelsData extends StatefulWidget {
final int? hotelsId; // <-- Add this final int? hotelsId; // <-- Add this
final Map<String, dynamic>? hotelsData; final Map<String, dynamic>? hotelsData;
const HotelsData( const HotelsData({
{super.key, super.key,
required this.isDesktop, required this.isDesktop,
this.layoutColor, this.layoutColor,
required this.fetchGetHotels, required this.fetchGetHotels,
this.hotelsId, this.hotelsId,
this.hotelsData}); this.hotelsData,
});
@override @override
HotelsDataState createState() => HotelsDataState(); HotelsDataState createState() => HotelsDataState();
@ -110,7 +111,8 @@ class HotelsDataState extends State<HotelsData> {
if (data == null) return; if (data == null) return;
setState(() { setState(() {
selectedCountry = data['country_code']; // For dropdown selectedCountry = data['country_code']; // For dropdown
selectedCountryName = data['country_name']; // For dropdown label or display selectedCountryName =
data['country_name']; // For dropdown label or display
controllers['city']?.text = data['city'] ?? ''; controllers['city']?.text = data['city'] ?? '';
controllers['hotel_chain']?.text = data['hotel_chain'] ?? ''; controllers['hotel_chain']?.text = data['hotel_chain'] ?? '';
controllers['hotel_name']?.text = data['hotel_name'] ?? ''; controllers['hotel_name']?.text = data['hotel_name'] ?? '';
@ -148,7 +150,12 @@ class HotelsDataState extends State<HotelsData> {
"city": controllers["city"]?.text, "city": controllers["city"]?.text,
}; };
final requiredFields = ["hotel_name","hotel_chain","country_code","city"]; final requiredFields = [
"hotel_name",
"hotel_chain",
"country_code",
"city",
];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -174,7 +181,6 @@ class HotelsDataState extends State<HotelsData> {
} }
Future<void> postHotelsData({int isActive = 1}) async { Future<void> postHotelsData({int isActive = 1}) async {
final hotelsData = hotels_Details(); final hotelsData = hotels_Details();
final String apiUrldata; final String apiUrldata;
@ -184,16 +190,20 @@ class HotelsDataState extends State<HotelsData> {
apiUrldata = '$apiUrl/api/updateHotels/$hotelsDataId'; apiUrldata = '$apiUrl/api/updateHotels/$hotelsDataId';
hotelsData["hotel_id"] = hotelsDataId.toString(); hotelsData["hotel_id"] = hotelsDataId.toString();
hotelsData["updated_by"] = userId; hotelsData["updated_by"] = userId;
(hotelsData.containsKey("created_by")) ? hotelsData.remove("created_by") : '' ; (hotelsData.containsKey("created_by"))
(hotelsData.containsKey("country_name")) ? hotelsData.remove("country_name") : '' ; ? hotelsData.remove("created_by")
: '';
(hotelsData.containsKey("country_name"))
? hotelsData.remove("country_name")
: '';
} else { } else {
print("for add Hotel id - null"); print("for add Hotel id - null");
apiUrldata = '$apiUrl/api/createHotels'; apiUrldata = '$apiUrl/api/createHotels';
print("called apiUrl - $apiUrldata"); print("called apiUrl - $apiUrldata");
hotelsData["created_by"] = userId; hotelsData["created_by"] = userId;
(hotelsData.containsKey("country_name")) ? hotelsData.remove("country_name") : '' ; (hotelsData.containsKey("country_name"))
? hotelsData.remove("country_name")
: '';
} }
final token = await getToken(); // Fetch token final token = await getToken(); // Fetch token
@ -210,7 +220,8 @@ class HotelsDataState extends State<HotelsData> {
}; };
final body = jsonEncode(hotelsData); final body = jsonEncode(hotelsData);
final response = hotelsDataId != null final response =
hotelsDataId != null
? 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);
@ -254,7 +265,7 @@ class HotelsDataState extends State<HotelsData> {
// 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
@ -281,20 +292,18 @@ class HotelsDataState extends State<HotelsData> {
], ],
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Divider( Divider(thickness: 0.2, color: Colors.blueGrey.shade100),
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
const SizedBox(height: 10), const SizedBox(height: 10),
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -313,7 +322,8 @@ class HotelsDataState extends State<HotelsData> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)), ),
),
), ),
if (errorMessages["hotel_name"] != null) ...[ if (errorMessages["hotel_name"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -329,11 +339,12 @@ class HotelsDataState extends State<HotelsData> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Hotel Chain", "Hotel Chain *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -352,7 +363,8 @@ class HotelsDataState extends State<HotelsData> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)), ),
),
), ),
if (errorMessages["hotel_chain"] != null) ...[ if (errorMessages["hotel_chain"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -363,16 +375,104 @@ class HotelsDataState extends State<HotelsData> {
], ],
], ],
), ),
SizedBox(height: 10),
// - It has been observed that many of the dropdowns have overlapping issues, causing label names to be hidden - just copied searchable dropdown - still not completed (user mangement screen only ) master page except policy - my trips - flight taxi train insurance, visa misscenllo color white size padding data
// - Delete option is not working in the policy list page - completed
// - Label Names for all the modules should be set bold as it is looking like normal text in user management compared to trips page - completed
// - In the masters org mangement search option not working for traveller - particular 4 master page - - issues occur - commpleted - email master working fine, traveller master working fine, amount master working fine, group - completed
// - QC- Authentication - - completed - ask to check
const SizedBox(height: 10),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"City", "Country *",
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),
CustomTextFieldForexWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 250),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Country...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
selectedCountryName = newValue;
});
},
),
),
),
if (errorMessages["country_code"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["country_code"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"City *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -391,7 +491,8 @@ class HotelsDataState extends State<HotelsData> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)), ),
),
), ),
if (errorMessages["city"] != null) ...[ if (errorMessages["city"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -402,86 +503,7 @@ class HotelsDataState extends State<HotelsData> {
], ],
], ],
), ),
const SizedBox(height: 10), SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Country",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(
backgroundColor: Colors.white,
),
constraints: BoxConstraints(maxHeight: 250),
itemBuilder: (context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Country...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 1,
),
),
),
dropdownBuilder: (context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry = countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
selectedCountryName = newValue;
});
},
),
),
),
if (errorMessages["country_code"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["country_code"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
SizedBox( height: 15 ),
// if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,),
if (hotelsDataId != null) if (hotelsDataId != null)
Row( Row(
@ -492,7 +514,8 @@ class HotelsDataState extends State<HotelsData> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
Tooltip( Tooltip(
message: message:
@ -508,13 +531,10 @@ class HotelsDataState extends State<HotelsData> {
), ),
), ),
), ),
) ),
], ],
), ),
if (hotelsDataId != null) if (hotelsDataId != null) SizedBox(height: 15),
SizedBox(
height: 15,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -551,13 +571,17 @@ class HotelsDataState extends State<HotelsData> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
child: Text('Save', child: Text(
'Save',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)), fontSize: 11,
color: Colors.white,
),
),
), ),
), ),
], ],
) ),
// : SizedBox.shrink(), // : SizedBox.shrink(),
], ],
), ),

View File

@ -102,7 +102,7 @@ class HotelsDataListState extends State<HotelsDataList> {
Future<List<dynamic>> fetchGetHotels() async { Future<List<dynamic>> fetchGetHotels() async {
orgId = await getOrgId(); orgId = await getOrgId();
final String apiUrlData = '$apiUrl/api/getHotels'; final String apiUrlData = '$apiUrl/api/getHotels?for=table_view';
final String? token = await getToken(); final String? token = await getToken();
@ -646,7 +646,7 @@ class HotelsDataListState extends State<HotelsDataList> {
color: color:
hotels['is_active'] == "1" hotels['is_active'] == "1"
? Colors.green ? Colors.green
: Colors.red, : Colors.grey,
), ),
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,

View File

@ -21,8 +21,8 @@ class FlightScreen extends StatefulWidget {
final Map<String, dynamic>? selectedItem; final Map<String, dynamic>? selectedItem;
final ValueNotifier<String?> tripTypeNotifier; final ValueNotifier<String?> tripTypeNotifier;
FlightScreen( FlightScreen({
{Key? key, Key? key,
required this.apiData, required this.apiData,
required this.loginUser, required this.loginUser,
required this.onClose, required this.onClose,
@ -32,8 +32,8 @@ class FlightScreen extends StatefulWidget {
required this.hasAction, required this.hasAction,
this.tripType, this.tripType,
required this.tripTypeNotifier, required this.tripTypeNotifier,
this.apiDataForClass}) this.apiDataForClass,
: super(key: key); }) : super(key: key);
@override @override
FlightScreenState createState() => FlightScreenState(); FlightScreenState createState() => FlightScreenState();
@ -70,7 +70,7 @@ class FlightScreenState extends State<FlightScreen> {
"_date", "_date",
"_visa", "_visa",
"_time", "_time",
"_comments" "_comments",
]; ];
Map<String, FocusNode> focusNodes = {}; Map<String, FocusNode> focusNodes = {};
@ -148,14 +148,18 @@ class FlightScreenState extends State<FlightScreen> {
// Loop through each row and add listeners to clear errors // Loop through each row and add listeners to clear errors
for (int i = 1; i <= rowCount; i++) { for (int i = 1; i <= rowCount; i++) {
textControllers["_from${i}Controller"] textControllers["_from${i}Controller"]?.addListener(
?.addListener(() => _clearError("from_place_$i")); () => _clearError("from_place_$i"),
textControllers["_to${i}Controller"] );
?.addListener(() => _clearError("to_place_$i")); textControllers["_to${i}Controller"]?.addListener(
textControllers["_date${i}Controller"] () => _clearError("to_place_$i"),
?.addListener(() => _clearError("date_$i")); );
textControllers["_time${i}Controller"] textControllers["_date${i}Controller"]?.addListener(
?.addListener(() => _clearError("time_$i")); () => _clearError("date_$i"),
);
textControllers["_time${i}Controller"]?.addListener(
() => _clearError("time_$i"),
);
} }
// loadCountryList(); // loadCountryList();
@ -178,17 +182,16 @@ class FlightScreenState extends State<FlightScreen> {
} }
Map<String, String?> getFlightTripDateRange( Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) { List<Map<String, dynamic>> flightData,
final allTrips = flightData ) {
final allTrips =
flightData
.expand((flight) => flight['trips'] ?? []) .expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>() .whereType<Map<String, dynamic>>()
.toList(); .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) {
@ -293,7 +296,8 @@ class FlightScreenState extends State<FlightScreen> {
print("Text Controllers KeysII: ${textControllers.keys.toList()}"); print("Text Controllers KeysII: ${textControllers.keys.toList()}");
// Determine the row count based on selectedTripType // Determine the row count based on selectedTripType
int rowCount = selectedTripType == "Roundtrip" int rowCount =
selectedTripType == "Roundtrip"
? 2 ? 2
: selectedTripType == "Multitrip" : selectedTripType == "Multitrip"
? multiTripRowCount ? multiTripRowCount
@ -476,10 +480,12 @@ class FlightScreenState extends State<FlightScreen> {
// TextEditingController(text: trip["from_place"]); // TextEditingController(text: trip["from_place"]);
// textControllers["_to${index}Controller"] = // textControllers["_to${index}Controller"] =
// TextEditingController(text: trip["to_place"]); // TextEditingController(text: trip["to_place"]);
textControllers["_date${index}Controller"] = textControllers["_date${index}Controller"] = TextEditingController(
TextEditingController(text: trip["date"]); text: trip["date"],
textControllers["_time${index}Controller"] = );
TextEditingController(text: trip["time"]); textControllers["_time${index}Controller"] = TextEditingController(
text: trip["time"],
);
// Check if editing and flight_trip_id exists for this trip // Check if editing and flight_trip_id exists for this trip
if (widget.selectedItem != null && if (widget.selectedItem != null &&
@ -541,10 +547,9 @@ class FlightScreenState extends State<FlightScreen> {
final currDateTime = format.parse("$currDateStr $currTimeStr"); final currDateTime = format.parse("$currDateStr $currTimeStr");
if (!currDateTime.isAfter(prevDateTime)) { if (!currDateTime.isAfter(prevDateTime)) {
errorMessages["time_$index"] = "Must be after previous time"; errorMessages["time_$index"] = "30 mins gap required";
} else if (currDateTime.difference(prevDateTime).inMinutes < 30) { } else if (currDateTime.difference(prevDateTime).inMinutes < 30) {
errorMessages["time_$index"] = errorMessages["time_$index"] = "30 mins gap required";
"Must be least 30 mins after previous time";
} else { } else {
errorMessages.remove("time_$index"); errorMessages.remove("time_$index");
} }
@ -665,14 +670,15 @@ class FlightScreenState extends State<FlightScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile; bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container( return Container(
// color: Color(0xFFF4F4FB), // color: Color(0xFFF4F4FB),
// color: Color(0xFFF9F9F9), // Slightly lighter than white // color: Color(0xFFF9F9F9), // Slightly lighter than white
child: Form( child: Form(
key: _formKey, key: _formKey,
child: Padding( child: Padding(
@ -684,13 +690,14 @@ class FlightScreenState extends State<FlightScreen> {
child: Center( child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)), child: Column(children: _buildAccomadtionForm(isDesktop)),
), ),
) ),
], ],
), ),
), ),
), ),
); );
}); },
);
} }
List<Widget> _buildAccomadtionForm(bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -703,14 +710,14 @@ class FlightScreenState extends State<FlightScreen> {
List<List<Widget>> rowBuilders = [ List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop, 1), // _builClassType(isDesktop, 1),
_buildSecondRow(isDesktop, 1) _buildSecondRow(isDesktop, 1),
]; ];
List<List<Widget>> rowRoundBuilders = [ List<List<Widget>> rowRoundBuilders = [
// _builClassType(isDesktop, 1), // _builClassType(isDesktop, 1),
_buildSecondRow(isDesktop, 1), _buildSecondRow(isDesktop, 1),
// _builClassType(isDesktop, 2), // _builClassType(isDesktop, 2),
_buildSecondRow(isDesktop, 2) _buildSecondRow(isDesktop, 2),
]; ];
print("Trip Type Selected: $selectedTripType"); print("Trip Type Selected: $selectedTripType");
@ -740,7 +747,6 @@ class FlightScreenState extends State<FlightScreen> {
// ...List.generate(multiTripRowCount, (index) => // ...List.generate(multiTripRowCount, (index) =>
// buildResponsiveRow(_builClassType(isDesktop, index + 1) + _buildSecondRow(isDesktop, index + 1)) // buildResponsiveRow(_builClassType(isDesktop, index + 1) + _buildSecondRow(isDesktop, index + 1))
// ).expand((row) => row), // ).expand((row) => row),
if (selectedTripType == "Multitrip") if (selectedTripType == "Multitrip")
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
@ -811,39 +817,40 @@ class FlightScreenState extends State<FlightScreen> {
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),
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
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_value'], value: item['dropdown_value'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .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),
),
), ),
); );
} }
@ -858,7 +865,8 @@ class FlightScreenState extends State<FlightScreen> {
height: 40, height: 40,
width: double.infinity, width: double.infinity,
child: DropdownSearch<String>( child: DropdownSearch<String>(
items: purposeList items:
purposeList
.map((item) => item['dropdown_value'] as String) .map((item) => item['dropdown_value'] as String)
.toList(), .toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
@ -876,11 +884,13 @@ class FlightScreenState extends State<FlightScreen> {
errorMessages.clear(); errorMessages.clear();
}); });
print( print(
"Updating form data: Flight -> trip_type -> $selectedTripType"); "Updating form data: Flight -> trip_type -> $selectedTripType",
);
_initializeFields(); _initializeFields();
}, },
selectedItem: selectedTripType, selectedItem: selectedTripType,
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
selectedItem ?? "Select", selectedItem ?? "Select",
@ -890,13 +900,17 @@ class FlightScreenState extends State<FlightScreen> {
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
constraints: BoxConstraints(maxHeight: 100), constraints: BoxConstraints(maxHeight: 100),
menuProps: MenuProps(backgroundColor: Colors.white), menuProps: MenuProps(backgroundColor: Colors.white),
itemBuilder: (context, item, isSelected) => Padding( itemBuilder:
padding: (context, item, isSelected) => Padding(
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0), padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text( child: Text(
item, item,
style: TextStyle( style: TextStyle(
fontSize: 13), // Custom text size for dropdown items fontSize: 13,
), // Custom text size for dropdown items
), ),
), ),
), ),
@ -999,9 +1013,9 @@ class FlightScreenState extends State<FlightScreen> {
return [ return [
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
// padding: const EdgeInsets.only(left: 10, right: 10), // padding: const EdgeInsets.only(left: 10, right: 10),
// color: Colors.white, // color: Colors.white,
child: Text( child: Text(
"Trip ${index}", "Trip ${index}",
style: TextStyle( style: TextStyle(
@ -1012,17 +1026,14 @@ class FlightScreenState extends State<FlightScreen> {
), ),
), ),
SizedBox( SizedBox(
width: isDesktop width:
isDesktop
? MediaQuery.of(context).size.width * 0.58 ? MediaQuery.of(context).size.width * 0.58
: 80, // Ensure full width : 80, // Ensure full width
child: Stack( child: Stack(
alignment: Alignment.center, // Centers the icon alignment: Alignment.center, // Centers the icon
children: [ children: [
Divider( Divider(color: Color(0xFF8B8FB2), thickness: 0.5, height: 20),
color: Color(0xFF8B8FB2),
thickness: 0.5,
height: 20,
),
Container( Container(
// padding: EdgeInsets.all(4), // padding: EdgeInsets.all(4),
color: Colors.white, // Background to avoid overlapping color: Colors.white, // Background to avoid overlapping
@ -1064,7 +1075,6 @@ class FlightScreenState extends State<FlightScreen> {
// ], // ],
// ), // ),
// ), // ),
Container( Container(
// color: Colors.white, // color: Colors.white,
// padding: const EdgeInsets.only(left: 10, right: 10), // padding: const EdgeInsets.only(left: 10, right: 10),
@ -1076,7 +1086,7 @@ class FlightScreenState extends State<FlightScreen> {
color: Colors.blueAccent, color: Colors.blueAccent,
iconSize: 20, iconSize: 20,
), ),
) ),
]; ];
} }
@ -1085,19 +1095,24 @@ class FlightScreenState extends State<FlightScreen> {
List<dynamic> purposeList = widget.apiDataForClass?['flight_class'] ?? []; List<dynamic> purposeList = widget.apiDataForClass?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems =
.map((item) => DropdownMenuItem<String>( purposeList
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .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),
),
), ),
); );
} }
@ -1120,8 +1135,9 @@ class FlightScreenState extends State<FlightScreen> {
flightLastTripDateNotifier.value != null && flightLastTripDateNotifier.value != null &&
flightLastTripDateNotifier.value!.isNotEmpty) { flightLastTripDateNotifier.value!.isNotEmpty) {
try { try {
final tripDate = DateFormat('dd-MM-yyyy') final tripDate = DateFormat(
.parseStrict(flightLastTripDateNotifier.value!); 'dd-MM-yyyy',
).parseStrict(flightLastTripDateNotifier.value!);
if (tripDate.isAfter(today)) { if (tripDate.isAfter(today)) {
firstDate = tripDate; firstDate = tripDate;
} }
@ -1133,8 +1149,9 @@ class FlightScreenState extends State<FlightScreen> {
textControllers["_date${index - 1}Controller"]?.text; textControllers["_date${index - 1}Controller"]?.text;
if (previousDateString != null && previousDateString.isNotEmpty) { if (previousDateString != null && previousDateString.isNotEmpty) {
try { try {
final previousDate = final previousDate = DateFormat(
DateFormat('dd-MM-yyyy').parseStrict(previousDateString); 'dd-MM-yyyy',
).parseStrict(previousDateString);
if (previousDate.isAfter(today)) { if (previousDate.isAfter(today)) {
firstDate = previousDate; firstDate = previousDate;
} }
@ -1144,7 +1161,8 @@ class FlightScreenState extends State<FlightScreen> {
} }
} }
DateTime initialDate = _selectedCheckOutDate != null && DateTime initialDate =
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(firstDate) _selectedCheckOutDate!.isAfter(firstDate)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: firstDate; : firstDate;
@ -1160,14 +1178,18 @@ class FlightScreenState extends State<FlightScreen> {
setState(() { setState(() {
_selectedCheckOutDate = pickedDate; _selectedCheckOutDate = pickedDate;
// _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
textControllers["_date${index}Controller"]?.text = textControllers["_date${index}Controller"]?.text = DateFormat(
DateFormat('dd-MM-yyyy').format(pickedDate); 'dd-MM-yyyy',
).format(pickedDate);
}); });
} }
} }
Future<void> _selectCheckOutTime( Future<void> _selectCheckOutTime(
BuildContext context, int index, VoidCallback onPicked) async { BuildContext context,
int index,
VoidCallback onPicked,
) async {
TimeOfDay? pickedTime = await showTimePicker( TimeOfDay? pickedTime = await showTimePicker(
context: context, context: context,
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
@ -1179,8 +1201,13 @@ class FlightScreenState extends State<FlightScreen> {
// 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,
),
); );
// _timeController.text = formattedTime; // _timeController.text = formattedTime;
textControllers["_time${index}Controller"]?.text = formattedTime; textControllers["_time${index}Controller"]?.text = formattedTime;
@ -1208,11 +1235,12 @@ class FlightScreenState extends State<FlightScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"From", "From*",
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(
@ -1222,10 +1250,12 @@ class FlightScreenState extends State<FlightScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: isCountryLoading child:
isCountryLoading
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: DropdownSearch<String>( : DropdownSearch<String>(
selectedItem: selectedFrom[index] != null selectedItem:
selectedFrom[index] != null
? countryMap[selectedFrom[index]] ? countryMap[selectedFrom[index]]
: null, : null,
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
@ -1236,20 +1266,24 @@ class FlightScreenState extends State<FlightScreen> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search...", hintText: "Search...",
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(
horizontal: 10, vertical: 1), horizontal: 10,
vertical: 1,
), ),
style: TextStyle(fontSize: 12)),
menuProps: MenuProps(
backgroundColor: Colors.white,
), ),
itemBuilder: (context, item, isSelected) => Padding( style: TextStyle(fontSize: 12),
),
menuProps: MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0), horizontal: 8.0,
vertical: 6.0,
),
child: Text( child: Text(
item, item,
style: TextStyle( style: TextStyle(
fontSize: fontSize: 13,
13), // 👈 Set your desired text size here ), // 👈 Set your desired text size here
), ),
), ),
), ),
@ -1260,7 +1294,8 @@ class FlightScreenState extends State<FlightScreen> {
contentPadding: EdgeInsets.symmetric(horizontal: 1), contentPadding: EdgeInsets.symmetric(horizontal: 1),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
selectedItem ?? "Select", selectedItem ?? "Select",
@ -1273,15 +1308,18 @@ class FlightScreenState extends State<FlightScreen> {
// .firstWhere((entry) => entry.value == newValue) // .firstWhere((entry) => entry.value == newValue)
// .key; // .key;
selectedFrom[index] = countryMap.entries selectedFrom[index] =
.firstWhere((entry) => entry.value == newValue) countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key; .key;
print(selectedFrom[index]); print(selectedFrom[index]);
}); });
}, },
), ),
) ),
// child: SizedBox( // child: SizedBox(
// height: 40, // height: 40,
@ -1302,30 +1340,21 @@ class FlightScreenState extends State<FlightScreen> {
), ),
if (errorMessages["from_place_$index"] != null) ...[ if (errorMessages["from_place_$index"] != 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) SizedBox(width: 20) else SizedBox(height: 8),
SizedBox(
width: 20,
)
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.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
@ -1333,10 +1362,12 @@ class FlightScreenState extends State<FlightScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: isCountryLoading child:
isCountryLoading
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: DropdownSearch<String>( : DropdownSearch<String>(
selectedItem: selectedTo[index] != null selectedItem:
selectedTo[index] != null
? countryMap[selectedTo[index]] ? countryMap[selectedTo[index]]
: null, : null,
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
@ -1347,20 +1378,24 @@ class FlightScreenState extends State<FlightScreen> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search...", hintText: "Search...",
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(
horizontal: 10, vertical: 1), horizontal: 10,
vertical: 1,
), ),
style: TextStyle(fontSize: 12)),
menuProps: MenuProps(
backgroundColor: Colors.white,
), ),
itemBuilder: (context, item, isSelected) => Padding( style: TextStyle(fontSize: 12),
),
menuProps: MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0), horizontal: 8.0,
vertical: 6.0,
),
child: Text( child: Text(
item, item,
style: TextStyle( style: TextStyle(
fontSize: fontSize: 13,
13), // 👈 Set your desired text size here ), // 👈 Set your desired text size here
), ),
), ),
), ),
@ -1371,7 +1406,8 @@ class FlightScreenState extends State<FlightScreen> {
contentPadding: EdgeInsets.symmetric(horizontal: 1), contentPadding: EdgeInsets.symmetric(horizontal: 1),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
selectedItem ?? "Select Country", selectedItem ?? "Select Country",
@ -1380,30 +1416,26 @@ class FlightScreenState extends State<FlightScreen> {
), ),
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
selectedTo[index] = countryMap.entries selectedTo[index] =
.firstWhere((entry) => entry.value == newValue) countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key; .key;
print(selectedTo[index]); print(selectedTo[index]);
}); });
}, },
), ),
)), ),
),
if (errorMessages["to_place_$index"] != null) ...[ if (errorMessages["to_place_$index"] != 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: [
@ -1412,7 +1444,8 @@ class FlightScreenState extends State<FlightScreen> {
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(
@ -1423,7 +1456,8 @@ class FlightScreenState extends State<FlightScreen> {
// : MediaQuery.of(context).size.width * 0.66, // : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: isFlightClassLoading child:
isFlightClassLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: DropdownButtonFormField<String>( : DropdownButtonFormField<String>(
focusNode: focusNodes["_class${index}FocusNode"], focusNode: focusNodes["_class${index}FocusNode"],
@ -1435,9 +1469,11 @@ class FlightScreenState extends State<FlightScreen> {
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding horizontal: 10,
), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged:
purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedClasses[index] = newValue; selectedClasses[index] = newValue;
@ -1452,21 +1488,17 @@ class FlightScreenState extends State<FlightScreen> {
), ),
], ],
), ),
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.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
@ -1489,8 +1521,11 @@ class FlightScreenState extends State<FlightScreen> {
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,
),
), ),
), ),
), ),
@ -1499,28 +1534,21 @@ class FlightScreenState extends State<FlightScreen> {
), ),
if (errorMessages["date_$index"] != null) ...[ if (errorMessages["date_$index"] != 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.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
@ -1538,7 +1566,8 @@ class FlightScreenState extends State<FlightScreen> {
_selectCheckOutTime(context, index, () { _selectCheckOutTime(context, index, () {
validateTimeDifference(index); validateTimeDifference(index);
setState( setState(
() {}); // Force rebuild to show the error immediately () {},
); // Force rebuild to show the error immediately
}); });
}, },
child: AbsorbPointer( child: AbsorbPointer(
@ -1554,8 +1583,11 @@ class FlightScreenState extends State<FlightScreen> {
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,
),
), ),
), ),
), ),
@ -1579,13 +1611,9 @@ class FlightScreenState extends State<FlightScreen> {
onPressed: () { onPressed: () {
removeTrip(index); removeTrip(index);
}, },
icon: Icon( icon: Icon(Icons.close, color: Colors.redAccent, size: 20),
Icons.close,
color: Colors.redAccent,
size: 20,
), ),
), ),
)
]; ];
} }
@ -1594,11 +1622,14 @@ class FlightScreenState extends State<FlightScreen> {
widget.apiData?['flight_visa_available'] ?? []; widget.apiData?['flight_visa_available'] ?? [];
// Default selected value // Default selected value
List<DropdownMenuItem<String>> dropdownItems = visa_available List<DropdownMenuItem<String>> dropdownItems =
.map((item) => DropdownMenuItem<String>( visa_available
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .toList();
selectedvisa_available ??= selectedvisa_available ??=
@ -1608,8 +1639,10 @@ class FlightScreenState extends State<FlightScreen> {
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),
),
), ),
); );
} }
@ -1622,7 +1655,8 @@ class FlightScreenState extends State<FlightScreen> {
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(
@ -1641,10 +1675,12 @@ class FlightScreenState extends State<FlightScreen> {
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
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: visa_available.isNotEmpty onChanged:
visa_available.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedvisa_available = newValue; selectedvisa_available = newValue;
@ -1652,7 +1688,8 @@ class FlightScreenState extends State<FlightScreen> {
// Reset `multiTripRowCount` when switching away from Multitrip // Reset `multiTripRowCount` when switching away from Multitrip
}); });
print( print(
"Updating form data: Flight -> trip_type -> $selectedvisa_available"); "Updating form data: Flight -> trip_type -> $selectedvisa_available",
);
// _initializeRows(); // _initializeRows();
} }
@ -1664,13 +1701,8 @@ class FlightScreenState extends State<FlightScreen> {
), ),
], ],
), ),
if (isDesktop) if (isDesktop) SizedBox(width: 20),
SizedBox( SizedBox(height: 5),
width: 20,
),
SizedBox(
height: 5,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -1679,7 +1711,8 @@ class FlightScreenState extends State<FlightScreen> {
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(
@ -1705,9 +1738,7 @@ class FlightScreenState extends State<FlightScreen> {
], ],
), ),
if (isDesktop) Spacer(), if (isDesktop) Spacer(),
SizedBox( SizedBox(height: 5),
height: 5,
),
Column( Column(
children: [ children: [
Row( Row(
@ -1724,11 +1755,14 @@ class FlightScreenState extends State<FlightScreen> {
widget.apiData?['flight_visa_available'] ?? []; widget.apiData?['flight_visa_available'] ?? [];
// Default selected value // Default selected value
List<DropdownMenuItem<String>> dropdownItems = visa_available List<DropdownMenuItem<String>> dropdownItems =
.map((item) => DropdownMenuItem<String>( visa_available
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .toList();
selectedvisa_available ??= selectedvisa_available ??=
@ -1738,8 +1772,10 @@ class FlightScreenState extends State<FlightScreen> {
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),
),
), ),
); );
} }
@ -1753,14 +1789,16 @@ class FlightScreenState extends State<FlightScreen> {
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: _tripTypeFocused, // isFocused: _tripTypeFocused,
isFocused: focusStates["_visa1Focused"] ?? false, isFocused: focusStates["_visa1Focused"] ?? false,
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( child: SizedBox(
@ -1772,10 +1810,12 @@ class FlightScreenState extends State<FlightScreen> {
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
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: visa_available.isNotEmpty onChanged:
visa_available.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedvisa_available = newValue; selectedvisa_available = newValue;
@ -1783,7 +1823,8 @@ class FlightScreenState extends State<FlightScreen> {
// Reset `multiTripRowCount` when switching away from Multitrip // Reset `multiTripRowCount` when switching away from Multitrip
}); });
print( print(
"Updating form data: Flight -> trip_type -> $selectedvisa_available"); "Updating form data: Flight -> trip_type -> $selectedvisa_available",
);
// _initializeRows(); // _initializeRows();
} }
@ -1807,9 +1848,7 @@ class FlightScreenState extends State<FlightScreen> {
}, },
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(
@ -1818,7 +1857,6 @@ class FlightScreenState extends State<FlightScreen> {
), ),
), ),
SizedBox(width: 10), // Space between buttons SizedBox(width: 10), // Space between buttons
// Save Changes Button // Save Changes Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
@ -1826,9 +1864,7 @@ class FlightScreenState extends State<FlightScreen> {
}, },
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

@ -223,8 +223,6 @@ class _ForexScreenState extends State<ForexScreen> {
} }
bool isValidForexData(Map<String, dynamic> data) { bool isValidForexData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = [ List<String> requiredFields = [
"start_date", "start_date",
@ -232,7 +230,7 @@ class _ForexScreenState extends State<ForexScreen> {
"country_code", "country_code",
"deposit_on_card", "deposit_on_card",
"deposit_on_cash", "deposit_on_cash",
// "card_number" // "card_number",
]; ];
// If have_card is "1", then delivery_location is required // If have_card is "1", then delivery_location is required
@ -246,11 +244,17 @@ class _ForexScreenState extends State<ForexScreen> {
// 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";
} }
} }
return errorMessages.isEmpty; // Valid if there are no errors return errorMessages.values.every((msg) => msg.trim().isEmpty);
// if (hasErrors) {
// print("At least one error message is present.");
// }
//
// return errorMessages.isEmpty; // Valid if there are no errors
} }
Map<String, String?> getFlightTripDateRange( Map<String, String?> getFlightTripDateRange(
@ -286,7 +290,7 @@ class _ForexScreenState extends State<ForexScreen> {
Map<String, dynamic> data = forexData; Map<String, dynamic> data = forexData;
if (!isValidForexData(data)) { 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
@ -613,7 +617,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
} else { } else {
errorMessages["deposit_on_card"] = ""; // Clear error if valid errorMessages.remove("deposit_on_card"); // Clear error if valid
} }
// Refresh UI if using StatefulWidget // Refresh UI if using StatefulWidget
@ -621,7 +625,8 @@ 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");
print("_validateCashAmount - $value - $fifteenPercent"); print("_validateCashAmount - $value - $fifteenPercent");
int? enteredAmount = int.tryParse(value); int? enteredAmount = int.tryParse(value);
@ -642,11 +647,18 @@ class _ForexScreenState extends State<ForexScreen> {
textControllers["_card"]?.text = difference.toString(); textControllers["_card"]?.text = difference.toString();
if (enteredAmount > fifteenPercent) { if (enteredAmount > fifteenPercent) {
if (checkValidAmount > 0) {
textControllers["_card"]?.text = "0";
errorMessages["deposit_on_card"] =
"Enter Valid Amount"; // Clear error if valid
}
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["deposit_on_card"] = " "; // Clear error if valid errorMessages.remove("deposit_on_cash");
// errorMessages["deposit_on_card"] = " "; // Clear error if valid
} else { } else {
errorMessages["deposit_on_cash"] = ""; // Clear error if valid // errorMessages["deposit_on_cash"] = ""; // Clear error if valid
errorMessages.remove("deposit_on_cash");
} }
// Refresh UI if using StatefulWidget // Refresh UI if using StatefulWidget
@ -887,7 +899,7 @@ class _ForexScreenState extends State<ForexScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Start Date", "Start Date *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -958,7 +970,7 @@ class _ForexScreenState extends State<ForexScreen> {
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(
"Select Start Date", errorMessages["start_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
@ -969,7 +981,7 @@ class _ForexScreenState extends State<ForexScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"End Date", "End Date *",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -1123,7 +1135,7 @@ class _ForexScreenState extends State<ForexScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Country", "Country*",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -1179,7 +1191,7 @@ class _ForexScreenState extends State<ForexScreen> {
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(
"Select Country", errorMessages["country_code"]!,
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
@ -1600,8 +1612,12 @@ class _ForexScreenState extends State<ForexScreen> {
child: TextField( child: TextField(
focusNode: focusNodes["_cash"], focusNode: focusNodes["_cash"],
controller: textControllers["_cash"], controller: textControllers["_cash"],
style: const TextStyle(fontSize: 12),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly, // Only allow digits
],
style: const TextStyle(fontSize: 12),
// keyboardType: TextInputType.number,
onChanged: (value) { onChanged: (value) {
// errorMessages["deposit_on_cash"] = ""; // errorMessages["deposit_on_cash"] = "";
_validateCashAmount( _validateCashAmount(
@ -1653,6 +1669,9 @@ class _ForexScreenState extends State<ForexScreen> {
controller: textControllers["_card"], controller: textControllers["_card"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly, // Only allow digits
],
onChanged: (value) { onChanged: (value) {
_validateCardAmount( _validateCardAmount(
value, value,
@ -1884,7 +1903,7 @@ class _ForexScreenState extends State<ForexScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Card Number", "Card Number*",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,

View File

@ -17,14 +17,15 @@ class TrainScreen extends StatefulWidget {
final String? loginUser; final String? loginUser;
final String? tripType; final String? tripType;
TrainScreen( TrainScreen({
{required this.onClose, required this.onClose,
this.apiData, this.apiData,
required this.onSavetrain, required this.onSavetrain,
required this.selectedItem, required this.selectedItem,
required this.loginUser, required this.loginUser,
this.apiDataForClass, this.apiDataForClass,
this.tripType}); this.tripType,
});
@override @override
_TrainScreenState createState() => _TrainScreenState(); _TrainScreenState createState() => _TrainScreenState();
@ -232,7 +233,7 @@ class _TrainScreenState extends State<TrainScreen> {
"from_station", "from_station",
"to_station", "to_station",
"date", "date",
"time" "time",
]; ];
// Check validation for each field // Check validation for each field
@ -287,9 +288,11 @@ class _TrainScreenState extends State<TrainScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile; bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container( return Container(
// color: Color(0xFFF4F4FB), // color: Color(0xFFF4F4FB),
@ -325,13 +328,14 @@ class _TrainScreenState extends State<TrainScreen> {
child: Center( child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)), child: Column(children: _buildAccomadtionForm(isDesktop)),
), ),
) ),
], ],
), ),
), ),
), ),
); );
}); },
);
} }
List<Widget> _buildAccomadtionForm(bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -344,7 +348,7 @@ class _TrainScreenState extends State<TrainScreen> {
List<List<Widget>> rowBuilders = [ List<List<Widget>> rowBuilders = [
_builClassType(isDesktop), _builClassType(isDesktop),
_buildSecondRow(isDesktop) _buildSecondRow(isDesktop),
]; ];
return [ return [
@ -369,7 +373,8 @@ class _TrainScreenState extends State<TrainScreen> {
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
@ -377,38 +382,35 @@ class _TrainScreenState extends State<TrainScreen> {
: Column(children: _buildTripType(isDesktop)), : Column(children: _buildTripType(isDesktop)),
if (errorMessages["train_no"] != null) ...[ if (errorMessages["train_no"] != 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,
),
]; ];
} }
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
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_value'], value: item['dropdown_value'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .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),
),
), ),
); );
} }
@ -455,7 +457,8 @@ class _TrainScreenState extends State<TrainScreen> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null && initialDate:
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today) _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
@ -483,8 +486,13 @@ class _TrainScreenState extends State<TrainScreen> {
// 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,
),
); );
_timeController.text = formattedTime; _timeController.text = formattedTime;
}); });
@ -495,19 +503,24 @@ class _TrainScreenState extends State<TrainScreen> {
List<dynamic> purposeList = widget.apiDataForClass?['train_class'] ?? []; List<dynamic> purposeList = widget.apiDataForClass?['train_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems =
.map((item) => DropdownMenuItem<String>( purposeList
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .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),
),
), ),
); );
} }
@ -525,7 +538,8 @@ class _TrainScreenState extends State<TrainScreen> {
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(
@ -543,10 +557,12 @@ class _TrainScreenState extends State<TrainScreen> {
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
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:
purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedClass = newValue; selectedClass = newValue;
@ -559,19 +575,11 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
if (errorMessages["class"] != null) ...[ if (errorMessages["class"] != 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: [
@ -580,7 +588,8 @@ class _TrainScreenState extends State<TrainScreen> {
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(
@ -588,41 +597,51 @@ class _TrainScreenState extends State<TrainScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: isCountryLoading child:
isCountryLoading
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: DropdownSearch<String>( : DropdownSearch<String>(
// selectedItem: selectedFrom != null // selectedItem: selectedFrom != null
// ? countryMap[selectedFrom] // ? countryMap[selectedFrom]
// : null, // : null,
selectedItem:
selectedItem: selectedFrom != null selectedFrom != null
? countryMap[ ? countryMap[selectedFrom] // get the display value from code
selectedFrom] // get the display value from code
: null, : null,
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
menuProps: MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 230),
showSearchBox: true, showSearchBox: true,
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search ...", hintText: "Search ...",
contentPadding: contentPadding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 10), horizontal: 10,
), ),
), ),
), ),
),
items: countryMap.values.toList(), items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
selectedItem ?? "Select", selectedItem ?? "Select",
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
), ),
), ),
// onChanged: (String? newValue) { // onChanged: (String? newValue) {
// setState(() { // setState(() {
// // selectedFrom[index] = countryMap.entries // // selectedFrom[index] = countryMap.entries
@ -636,16 +655,18 @@ class _TrainScreenState extends State<TrainScreen> {
// print(selectedFrom); // print(selectedFrom);
// }); // });
// }, // },
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
selectedFrom = countryMap.entries selectedFrom =
.firstWhere((entry) => entry.value == newValue) countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key; .key;
}); });
}, },
), ),
) ),
// child: SizedBox( // child: SizedBox(
// height: 40, // height: 40,
// child: TextField( // child: TextField(
@ -664,19 +685,11 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
if (errorMessages["from_station"] != null) ...[ if (errorMessages["from_station"] != 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: [
@ -685,7 +698,8 @@ class _TrainScreenState extends State<TrainScreen> {
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(
@ -693,20 +707,24 @@ class _TrainScreenState extends State<TrainScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: isCountryLoading child:
isCountryLoading
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: DropdownSearch<String>( : DropdownSearch<String>(
selectedItem: selectedTo != null selectedItem:
? countryMap[ selectedTo != null
selectedTo] // get the display value from code ? countryMap[selectedTo] // get the display value from code
: null, : null,
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
menuProps: MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 230),
showSearchBox: true, showSearchBox: true,
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search ...", hintText: "Search ...",
contentPadding: contentPadding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 10), horizontal: 10,
),
), ),
), ),
), ),
@ -717,7 +735,8 @@ class _TrainScreenState extends State<TrainScreen> {
contentPadding: EdgeInsets.symmetric(horizontal: 1), contentPadding: EdgeInsets.symmetric(horizontal: 1),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
selectedItem ?? "Select", selectedItem ?? "Select",
@ -726,8 +745,11 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
selectedTo = countryMap.entries selectedTo =
.firstWhere((entry) => entry.value == newValue) countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key; .key;
}); });
}, },
@ -736,19 +758,11 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
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( 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: [
@ -757,7 +771,8 @@ class _TrainScreenState extends State<TrainScreen> {
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(
@ -779,8 +794,11 @@ class _TrainScreenState extends State<TrainScreen> {
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,
),
), ),
), ),
), ),
@ -789,19 +807,11 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
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: [
@ -810,7 +820,8 @@ class _TrainScreenState extends State<TrainScreen> {
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(
@ -832,8 +843,11 @@ class _TrainScreenState extends State<TrainScreen> {
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,
),
), ),
), ),
), ),
@ -842,10 +856,7 @@ 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( Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
], ],
], ],
), ),
@ -862,7 +873,8 @@ class _TrainScreenState extends State<TrainScreen> {
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(
@ -890,9 +902,7 @@ class _TrainScreenState extends State<TrainScreen> {
], ],
), ),
if (isDesktop) Spacer(), if (isDesktop) Spacer(),
SizedBox( SizedBox(height: 5),
height: 5,
),
Column( Column(
children: [ children: [
Row( Row(
@ -900,7 +910,7 @@ class _TrainScreenState extends State<TrainScreen> {
children: _handleAction(isDesktop), children: _handleAction(isDesktop),
), ),
], ],
) ),
]; ];
} }
@ -913,9 +923,7 @@ class _TrainScreenState extends State<TrainScreen> {
}, },
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(
@ -924,7 +932,6 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
), ),
SizedBox(width: 10), // Space between buttons SizedBox(width: 10), // Space between buttons
// Save Changes Button // Save Changes Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
@ -932,9 +939,7 @@ class _TrainScreenState extends State<TrainScreen> {
}, },
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

@ -23,6 +23,7 @@ import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart'; import '../../services/apiService.dart';
import '../../widgets/custom_radio_button.dart'; import '../../widgets/custom_radio_button.dart';
import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_field.dart';
import '../../widgets/saving_loader.dart';
import '../approvals/approval_dialogs.dart'; import '../approvals/approval_dialogs.dart';
import '../dialog/user_selection_dialog.dart'; import '../dialog/user_selection_dialog.dart';
import '../itnerary/flights.dart'; import '../itnerary/flights.dart';
@ -909,8 +910,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
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
selectedCostCenterId ??= // selectedCostCenterId ??=
costCenterIds.isNotEmpty ? costCenterIds.first : null; // costCenterIds.isNotEmpty ? costCenterIds.first : null;
}); });
print('plansJSON'); print('plansJSON');
@ -1069,11 +1070,16 @@ class CreateNewPlansState extends State<CreateNewPlan> {
miscellaneousList, miscellaneousList,
]; ];
bool anyServiceSelected = serviceLists.any( // bool anyServiceSelected = serviceLists.any(
(list) => list != null && list.isNotEmpty, // (list) => list != null && list.isNotEmpty,
); // );
bool anyServiceSelected = serviceLists.any((list) {
return list != null &&
list.any((entry) => entry['is_active'].toString() == "1");
});
if (!anyServiceSelected) { if (!anyServiceSelected) {
// validationErrors["services"] = "Please select at least one service"; validationErrors["services"] = "Please select at least one service";
setState(() { setState(() {
temporaryMessage = "Please select at least one service"; temporaryMessage = "Please select at least one service";
@ -1172,7 +1178,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
} }
void handleSubmit() { void handleSubmit() {
setState(() { setState(() async {
if (validateForm() && temporaryMessage == null) { if (validateForm() && temporaryMessage == null) {
// 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
@ -1183,24 +1189,17 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// "${planData['traveller_id']}," // "${planData['traveller_id']},"
// " ${selectedPlanId}, " // " ${selectedPlanId}, "
// " "); // " ");
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const SavingLoader(),
);
// postPlanData(planData);
postPlanData(planData); await postPlanData(planData);
final currentUri = // Close loading dialog (ONLY if still mounted)
GoRouterState.of( if (mounted) Navigator.of(context, rootNavigator: true).pop();
context,
).uri.toString(); // safer than `.location`
print("currentUri - $currentUri");
if (currentUri == "/allTrips/trips") {
context.go('/listAllPlan');
} else if (currentUri == "/createPlan") {
context.go('/listPlan');
} else {
widget.isApprover
? context.go('/approvallist')
: context.go('/listPlan');
}
} }
}); });
} }
@ -1233,6 +1232,22 @@ class CreateNewPlansState extends State<CreateNewPlan> {
if (response.statusCode == 200) { if (response.statusCode == 200) {
print("Plan submitted successfully!"); print("Plan submitted successfully!");
print("Response: ${response.body}"); print("Response: ${response.body}");
final currentUri =
GoRouterState.of(
context,
).uri.toString(); // safer than `.location`
print("currentUri - $currentUri");
if (currentUri == "/allTrips/trips") {
context.go('/listAllPlan');
} else if (currentUri == "/createPlan") {
context.go('/listPlan');
} else {
widget.isApprover
? context.go('/approvallist')
: context.go('/listPlan');
}
} 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}");
@ -2184,6 +2199,14 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// ), // ),
// ), // ),
), ),
if (validationErrors["cost_center_id"] != null)
Padding(
padding: EdgeInsets.only(top: 4),
child: Text(
validationErrors["cost_center_id"]!,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.red),
),
),
], ],
), ),
SizedBox(width: 25, height: 5), SizedBox(width: 25, height: 5),
@ -2307,6 +2330,14 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
), ),
), ),
if (validationErrors["purpose_of_travel"] != null)
Padding(
padding: EdgeInsets.only(top: 4),
child: Text(
validationErrors["purpose_of_travel"]!,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.red),
),
),
// CustomTextFieldWrapper( // CustomTextFieldWrapper(
// isFocused: false, // Dropdown doesn't use focus // isFocused: false, // Dropdown doesn't use focus
@ -2466,6 +2497,14 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
), ),
), ),
if (validationErrors["functional_department"] != null)
Padding(
padding: EdgeInsets.only(top: 4),
child: Text(
validationErrors["functional_department"]!,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.red),
),
),
], ],
), ),
]; ];
@ -2475,7 +2514,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"},
]; ];
return [ return [

View File

@ -39,8 +39,8 @@ class DynamicItinerary extends StatefulWidget {
final GlobalKey<FlightScreenState> flightScreenKey; final GlobalKey<FlightScreenState> flightScreenKey;
final ValueNotifier<String?> tripTypeNotifier; final ValueNotifier<String?> tripTypeNotifier;
const DynamicItinerary( const DynamicItinerary({
{super.key, super.key,
required this.apiData, required this.apiData,
required this.onItineraryUpdate, required this.onItineraryUpdate,
required this.apiCountryData, required this.apiCountryData,
@ -51,7 +51,8 @@ class DynamicItinerary extends StatefulWidget {
this.tripType, this.tripType,
this.apiDataForClass, this.apiDataForClass,
required this.tripTypeNotifier, required this.tripTypeNotifier,
required this.flightScreenKey}); required this.flightScreenKey,
});
@override @override
DynamicItineraryState createState() => DynamicItineraryState(); DynamicItineraryState createState() => DynamicItineraryState();
@ -135,7 +136,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
if (rawServices != null && rawServices is String) { if (rawServices != null && rawServices is String) {
try { try {
List<dynamic> decoded = json.decode(rawServices); List<dynamic> decoded = json.decode(rawServices);
List<Map<String, String>> formatted = decoded List<Map<String, String>> formatted =
decoded
.map((e) => {"service_id": e['service_id'].toString()}) .map((e) => {"service_id": e['service_id'].toString()})
.toList(); .toList();
@ -168,7 +170,7 @@ class DynamicItineraryState extends State<DynamicItinerary> {
"insurance", "insurance",
"visa", "visa",
"miscellaneous", "miscellaneous",
"taxi" "taxi",
]; ];
} else { } else {
// tripType is null or not 1/2, allow everything // tripType is null or not 1/2, allow everything
@ -242,21 +244,25 @@ class DynamicItineraryState extends State<DynamicItinerary> {
final selectedIds = final selectedIds =
selectedOrgServiceIds.map((e) => e['service_id']).toSet(); selectedOrgServiceIds.map((e) => e['service_id']).toSet();
final additionalServices = selectedAllServices!.where((service) { final additionalServices =
selectedAllServices!.where((service) {
final name = (service['name'] ?? "").toString().toLowerCase(); final name = (service['name'] ?? "").toString().toLowerCase();
final id = service['service_id'].toString(); final id = service['service_id'].toString();
final isNameAllowed = final isNameAllowed =
allowedServiceNames.isEmpty || allowedServiceNames.contains(name); allowedServiceNames.isEmpty ||
allowedServiceNames.contains(name);
return filledItineraryKeys.contains(name) && return filledItineraryKeys.contains(name) &&
!selectedIds.contains(id) && !selectedIds.contains(id) &&
isNameAllowed; isNameAllowed;
}).toList(); }).toList();
final originalFiltered = selectedAllServices!.where((service) { final originalFiltered =
selectedAllServices!.where((service) {
final name = (service['name'] ?? "").toString().toLowerCase(); final name = (service['name'] ?? "").toString().toLowerCase();
final id = service['service_id'].toString(); final id = service['service_id'].toString();
final isNameAllowed = final isNameAllowed =
allowedServiceNames.isEmpty || allowedServiceNames.contains(name); allowedServiceNames.isEmpty ||
allowedServiceNames.contains(name);
return selectedIds.contains(id) && isNameAllowed; return selectedIds.contains(id) && isNameAllowed;
}).toList(); }).toList();
@ -266,21 +272,25 @@ class DynamicItineraryState extends State<DynamicItinerary> {
}); });
print( print(
"Services chosen based on filled keys + selected: $ServicesChoosed"); "Services chosen based on filled keys + selected: $ServicesChoosed",
);
} else { } else {
final selectedIds = final selectedIds =
selectedOrgServiceIds.map((e) => e['service_id']).toSet(); selectedOrgServiceIds.map((e) => e['service_id']).toSet();
final filtered = selectedAllServices!.where((service) { final filtered =
selectedAllServices!.where((service) {
final name = (service['name'] ?? "").toString().toLowerCase(); final name = (service['name'] ?? "").toString().toLowerCase();
final isNameAllowed = final isNameAllowed =
allowedServiceNames.isEmpty || allowedServiceNames.contains(name); allowedServiceNames.isEmpty ||
allowedServiceNames.contains(name);
return selectedIds.contains(service['service_id'].toString()) && return selectedIds.contains(service['service_id'].toString()) &&
isNameAllowed; isNameAllowed;
}).toList(); }).toList();
setState(() { setState(() {
ServicesChoosed = filtered ServicesChoosed =
filtered
..sort((a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0)); ..sort((a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0));
}); });
@ -296,23 +306,32 @@ class DynamicItineraryState extends State<DynamicItinerary> {
setState(() { setState(() {
itineraryData = { itineraryData = {
"Train": List<Map<String, dynamic>>.from( "Train": List<Map<String, dynamic>>.from(
widget.selectedPlanData['train'] ?? []), widget.selectedPlanData['train'] ?? [],
),
"Bus": List<Map<String, dynamic>>.from( "Bus": List<Map<String, dynamic>>.from(
widget.selectedPlanData['bus'] ?? []), widget.selectedPlanData['bus'] ?? [],
),
"Taxi": List<Map<String, dynamic>>.from( "Taxi": List<Map<String, dynamic>>.from(
widget.selectedPlanData['taxi'] ?? []), widget.selectedPlanData['taxi'] ?? [],
),
"Miscellaneous": List<Map<String, dynamic>>.from( "Miscellaneous": List<Map<String, dynamic>>.from(
widget.selectedPlanData['miscellaneous'] ?? []), widget.selectedPlanData['miscellaneous'] ?? [],
),
"Flight": List<Map<String, dynamic>>.from( "Flight": List<Map<String, dynamic>>.from(
widget.selectedPlanData['flight'] ?? []), widget.selectedPlanData['flight'] ?? [],
),
"Accomodation": List<Map<String, dynamic>>.from( "Accomodation": List<Map<String, dynamic>>.from(
widget.selectedPlanData['accomodation'] ?? []), widget.selectedPlanData['accomodation'] ?? [],
),
"Insurance": List<Map<String, dynamic>>.from( "Insurance": List<Map<String, dynamic>>.from(
widget.selectedPlanData['insurance'] ?? []), widget.selectedPlanData['insurance'] ?? [],
),
"Visa": List<Map<String, dynamic>>.from( "Visa": List<Map<String, dynamic>>.from(
widget.selectedPlanData['visa'] ?? []), widget.selectedPlanData['visa'] ?? [],
),
"Forex": List<Map<String, dynamic>>.from( "Forex": List<Map<String, dynamic>>.from(
widget.selectedPlanData['forex'] ?? []), widget.selectedPlanData['forex'] ?? [],
),
}; };
}); });
} else { } else {
@ -330,7 +349,7 @@ class DynamicItineraryState extends State<DynamicItinerary> {
"accomodation", "accomodation",
"insurance", "insurance",
"visa", "visa",
"forex" "forex",
]; ];
// for (String key in keys) { // for (String key in keys) {
@ -416,7 +435,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
if (existingId != null && existingId != 0) { if (existingId != null && existingId != 0) {
// int itemId = itemList.indexWhere((item) => item["id"] == existingId); // int itemId = itemList.indexWhere((item) => item["id"] == existingId);
int itemId = itemList.indexWhere( int itemId = itemList.indexWhere(
(item) => item[idKey]?.toString() == existingId.toString()); (item) => item[idKey]?.toString() == existingId.toString(),
);
if (itemId != -1) { if (itemId != -1) {
print(" Updating existing item with id: $existingId"); print(" Updating existing item with id: $existingId");
@ -428,8 +448,9 @@ class DynamicItineraryState extends State<DynamicItinerary> {
// CASE 1: Update if indx exists in list // CASE 1: Update if indx exists in list
if (existingIndex != null && existingIndex != 0) { if (existingIndex != null && existingIndex != 0) {
int itemIndex = int itemIndex = itemList.indexWhere(
itemList.indexWhere((item) => item["indx"] == existingIndex); (item) => item["indx"] == existingIndex,
);
if (itemIndex != -1) { if (itemIndex != -1) {
print("Updating existing item with indx: $existingIndex"); print("Updating existing item with indx: $existingIndex");
newData["is_active"] = "1"; newData["is_active"] = "1";
@ -494,6 +515,7 @@ class DynamicItineraryState extends State<DynamicItinerary> {
}); });
print(" onItineraryUpdate - $type - ${itineraryData[type]!} "); print(" onItineraryUpdate - $type - ${itineraryData[type]!} ");
widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
print("ItienreayDATE - $itineraryData");
} }
// void handleItineraryUpdate(String type, Map<String, dynamic> newData) { // void handleItineraryUpdate(String type, Map<String, dynamic> newData) {
@ -583,8 +605,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
onOpen: handleEdit, onOpen: handleEdit,
onAddNew: handlecreateNewPlan, onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode, isViewMode: widget.isViewMode,
onDeleteAccommodation: (data) => onDeleteAccommodation:
handleItinerarydelete("Accomodation", data), (data) => handleItinerarydelete("Accomodation", data),
); );
break; break;
case "Miscellaneous": case "Miscellaneous":
@ -594,8 +616,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
isViewMode: widget.isViewMode, isViewMode: widget.isViewMode,
onAddNew: handlecreateNewPlan, onAddNew: handlecreateNewPlan,
apiData: widget.apiData, apiData: widget.apiData,
onDeleteMiscellaneous: (data) => onDeleteMiscellaneous:
handleItinerarydelete("Miscellaneous", data), (data) => handleItinerarydelete("Miscellaneous", data),
); );
break; break;
case "Flight": case "Flight":
@ -608,7 +630,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
onAddNew: handlecreateNewPlan, onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode, isViewMode: widget.isViewMode,
apiData: widget.apiData, apiData: widget.apiData,
onDeleteFlight: (data) => handleItinerarydelete("Flight", data)); onDeleteFlight: (data) => handleItinerarydelete("Flight", data),
);
break; break;
} }
@ -621,7 +644,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
loginUser: widget.loginUser, loginUser: widget.loginUser,
onSavetrain: (data) => handleItineraryUpdate("Train", data), onSavetrain: (data) => handleItineraryUpdate("Train", data),
tripType: widget.tripType, tripType: widget.tripType,
selectedItem: selectedItem); selectedItem: selectedItem,
);
break; break;
case "Taxi": case "Taxi":
selectedWidget = TaxiScreen( selectedWidget = TaxiScreen(
@ -629,7 +653,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
apiData: widget.apiData, apiData: widget.apiData,
loginUser: widget.loginUser, loginUser: widget.loginUser,
onSavetaxi: (data) => handleItineraryUpdate("Taxi", data), onSavetaxi: (data) => handleItineraryUpdate("Taxi", data),
selectedItem: selectedItem); selectedItem: selectedItem,
);
break; break;
case "Bus": case "Bus":
selectedWidget = BusScreen( selectedWidget = BusScreen(
@ -637,7 +662,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
apiData: widget.apiData, apiData: widget.apiData,
loginUser: widget.loginUser, loginUser: widget.loginUser,
onSaveBus: (data) => handleItineraryUpdate("Bus", data), onSaveBus: (data) => handleItineraryUpdate("Bus", data),
selectedItem: selectedItem); selectedItem: selectedItem,
);
break; break;
case "Insurance": case "Insurance":
selectedWidget = InsuranceScreen( selectedWidget = InsuranceScreen(
@ -666,8 +692,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
onClose: handleClose, onClose: handleClose,
apiData: widget.apiData, apiData: widget.apiData,
loginUser: widget.loginUser, loginUser: widget.loginUser,
onSaveMiscellaneous: (data) => onSaveMiscellaneous:
handleItineraryUpdate("Miscellaneous", data), (data) => handleItineraryUpdate("Miscellaneous", data),
selectedItem: selectedItem, selectedItem: selectedItem,
selectedIndex: selectedIndex, selectedIndex: selectedIndex,
); );
@ -676,8 +702,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
selectedWidget = AccomodationScreen( selectedWidget = AccomodationScreen(
onClose: handleClose, onClose: handleClose,
loginUser: widget.loginUser, loginUser: widget.loginUser,
onSaveAccomadation: (data) => onSaveAccomadation:
handleItineraryUpdate("Accomodation", data), (data) => handleItineraryUpdate("Accomodation", data),
selectedItem: selectedItem, selectedItem: selectedItem,
flightData: itineraryData["Flight"]!, flightData: itineraryData["Flight"]!,
); );
@ -776,7 +802,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
// ); // );
// }); // });
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile; bool isMobile = sizingInfo.isMobile;
return Stack( return Stack(
@ -785,7 +812,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
// Second container (yellow box) // Second container (yellow box)
Container( Container(
margin: EdgeInsets.only( margin: EdgeInsets.only(
top: 40), // Push it down to make room for the tab bar top: 40,
), // Push it down to make room for the tab bar
padding: EdgeInsets.all(12), padding: EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, // Card background color: Colors.white, // Card background
@ -835,12 +863,11 @@ class DynamicItineraryState extends State<DynamicItinerary> {
), ),
], ],
), ),
child: isMobile child:
isMobile
? SingleChildScrollView( ? SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(children: _buildOptions()),
children: _buildOptions(),
),
) )
: Row( : Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
@ -850,11 +877,31 @@ class DynamicItineraryState extends State<DynamicItinerary> {
), ),
], ],
); );
}); },
);
}
bool hasValidItineraryEntries() {
if (ServicesChoosed == null || ServicesChoosed!.isEmpty) return false;
for (var service in ServicesChoosed!) {
final serviceName = service['name'];
final entries = itineraryData[serviceName];
// Check if there is at least one active entry (is_active == 1)
final hasActive =
entries?.any((entry) => entry['is_active'] == 1) ?? false;
if (!hasActive) {
return false; // Fail fast if any one service has no active entries
}
}
return true; // All selected services have at least one active entry
} }
List<Widget> _buildOptions() { List<Widget> _buildOptions() {
if (ServicesChoosed == null) return []; if (ServicesChoosed == null && !hasValidItineraryEntries()) return [];
if (ServicesChoosed != null && if (ServicesChoosed != null &&
ServicesChoosed!.isNotEmpty && ServicesChoosed!.isNotEmpty &&
@ -875,18 +922,28 @@ class DynamicItineraryState extends State<DynamicItinerary> {
// } // }
return ServicesChoosed!.map((service) { return ServicesChoosed!.map((service) {
final serviceName = service['name'];
final serviceEntries = itineraryData[serviceName];
final hasActive =
serviceEntries?.any((entry) => entry['is_active'] == "1") ?? false;
print("Service1: $serviceName");
print("Entries1: $serviceEntries");
print("Has Active1: $hasActive");
return Padding( return Padding(
padding: const EdgeInsets.only(right: 20.0), padding: const EdgeInsets.only(right: 20.0),
child: _buildOption( child: _buildOption(
service, itineraryData[service['name']]?.isNotEmpty ?? false), service,
hasActive,
// itineraryData[service['name']]?.isNotEmpty ?? false,
),
); );
}).toList(); }).toList();
} }
Widget _buildOption( Widget _buildOption(Map<String, dynamic> service, bool hasData) {
Map<String, dynamic> service,
bool hasData,
) {
String name = service['name']; String name = service['name'];
String iconUrl = service['icon']; // Can be empty string String iconUrl = service['icon']; // Can be empty string
IconData fallbackIcon = _getLocalIconForService(name); IconData fallbackIcon = _getLocalIconForService(name);
@ -921,7 +978,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
return Icon( return Icon(
fallbackIcon, fallbackIcon,
size: 25, size: 25,
color: isOptionSelected color:
isOptionSelected
? Color(0xFF114D8B) ? Color(0xFF114D8B)
: Color(0xFF475569), : Color(0xFF475569),
); );
@ -930,9 +988,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
: Icon( : Icon(
fallbackIcon, fallbackIcon,
size: 25, size: 25,
color: isOptionSelected color:
? Color(0xFF114D8B) isOptionSelected ? Color(0xFF114D8B) : Color(0xFF475569),
: Color(0xFF475569),
), ),
SizedBox(height: 2), SizedBox(height: 2),
Row( Row(
@ -943,10 +1000,10 @@ class DynamicItineraryState extends State<DynamicItinerary> {
// style: GoogleFonts.poppins( fontSize: 12, // style: GoogleFonts.poppins( fontSize: 12,
// fontWeight: FontWeight.w600, // fontWeight: FontWeight.w600,
// color: Color(0xFF575A74)) // color: Color(0xFF575A74))
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: isOptionSelected color:
isOptionSelected
? Color(0xFF114D8B) ? Color(0xFF114D8B)
: Color(0xFF475569), : Color(0xFF475569),
fontFamily: "Inter", fontFamily: "Inter",
@ -964,86 +1021,87 @@ class DynamicItineraryState extends State<DynamicItinerary> {
); );
} }
Widget _buildOption1( // Widget _buildOption1(
Map<String, dynamic> service, // Map<String, dynamic> service,
bool hasData, // bool hasData,
) { // )
String name = service['name']; // {
String iconUrl = service['icon']; // Can be empty string // String name = service['name'];
// Optional: define local icon fallback if iconUrl is empty // String iconUrl = service['icon']; // Can be empty string
IconData fallbackIcon = _getLocalIconForService(name); // // Optional: define local icon fallback if iconUrl is empty
// final idMap = {"service_id": service['service_id'].toString()}; // IconData fallbackIcon = _getLocalIconForService(name);
// final isSelected = selectedServiceIds.contains(idMap); // // final idMap = {"service_id": service['service_id'].toString()};
// // final isSelected = selectedServiceIds.contains(idMap);
String serviceId = service['service_id'].toString(); //
// bool isSelected = selectedServiceIds.contains(serviceId); // String serviceId = service['service_id'].toString();
// bool isSelected = // // bool isSelected = selectedServiceIds.contains(serviceId);
// selectedServiceIds.any((item) => item["service_id"] == serviceId); // // bool isSelected =
// // selectedServiceIds.any((item) => item["service_id"] == serviceId);
return GestureDetector( //
onTap: () { // return GestureDetector(
setState(() { // onTap: () {
selectedListOption = name; // setState(() {
isSelected = false; // selectedListOption = name;
}); // isSelected = false;
}, // });
child: Row(children: [ // },
iconUrl.isNotEmpty // child: Row(children: [
? Image.network( // iconUrl.isNotEmpty
iconUrl, // ? Image.network(
width: 18, // iconUrl,
height: 18, // width: 18,
errorBuilder: (context, error, stackTrace) { // height: 18,
return Icon( // errorBuilder: (context, error, stackTrace) {
fallbackIcon, // return Icon(
size: 18, // fallbackIcon,
color: selectedListOption == name // size: 18,
? Color(0xFF114D8B) // color: selectedListOption == name
: Color(0xFF475569), // ? Color(0xFF114D8B)
); // : Color(0xFF475569),
}, // );
) // },
: Icon( // )
fallbackIcon, // : Icon(
size: 18, // fallbackIcon,
color: selectedListOption == name // size: 18,
? Color(0xFF114D8B) // color: selectedListOption == name
: Color(0xFF475569), // ? Color(0xFF114D8B)
), // : Color(0xFF475569),
// ),
SizedBox(width: 2), //
Text( // SizedBox(width: 2),
name, // Text(
style: TextStyle( // name,
fontSize: 14, // style: TextStyle(
// color: selectedListOption == title ? Colors.blueAccent : Color(0xFF575A74), // fontSize: 14,
color: selectedListOption == name // // color: selectedListOption == title ? Colors.blueAccent : Color(0xFF575A74),
? Color(0xFF114D8B) // color: selectedListOption == name
: Color(0xFF475569), // ? Color(0xFF114D8B)
fontFamily: "Archivo", // : Color(0xFF475569),
fontWeight: selectedListOption == name // fontFamily: "Archivo",
? FontWeight.bold // fontWeight: selectedListOption == name
: FontWeight.w500), // ? FontWeight.bold
// fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)), // : FontWeight.w500),
), // // fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
// ),
SizedBox(width: 2), //
// if (selectedListOption == title && widget.isViewMode == false) // SizedBox(width: 2),
if (hasData) // // if (selectedListOption == title && widget.isViewMode == false)
Icon(Icons.circle, size: 8, color: Colors.green // if (hasData)
// color: Colors.grey, // Icon(Icons.circle, size: 8, color: Colors.green
) // // color: Colors.grey,
// Container( // )
// height: 10, // // Container(
// width: 10, // // height: 10,
// // decoration: BoxDecoration( // // width: 10,
// // shape: BoxShape.circle, // // // decoration: BoxDecoration(
// // border: Border.all(color: Colors.green, width: 1.5), // // // shape: BoxShape.circle,
// // ), // // // border: Border.all(color: Colors.green, width: 1.5),
// child:), // // // ),
]), // // child:),
); // ]),
} // );
// }
IconData _getLocalIconForService(String name) { IconData _getLocalIconForService(String name) {
switch (name.toLowerCase()) { switch (name.toLowerCase()) {

View File

@ -79,14 +79,15 @@ class _PolicyState extends State<Policy> {
List<Map<String, dynamic>>? policy_details = []; List<Map<String, dynamic>>? policy_details = [];
Map<String, dynamic> get policyData { Map<String, dynamic> get policyData {
List<Map<String, dynamic>> policyDetails = policy_details!.where((service) { List<Map<String, dynamic>> policyDetails =
policy_details!.where((service) {
// Only check these specific fields for emptiness // Only check these specific fields for emptiness
final fieldsToCheck = [ final fieldsToCheck = [
'cost', 'cost',
'class', 'class',
'a1_action', 'a1_action',
'a2_action', 'a2_action',
'a3_action' 'a3_action',
]; ];
// If any of the important fields has a value, keep it // If any of the important fields has a value, keep it
@ -124,8 +125,9 @@ class _PolicyState extends State<Policy> {
loadInitialData(); loadInitialData();
if (widget.policy != null) { if (widget.policy != null) {
final details = final details = List<Map<String, dynamic>>.from(
List<Map<String, dynamic>>.from(widget.policy!['policy_details']); widget.policy!['policy_details'],
);
policyCriteriaKey.currentState?.loadPolicyDetails(details); policyCriteriaKey.currentState?.loadPolicyDetails(details);
policyCriteriaKey.currentState?.fetchTrainFlightClass(); policyCriteriaKey.currentState?.fetchTrainFlightClass();
@ -138,11 +140,13 @@ class _PolicyState extends State<Policy> {
String? bodyStringColor = await getBodyColor(); String? bodyStringColor = await getBodyColor();
setState(() { setState(() {
layoutColor = layoutString != null layoutColor =
layoutString != null
? Color(int.parse(layoutString)) ? Color(int.parse(layoutString))
: Colors.redAccent; : Colors.redAccent;
bodyColor = bodyStringColor != null bodyColor =
bodyStringColor != null
? Color(int.parse(bodyStringColor)) ? Color(int.parse(bodyStringColor))
: Colors.white; : Colors.white;
}); });
@ -175,7 +179,8 @@ class _PolicyState extends State<Policy> {
if (rawServices != null && rawServices is String) { if (rawServices != null && rawServices is String) {
try { try {
List<dynamic> decoded = json.decode(rawServices); List<dynamic> decoded = json.decode(rawServices);
List<Map<String, String>> formatted = decoded List<Map<String, String>> formatted =
decoded
.map((e) => {"service_id": e['service_id'].toString()}) .map((e) => {"service_id": e['service_id'].toString()})
.toList(); .toList();
@ -205,16 +210,21 @@ class _PolicyState extends State<Policy> {
selectedOrgServiceIds.map((e) => e['service_id']).toSet(); selectedOrgServiceIds.map((e) => e['service_id']).toSet();
if (widget.policy != null) { if (widget.policy != null) {
final details = final details = List<Map<String, dynamic>>.from(
List<Map<String, dynamic>>.from(widget.policy!['policy_details']); widget.policy!['policy_details'],
);
print( print(
"UUFiltered Selected Services - ${widget.policy!['services_ids']} "); "UUFiltered Selected Services - ${widget.policy!['services_ids']} ",
);
// pr int("UUFiltered Selected Services - $details"); // pr int("UUFiltered Selected Services - $details");
final filtered = selectedAllServices! final filtered =
.where((service) => selectedAllServices!
selectedIds.contains(service['service_id'].toString())) .where(
(service) =>
selectedIds.contains(service['service_id'].toString()),
)
.toList(); .toList();
setState(() { setState(() {
@ -236,12 +246,15 @@ class _PolicyState extends State<Policy> {
final decoded = jsonDecode(widget.policy!['services_ids']); final decoded = jsonDecode(widget.policy!['services_ids']);
setState(() { setState(() {
services = List<Map<String, dynamic>>.from(decoded) services =
.map((service) => { List<Map<String, dynamic>>.from(decoded)
.map(
(service) => {
'service_id': service['service_id'].toString(), 'service_id': service['service_id'].toString(),
'name': service['name'].toString(), 'name': service['name'].toString(),
'order': service['order'].toString(), 'order': service['order'].toString(),
}) },
)
.toList(); .toList();
}); });
@ -250,9 +263,12 @@ class _PolicyState extends State<Policy> {
print("Filtered Selected Services Added to Policy: $ServicesChoosed"); print("Filtered Selected Services Added to Policy: $ServicesChoosed");
} else { } else {
final filtered = selectedAllServices! final filtered =
.where((service) => selectedAllServices!
selectedIds.contains(service['service_id'].toString())) .where(
(service) =>
selectedIds.contains(service['service_id'].toString()),
)
.toList(); .toList();
print("ServicesChoosedYY: $ServicesChoosed"); print("ServicesChoosedYY: $ServicesChoosed");
@ -264,13 +280,16 @@ class _PolicyState extends State<Policy> {
// ServicesChoosed = filtered; // ServicesChoosed = filtered;
services = ServicesChoosed! services =
.map((service) => { ServicesChoosed!
.map(
(service) => {
'service_id': service['service_id'].toString(), 'service_id': service['service_id'].toString(),
'name': 'name':
service['name'].toString(), // no space before 'name' service['name'].toString(), // no space before 'name'
'order': service['order'].toString(), 'order': service['order'].toString(),
}) },
)
.toList(); .toList();
}); });
@ -336,7 +355,7 @@ class _PolicyState extends State<Policy> {
// Validate required fields // Validate required fields
if (data["name"] == null || data["name"].toString().trim().isEmpty) { if (data["name"] == null || data["name"].toString().trim().isEmpty) {
errorMessages["name"] = "Policy name is required."; errorMessages["name"] = "Required"; // "Policy name is required.";
} }
// Validate that either domestic or international is selected // Validate that either domestic or international is selected
@ -344,10 +363,12 @@ class _PolicyState extends State<Policy> {
final international = data["international"]?.toString() ?? "0"; final international = data["international"]?.toString() ?? "0";
print( print(
"domestic: ${data["domestic"]}, international: ${data["international"]}"); "domestic: ${data["domestic"]}, international: ${data["international"]}",
);
if (domestic != "1" && international != "1") { if (domestic != "1" && international != "1") {
errorMessages["trip_type"] = "Please select Domestic or International."; errorMessages["trip_type"] =
"Required"; // "Please select Domestic or International.";
} }
// Validate at least one policy_detail with valid content // Validate at least one policy_detail with valid content
@ -359,7 +380,7 @@ class _PolicyState extends State<Policy> {
'class', 'class',
'a1_action', 'a1_action',
'a2_action', 'a2_action',
'a3_action' 'a3_action',
]; ];
return fieldsToCheck.any((field) { return fieldsToCheck.any((field) {
final value = service[field]; final value = service[field];
@ -369,7 +390,7 @@ class _PolicyState extends State<Policy> {
if (!hasAtLeastOneDetail) { if (!hasAtLeastOneDetail) {
errorMessages["policy_details"] = errorMessages["policy_details"] =
"At least one valid policy detail is required."; "Required"; // "At least one valid policy detail is required.";
} }
return errorMessages.isEmpty; return errorMessages.isEmpty;
@ -424,19 +445,24 @@ class _PolicyState extends State<Policy> {
@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: 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:
isDesktop
? EdgeInsets.symmetric( ? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding 0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height * vertical:
MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding 0, // 5% of screen height as vertical padding
) )
: EdgeInsets.all(8), : EdgeInsets.all(8),
@ -446,7 +472,6 @@ class _PolicyState extends State<Policy> {
child: Row( child: Row(
children: [ children: [
// if (isDesktop) CustomDrawer(isDesktop: true), // if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(child: buildData(isDesktop, context)), Expanded(child: buildData(isDesktop, context)),
// Expanded( // Expanded(
// child: Container( // child: Container(
@ -493,7 +518,8 @@ class _PolicyState extends State<Policy> {
// ], // ],
// ), // ),
); );
}); },
);
} }
Widget buildData(bool isDesktop, context) { Widget buildData(bool isDesktop, context) {
@ -524,7 +550,8 @@ class _PolicyState extends State<Policy> {
Container( Container(
color: Colors.white, color: Colors.white,
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: isDesktop child:
isDesktop
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: _buildSubmit(isDesktop), children: _buildSubmit(isDesktop),
@ -544,7 +571,8 @@ class _PolicyState extends State<Policy> {
// 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:
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,
// decoration: BoxDecoration( // decoration: BoxDecoration(
@ -582,7 +610,8 @@ class _PolicyState extends State<Policy> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
], ],
), ),
@ -593,7 +622,8 @@ class _PolicyState extends State<Policy> {
isDesktop ? SizedBox(height: 0) : SizedBox(height: 5), isDesktop ? SizedBox(height: 0) : SizedBox(height: 5),
Container( Container(
padding: isDesktop ? const EdgeInsets.only(left: 35) : null, padding: isDesktop ? const EdgeInsets.only(left: 35) : null,
child: isDesktop child:
isDesktop
? Row( ? Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -611,18 +641,13 @@ class _PolicyState extends State<Policy> {
], ],
), ),
), ),
SizedBox( SizedBox(height: 10),
height: 10, Divider(thickness: 0.1, color: Colors.grey),
),
Divider(
thickness: 0.1,
color: Colors.grey,
),
if (errorMessages["policy_details"] != null) ...[ if (errorMessages["policy_details"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
errorMessages["policy_details"]!, errorMessages["policy_details"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 10), style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
), ),
], ],
isDesktop isDesktop
@ -636,15 +661,17 @@ class _PolicyState extends State<Policy> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
Text( Text(
"Service Priority", "Service Priority",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
) ),
),
], ],
), ),
) )
@ -666,11 +693,7 @@ class _PolicyState extends State<Policy> {
_buildPolicyCategory(isDesktop), _buildPolicyCategory(isDesktop),
], ],
), ),
Column( Column(children: [_buildPolicyCategoryList(isDesktop)]),
children: [
_buildPolicyCategoryList(isDesktop),
],
),
], ],
), ),
), ),
@ -719,11 +742,14 @@ class _PolicyState extends State<Policy> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Policy Name", Text(
"Policy 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),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
isFocused: false, isFocused: false,
@ -736,8 +762,10 @@ class _PolicyState extends State<Policy> {
onChanged: (value) => _clearError("name"), onChanged: (value) => _clearError("name"),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Policy Name", labelText: "Policy Name",
labelStyle: labelStyle: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 12, color: Colors.grey), 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),
@ -747,8 +775,10 @@ class _PolicyState extends State<Policy> {
), ),
if (errorMessages["name"] != null) ...[ if (errorMessages["name"] != null) ...[
SizedBox(height: 5), SizedBox(height: 5),
Text(errorMessages["name"]!, Text(
style: GoogleFonts.poppins(color: Colors.red, fontSize: 10)), errorMessages["name"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
),
], ],
], ],
); );
@ -758,11 +788,14 @@ class _PolicyState extends State<Policy> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Policy Type", Text(
"Policy Type *",
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),
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
@ -770,8 +803,10 @@ class _PolicyState extends State<Policy> {
), ),
if (errorMessages["trip_type"] != null) ...[ if (errorMessages["trip_type"] != null) ...[
SizedBox(height: 5), SizedBox(height: 5),
Text(errorMessages["trip_type"]!, Text(
style: GoogleFonts.poppins(color: Colors.red, fontSize: 10)), errorMessages["trip_type"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
),
], ],
], ],
); );
@ -786,8 +821,10 @@ class _PolicyState extends State<Policy> {
// color: Colors.blueGrey.shade200, // color: Colors.blueGrey.shade200,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.blueGrey.shade100, width: 0.35)), border: Border.all(color: Colors.blueGrey.shade100, width: 0.35),
child: isDesktop ),
child:
isDesktop
? Padding( ? Padding(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
child: Column( child: Column(
@ -900,16 +937,13 @@ class _PolicyState extends State<Policy> {
color: Colors.grey.withOpacity(0.3), color: Colors.grey.withOpacity(0.3),
blurRadius: 2, blurRadius: 2,
offset: const Offset(0, 1), offset: const Offset(0, 1),
) ),
], ],
), ),
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
name, name,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(color: Colors.black, fontSize: 12),
color: Colors.black,
fontSize: 12,
),
), ),
), ),
); );
@ -942,7 +976,7 @@ class _PolicyState extends State<Policy> {
color: Colors.grey.withOpacity(0.3), color: Colors.grey.withOpacity(0.3),
blurRadius: 2, blurRadius: 2,
offset: const Offset(0, 1), offset: const Offset(0, 1),
) ),
], ],
), ),
alignment: Alignment.center, alignment: Alignment.center,
@ -967,10 +1001,10 @@ class _PolicyState extends State<Policy> {
padding: isDesktop ? const EdgeInsets.only(left: 30, top: 8) : null, padding: isDesktop ? const EdgeInsets.only(left: 30, top: 8) : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.62 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.62 : null,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.75 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.75 : null,
child: isDesktop child:
isDesktop
? Container( ? Container(
// color: Colors.amber, // color: Colors.amber,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [_buildPolicyServiceOrdering(isDesktop)], children: [_buildPolicyServiceOrdering(isDesktop)],
@ -992,8 +1026,9 @@ class _PolicyState extends State<Policy> {
} }
// Sort services by 'order' // Sort services by 'order'
ServicesChoosed! ServicesChoosed!.sort(
.sort((a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0)); (a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0),
);
List<String> services = List<String> services =
ServicesChoosed!.map((service) => service['name'].toString()).toList(); ServicesChoosed!.map((service) => service['name'].toString()).toList();
@ -1003,16 +1038,22 @@ class _PolicyState extends State<Policy> {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Flex( child: Flex(
direction: Axis.horizontal, direction: Axis.horizontal,
children: services.asMap().entries.map((entry) { children:
services.asMap().entries.map((entry) {
int index = entry.key + 1; int index = entry.key + 1;
String service = entry.value; String service = entry.value;
String serviceId = index.toString(); String serviceId = index.toString();
bool isSelected = selectedServiceIndex.value == index.toString(); bool isSelected =
selectedServiceIndex.value == index.toString();
return SizedBox( return SizedBox(
// width: isDesktop ? 40 : null, // width: isDesktop ? 40 : null,
height: isDesktop height:
? max((MediaQuery.of(context).size.height * 0.075), 10) isDesktop
? max(
(MediaQuery.of(context).size.height * 0.075),
10,
)
: 45, : 45,
// max((MediaQuery.of(context).size.height * 0.09), 10) // max((MediaQuery.of(context).size.height * 0.09), 10)
@ -1024,7 +1065,8 @@ class _PolicyState extends State<Policy> {
selectedService = service; selectedService = service;
print( print(
" selectedServiceIndex.value - ${selectedServiceIndex.value}"); " selectedServiceIndex.value - ${selectedServiceIndex.value}",
);
// policyCriteriaKey.currentState?.fieldForPolicy(); // policyCriteriaKey.currentState?.fieldForPolicy();
// policyCriteriaKey.currentState // policyCriteriaKey.currentState
@ -1035,7 +1077,8 @@ class _PolicyState extends State<Policy> {
showCost = true; showCost = true;
int serviceCode = selectedService == "Flight" ? 1 : 2; int serviceCode = selectedService == "Flight" ? 1 : 2;
policyCriteriaKey.currentState?.fetchTrainFlightClass(); policyCriteriaKey.currentState
?.fetchTrainFlightClass();
} else if (selectedService == "Accommodation") { } else if (selectedService == "Accommodation") {
showClass = true; showClass = true;
showCost = false; showCost = false;
@ -1047,10 +1090,13 @@ class _PolicyState extends State<Policy> {
}, },
child: Container( child: Container(
margin: const EdgeInsets.all(5), margin: const EdgeInsets.all(5),
padding: isDesktop padding:
isDesktop
? const EdgeInsets.all(8) ? const EdgeInsets.all(8)
: const EdgeInsets.symmetric( : const EdgeInsets.symmetric(
horizontal: 8, vertical: 3), horizontal: 8,
vertical: 3,
),
alignment: Alignment.center, alignment: Alignment.center,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -1058,19 +1104,24 @@ class _PolicyState extends State<Policy> {
Text( Text(
service, service,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
color: isSelected color:
isSelected
? const Color(0xFF114D8B) ? const Color(0xFF114D8B)
: Colors.black87, : Colors.black87,
fontSize: 13, fontSize: 13,
fontWeight: fontWeight:
isSelected ? FontWeight.bold : FontWeight.w500, isSelected
decoration: TextDecoration ? FontWeight.bold
: FontWeight.w500,
decoration:
TextDecoration
.none, // remove built-in underline .none, // remove built-in underline
), ),
), ),
if (isSelected) if (isSelected)
const SizedBox( const SizedBox(
height: 1), // spacing between text and underline height: 1,
), // spacing between text and underline
if (isSelected) if (isSelected)
Container( Container(
height: 2, height: 2,
@ -1080,7 +1131,8 @@ class _PolicyState extends State<Policy> {
], ],
), ),
), ),
)); ),
);
}).toList(), }).toList(),
), ),
), ),
@ -1112,15 +1164,21 @@ class _PolicyState extends State<Policy> {
scrollDirection: isDesktop ? Axis.vertical : Axis.horizontal, scrollDirection: isDesktop ? Axis.vertical : Axis.horizontal,
child: Flex( child: Flex(
direction: isDesktop ? Axis.vertical : Axis.horizontal, direction: isDesktop ? Axis.vertical : Axis.horizontal,
children: services.asMap().entries.map((entry) { children:
services.asMap().entries.map((entry) {
int index = entry.key + 1; int index = entry.key + 1;
String service = entry.value; String service = entry.value;
bool isSelected = selectedServiceIndex.value == index.toString(); bool isSelected =
selectedServiceIndex.value == index.toString();
return SizedBox( return SizedBox(
width: isDesktop ? 180 : null, width: isDesktop ? 180 : null,
height: isDesktop height:
? max((MediaQuery.of(context).size.height * 0.075), 10) isDesktop
? max(
(MediaQuery.of(context).size.height * 0.075),
10,
)
: 45, : 45,
// max((MediaQuery.of(context).size.height * 0.09), 10) // max((MediaQuery.of(context).size.height * 0.09), 10)
@ -1148,9 +1206,15 @@ class _PolicyState extends State<Policy> {
}, },
child: Container( child: Container(
margin: EdgeInsets.all(5), margin: EdgeInsets.all(5),
padding: isDesktop padding:
isDesktop
? EdgeInsets.all(8) ? EdgeInsets.all(8)
: EdgeInsets.only(top: 3, bottom: 3, left: 8, right: 8), : EdgeInsets.only(
top: 3,
bottom: 3,
left: 8,
right: 8,
),
decoration: BoxDecoration( decoration: BoxDecoration(
// color: Colors.blue, // color: Colors.blue,
color: isSelected ? Color(0xFF114D8B) : Colors.white, color: isSelected ? Color(0xFF114D8B) : Colors.white,
@ -1176,10 +1240,12 @@ class _PolicyState extends State<Policy> {
color: isSelected ? Colors.white : Colors.black87, color: isSelected ? Colors.white : Colors.black87,
fontSize: 13, fontSize: 13,
fontWeight: fontWeight:
isSelected ? FontWeight.bold : FontWeight.w100), isSelected ? FontWeight.bold : FontWeight.w100,
), ),
), ),
)); ),
),
);
}).toList(), }).toList(),
), ),
), ),
@ -1210,7 +1276,8 @@ class _PolicyState extends State<Policy> {
}); });
}); });
}, },
)); ),
);
} }
List<Widget> _buildTripType(bool isDesktop) { List<Widget> _buildTripType(bool isDesktop) {
@ -1232,7 +1299,8 @@ class _PolicyState extends State<Policy> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
color: _selectedTripType == "1" ? Colors.white : Colors.black, color: _selectedTripType == "1" ? Colors.white : Colors.black,
fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null, fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null,
fontSize: 13), fontSize: 13,
),
), ),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
@ -1259,11 +1327,12 @@ class _PolicyState extends State<Policy> {
width: _selectedTripType == "1" ? 2 : 1, width: _selectedTripType == "1" ? 2 : 1,
), ),
), ),
child: _selectedTripType == "1" child:
_selectedTripType == "1"
? Icon(Icons.rectangle, size: 8, color: Colors.white) ? Icon(Icons.rectangle, size: 8, color: Colors.white)
: null, // Add checkmark if selected : null, // Add checkmark if selected
), ),
) ),
], ],
), ),
), ),
@ -1314,11 +1383,12 @@ class _PolicyState extends State<Policy> {
width: _selectedTripType == "2" ? 2 : 1, width: _selectedTripType == "2" ? 2 : 1,
), ),
), ),
child: _selectedTripType == "2" child:
_selectedTripType == "2"
? Icon(Icons.rectangle, size: 8, color: Colors.white) ? Icon(Icons.rectangle, size: 8, color: Colors.white)
: null, // Add checkmark if selected : null, // Add checkmark if selected
), ),
) ),
], ],
), ),
@ -1356,15 +1426,12 @@ class _PolicyState extends State<Policy> {
onPressed: () { onPressed: () {
context.go('/PolicyList'); context.go('/PolicyList');
}, },
child: Text( child: Text("Cancel", style: GoogleFonts.poppins(fontSize: 10)),
"Cancel",
style: GoogleFonts.poppins(fontSize: 10),
)),
SizedBox(
width: 20,
), ),
SizedBox(width: 20),
MouseRegion( MouseRegion(
cursor: isViewMode cursor:
isViewMode
? SystemMouseCursors.forbidden ? SystemMouseCursors.forbidden
: SystemMouseCursors.click, : SystemMouseCursors.click,
child: ElevatedButton( child: ElevatedButton(
@ -1384,12 +1451,9 @@ class _PolicyState extends State<Policy> {
), ),
onPressed: onPressed:
isViewMode ? null : handleSubmit, // Disable when in view mode isViewMode ? null : handleSubmit, // Disable when in view mode
child: Text( child: Text("Submit", style: GoogleFonts.poppins(fontSize: 10)),
"Submit",
style: GoogleFonts.poppins(fontSize: 10),
), ),
), ),
)
]; ];
} }
@ -1410,10 +1474,7 @@ class _PolicyState extends State<Policy> {
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
dense: true, dense: true,
title: Text( title: Text("Domestic", style: GoogleFonts.poppins(fontSize: 12)),
"Domestic",
style: GoogleFonts.poppins(fontSize: 12),
),
value: "1", value: "1",
groupValue: _selectedTripType, groupValue: _selectedTripType,
onChanged: (value) { onChanged: (value) {

View File

@ -14,14 +14,12 @@ import '../../services/apiService.dart';
import '../../utils/auth_utils.dart'; import '../../utils/auth_utils.dart';
import '../../utils/pagination.dart'; import '../../utils/pagination.dart';
class PolicyList extends StatefulWidget { class PolicyList extends StatefulWidget {
@override @override
_PolicyListState createState() => _PolicyListState(); _PolicyListState createState() => _PolicyListState();
} }
class _PolicyListState extends State<PolicyList> { class _PolicyListState extends State<PolicyList> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
late Future<List<dynamic>> futurePolicy; late Future<List<dynamic>> futurePolicy;
@ -37,7 +35,6 @@ class _PolicyListState extends State<PolicyList> {
List filteredPolicy = []; List filteredPolicy = [];
TextEditingController searchController = TextEditingController(); TextEditingController searchController = TextEditingController();
int currentPage = 0; int currentPage = 0;
int itemsPerPage = 10; int itemsPerPage = 10;
@ -84,8 +81,6 @@ class _PolicyListState extends State<PolicyList> {
return prefs.getString('auth_token'); return prefs.getString('auth_token');
} }
Future<List<dynamic>> fetchPolicy() async { Future<List<dynamic>> fetchPolicy() async {
final data = await apiService.fetchAllPolicy(); final data = await apiService.fetchAllPolicy();
return data; // Returning raw JSON list return data; // Returning raw JSON list
@ -95,28 +90,35 @@ class _PolicyListState extends State<PolicyList> {
void refreshPolicyList() { void refreshPolicyList() {
setState(() { setState(() {
futurePolicy = fetchPolicy(); // Re-fetch users after status update futurePolicy = fetchPolicy(); // Re-fetch users after status update
futurePolicy.then((object) {
setState(() {
allPolicy = object;
});
});
// Wait for futurePlans to be fetched and update allPlans // Wait for futurePlans to be fetched and update allPlans
}); });
} }
void filterPolicy(String query) { void filterPolicy(String query) {
print("allPolicy before filtering: $query");
final lowerQuery = query.toLowerCase(); final lowerQuery = query.toLowerCase();
setState(() { setState(() {
filteredPolicy = filteredPolicy =
allPolicy.where((object) { allPolicy.where((object) {
return (object['name']?.toLowerCase().contains(lowerQuery) ?? final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive";
return (object['policy_id']?.toLowerCase().contains(lowerQuery) ??
false) || false) ||
(object['name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['policy_type']?.toLowerCase().contains(lowerQuery) ?? (object['policy_type']?.toLowerCase().contains(lowerQuery) ??
false) || false) ||
(object['is_active']?.toLowerCase().contains(lowerQuery) ?? false);}).toList(); (isActiveStatus.contains(lowerQuery));
}).toList();
currentPage = 0; currentPage = 0;
}); });
print("filtered: $filteredPolicy"); print("filteredPolicy: $filteredPolicy");
} }
void handleActiveStatus( void handleActiveStatus(
Map<String, dynamic> policyData, Map<String, dynamic> policyData,
String policyId, String policyId,
@ -159,7 +161,6 @@ class _PolicyListState extends State<PolicyList> {
if (response.statusCode == 200) { if (response.statusCode == 200) {
print("policyData submitted successfully!"); print("policyData submitted successfully!");
print("Response: ${response.body}"); print("Response: ${response.body}");
loadAllGroups(); loadAllGroups();
} else { } else {
print("Failed to submit policyData. Status: ${response.statusCode}"); print("Failed to submit policyData. Status: ${response.statusCode}");
@ -175,31 +176,45 @@ class _PolicyListState extends State<PolicyList> {
print("policystatus: $status"); print("policystatus: $status");
print("policysData: $policydata"); print("policysData: $policydata");
// handleActiveStatus(groupdata, groupId, status);
print("Calling handleActiveStatus with: id=$policyId, status=$status");
handleActiveStatus(policydata, policyId.toString(), status.toString()); handleActiveStatus(policydata, policyId.toString(), status.toString());
} }
Future<void> refreshData() async {
loadAllGroups();
}
Future<void> loadAllGroups() async { Future<void> loadAllGroups() async {
try { try {
final result = await apiService.fetchAllPolicy(); final result = await apiService.fetchAllPolicy();
// Sort by policy_id descending (latest first)
result.sort((a, b) {
int idA = int.tryParse(a['policy_id'].toString()) ?? 0;
int idB = int.tryParse(b['policy_id'].toString()) ?? 0;
return idB.compareTo(idA); // latest first
});
setState(() { setState(() {
futurePolicy = result as Future<List>; allPolicy = result;
}); });
print("Fetched services: $futurePolicy"); print("Fetched services: $allPolicy");
} catch (e) { } catch (e) {
print('Error fetching role list: $e'); print('Error fetching role list: $e');
} }
} }
// Future<void> loadAllGroups_old() async {
// try {
// final result = await apiService.fetchAllPolicy();
//
// // Sort by policy_id descending (latest first)
// result.sort((a, b) {
// int idA = int.tryParse(a['policy_id'].toString()) ?? 0;
// int idB = int.tryParse(b['policy_id'].toString()) ?? 0;
// return idB.compareTo(idA); // latest first
// });
//
// setState(() {
// futurePolicy = result as Future<List>;
// });
// print("Fetched services: $futurePolicy");
// refreshPolicyList();
// } catch (e) {
// print('Error fetching role list: $e');
// }
// }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -362,7 +377,6 @@ class _PolicyListState extends State<PolicyList> {
// Print the resolved value // Print the resolved value
print("CREATELIAS - $policyData"); print("CREATELIAS - $policyData");
context.go('/Policy'); context.go('/Policy');
}, },
child: Row( child: Row(
mainAxisSize: mainAxisSize:
@ -546,7 +560,8 @@ class _PolicyListState extends State<PolicyList> {
rows: rows:
paginatedUser.map((policy) { paginatedUser.map((policy) {
String policyId = String policyId =
policy['policy_id'].toString(); // Get policy ID policy['policy_id']
.toString(); // Get policy ID
bool isSelected = selectedPolicyId == policyId; bool isSelected = selectedPolicyId == policyId;
return DataRow( return DataRow(
@ -588,7 +603,8 @@ class _PolicyListState extends State<PolicyList> {
), ),
DataCell( DataCell(
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment:
MainAxisAlignment.start,
children: [ children: [
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
@ -596,14 +612,23 @@ class _PolicyListState extends State<PolicyList> {
final intPolicyId = final intPolicyId =
rawId is int rawId is int
? rawId ? rawId
: int.tryParse(rawId.toString()) ?? 0; : int.tryParse(
rawId.toString(),
) ??
0;
Map<String, dynamic> policyData = await apiService Map<String, dynamic> policyData =
.getSinglePolicy(intPolicyId); await apiService
.getSinglePolicy(
intPolicyId,
);
print("PolicyDATa: $policyData"); print("PolicyDATa: $policyData");
context.go("/Policy", extra: policyData); context.go(
"/Policy",
extra: policyData,
);
}, },
child: Tooltip( child: Tooltip(
message: 'Edit Policy Details', message: 'Edit Policy Details',
@ -618,13 +643,16 @@ class _PolicyListState extends State<PolicyList> {
GestureDetector( GestureDetector(
onTap: () { onTap: () {
final idStr = policy['policy_id']; final idStr = policy['policy_id'];
final id = int.tryParse(idStr.toString()); final id = int.tryParse(
idStr.toString(),
);
if (id == null) { if (id == null) {
print("group_id is null"); print("group_id is null");
return; return;
} }
final status = policy['is_active']; final status =
policy['is_active'];
deletePolicy(policy, id, status); deletePolicy(policy, id, status);
}, },
@ -635,7 +663,8 @@ class _PolicyListState extends State<PolicyList> {
width: 20, width: 20,
height: 15, height: 15,
), ),
),), ),
),
], ],
), ),
), ),
@ -654,7 +683,10 @@ class _PolicyListState extends State<PolicyList> {
return Card( return Card(
color: Colors.white, color: Colors.white,
margin: EdgeInsets.symmetric(horizontal: 12, vertical: 6), margin: EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
@ -666,7 +698,8 @@ class _PolicyListState extends State<PolicyList> {
children: [ children: [
// Row 1: Policy Name and Actions // Row 1: Policy Name and Actions
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: RichText( child: RichText(
@ -684,11 +717,12 @@ class _PolicyListState extends State<PolicyList> {
), ),
), ),
TextSpan( TextSpan(
text: "${object['name'] ?? 'N/A'}", text:
"${object['name'] ?? 'N/A'}",
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
fontWeight: FontWeight.normal fontWeight: FontWeight.normal,
), ),
), ),
], ],
@ -701,16 +735,26 @@ class _PolicyListState extends State<PolicyList> {
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
final rawId = object['policy_id']; final rawId = object['policy_id'];
final intPolicyId = rawId is int final intPolicyId =
rawId is int
? rawId ? rawId
: int.tryParse(rawId.toString()) ?? 0; : int.tryParse(
rawId.toString(),
) ??
0;
Map<String, dynamic> policyData = Map<String, dynamic> policyData =
await apiService.getSinglePolicy(intPolicyId); await apiService
.getSinglePolicy(
intPolicyId,
);
print("PolicyDATa: $policyData"); print("PolicyDATa: $policyData");
context.go("/Policy", extra: policyData); context.go(
"/Policy",
extra: policyData,
);
}, },
child: Tooltip( child: Tooltip(
message: 'Edit Policy Details', message: 'Edit Policy Details',
@ -725,7 +769,9 @@ class _PolicyListState extends State<PolicyList> {
GestureDetector( GestureDetector(
onTap: () { onTap: () {
final idStr = object['policy_id']; final idStr = object['policy_id'];
final id = int.tryParse(idStr.toString()); final id = int.tryParse(
idStr.toString(),
);
if (id == null) { if (id == null) {
print("policy_id is null"); print("policy_id is null");
@ -750,7 +796,6 @@ class _PolicyListState extends State<PolicyList> {
), ),
SizedBox(height: 8), // Spacing SizedBox(height: 8), // Spacing
// Row 2: Policy Type // Row 2: Policy Type
RichText( RichText(
text: TextSpan( text: TextSpan(
@ -767,13 +812,14 @@ class _PolicyListState extends State<PolicyList> {
), ),
), ),
TextSpan( TextSpan(
text: object['domestic'] == "1" text:
object['domestic'] == "1"
? "Domestic" ? "Domestic"
: "International", : "International",
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
fontWeight: FontWeight.normal fontWeight: FontWeight.normal,
), ),
), ),
], ],
@ -787,7 +833,6 @@ class _PolicyListState extends State<PolicyList> {
); );
} }
Widget buildMobileCardView2(List<dynamic> paginatedUser) { Widget buildMobileCardView2(List<dynamic> paginatedUser) {
return ListView.builder( return ListView.builder(
itemCount: paginatedUser.length, itemCount: paginatedUser.length,
@ -810,7 +855,6 @@ class _PolicyListState extends State<PolicyList> {
children: [ children: [
Column( Column(
children: [ children: [
Expanded( Expanded(
flex: 1, flex: 1,
child: Text( child: Text(
@ -861,14 +905,22 @@ class _PolicyListState extends State<PolicyList> {
final intPolicyId = final intPolicyId =
rawId is int rawId is int
? rawId ? rawId
: int.tryParse(rawId.toString()) ?? 0; : int.tryParse(
rawId.toString(),
) ??
0;
Map<String, dynamic> policyData = await apiService Map<String, dynamic> policyData =
.getSinglePolicy(intPolicyId); await apiService.getSinglePolicy(
intPolicyId,
);
print("PolicyDATa: $policyData"); print("PolicyDATa: $policyData");
context.go("/Policy", extra: policyData); context.go(
"/Policy",
extra: policyData,
);
}, },
child: Tooltip( child: Tooltip(
message: 'Edit Policy Details', message: 'Edit Policy Details',
@ -876,13 +928,16 @@ class _PolicyListState extends State<PolicyList> {
'assets/images/IconsImg/edit.png', 'assets/images/IconsImg/edit.png',
width: 20, width: 20,
height: 15, height: 15,
),), ),
),
), ),
SizedBox(width: 5), SizedBox(width: 5),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
final idStr = object['policy_id']; final idStr = object['policy_id'];
final id = int.tryParse(idStr.toString()); final id = int.tryParse(
idStr.toString(),
);
if (id == null) { if (id == null) {
print("group_id is null"); print("group_id is null");
@ -898,7 +953,8 @@ class _PolicyListState extends State<PolicyList> {
'assets/images/IconsImg/delete.png', 'assets/images/IconsImg/delete.png',
width: 20, width: 20,
height: 15, height: 15,
),), ),
),
), ),
], ],
), ),

View File

@ -19,13 +19,14 @@ class TravellerData extends StatefulWidget {
final int? travellerId; // <-- Add this final int? travellerId; // <-- Add this
final Map<String, dynamic>? travellerData; final Map<String, dynamic>? travellerData;
const TravellerData( const TravellerData({
{super.key, super.key,
required this.isDesktop, required this.isDesktop,
this.layoutColor, this.layoutColor,
required this.fetchGetTraveller, required this.fetchGetTraveller,
this.travellerId, this.travellerId,
this.travellerData}); this.travellerData,
});
@override @override
TravellerDataState createState() => TravellerDataState(); TravellerDataState createState() => TravellerDataState();
@ -49,12 +50,7 @@ class TravellerDataState extends State<TravellerData> {
int? travellerDataId; int? travellerDataId;
late String isActive = "1"; late String isActive = "1";
List<String> dataHeader = [ List<String> dataHeader = ["first_name", "last_name", "email", "mobile"];
"first_name",
"last_name",
"email",
"mobile",
];
Map<String, dynamic> travellerDetails() { Map<String, dynamic> travellerDetails() {
final data = { final data = {
@ -72,7 +68,6 @@ class TravellerDataState extends State<TravellerData> {
void initState() { void initState() {
super.initState(); super.initState();
apiData = null; apiData = null;
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
@ -115,7 +110,6 @@ class TravellerDataState extends State<TravellerData> {
}); });
} }
void toggleStatus() { void toggleStatus() {
setState(() { setState(() {
isActive = isActive == "1" ? "0" : "1"; isActive = isActive == "1" ? "0" : "1";
@ -155,8 +149,9 @@ class TravellerDataState extends State<TravellerData> {
} }
if (data["email"] != null && data["email"].toString().isNotEmpty) { if (data["email"] != null && data["email"].toString().isNotEmpty) {
if (!RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") if (!RegExp(
.hasMatch(data["email"].toString())) { r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
).hasMatch(data["email"].toString())) {
errorMessages["email"] = "Invalid email format"; // Invalid email format errorMessages["email"] = "Invalid email format"; // Invalid email format
} }
} }
@ -195,7 +190,9 @@ class TravellerDataState extends State<TravellerData> {
apiUrldata = '$apiUrl/api/travellers/update/$travellerDataId'; apiUrldata = '$apiUrl/api/travellers/update/$travellerDataId';
travellerData["traveller_id"] = travellerDataId.toString(); travellerData["traveller_id"] = travellerDataId.toString();
travellerData["updated_by"] = userId; travellerData["updated_by"] = userId;
(travellerData.containsKey("created_by")) ? travellerData.remove("created_by") : '' ; (travellerData.containsKey("created_by"))
? travellerData.remove("created_by")
: '';
} else { } else {
print("for add Traveller id - null"); print("for add Traveller id - null");
apiUrldata = '$apiUrl/api/travellers/create'; apiUrldata = '$apiUrl/api/travellers/create';
@ -217,11 +214,11 @@ class TravellerDataState extends State<TravellerData> {
}; };
final body = jsonEncode(travellerData); final body = jsonEncode(travellerData);
final response = travellerDataId != null final response =
travellerDataId != null
? 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) { switch (response.statusCode) {
case 200: case 200:
print("Update - Response: ${response.body}"); print("Update - Response: ${response.body}");
@ -241,7 +238,6 @@ class TravellerDataState extends State<TravellerData> {
print("Failed to submit traveller. Status: ${response.statusCode}"); print("Failed to submit traveller. 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");
} }
@ -249,7 +245,6 @@ class TravellerDataState extends State<TravellerData> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30), contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30),
@ -264,27 +259,30 @@ class TravellerDataState extends State<TravellerData> {
Row( Row(
children: [ children: [
Text( Text(
(travellerDataId != null) ? 'Edit Traveller' : 'Create Traveller', (travellerDataId != null)
style: GoogleFonts.poppins(fontSize: 15, color: Colors.black), ? 'Edit Traveller'
: 'Create Traveller',
style: GoogleFonts.poppins(
fontSize: 15,
color: Colors.black,
),
), ),
const Spacer(), const Spacer(),
], ],
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Divider( Divider(thickness: 0.2, color: Colors.blueGrey.shade100),
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
const SizedBox(height: 5), const SizedBox(height: 5),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"First Name", "First 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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -299,12 +297,16 @@ class TravellerDataState extends State<TravellerData> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "First Name", labelText: "First Name",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey), labelStyle: TextStyle(
fontSize: 11,
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),
), ),
)), ),
),
), ),
if (errorMessages["first_name"] != null) ...[ if (errorMessages["first_name"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -315,18 +317,17 @@ class TravellerDataState extends State<TravellerData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Last Name", "Last 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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -341,12 +342,16 @@ class TravellerDataState extends State<TravellerData> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Last Name", labelText: "Last Name",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey), labelStyle: TextStyle(
fontSize: 11,
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),
), ),
)), ),
),
), ),
if (errorMessages["last_name"] != null) ...[ if (errorMessages["last_name"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -357,18 +362,17 @@ class TravellerDataState extends State<TravellerData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Email", "Email *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -383,12 +387,16 @@ class TravellerDataState extends State<TravellerData> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Email", labelText: "Email",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey), labelStyle: TextStyle(
fontSize: 11,
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),
), ),
)), ),
),
), ),
if (errorMessages["email"] != null) ...[ if (errorMessages["email"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -399,18 +407,17 @@ class TravellerDataState extends State<TravellerData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Mobile", "Mobile *",
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),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -425,12 +432,16 @@ class TravellerDataState extends State<TravellerData> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Mobile", labelText: "Mobile",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey), labelStyle: TextStyle(
fontSize: 11,
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),
), ),
)), ),
),
), ),
if (errorMessages["mobile"] != null) ...[ if (errorMessages["mobile"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
@ -441,9 +452,7 @@ class TravellerDataState extends State<TravellerData> {
], ],
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
if (travellerDataId != null) if (travellerDataId != null)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -453,11 +462,14 @@ class TravellerDataState extends State<TravellerData> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
Tooltip( Tooltip(
message: message:
isActive == "1" ? "Tap to deactivate" : "Tap to activate", isActive == "1"
? "Tap to deactivate"
: "Tap to activate",
child: GestureDetector( child: GestureDetector(
onTap: toggleStatus, onTap: toggleStatus,
child: Text( child: Text(
@ -469,13 +481,10 @@ class TravellerDataState extends State<TravellerData> {
), ),
), ),
), ),
) ),
], ],
), ),
if (travellerDataId != null) if (travellerDataId != null) SizedBox(height: 15),
SizedBox(
height: 15,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -512,18 +521,22 @@ class TravellerDataState extends State<TravellerData> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
child: Text('Save', child: Text(
'Save',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)), fontSize: 11,
color: Colors.white,
),
),
), ),
), ),
], ],
) ),
// : SizedBox.shrink(), // : SizedBox.shrink(),
], ],
), ),
) ),
) ),
); );
} }
} }

View File

@ -67,11 +67,13 @@ class TravellerListState extends State<TravellerList> {
String? bodyStringColor = await getBodyColor(); String? bodyStringColor = await getBodyColor();
setState(() { setState(() {
layoutColor = layoutString != null layoutColor =
layoutString != null
? Color(int.parse(layoutString)) ? Color(int.parse(layoutString))
: Colors.redAccent; : Colors.redAccent;
bodyColor = bodyStringColor != null bodyColor =
bodyStringColor != null
? Color(int.parse(bodyStringColor)) ? Color(int.parse(bodyStringColor))
: Colors.white; : Colors.white;
}); });
@ -98,8 +100,8 @@ class TravellerListState extends State<TravellerList> {
Future<List<dynamic>> fetchGetTraveller() async { Future<List<dynamic>> fetchGetTraveller() async {
String? ordId = await getOrgId(); String? ordId = await getOrgId();
final String apiUrlData = '$apiUrl/api/travellers?org_id=$ordId'; final String apiUrlData =
'$apiUrl/api/travellers?for=table_view&org_id=$ordId';
final String? token = await getToken(); final String? token = await getToken();
@ -143,13 +145,21 @@ class TravellerListState extends State<TravellerList> {
print("all before filtering: $query"); print("all before filtering: $query");
final lowerQuery = query.toLowerCase(); final lowerQuery = query.toLowerCase();
setState(() { setState(() {
filteredTraveller = allTraveller.where((object) { filteredTraveller =
allTraveller.where((object) {
final isActiveStatus = final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive"; object['is_active'] == "1" ? "active" : "inactive";
return (object['traveller_id']?.toLowerCase().contains(lowerQuery) ?? return (object['traveller_id']?.toLowerCase().contains(
lowerQuery,
) ??
false) || false) ||
(object['name']?.toLowerCase().contains(lowerQuery) ?? false) || (object['first_name']?.toLowerCase().contains(lowerQuery) ??
(object['description']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['last_name']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['mobile']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['email']?.toLowerCase().contains(lowerQuery) ??
false) || false) ||
(isActiveStatus.contains(lowerQuery)); (isActiveStatus.contains(lowerQuery));
}).toList(); }).toList();
@ -160,8 +170,10 @@ class TravellerListState extends State<TravellerList> {
@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: Color(0xFFf5f5f5), backgroundColor: Color(0xFFf5f5f5),
@ -170,11 +182,14 @@ class TravellerListState extends State<TravellerList> {
appBar: CustomAppBar(isDesktop: isDesktop), appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false), drawer: CustomDrawer(isDesktop: false),
body: Padding( body: Padding(
padding: isDesktop padding:
isDesktop
? EdgeInsets.symmetric( ? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding 0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height * vertical:
MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding 0, // 5% of screen height as vertical padding
) )
: EdgeInsets.all(0), : EdgeInsets.all(0),
@ -187,7 +202,8 @@ class TravellerListState extends State<TravellerList> {
), ),
), ),
); );
}); },
);
} }
Widget buildGroupList(bool isDesktop) { Widget buildGroupList(bool isDesktop) {
@ -215,7 +231,8 @@ class TravellerListState extends State<TravellerList> {
// ? 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),
// padding: const EdgeInsets.all(10), // padding: const EdgeInsets.all(10),
height: isDesktop height:
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,
@ -247,9 +264,7 @@ class TravellerListState extends State<TravellerList> {
], ],
), ),
if (isDesktop) if (isDesktop)
SizedBox( SizedBox(width: MediaQuery.of(context).size.width * 0.16),
width: MediaQuery.of(context).size.width * 0.16,
),
if (isDesktop) if (isDesktop)
Container( Container(
@ -261,7 +276,9 @@ class TravellerListState extends State<TravellerList> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search ...", hintText: "Search ...",
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 12, color: Color(0xFF9E9DBD)), fontSize: 12,
color: Color(0xFF9E9DBD),
),
prefixIcon: Icon( prefixIcon: Icon(
Icons.search, Icons.search,
color: Color(0xFF9E9DBD), color: Color(0xFF9E9DBD),
@ -273,17 +290,19 @@ class TravellerListState extends State<TravellerList> {
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.grey.shade200, width: 0.5), color: Colors.grey.shade200,
width: 0.5,
),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.grey.shade300, width: 1), color: Colors.grey.shade300,
width: 1,
), ),
), ),
style: GoogleFonts.poppins(
fontSize: 12,
), ),
style: GoogleFonts.poppins(fontSize: 12),
), ),
), ),
// SizedBox(width: 16), // SizedBox(width: 16),
@ -297,16 +316,18 @@ class TravellerListState extends State<TravellerList> {
disabledForegroundColor: Colors.white, disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: side: BorderSide(color: Color(0xFF114D8B), width: 2),
BorderSide(color: Color(0xFF114D8B), width: 2),
), ),
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: 20, vertical: 12), horizontal: 20,
vertical: 12,
),
), ),
onPressed: () async { onPressed: () async {
showDialog( showDialog(
context: context, context: context,
builder: (context) => TravellerData( builder:
(context) => TravellerData(
isDesktop: isDesktop, isDesktop: isDesktop,
layoutColor: layoutColor!, layoutColor: layoutColor!,
fetchGetTraveller: refreshData, fetchGetTraveller: refreshData,
@ -338,10 +359,7 @@ class TravellerListState extends State<TravellerList> {
], ],
), ),
if (!isDesktop) if (!isDesktop) SizedBox(height: 5),
SizedBox(
height: 5,
),
isDesktop isDesktop
? SizedBox.shrink() ? SizedBox.shrink()
: Row( : Row(
@ -356,7 +374,9 @@ class TravellerListState extends State<TravellerList> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search ...", hintText: "Search ...",
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 12, color: Color(0xFF9E9DBD)), fontSize: 12,
color: Color(0xFF9E9DBD),
),
prefixIcon: Icon( prefixIcon: Icon(
Icons.search, Icons.search,
color: Color(0xFF9E9DBD), color: Color(0xFF9E9DBD),
@ -369,17 +389,18 @@ class TravellerListState extends State<TravellerList> {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.grey.shade200, color: Colors.grey.shade200,
width: 0.5), width: 0.5,
),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.grey.shade300, width: 1), color: Colors.grey.shade300,
width: 1,
), ),
), ),
style: GoogleFonts.poppins(
fontSize: 12,
), ),
style: GoogleFonts.poppins(fontSize: 12),
), ),
), ),
// SizedBox(width: 16), // SizedBox(width: 16),
@ -400,16 +421,6 @@ class TravellerListState extends State<TravellerList> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// const Icon(Icons.error_outline,
// color: Colors.redAccent, size: 60),
// const SizedBox(height: 16),
// Text(
// "Oops!",
// style: GoogleFonts.poppins(
// fontSize: 20,
// fontWeight: FontWeight.bold,
// color: Colors.redAccent),
// ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
"No Traveller Available ", "No Traveller Available ",
@ -417,14 +428,17 @@ class TravellerListState extends State<TravellerList> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.grey), color: Colors.grey,
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
"Please Create Traveller Details", "Please Create Traveller Details",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 16, color: Colors.grey), fontSize: 16,
color: Colors.grey,
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
], ],
@ -433,7 +447,8 @@ class TravellerListState extends State<TravellerList> {
); );
} }
/* Here collect the list to displayed the data in table or card Used */ /* Here collect the list to displayed the data in table or card Used */
List<dynamic> object = filteredTraveller.isNotEmpty List<dynamic> object =
filteredTraveller.isNotEmpty
? filteredTraveller ? filteredTraveller
: allTraveller; : allTraveller;
@ -442,12 +457,12 @@ class TravellerListState extends State<TravellerList> {
DateTime dateA = DateTime.parse(a['created_on']); DateTime dateA = DateTime.parse(a['created_on']);
DateTime dateB = DateTime.parse(b['created_on']); DateTime dateB = DateTime.parse(b['created_on']);
return dateB return dateB.compareTo(dateA); // Descending: newest first
.compareTo(dateA); // Descending: newest first
}); });
/* For pagination for list ... */ /* For pagination for list ... */
List paginatedTraveller = object List paginatedTraveller =
object
.skip(currentPage * itemsPerPage) .skip(currentPage * itemsPerPage)
.take(itemsPerPage) .take(itemsPerPage)
.toList(); .toList();
@ -455,8 +470,7 @@ class TravellerListState extends State<TravellerList> {
/* Table ... */ /* Table ... */
Widget table = LayoutBuilder( Widget table = LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
double minWidth = double minWidth = isDesktop ? constraints.maxWidth : 1300;
isDesktop ? constraints.maxWidth : 1300;
return ConstrainedBox( return ConstrainedBox(
constraints: BoxConstraints(minWidth: minWidth), constraints: BoxConstraints(minWidth: minWidth),
@ -465,7 +479,9 @@ class TravellerListState extends State<TravellerList> {
columnSpacing: isDesktop ? 24.0 : 16.0, columnSpacing: isDesktop ? 24.0 : 16.0,
border: TableBorder( border: TableBorder(
horizontalInside: BorderSide( horizontalInside: BorderSide(
width: 0.5, color: Colors.grey.shade200), width: 0.5,
color: Colors.grey.shade200,
),
), ),
columns: [ columns: [
DataColumn( DataColumn(
@ -473,67 +489,90 @@ class TravellerListState extends State<TravellerList> {
'Name', 'Name',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600), fontWeight: FontWeight.w600,
)), ),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Email', 'Email',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600), fontWeight: FontWeight.w600,
)), ),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Mobile', 'Mobile',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600), fontWeight: FontWeight.w600,
)), ),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Status', 'Status',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600), fontWeight: FontWeight.w600,
)), ),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Actions', 'Actions',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600), fontWeight: FontWeight.w600,
)), ),
),
),
], ],
rows: paginatedTraveller.map((tableObject) { rows:
String fullName = '${tableObject['first_name'] ?? ''} ${tableObject['last_name'] ?? ''}'; paginatedTraveller.map((tableObject) {
String fullName =
'${tableObject['first_name'] ?? ''} ${tableObject['last_name'] ?? ''}';
String travellerId = String travellerId =
tableObject['traveller_id'] tableObject['traveller_id']
.toString(); // Get user ID .toString(); // Get user ID
bool isSelected = bool isSelected =
selectedTravellerId == travellerId; selectedTravellerId == travellerId;
return DataRow(cells: [ return DataRow(
DataCell(Text(fullName ?? 'N/A', cells: [
DataCell(
Text(
fullName ?? 'N/A',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
))), ),
),
),
DataCell( DataCell(
Text(tableObject['email'] ?? 'N/A', Text(
tableObject['email'] ?? 'N/A',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
), ),
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis)), overflow: TextOverflow.ellipsis,
),
),
DataCell( DataCell(
Text(tableObject['mobile'] ?? 'N/A', Text(
tableObject['mobile'] ?? 'N/A',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
), ),
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis)), overflow: TextOverflow.ellipsis,
),
),
DataCell( DataCell(
Text( Text(
tableObject['is_active'] == "1" tableObject['is_active'] == "1"
@ -542,52 +581,52 @@ class TravellerListState extends State<TravellerList> {
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
color: tableObject['is_active'] == "1" color:
tableObject['is_active'] == "1"
? Colors.green ? Colors.green
: Colors.red, : Colors.grey,
), ),
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
DataCell( DataCell(
// UserActionsMenu(
// user: forex,
// getUserDetails: (id) =>
// apiService.getSingleUser(id),
// ),
GestureDetector( GestureDetector(
child: Image.asset( child: Image.asset(
'assets/images/IconsImg/edit.png', 'assets/images/IconsImg/edit.png',
width: 20, width: 20,
height: 15), height: 15,
),
onTap: () async { onTap: () async {
// final userId = getUserId(user['user_id']); // final userId = getUserId(user['user_id']);
// final usersData = await getUserDetails(userId); // final usersData = await getUserDetails(userId);
// //
final travellerId = int.tryParse( final travellerId = int.tryParse(
tableObject['traveller_id'] tableObject['traveller_id']
.toString()); .toString(),
);
if (travellerId != null) { if (travellerId != null) {
print( print(
"Table cell - traveller Id -- $travellerId"); "Table cell - traveller Id -- $travellerId",
);
final data = await apiService final data = await apiService
.getTravellerDetailsFind( .getTravellerDetailsFind(
travellerId); travellerId,
);
print("TravellerId -- $data"); print("TravellerId -- $data");
showDialog( showDialog(
context: context, context: context,
builder: (context) => builder:
TravellerData( (context) => TravellerData(
isDesktop: isDesktop, isDesktop: isDesktop,
travellerId: travellerId:
travellerId, // Pass the ID travellerId, // Pass the ID
travellerData: data, travellerData: data,
layoutColor: layoutColor!, layoutColor: layoutColor!,
// fetchGetForex: fetchGetForex, fetchGetTraveller:
fetchGetTraveller: refreshData, refreshData,
// role: // role:
// "Travel Agent" // "Travel Agent"
), ),
@ -598,7 +637,8 @@ class TravellerListState extends State<TravellerList> {
}, },
), ),
), ),
]); ],
);
}).toList(), }).toList(),
), ),
); );
@ -611,11 +651,14 @@ class TravellerListState extends State<TravellerList> {
itemCount: paginatedUser.length, itemCount: paginatedUser.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final cardObject = paginatedUser[index]; final cardObject = paginatedUser[index];
String fullName = '${cardObject['first_name'] ?? ''} ${cardObject['last_name'] ?? ''}'; String fullName =
'${cardObject['first_name'] ?? ''} ${cardObject['last_name'] ?? ''}';
return Card( return Card(
color: Colors.white, color: Colors.white,
margin: EdgeInsets.symmetric( margin: EdgeInsets.symmetric(
horizontal: 12, vertical: 6), horizontal: 12,
vertical: 6,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
@ -635,34 +678,36 @@ class TravellerListState extends State<TravellerList> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 10, fontSize: 10,
color: Colors.black87, color: Colors.black87,
fontWeight: FontWeight.w700), fontWeight: FontWeight.w700,
),
), ),
GestureDetector( GestureDetector(
child: Image.asset( child: Image.asset(
'assets/images/IconsImg/edit.png', 'assets/images/IconsImg/edit.png',
width: 20, width: 20,
height: 15), height: 15,
),
onTap: () async { onTap: () async {
// final userId = getUserId(user['user_id']); // final userId = getUserId(user['user_id']);
// final usersData = await getUserDetails(userId); // final usersData = await getUserDetails(userId);
// //
final travellerId = int.tryParse( final travellerId = int.tryParse(
cardObject['traveller_id'] cardObject['traveller_id'].toString(),
.toString()); );
if (travellerId != null) { if (travellerId != null) {
print( print("travellerId -- $travellerId");
"travellerId -- $travellerId");
final data = await apiService final data = await apiService
.getTravellerDetailsFind( .getTravellerDetailsFind(
travellerId); travellerId,
);
print("TravellerId -- $data"); print("TravellerId -- $data");
showDialog( showDialog(
context: context, context: context,
builder: (context) => builder:
TravellerData( (context) => TravellerData(
isDesktop: isDesktop, isDesktop: isDesktop,
travellerId: travellerId:
travellerId, // Pass the ID travellerId, // Pass the ID
@ -680,79 +725,6 @@ class TravellerListState extends State<TravellerList> {
} }
}, },
), ),
// PopupMenuButton<int>(
// color: Colors.white,
// padding: EdgeInsets.zero,
// offset: Offset(0, 30),
// icon: Icon(
// Icons.more_vert,
// color: Color(0xFF475569),
// size: 14,
// ),
// itemBuilder: (context) => [
// CustomPopupMenuEntry(
// child: Container(
// padding: EdgeInsets.symmetric(
// horizontal: 8, vertical: 8),
// child: Row(
// mainAxisSize:
// MainAxisSize.min,
// mainAxisAlignment:
// MainAxisAlignment.center,
// children: [
// IconButton(
// icon: Icon(
// Icons
// .remove_red_eye,
// color: Color(
// 0xFF475569),
// size: 18),
// onPressed: () {
// print(
// "USerDAta - $user");
// // dynamic usersData = apiService
// // .getSingleUser(user[
// // 'user_id']
// // is String
// // ? int.parse(user[
// // 'user_id'])
// // : user[
// // 'user_id']);
// //
// // print(
// // "USerDAta - $usersData");
//
// context.go(
// "/CreateUserDetails",
// extra: {
// "selectedUser":
// user,
// "isViewMode": true
// },
// );
// }),
// IconButton(
// icon: Image.asset(
// 'assets/images/IconsImg/edit.png',
// width: 20,
// height: 15),
// onPressed: () {
// context.go(
// "/CreateUserDetails",
// extra: {
// "selectedUser":
// user,
// "isViewMode": false
// },
// );
// },
// ),
// ],
// ),
// ),
// ),
// ],
// ),
], ],
), ),
@ -766,25 +738,25 @@ class TravellerListState extends State<TravellerList> {
CrossAxisAlignment.start, CrossAxisAlignment.start,
children: [ children: [
Text( Text(
cardObject['email'] ?? '', '${cardObject['email'] ?? 'N/A'}',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 10, fontSize: 10,
color: Colors.black87), color: Colors.black87,
),
), ),
], ],
), ),
SizedBox( SizedBox(width: 10),
width: 10,
),
Column( Column(
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.start, CrossAxisAlignment.start,
children: [ children: [
Text( Text(
cardObject['mobile'] ?? '', '${cardObject['mobile'] ?? 'N/A'}',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 10, fontSize: 10,
color: Colors.black87), color: Colors.black87,
),
), ),
], ],
), ),
@ -805,7 +777,8 @@ class TravellerListState extends State<TravellerList> {
// mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: isDesktop child:
isDesktop
? (searchController.text.isNotEmpty && ? (searchController.text.isNotEmpty &&
filteredTraveller.isEmpty filteredTraveller.isEmpty
? Center( ? Center(
@ -813,7 +786,8 @@ class TravellerListState extends State<TravellerList> {
"No matches found", "No matches found",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, fontSize: 14,
color: Colors.grey), color: Colors.grey,
),
), ),
) )
: SingleChildScrollView( : SingleChildScrollView(
@ -827,11 +801,13 @@ class TravellerListState extends State<TravellerList> {
"No matches found", "No matches found",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, fontSize: 14,
color: Colors.grey), color: Colors.grey,
),
), ),
) )
: buildMobileCardView( : buildMobileCardView(
paginatedTraveller)), paginatedTraveller,
)),
), ),
// Expanded( // Expanded(
// child: isDesktop // child: isDesktop
@ -862,9 +838,11 @@ class TravellerListState extends State<TravellerList> {
), ),
); );
}, },
) ),
]), ],
)), ),
),
),
); );
} }
} }

View File

@ -663,6 +663,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
"last_name", "last_name",
"email", "email",
"mobile_no", "mobile_no",
"role_id",
// "employeeCode", // "employeeCode",
]; ];
@ -1125,6 +1126,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
}, },
); );
case "travel": case "travel":
final fullName =
"${controllers["Fname"]?.text ?? ""} ${controllers["Lname"]?.text ?? ""}"
.trim();
return TravellerDetails( return TravellerDetails(
key: travellerDetailsKey, key: travellerDetailsKey,
isDesktop: isDesktop, isDesktop: isDesktop,
@ -1134,6 +1138,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
travelDetails: travelDetailsDataFromAPI, // 👈 Pass this down travelDetails: travelDetailsDataFromAPI, // 👈 Pass this down
passportFileUrlFromApi: passportFileUrlFromApi, passportFileUrlFromApi: passportFileUrlFromApi,
userIdApi: userIdApi, userIdApi: userIdApi,
fullName: fullName,
); );
default: default:
return PersonalDetails( return PersonalDetails(

View File

@ -314,6 +314,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox(height: 20), SizedBox(height: 20),
_buildPassportDataRow1(widget.isDesktop),
SizedBox(height: 10),
_buildFirstRow(widget.isDesktop), _buildFirstRow(widget.isDesktop),
SizedBox(height: 10), SizedBox(height: 10),
_buildSecondRow(widget.isDesktop), _buildSecondRow(widget.isDesktop),
@ -329,6 +331,25 @@ class PersonalDetailsState extends State<PersonalDetails> {
); );
} }
Widget _buildPassportDataRow1(bool isDesktop) {
return Container(
color: Colors.white,
child: Row(
children: [
Text(
"* Please fill name details as per in passport *",
style: GoogleFonts.poppins(
fontSize: 10,
color: Colors.black87,
fontStyle: FontStyle.italic,
letterSpacing: 0.5,
),
),
],
),
);
}
Future<void> loadAllServices() async { Future<void> loadAllServices() async {
try { try {
final result = await apiService.fetchAllServices(); final result = await apiService.fetchAllServices();
@ -1500,7 +1521,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Role", "Role*",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -1579,6 +1600,13 @@ class PersonalDetailsState extends State<PersonalDetails> {
), ),
), ),
), ),
if (widget.errorMessages["role_id"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
widget.errorMessages["role_id"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
], ],
); );
} }

View File

@ -32,6 +32,7 @@ class TravellerDetails extends StatefulWidget {
final Map<String, dynamic>? travelDetails; final Map<String, dynamic>? travelDetails;
final String? passportFileUrlFromApi; final String? passportFileUrlFromApi;
final String? userIdApi; final String? userIdApi;
final String fullName;
const TravellerDetails({ const TravellerDetails({
Key? key, Key? key,
@ -42,6 +43,7 @@ class TravellerDetails extends StatefulWidget {
this.travelDetails, this.travelDetails,
this.passportFileUrlFromApi, this.passportFileUrlFromApi,
this.userIdApi, this.userIdApi,
required this.fullName,
}) : super(key: key); }) : super(key: key);
@override @override
TravellerDetailsState createState() => TravellerDetailsState(); TravellerDetailsState createState() => TravellerDetailsState();
@ -54,6 +56,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
int? expandedIndex; int? expandedIndex;
bool isCountryLoading = true; bool isCountryLoading = true;
Map<String, String> errorMessages = {};
late final userId; late final userId;
String? selectedFileNames; String? selectedFileNames;
Uint8List? passportDocumentBytes; Uint8List? passportDocumentBytes;
@ -154,18 +158,16 @@ class TravellerDetailsState extends State<TravellerDetails> {
String value, String value,
String? userId, String? userId,
) async { ) async {
var newField = "";
try { try {
final response = await apiService.CheckDuplicate( final response = await apiService.CheckDuplicate(
label, label,
newField, field,
value, value,
userId, userId,
); );
if (response.isNotEmpty) { if (response.isNotEmpty) {
_clearError(field); _clearError(field);
widget.errorMessages[field] = errorMessages[field] = response['message'] ?? "$label Already Exists";
response['message'] ?? "$label already exists";
print("Duplicate found: ${response['message']}"); print("Duplicate found: ${response['message']}");
return; return;
} else { } else {
@ -177,9 +179,37 @@ class TravellerDetailsState extends State<TravellerDetails> {
} }
} }
// Future<void> apiCheckDuplicate(
// String label,
// String field,
// String value,
// String? userId,
// ) async {
// var newField = "";
// try {
// final response = await apiService.CheckDuplicate(
// label,
// newField,
// value,
// userId,
// );
// if (response.isNotEmpty) {
// _clearError(field);
// errorMessages[field] = response['message'] ?? "$label already exists";
// print("Duplicate found: ${response['message']}");
// return;
// } else {
// _clearError(field);
// print("No duplicates found.");
// }
// } catch (e) {
// print("Error in checkDuplicate: $e");
// }
// }
void _clearError(String field) { void _clearError(String field) {
setState(() { setState(() {
widget.errorMessages.remove(field); errorMessages.remove(field);
}); });
} }
@ -858,7 +888,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
// SizedBox( // SizedBox(
// height: 10, // height: 10,
// ), // ),
// _buildPassportDataRow1(widget.isDesktop), _buildPassportDataRow1(widget.isDesktop),
SizedBox(height: 10), SizedBox(height: 10),
_buildPassportDataRow2(widget.isDesktop), _buildPassportDataRow2(widget.isDesktop),
SizedBox(height: 10), SizedBox(height: 10),
@ -870,28 +900,49 @@ class TravellerDetailsState extends State<TravellerDetails> {
Widget _buildPassportDataRow1(bool isDesktop) { Widget _buildPassportDataRow1(bool isDesktop) {
return Container( return Container(
color: Colors.white, color: Colors.white,
child: child: Row(
widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
buildFirstNameField(), Text(
Spacer(), "Name as per passport : ",
buildLastNameField(), style: GoogleFonts.poppins(
Spacer(), // Space after Last Name fontSize: 11,
buildNationality(), fontStyle: FontStyle.italic,
], letterSpacing: 0.5,
) ),
: Column( ),
crossAxisAlignment: CrossAxisAlignment.start, Text(
children: [ "${widget.fullName} ",
buildFirstNameField(), style: GoogleFonts.poppins(
SizedBox(height: 8), // Vertical space fontSize: 10,
buildLastNameField(), fontWeight: FontWeight.w600,
SizedBox(height: 8), letterSpacing: 0.5,
buildNationality(), color: Colors.black87,
// fontStyle: FontStyle.italic,
),
),
], ],
), ),
// child: widget.isDesktop
// ? Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// buildFirstNameField(),
// Spacer(),
// buildLastNameField(),
// Spacer(), // Space after Last Name
// buildNationality(),
// ],
// )
// : Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// buildFirstNameField(),
// SizedBox(height: 8), // Vertical space
// buildLastNameField(),
// SizedBox(height: 8),
// buildNationality(),
// ],
// ),
); );
} }
@ -984,13 +1035,6 @@ class TravellerDetailsState extends State<TravellerDetails> {
), ),
), ),
), ),
if (widget.errorMessages["first_name"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
widget.errorMessages["first_name"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
], ],
); );
} }
@ -1033,13 +1077,6 @@ class TravellerDetailsState extends State<TravellerDetails> {
), ),
), ),
), ),
if (widget.errorMessages["last_name"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
widget.errorMessages["last_name"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
], ],
); );
} }
@ -1082,13 +1119,6 @@ class TravellerDetailsState extends State<TravellerDetails> {
), ),
), ),
), ),
if (widget.errorMessages["last_name"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
widget.errorMessages["last_name"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
], ],
); );
} }
@ -1137,10 +1167,10 @@ class TravellerDetailsState extends State<TravellerDetails> {
), ),
), ),
), ),
if (widget.errorMessages["passport_number"] != null) ...[ if (errorMessages["passport_number"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
widget.errorMessages["passport_number"]!, errorMessages["passport_number"]!,
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
@ -2375,6 +2405,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
), ),
), ),
), ),
if (errorMessages["forex_pre_paid_card_number"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["forex_pre_paid_card_number"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
], ],
); );
} }

View File

@ -1,3 +1,3 @@
//api url //api url
const String apiUrl = 'http://apitest.tripapprovaltool.com/tstat_be'; const String apiUrl = 'http://apitest.tripapprovaltool.com/tstat_be';
// const String apiUrl = 'https://uat.tripapprovaltool.com'; // const String apiUrl = 'https://uat.tripapprovaltool.com/tstat_be';

View File

@ -143,7 +143,7 @@ class OrganizationSettingState extends State<OrganizationSetting> {
{ {
'value': '/traveller', 'value': '/traveller',
'icon': Icons.travel_explore, 'icon': Icons.travel_explore,
'label': 'Traveller', 'label': 'Traveller (Non Employee)',
'description': 'Create and Edit Traveller', 'description': 'Create and Edit Traveller',
}, },
]; ];

View File

@ -318,7 +318,7 @@ class ApiService {
Future<List<dynamic>> fetchAllGroup() async { Future<List<dynamic>> fetchAllGroup() async {
String? orgId = await getOrgId(); String? orgId = await getOrgId();
final String apiUrldata = '$apiUrl/api/groups?org_id=$orgId'; final String apiUrldata = '$apiUrl/api/groups?for=table_view&org_id=$orgId';
final token = await getToken(); final token = await getToken();
@ -392,7 +392,7 @@ class ApiService {
Future<List<dynamic>> fetchAllPolicy() async { Future<List<dynamic>> fetchAllPolicy() async {
String? orgId = await getOrgId(); String? orgId = await getOrgId();
final String apiUrldata = '$apiUrl/api/policy?org_id=$orgId'; final String apiUrldata = '$apiUrl/api/policy?for=table_view&org_id=$orgId';
final token = await getToken(); final token = await getToken();

View File

@ -0,0 +1,35 @@
// saving_loader.dart
import 'package:flutter/material.dart';
class SavingLoader extends StatelessWidget {
const SavingLoader({super.key});
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
width: 80,
height: 10,
child: LinearProgressIndicator(
backgroundColor: Colors.green,
color: Colors.white,
),
),
const SizedBox(height: 12),
const Text(
"Your changes are being saved.",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
],
),
),
);
}
}