diff --git a/lib/Screens/allTrips/list_all_plans.dart b/lib/Screens/allTrips/list_all_plans.dart index 81ff48b..90cf818 100644 --- a/lib/Screens/allTrips/list_all_plans.dart +++ b/lib/Screens/allTrips/list_all_plans.dart @@ -763,7 +763,7 @@ class _ListAllPlansState extends State { Icons.remove_red_eye, color: Color(0xFF475569), size: 18), - tooltip: 'View Trips', + tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context); // Close popup manually @@ -777,6 +777,7 @@ class _ListAllPlansState extends State { 'assets/images/IconsImg/edit.png', width: 20, height: 15), + tooltip: 'Edit The Trip Details', onPressed: () { Navigator.pop(context); ApiService.viewPlan( @@ -788,7 +789,7 @@ class _ListAllPlansState extends State { icon: Icon( Icons.cancel_rounded, size: 18), - tooltip: 'Cancellation Trips', + tooltip: 'Cancellation The Trip Details', onPressed: () { Navigator.pop(context); deletePlan(plan.planId); @@ -798,7 +799,7 @@ class _ListAllPlansState extends State { icon: Icon(Icons.download, color: Color(0xFF114D8B), size: 18), - tooltip: 'Download Trips Detials', + tooltip: 'Download The Trip Details', onPressed: () { Navigator.pop(context); apiService.getPdfDownload( @@ -811,7 +812,7 @@ class _ListAllPlansState extends State { color: Color(0xFF475569), size: 11, ), - tooltip: 'Trips Comments', + tooltip: 'Trip Comments', onPressed: () { showDialog( context: context, diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 9a1a7f0..0c1bd35 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -747,6 +747,7 @@ class _ApprovalListState extends State { color: Color(0xFF475569), size: 18), + tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context); // Close popup manually @@ -760,6 +761,7 @@ class _ApprovalListState extends State { 'assets/images/IconsImg/edit.png', width: 20, height: 15), + tooltip: 'Edit The Trip Details', onPressed: () { print( "Approver Edit : ${plan.approverId}"); @@ -780,6 +782,7 @@ class _ApprovalListState extends State { icon: Icon( Icons.cancel_rounded, size: 18), + tooltip: 'Cancellation The Trip Details', onPressed: () { Navigator.pop(context); deletePlan(plan.planId); @@ -789,6 +792,7 @@ class _ApprovalListState extends State { icon: Icon(Icons.download, color: Color(0xFF114D8B), size: 18), + tooltip: 'Download The Trip Details', onPressed: () { Navigator.pop(context); apiService.getPdfDownload( @@ -801,6 +805,7 @@ class _ApprovalListState extends State { color: Color(0xFF475569), size: 11, ), + tooltip: 'Approver Comment', onPressed: () { showDialog( context: context, @@ -946,6 +951,7 @@ class _ApprovalListState extends State { color: Color(0xFF475569), size: 18), + tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context); // Close popup manually @@ -959,6 +965,7 @@ class _ApprovalListState extends State { 'assets/images/IconsImg/edit.png', width: 20, height: 15), + tooltip: 'Edit The Trip Details', onPressed: () { Navigator.pop(context); ApiService @@ -975,6 +982,7 @@ class _ApprovalListState extends State { icon: Icon( Icons.cancel_rounded, size: 18), + tooltip: 'Cancellation The Trip Details', onPressed: () { Navigator.pop(context); deletePlan(plan.planId); @@ -984,6 +992,7 @@ class _ApprovalListState extends State { icon: Icon(Icons.download, color: Color(0xFF114D8B), size: 18), + tooltip: 'Download The Trip Details', onPressed: () { Navigator.pop(context); apiService.getPdfDownload( @@ -996,6 +1005,7 @@ class _ApprovalListState extends State { color: Color(0xFF475569), size: 11, ), + tooltip: 'Trip Comments', onPressed: () { showDialog( context: context, diff --git a/lib/Screens/costCenter/costCenter_list.dart b/lib/Screens/costCenter/costCenter_list.dart index 79b102d..57fa433 100644 --- a/lib/Screens/costCenter/costCenter_list.dart +++ b/lib/Screens/costCenter/costCenter_list.dart @@ -535,10 +535,14 @@ class CostCenterListState extends State { // apiService.getSingleUser(id), // ), GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit CostCenter Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); @@ -611,10 +615,14 @@ class CostCenterListState extends State { ), GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit CostCenter Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); diff --git a/lib/Screens/department/department_list.dart b/lib/Screens/department/department_list.dart index 63e0965..086d446 100644 --- a/lib/Screens/department/department_list.dart +++ b/lib/Screens/department/department_list.dart @@ -619,10 +619,14 @@ class DepartmentListState extends State { ), GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit Department Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); diff --git a/lib/Screens/forex/forex_list.dart b/lib/Screens/forex/forex_list.dart index 58dae9b..9e0f1b6 100644 --- a/lib/Screens/forex/forex_list.dart +++ b/lib/Screens/forex/forex_list.dart @@ -722,10 +722,14 @@ class ForexDataListState extends State { // apiService.getSingleUser(id), // ), GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit ForEx Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); @@ -798,10 +802,14 @@ class ForexDataListState extends State { ), GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit Forex Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); @@ -1040,4 +1048,6 @@ class ForexDataListState extends State { )), ); } + + } diff --git a/lib/Screens/hotels/hotels_list.dart b/lib/Screens/hotels/hotels_list.dart index 281eba6..9858dc9 100644 --- a/lib/Screens/hotels/hotels_list.dart +++ b/lib/Screens/hotels/hotels_list.dart @@ -603,10 +603,14 @@ class HotelsDataListState extends State { // apiService.getSingleUser(id), // ), GestureDetector( - child: Image.asset( - 'assets/images/IconsImg/edit.png', + child: Tooltip( + message: 'Delete Hotel Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); diff --git a/lib/Screens/itnerary_list/accomodation_list.dart b/lib/Screens/itnerary_list/accomodation_list.dart index 432e160..6599b5c 100644 --- a/lib/Screens/itnerary_list/accomodation_list.dart +++ b/lib/Screens/itnerary_list/accomodation_list.dart @@ -245,14 +245,26 @@ class AccomodationListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Accomodation"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Accomodation Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteAccommodation(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Accommodation Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/bus_list.dart b/lib/Screens/itnerary_list/bus_list.dart index 07795fa..e4e097a 100644 --- a/lib/Screens/itnerary_list/bus_list.dart +++ b/lib/Screens/itnerary_list/bus_list.dart @@ -224,14 +224,26 @@ class BusListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Bus"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Edit Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteBus(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Bus Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/flight_list.dart b/lib/Screens/itnerary_list/flight_list.dart index 1c837d8..f064d3b 100644 --- a/lib/Screens/itnerary_list/flight_list.dart +++ b/lib/Screens/itnerary_list/flight_list.dart @@ -290,14 +290,26 @@ class _FlightListWidgetState extends State { Spacer(), GestureDetector( onTap: () => widget.onOpen(true, item, "Flight"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Flight Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => widget.onDeleteFlight(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Flight Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/forex_list.dart b/lib/Screens/itnerary_list/forex_list.dart index 3b826ed..da9c9a4 100644 --- a/lib/Screens/itnerary_list/forex_list.dart +++ b/lib/Screens/itnerary_list/forex_list.dart @@ -261,14 +261,26 @@ class ForexListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Forex"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Forex Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteForex(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Forex Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), item['forex_id'] != null ? IconButton( diff --git a/lib/Screens/itnerary_list/insurance_list.dart b/lib/Screens/itnerary_list/insurance_list.dart index 5c88d65..9e0b1de 100644 --- a/lib/Screens/itnerary_list/insurance_list.dart +++ b/lib/Screens/itnerary_list/insurance_list.dart @@ -202,14 +202,26 @@ class InsuranceListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Insurance"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Insurance Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteInsurance(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Insurance Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), @@ -445,14 +457,26 @@ class InsuranceListWidget extends StatelessWidget { children: [ GestureDetector( onTap: () => onOpen(true, item, "Insurance"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Insurance Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteInsurance(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Insurance Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), IconButton( icon: Icon(Icons.keyboard_arrow_down_outlined, diff --git a/lib/Screens/itnerary_list/miscellaneous_list.dart b/lib/Screens/itnerary_list/miscellaneous_list.dart index 30e288b..306244c 100644 --- a/lib/Screens/itnerary_list/miscellaneous_list.dart +++ b/lib/Screens/itnerary_list/miscellaneous_list.dart @@ -162,14 +162,26 @@ class MiscellaneousListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Miscellaneous"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Miscellaneous Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onOpen(true, item, "Miscellaneous"), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Miscellaneous Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/taxi_list.dart b/lib/Screens/itnerary_list/taxi_list.dart index 3f3d4b6..e438d73 100644 --- a/lib/Screens/itnerary_list/taxi_list.dart +++ b/lib/Screens/itnerary_list/taxi_list.dart @@ -246,14 +246,26 @@ class TaxiListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Taxi"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Taxi Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => (item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Taxi Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/train_list.dart b/lib/Screens/itnerary_list/train_list.dart index cdacc3c..bf3913f 100644 --- a/lib/Screens/itnerary_list/train_list.dart +++ b/lib/Screens/itnerary_list/train_list.dart @@ -333,14 +333,26 @@ class _TrainListWidgetState extends State { Spacer(), GestureDetector( onTap: () => widget.onOpen(true, item, "Train"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Train Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => widget.onDeleteTrain(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Train Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/visa_list.dart b/lib/Screens/itnerary_list/visa_list.dart index 589af86..9429a8e 100644 --- a/lib/Screens/itnerary_list/visa_list.dart +++ b/lib/Screens/itnerary_list/visa_list.dart @@ -301,14 +301,26 @@ class VisaListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Visa"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Visa Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteMiscellaneous(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Visa Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/plans/list_plans.dart b/lib/Screens/plans/list_plans.dart index 67b6a32..bf1a68d 100644 --- a/lib/Screens/plans/list_plans.dart +++ b/lib/Screens/plans/list_plans.dart @@ -827,6 +827,7 @@ class _ListPlansState extends State { Icons.remove_red_eye, color: Color(0xFF475569), size: 18), + tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context); // Close popup manually @@ -845,6 +846,7 @@ class _ListPlansState extends State { 'assets/images/IconsImg/edit.png', width: 20, height: 15), + tooltip: 'Edit Trip/Plans Details', onPressed: () { Navigator.pop(context); ApiService.viewPlan( @@ -857,6 +859,7 @@ class _ListPlansState extends State { icon: Icon( Icons.cancel_rounded, size: 18), + tooltip: 'Cancellation The Trip Details', onPressed: () { Navigator.pop(context); deletePlan(plan.planId); @@ -866,6 +869,7 @@ class _ListPlansState extends State { icon: Icon(Icons.download, color: Color(0xFF114D8B), size: 18), + tooltip: 'Download The Trip Comments', onPressed: () { Navigator.pop(context); apiService.getPdfDownload( @@ -878,6 +882,7 @@ class _ListPlansState extends State { color: Color(0xFF475569), size: 11, ), + tooltip: 'Trip Comments', onPressed: () { showDialog( context: context, @@ -1023,6 +1028,7 @@ class _ListPlansState extends State { Icons.remove_red_eye, color: Color(0xFF475569), size: 18), + tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context); // Close popup manually @@ -1041,6 +1047,7 @@ class _ListPlansState extends State { 'assets/images/IconsImg/edit.png', width: 20, height: 15), + tooltip: 'Edit Trip/plan Details', onPressed: () { Navigator.pop(context); ApiService.viewPlan( @@ -1053,6 +1060,7 @@ class _ListPlansState extends State { icon: Icon( Icons.cancel_rounded, size: 18), + tooltip: 'Cancellation The Trip Details', onPressed: () { Navigator.pop(context); deletePlan(plan.planId); @@ -1062,6 +1070,7 @@ class _ListPlansState extends State { icon: Icon(Icons.download, color: Color(0xFF114D8B), size: 18), + tooltip: 'Download The Trip Comments', onPressed: () { Navigator.pop(context); apiService.getPdfDownload( @@ -1074,6 +1083,7 @@ class _ListPlansState extends State { color: Color(0xFF475569), size: 11, ), + tooltip: 'Trip Comments', onPressed: () { showDialog( context: context, diff --git a/lib/Screens/policy/policy_list.dart b/lib/Screens/policy/policy_list.dart index b4dc8bd..ffdc8f5 100644 --- a/lib/Screens/policy/policy_list.dart +++ b/lib/Screens/policy/policy_list.dart @@ -1,17 +1,19 @@ import 'dart:convert'; - import 'package:flutter/material.dart'; -import 'package:frontend/Screens/group/group.dart'; +import 'package:frontend/utils/auth_utils.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:responsive_builder/responsive_builder.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import '../../config/apiUrl.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; import '../../services/apiService.dart'; -import '../../utils/auth_utils.dart'; +import '../../utils/pagination.dart'; +import '../../widgets/custom_popup.dart'; +import '../../widgets/popup_userList_action.dart'; class PolicyList extends StatefulWidget { @override @@ -20,19 +22,42 @@ class PolicyList extends StatefulWidget { class _PolicyListState extends State { final ApiService apiService = ApiService(); - List? apiAllGroups; + late Future> futureUsers; + + late Map userSingleData; + List? apiCountryData; + String? selectedUserId; + String? orgId; + Color? layoutColor; Color? bodyColor; + List allUsers = []; + List filteredUsers = []; + TextEditingController searchController = TextEditingController(); + + int currentPage = 0; + int itemsPerPage = 10; + @override void initState() { super.initState(); + futureUsers = fetchUsers(); + + futureUsers.then((users) { + setState(() { + allUsers = users; + }); + }); WidgetsBinding.instance.addPostFrameCallback((_) { - loadAllGroups(); + fetchCountryList(); loadInitialData(); + loadAllGroups(); }); + + // futurePlans = fetchPlans(); } void loadInitialData() async { @@ -70,11 +95,243 @@ class _PolicyListState extends State { } } + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + // Future> fetchSingleUsers(userId) async { + // orgId = await getOrgId(); + // final String apiUrlData = '$apiUrl/api/users/find/$userId'; + // + // final String? token = await getToken(); + // + // print("Fetch Users"); + // print("TOEKRWE: $token"); + // + // if (token == null) { + // throw Exception('Token not found. Please log in.'); + // } + // + // final response = await http.get( + // Uri.parse(apiUrlData), + // headers: { + // 'Authorization': 'Bearer $token', + // 'Content-Type': 'application/json', + // }, + // ); + // + // if (response.statusCode == 200) { + // final data = json.decode(response.body); + // return data['data']; // Returning raw JSON list + // } else { + // throw Exception('Failed to load users'); + // } + // } + + Future> fetchUsers() async { + orgId = await getOrgId(); + final String apiUrlData = '$apiUrl/api/users?org_id=$orgId'; + final String? token = await getToken(); + + print("Fetch Users"); + print("TOEKRWE: $token"); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrlData), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + return data['data']; // Returning raw JSON list + } else { + throw Exception('Failed to load users'); + } + } + + Future fetchCountryList() async { + final String apiUrldata = '$apiUrl/api/getcountryMaster'; + + final token = await getToken(); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + try { + final data = json.decode(response.body); + print("Country - $data"); + + if (!data.containsKey('data') || data['data'] is! List) { + throw Exception( + "Invalid response format: 'data' field is missing or not a List"); + } + + List plansJson = data['data']; // 'data' is a Map, not a List + + if (data['data'] is List) { + List plansJson = data['data']; + print("plansJson.length - ${plansJson.length}"); + } else { + print("The 'data' key does not contain a list."); + } + + setState(() { + apiCountryData = plansJson; // Store API response in state + }); + print('plansJSONContry - $plansJson'); + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else { + throw Exception('Failed to load plans'); + } + } + + void handleDelete(userId) { + print("handDel - $userId"); + } + + Future createUserData( + Map userData, String userId, String newStatus) async { + final uri = Uri.parse('$apiUrl/api/users/update/$userId'); + + final String? token = await getToken(); + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $token'; + + // If updating, spoof the method Laravel-style + + request.fields['_method'] = 'PUT'; + request.fields['user_id'] = userId; + + print("STatus 2 - $newStatus"); + + // Add all non-null and non-empty user data fields + userData.forEach((key, value) { + if (value != null && value.toString().trim().isNotEmpty) { + request.fields[key] = value.toString(); + } + }); + + request.fields['is_active'] = newStatus; + + print("🚀 Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + print("✅ User Status submitted successfully! "); + print("📨 Response: ${response.body}"); + + refreshUserList(); + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("📨 Body: ${response.body}"); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + void handleToggleUserStatus(String userId, String currentStatus, + Map userData) async { + print("Toggling user status - $userId (Current: $currentStatus)"); + + final String apiUrlData = + '$apiUrl/api/users/update/$userId'; // API for updating user + final String? token = await getToken(); + + if (token == null) { + print("Error: Token not found"); + return; + } + + // Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1") + String newStatus = (currentStatus == "1") ? "0" : "1"; + + print("STatus 1 - $newStatus"); + + createUserData(userData, userId, newStatus); + + // try { + // final response = await http.put( + // Uri.parse(apiUrlData), + // headers: { + // 'Authorization': 'Bearer $token', + // 'Content-Type': 'application/json', + // }, + // body: jsonEncode({ + // "is_active": newStatus // Set new status dynamically + // }), + // ); + // + // if (response.statusCode == 200) { + // print("User status updated successfully to $newStatus!"); + // refreshUserList(); // Refresh users list after update + // } else { + // print("Failed to update user status. Status: ${response.statusCode}"); + // print("Error: ${response.body}"); + // } + // } catch (e) { + // print("Error updating user status: $e"); + // } + } + +// Refresh user list after update + void refreshUserList() { + setState(() { + futureUsers = fetchUsers(); // Re-fetch users after status update + // Wait for futurePlans to be fetched and update allPlans + }); + } + + void filterUsers(String query) { + print("allUsers before filtering: $query"); + final lowerQuery = query.toLowerCase(); + setState(() { + filteredUsers = allUsers.where((user) { + return (user['first_name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (user['last_name']?.toLowerCase().contains(lowerQuery) ?? false) || + (user['email']?.toLowerCase().contains(lowerQuery) ?? false) || + (user['role_value']?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); + }); + print("filteredPlans: $filteredUsers"); + } + void handleActiveStatus( - Map policyData, - String policyId, - String currentStatus, - ) async { + Map policyData, + String policyId, + String currentStatus, + ) async { print("Toggling user status - $policyId (Current: $currentStatus)"); final String apiUrlData = @@ -133,15 +390,6 @@ class _PolicyListState extends State { handleActiveStatus(policydata, policyId.toString(), status.toString()); } - // Future deleteGroupFromApi(int groupId) async { - // try { - // await apiService.deleteGroup(groupId); // your delete API call - // deleteGroup(groupId); // remove from UI list - // } catch (e) { - // print('Error deleting group: $e'); - // } - // }' - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -149,21 +397,24 @@ class _PolicyListState extends State { return Scaffold( backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), + // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( padding: isDesktop ? EdgeInsets.symmetric( - horizontal: MediaQuery.of(context).size.width * - 0.1, // 30% of screen width as horizontal padding - vertical: MediaQuery.of(context).size.height * - 0, // 5% of screen height as vertical padding - ) + horizontal: MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: MediaQuery.of(context).size.height * + 0, // 5% of screen height as vertical padding + ) : EdgeInsets.all(0), child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), - Expanded(child: buildGroupList(isDesktop)) + // const Expanded(child: Center(child: Text("User Page Content"))), + Expanded(child: buildGroupList(isDesktop)), ], ), ), @@ -174,23 +425,23 @@ class _PolicyListState extends State { Widget buildGroupList(bool isDesktop) { return Container( margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, - padding: const EdgeInsets.only(left: 10, right: 10, top: 8), + padding: const EdgeInsets.all(1), decoration: BoxDecoration( color: isDesktop ? Colors.white : Color(0xFFFCFCFC), ), // decoration: BoxDecoration( // // color: Colors.amber, - // color: Color(0xFFE1F5FE), // // color: bodyColor, + // color: Color(0xFFE1F5FE), // border: Border.all( - // // color: Color(0xFFF7F7FB), // color: Colors.white, + // // color: Color(0xFFF7F7FB), // width: 3.5)), - child: buildGroupListLayout(isDesktop), + child: buildUserTable(isDesktop), ); } - Widget buildGroupListLayout(bool isDesktop) { + Widget buildUserTable(bool isDesktop) { return Container( // margin: isDesktop // ? EdgeInsets.all(10.0) @@ -199,208 +450,525 @@ class _PolicyListState extends State { height: isDesktop ? MediaQuery.of(context).size.height * 0.98 : MediaQuery.of(context).size.height, - // decoration: BoxDecoration( - // border: isDesktop - // ? Border.all( - // width: 2, - // color: Colors.white, - // // color: Color(0xFFF7F7FB), - // ) - // : null, - // color: Colors.white, - // // color: Color(0xFFF7F7FB), - // - // // color: Colors.amber, - // ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Text( - 'Policy List', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - IconButton( - icon: const Icon(Icons.keyboard_arrow_down), - onPressed: () {}, - ), - ], - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Color(0xFF114D8B), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - // side: BorderSide(color: , width: 1), - ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: () async { - // List users = await futureUsers; - context.go('/Policy'); - }, - child: Row( - children: [ - Text('New Policy', - style: GoogleFonts.poppins(fontSize: 12)), - SizedBox( - width: 5, - ), - Icon( - Icons.add_circle_outline_rounded, - color: Colors.white, - ), - ], - ), - ), - ], - ), - SizedBox( - height: 5, - ), - Row( - children: [ - Expanded( - child: Container( - height: MediaQuery.of(context).size.height * 0.8, - padding: const EdgeInsets.all(10), - // margin: const EdgeInsets.only(bottom: 10), - color: Colors.white, - // color: Colors.red.shade100, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - children: [ - buildGroupListView(isDesktop), - ], - ), - ), - ), - ), - ], - ) - ], - ), - ); - } - // Widget buildGroupListView(bool isDesktop) { - // return Container( - // child: Text("DAta"), - // ); - // } - - Widget buildGroupListView(bool isDesktop) { - if (apiAllGroups == null || apiAllGroups!.isEmpty) { - return Center(child: Text("No groups found.")); - } - - return ListView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: apiAllGroups!.length, - itemBuilder: (context, index) { - final policy = apiAllGroups![index]; - return Card( - // color: bodyColor, - // color: Color(0xFFF5F5F5), - color: Colors.white, - margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10), - child: Padding( - padding: const EdgeInsets.all(12.0), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + color: Colors.white, + padding: const EdgeInsets.all(10.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - flex: 1, - child: Text("Policy Name", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - Expanded( - flex: 1, - child: Text("Policy Type", + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + 'Users', style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400))), - // Expanded(flex: 1, child: Text("${policy['created_by']}")), - ], - ), - SizedBox(height: 4), - Row( - children: [ - Expanded( - flex: 1, - child: Text("${policy['name']}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600)), - ), - Expanded( - flex: 1, - child: Text( - policy['domestic'] == "1" - ? "Domestic" - : "International", + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ], + ), + if (isDesktop) + SizedBox( + width: MediaQuery.of(context).size.width * 0.28, + ), + + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterUsers, + decoration: InputDecoration( + hintText: "Search for a User", + hintStyle: TextStyle( + fontSize: 12, color: Color(0xFF9E9DBD)), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, width: 0.5), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, width: 1), + ), + ), style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600))), - // Expanded(flex: 1, child: Text("${policy['created_by']}")), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - GestureDetector( - onTap: () async { - final rawId = policy['policy_id']; - final intPolicyId = rawId is int - ? rawId - : int.tryParse(rawId.toString()) ?? 0; + fontSize: 12, + ), + ), + ), + // SizedBox(width: 16), + Spacer(), - Map policyData = - await apiService.getSinglePolicy(intPolicyId); + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: + BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: EdgeInsets.symmetric( + horizontal: 20, vertical: 12), + ), + onPressed: () async { + List users = await futureUsers; - print("PolicyDATa: $policyData"); + // Print the resolved value + print("CREATELIAS - $users"); - context.go("/Policy", extra: policyData); - }, - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), + context.go('/Policy'); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add Policy", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], + ), + ), + ], + ), + + if (!isDesktop) SizedBox( - width: 5, + height: 5, ), - GestureDetector( - onTap: () { - final idStr = policy['policy_id']; - final id = int.tryParse(idStr.toString()); + isDesktop + ? SizedBox.shrink() + : Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.8, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterUsers, + decoration: InputDecoration( + hintText: "Search for a User", + hintStyle: TextStyle( + fontSize: 12, color: Color(0xFF9E9DBD)), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, + width: 0.5), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, width: 1), + ), + ), + style: GoogleFonts.poppins( + fontSize: 12, + ), + ), + ), + // SizedBox(width: 16), + ], + ), + const SizedBox(height: 10), + FutureBuilder>( + future: futureUsers, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + 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), + Text( + "No Policy Available", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey), + ), + const SizedBox(height: 20), + Text( + "Please Create Policy", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, color: Colors.grey), + ), + const SizedBox(height: 20), + ], + ), + ), + ); + } - if (id == null) { - print("group_id is null"); - return; - } - final status = policy['is_active']; + List users = + filteredUsers.isNotEmpty ? filteredUsers : allUsers; - deletePolicy(policy, id, status); - }, - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - ], - ), - ], - ), - ), - ); - }, + users.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); + + return dateB + .compareTo(dateA); // Descending: newest first + }); + + List paginatedUser = users + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); + + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = + isDesktop ? constraints.maxWidth : 1300; + + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, color: Colors.grey.shade200), + ), + columns: [ + DataColumn( + label: Text( + 'Policy Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), + DataColumn( + label: Text( + 'Policy Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), + ], + rows: paginatedUser.map((user) { + String userId = + user['user_id'].toString(); // Get user ID + bool isSelected = selectedUserId == userId; + + return DataRow(cells: [ + DataCell(Text( + "${user['name'] ?? ''}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ))), + DataCell(Text( user['domestic'] == "1" + ? "Domestic" + : "International", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ))), + DataCell(GestureDetector( + onTap: () { + handleToggleUserStatus(user['user_id'], + user['is_active'], user); + }, + child: Text( + user['is_active'] == "1" + ? "Active" + : "Inactive", + style: TextStyle( + color: user['is_active'] == "1" + ? Colors.green + : Colors.grey, + fontFamily: "Inter", + fontWeight: FontWeight.w400), + ))), + DataCell( + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () async { + final rawId = user['policy_id']; + final intPolicyId = rawId is int + ? rawId + : int.tryParse(rawId.toString()) ?? 0; + + Map policyData = + await apiService.getSinglePolicy(intPolicyId); + + print("PolicyDATa: $policyData"); + + context.go("/Policy", extra: policyData); + }, + child: Tooltip( + message: 'Edit Policy Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + ), + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + final idStr = user['policy_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = user['is_active']; + + deletePolicy(user, id, status); + }, + child: Tooltip( + message: 'Delete Policy Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), + ), + ], + ), + ), + ]); + }).toList(), + ), + ); + }, + ); + + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final user = paginatedUser[index]; + return Card( + color: Colors.white, + margin: EdgeInsets.symmetric( + horizontal: 12, vertical: 6), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + flex: 1, + child: Text("Policy Name", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + Expanded( + flex: 1, + child: Text("Policy Type", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400))), + // Expanded(flex: 1, child: Text("${policy['created_by']}")), + ], + ), + SizedBox(height: 4), + Row( + children: [ + Expanded( + flex: 1, + child: Text("${user['name']}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600)), + ), + Expanded( + flex: 1, + child: Text( + user['domestic'] == "1" + ? "Domestic" + : "International", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600))), + // Expanded(flex: 1, child: Text("${policy['created_by']}")), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () async { + final rawId = user['policy_id']; + final intPolicyId = rawId is int + ? rawId + : int.tryParse(rawId.toString()) ?? 0; + + Map policyData = + await apiService.getSinglePolicy(intPolicyId); + + print("PolicyDATa: $policyData"); + + context.go("/Policy", extra: policyData); + }, + child: Tooltip( + message: 'Edit Policy Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + ), + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + final idStr = user['policy_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = user['is_active']; + + deletePolicy(user, id, status); + }, + child: Tooltip( + message: 'Delete Policy Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), + ), + ], + ), + + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: isDesktop + ? SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, // <-- your existing table + ) + : buildMobileCardView(paginatedUser), + ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: users.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ) + ]), + )), ); } } diff --git a/lib/widgets/popup_userList_action.dart b/lib/widgets/popup_userList_action.dart index e2f461f..aa66b34 100644 --- a/lib/widgets/popup_userList_action.dart +++ b/lib/widgets/popup_userList_action.dart @@ -41,8 +41,13 @@ class UserActionsMenu extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ GestureDetector( - child: Icon(Icons.remove_red_eye, - color: Color(0xFF475569), size: 18), + child: Tooltip( + message: 'View User Details', + child: Icon(Icons.remove_red_eye, + color: Color(0xFF475569), size: 18) + ), + // child: Icon(Icons.remove_red_eye, + // color: Color(0xFF475569), size: 18,), onTap: () async { Navigator.pop(context); // Close the menu final userId = getUserId(user['user_id']); @@ -55,8 +60,14 @@ class UserActionsMenu extends StatelessWidget { ), SizedBox(width: 8), GestureDetector( - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit User Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), onTap: () async { Navigator.pop(context); // Close the menu final userId = getUserId(user['user_id']);