diff --git a/lib/Screens/allTrips/list_all_plans.dart b/lib/Screens/allTrips/list_all_plans.dart index 223eee8..0744446 100644 --- a/lib/Screens/allTrips/list_all_plans.dart +++ b/lib/Screens/allTrips/list_all_plans.dart @@ -83,6 +83,7 @@ class _ListAllPlansState extends State { (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); }).toList(); + currentPage = 0; }); print("filteredPlans: $filteredPlans"); } @@ -276,41 +277,34 @@ class _ListAllPlansState extends State { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return MainLayout( - isDesktop: isDesktop, - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - Expanded(child: buildGroupListLayout(isDesktop)), - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // backgroundColor: Color(0xFFFCFCFC), + + // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), + // 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 + ) + : EdgeInsets.all(0), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildGroupListLayout(isDesktop)), + ], + ), ), ); - - // return Scaffold( - // backgroundColor: Color(0xFFf5f5f5), - // // backgroundColor: Color(0xFFFCFCFC), - // 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 - // ) - // : EdgeInsets.all(0), - // child: Row( - // children: [ - // // if (isDesktop) CustomDrawer(isDesktop: true), - // Expanded(child: buildGroupListLayout(isDesktop)), - // ], - // ), - // ), - // ); }, ); } @@ -866,7 +860,7 @@ class _ListAllPlansState extends State { size: 18, ), tooltip: - 'View Trips', + 'View The Trip Details', onPressed: () { Navigator.pop( context, @@ -885,6 +879,8 @@ class _ListAllPlansState extends State { width: 20, height: 15, ), + tooltip: + 'Edit The Trip Details', onPressed: () { Navigator.pop( context, @@ -904,7 +900,7 @@ class _ListAllPlansState extends State { size: 18, ), tooltip: - 'Cancellation Trips', + 'Cancellation The Trip Details', onPressed: () { Navigator.pop( context, @@ -923,7 +919,7 @@ class _ListAllPlansState extends State { size: 18, ), tooltip: - 'Download Trips Detials', + 'Download The Trip Details', onPressed: () { Navigator.pop( context, @@ -943,7 +939,7 @@ class _ListAllPlansState extends State { size: 11, ), tooltip: - 'Trips Comments', + 'Trip Comments', onPressed: () { showDialog( context: diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 9a1a7f0..2d9509b 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -324,6 +324,7 @@ class _ApprovalListState extends State { (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); }).toList(); + currentPage = 0; }); print("filteredPlans: $filteredPlans"); } @@ -747,6 +748,7 @@ class _ApprovalListState extends State { color: Color(0xFF475569), size: 18), + tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context); // Close popup manually @@ -760,6 +762,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 +783,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 +793,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 +806,7 @@ class _ApprovalListState extends State { color: Color(0xFF475569), size: 11, ), + tooltip: 'Approver Comment', onPressed: () { showDialog( context: context, @@ -946,6 +952,7 @@ class _ApprovalListState extends State { color: Color(0xFF475569), size: 18), + tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context); // Close popup manually @@ -959,6 +966,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 +983,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 +993,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 +1006,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..3d1e878 100644 --- a/lib/Screens/costCenter/costCenter_list.dart +++ b/lib/Screens/costCenter/costCenter_list.dart @@ -153,6 +153,7 @@ class CostCenterListState extends State { (object['description']?.toLowerCase().contains(lowerQuery) ?? false) || (isActiveStatus.contains(lowerQuery)); }).toList(); + currentPage = 0; }); print("filteredCostCenter: $filteredCostCenter"); @@ -535,10 +536,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 +616,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..a6c0b5e 100644 --- a/lib/Screens/department/department_list.dart +++ b/lib/Screens/department/department_list.dart @@ -151,6 +151,7 @@ class DepartmentListState extends State { false) || (isActiveStatus.contains(lowerQuery)); }).toList(); + currentPage = 0; }); print("filteredDepartment: $filteredDepartment"); } @@ -619,10 +620,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..bf64c2c 100644 --- a/lib/Screens/forex/forex_list.dart +++ b/lib/Screens/forex/forex_list.dart @@ -295,6 +295,7 @@ class ForexDataListState extends State { (forex['perdiem_amount']?.toLowerCase().contains(lowerQuery) ?? false); }).toList(); + currentPage = 0; }); print("filteredPlans: $filteredForex"); } @@ -722,10 +723,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 +803,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 +1049,6 @@ class ForexDataListState extends State { )), ); } + + } diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index ab9ef8d..17e205e 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -1,19 +1,22 @@ import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:frontend/Screens/group/group.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 'groupDetails.dart'; + + class GroupList extends StatefulWidget { @override _GroupListState createState() => _GroupListState(); @@ -22,18 +25,44 @@ class GroupList extends StatefulWidget { class _GroupListState extends State { final ApiService apiService = ApiService(); - List? apiAllGroups; + late Future> futureGroups; + + late Map groupSingleData; + List? apiCountryData; + String? selectedGroupId; + String? orgId; + Color? layoutColor; Color? bodyColor; + List allGroups = []; + List filteredGroups = []; + TextEditingController searchController = TextEditingController(); + + + int currentPage = 0; + int itemsPerPage = 10; + + List? apiAllGroups; + @override void initState() { super.initState(); + futureGroups = fetchGroups(); + + futureGroups.then((object) { + setState(() { + allGroups = object; + }); + }); WidgetsBinding.instance.addPostFrameCallback((_) { - loadAllGroups(); loadInitialData(); + fetchGroups(); + loadAllGroups(); }); + + // futurePlans = fetchPlans(); } void loadInitialData() async { @@ -41,37 +70,75 @@ class _GroupListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null + layoutColor = + layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; - bodyColor = bodyStringColor != null + bodyColor = + bodyStringColor != null ? Color(int.parse(bodyStringColor)) : Colors.white; }); } + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + + + + Future> fetchGroups() async { + final result = await apiService.fetchAllGroup(); + return result; // Returning raw JSON list + } + + Future loadAllGroups() async { try { final result = await apiService.fetchAllGroup(); setState(() { - apiAllGroups = result; + allGroups = result; }); - print("Fetched services: $apiAllGroups"); + print("Fetched services: $allGroups"); } catch (e) { print('Error fetching role list: $e'); } } + void filterGroups(String query) { + print("allGroups before filtering: $query"); + final lowerQuery = query.toLowerCase(); + + setState(() { + filteredGroups = + allGroups.where((group) { + return (group['name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (group['domestic_policy_name']?.toLowerCase().contains(lowerQuery) ?? + false) + (group['international_policy_name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (group['description']?.toLowerCase().contains(lowerQuery) ?? + false) || + (group['is_active']?.toLowerCase().contains(lowerQuery) ?? false);}).toList(); + currentPage = 0; + }); + + print("filtered: $filteredGroups"); + } + void handleActiveStatus( - Map groupData, - String groupId, - String currentStatus, - ) async { - print("Toggling user status - $groupId (Current: $currentStatus)"); + Map groupData, + String groupId, + String currentStatus, + ) async { + print("Toggling group status - $groupId (Current: $currentStatus)"); final String apiUrlData = - '$apiUrl/api/groups/update/$groupId'; // API for updating user + '$apiUrl/api/groups/update/$groupId'; // API for updating group final String? token = await getToken(); if (token == null) { @@ -97,14 +164,14 @@ class _GroupListState extends State { ); if (response.statusCode == 200 || response.statusCode == 201) { - print("User status updated successfully to $newStatus!"); - loadAllGroups(); // Refresh users list after update + print("Group status updated successfully to $newStatus!"); + loadAllGroups(); // Refresh groups list after update } else { - print("Failed to update user status. Status: ${response.statusCode}"); + print("Failed to update group status. Status: ${response.statusCode}"); print("Error: ${response.body}"); } } catch (e) { - print("Error updating user status: $e"); + print("Error updating group status: $e"); } } @@ -122,49 +189,45 @@ class _GroupListState extends State { loadAllGroups(); } - // 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) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - // backgroundColor: Colors.white, - backgroundColor: Color(0xFFf5f5f5), - 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 - ) - : EdgeInsets.all(8), - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - Expanded(child: buildGroupListLayout(isDesktop)) - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + 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 + ) + : EdgeInsets.all(0), + child: Row( + children: [ + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } - Widget buildGroupListLayout(bool isDesktop) { + Widget buildGroupList(bool isDesktop) { return Container( margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, - padding: const EdgeInsets.all(10), + padding: const EdgeInsets.all(1), decoration: BoxDecoration( color: isDesktop ? Colors.white : Color(0xFFFCFCFC), ), @@ -176,258 +239,689 @@ class _GroupListState extends State { // color: Colors.white, // // color: Color(0xFFF7F7FB), // width: 3.5)), - child: buildGroupData(isDesktop), + child: buildGroupTable(isDesktop), ); } - // Widget buildGroupListView(bool isDesktop) { - // return Container( - // child: Text("DAta"), - // ); - // } - - Widget buildGroupData(isDesktop) { + Widget buildGroupTable(bool isDesktop) { return Container( // margin: isDesktop // ? EdgeInsets.all(10.0) // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), // padding: const EdgeInsets.all(10), - height: isDesktop + 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, + + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + color: Colors.white, + padding: const EdgeInsets.all(10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Group List', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, + Row( + children: [ + Text( + 'Group', + style: GoogleFonts.poppins( + 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: filterGroups, + decoration: InputDecoration( + hintText: "Search for a Group", + 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), + ), ), - ), - 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('/CreateGroup'); - showDialog( - context: context, - builder: (context) => GroupData( - isDesktop: isDesktop, - groupId: null, - layoutColor: layoutColor!, - fetchGetGroup: refreshData + // SizedBox(width: 16), + Spacer(), + + 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 groups = await futureGroups; + // context.go('/CreateGroup'); + showDialog( + context: context, + builder: (context) => GroupData( + isDesktop: isDesktop, + groupId: null, + layoutColor: layoutColor!, + fetchGetGroup: refreshData + ), + ); - }, - child: Row( - children: [ - Text('New Group', 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.75, - 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( + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely children: [ - buildGroupListView(isDesktop), + Text( + "Add New Group", + 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(height: 5), + isDesktop + ? SizedBox.shrink() + : Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.8, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterGroups, + decoration: InputDecoration( + hintText: "Search for a Group", + 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: futureGroups, + 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 SizedBox(height: 8), + Text( + "No Group Found", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + Text( + "Please Create Group", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ); + } + + List groups = + filteredGroups.isNotEmpty ? filteredGroups : allGroups; + + groups.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 paginatedGroup = + groups + .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( + 'Group Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Domestic Policy', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'International Policy', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Description', + 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: + paginatedGroup.map((group) { + String groupId = + group['group_id'].toString(); // Get group ID + bool isSelected = selectedGroupId == groupId; + + return DataRow( + cells: [ + DataCell( + Text( + "${group['name'] ?? ''}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text("${group['domestic_policy_name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + "${group['international_policy_name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + "${group['description'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + group['is_active'] == "1" + ? "Active" + : "Inactive", + style: TextStyle( + color: + group['is_active'] == "1" + ? Colors.green + : Colors.grey, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), + DataCell( + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + GestureDetector( + onTap: () async { + if (group['group_id'] != null) { + final newGroupID = int.tryParse( + group['group_id'].toString()); + if (newGroupID != null) { + final data = await apiService.getGroupDetailsFind( + newGroupID); // ✅ Always an int + showDialog( + context: context, + builder: (context) => + GroupData( + isDesktop: isDesktop, + groupId: newGroupID, + // Pass the ID + groupData: data, + layoutColor: layoutColor!, + fetchGetGroup: refreshData + ), + ); + } + } else { + print("something went wrong check properly"); + } + }, + + // onTap: () { + // context.go("/CreateGroup", extra: group); + // }, + child: Tooltip( + message: 'Edit Group Details', + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), ), + ), + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + final idStr = group['group_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = group['is_active']; + // print("GroupId : ${group['group_id']} "); + deleteGroup(group, id, status); + }, + child: Tooltip( + message: 'Delete Group Details', + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15),), + ), + ], + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + Widget buildMobileCardView(List paginatedGroup) { + return ListView.builder( + itemCount: paginatedGroup.length, + itemBuilder: (context, index) { + final object = paginatedGroup[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 1: Policy Name and Actions + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "Group Name: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: "${object['name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal + ), + ), + ], + ), + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () async { + if (object['group_id'] != null) { + final newGroupID = int.tryParse( + object['group_id'].toString()); + if (newGroupID != null) { + final data = await apiService.getGroupDetailsFind( + newGroupID); // ✅ Always an int + showDialog( + context: context, + builder: (context) => + GroupData( + isDesktop: isDesktop, + groupId: newGroupID, + // Pass the ID + groupData: data, + layoutColor: layoutColor!, + fetchGetGroup: refreshData + ), + ); + } + } else { + print("something went wrong check properly"); + } + }, + + // onTap: () { + // context.go("/CreateGroup", extra: group); + // }, + child: Tooltip( message: 'Edit Group Details', + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15),), + ), + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + final idStr = object['group_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = object['is_active']; + // print("GroupId : ${group['group_id']} "); + deleteGroup(object, id, status); + }, + child: Tooltip( + message: 'Edit Group Details', + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15),), + ), + ], + ), + ], + ), + + SizedBox(height: 8), // Spacing + + // Row 2: + RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "Domestic Policy: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: "${object['domestic_policy_name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal + ), + ), + ], + ), + ), + SizedBox(height: 8), // Spacing + + // Row 3: + RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "International Policy: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: "${object['international_policy_name']}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal + ), + ), + ], + ), + ), + SizedBox(height: 8), // Spacing + + // Row 4: + RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "Description: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: "${object['description'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop + ? SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, // <-- your existing table + ) + : buildMobileCardView(paginatedGroup), + ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: groups.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, ), ], - ) - ], + ), + ), ), ); } - - 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 group = 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: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: Text("Group Name", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - Expanded( - child: Text("Domestic Policy", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - Expanded( - child: Text("International Policy", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - Expanded( - child: Text("Description", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - ], - ), - SizedBox(height: 4), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text("${group['name']}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600)), - ), - Expanded( - child: Text("${group['domestic_policy_name'] ?? 'N/A'}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600)), - ), - Expanded( - child: Text("${group['international_policy_name']}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600))), - Expanded( - child: Text("${group['description'] ?? 'N/A'}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600))), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - GestureDetector( - onTap: () async { - if (group['group_id'] != null) { - final newGroupID = int.tryParse( - group['group_id'].toString()); - if (newGroupID != null) { - final data = await apiService.getGroupDetailsFind( - newGroupID); // ✅ Always an int - showDialog( - context: context, - builder: (context) => - GroupData( - isDesktop: isDesktop, - groupId: newGroupID, - // Pass the ID - groupData: data, - layoutColor: layoutColor!, - fetchGetGroup: refreshData - ), - ); - } - } else { - print("something went wrong check properly"); - } - }, - - // onTap: () { - // context.go("/CreateGroup", extra: group); - // }, - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox( - width: 5, - ), - GestureDetector( - onTap: () { - final idStr = group['group_id']; - final id = int.tryParse(idStr.toString()); - - if (id == null) { - print("group_id is null"); - return; - } - final status = group['is_active']; - // print("GroupId : ${group['group_id']} "); - deleteGroup(group, id, status); - }, - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - ], - ), - ], - ), - ), - ); - }, - ); - } -} +} \ No newline at end of file diff --git a/lib/Screens/group/groupListBackUp.dart b/lib/Screens/group/groupListBackUp.dart new file mode 100644 index 0000000..ab02409 --- /dev/null +++ b/lib/Screens/group/groupListBackUp.dart @@ -0,0 +1,433 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:frontend/Screens/group/group.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 '../../config/apiUrl.dart'; +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import 'groupDetails.dart'; + +class GroupListBackUp extends StatefulWidget { + @override + _GroupListBackUpState createState() => _GroupListBackUpState(); +} + +class _GroupListBackUpState extends State { + final ApiService apiService = ApiService(); + + List? apiAllGroups; + Color? layoutColor; + Color? bodyColor; + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + loadAllGroups(); + loadInitialData(); + }); + } + + void loadInitialData() async { + String? layoutString = await getLayoutColor(); + String? bodyStringColor = await getBodyColor(); + + setState(() { + layoutColor = layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; + + bodyColor = bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; + }); + } + + Future loadAllGroups() async { + try { + final result = await apiService.fetchAllGroup(); + setState(() { + apiAllGroups = result; + }); + print("Fetched services: $apiAllGroups"); + } catch (e) { + print('Error fetching role list: $e'); + } + } + + void handleActiveStatus( + Map groupData, + String groupId, + String currentStatus, + ) async { + print("Toggling user status - $groupId (Current: $currentStatus)"); + + final String apiUrlData = + '$apiUrl/api/groups/update/$groupId'; // 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"); + + 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 || response.statusCode == 201) { + print("User status updated successfully to $newStatus!"); + loadAllGroups(); // 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"); + } + } + + void deleteGroup(Map groupdata, groupId, status) { + print("GroupId : $groupId"); + print("Groupstatus: $status"); + print("GroupsData: $groupdata"); + + // handleActiveStatus(groupdata, groupId, status); + print("Calling handleActiveStatus with: id=$groupId, status=$status"); + handleActiveStatus(groupdata, groupId.toString(), status.toString()); + } + + Future refreshData() async { + loadAllGroups(); + } + + // 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) { + bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return Scaffold( + // backgroundColor: Colors.white, + backgroundColor: Color(0xFFf5f5f5), + 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 + ) + : EdgeInsets.all(8), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildGroupListLayout(isDesktop)) + ], + ), + ), + ); + }); + } + + Widget buildGroupListLayout(bool isDesktop) { + return Container( + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), + // decoration: BoxDecoration( + // // color: Colors.amber, + // // color: bodyColor, + // color: Color(0xFFE1F5FE), + // border: Border.all( + // color: Colors.white, + // // color: Color(0xFFF7F7FB), + // width: 3.5)), + child: buildGroupData(isDesktop), + ); + } + + // Widget buildGroupListView(bool isDesktop) { + // return Container( + // child: Text("DAta"), + // ); + // } + + Widget buildGroupData(isDesktop) { + return Container( + // margin: isDesktop + // ? EdgeInsets.all(10.0) + // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), + // padding: const EdgeInsets.all(10), + 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( + 'Group 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('/CreateGroup'); + showDialog( + context: context, + builder: (context) => GroupData( + isDesktop: isDesktop, + groupId: null, + layoutColor: layoutColor!, + fetchGetGroup: refreshData + ), + ); + + + }, + child: Row( + children: [ + Text('New Group', 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.75, + 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) { + 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 group = 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: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text("Group Name", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + Expanded( + child: Text("Domestic Policy", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + Expanded( + child: Text("International Policy", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + Expanded( + child: Text("Description", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + ], + ), + SizedBox(height: 4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text("${group['name']}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600)), + ), + Expanded( + child: Text("${group['domestic_policy_name'] ?? 'N/A'}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600)), + ), + Expanded( + child: Text("${group['international_policy_name']}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600))), + Expanded( + child: Text("${group['description'] ?? 'N/A'}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600))), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () async { + if (group['group_id'] != null) { + final newGroupID = int.tryParse( + group['group_id'].toString()); + if (newGroupID != null) { + final data = await apiService.getGroupDetailsFind( + newGroupID); // ✅ Always an int + showDialog( + context: context, + builder: (context) => + GroupData( + isDesktop: isDesktop, + groupId: newGroupID, + // Pass the ID + groupData: data, + layoutColor: layoutColor!, + fetchGetGroup: refreshData + ), + ); + } + } else { + print("something went wrong check properly"); + } + }, + + // onTap: () { + // context.go("/CreateGroup", extra: group); + // }, + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + final idStr = group['group_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = group['is_active']; + // print("GroupId : ${group['group_id']} "); + deleteGroup(group, id, status); + }, + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/Screens/hotels/hotels_list.dart b/lib/Screens/hotels/hotels_list.dart index 281eba6..c23a904 100644 --- a/lib/Screens/hotels/hotels_list.dart +++ b/lib/Screens/hotels/hotels_list.dart @@ -200,6 +200,7 @@ class HotelsDataListState extends State { false) || (isActiveStatus.contains(lowerQuery)); }).toList(); + currentPage = 0; }); print("filteredHotels: $filteredHotels"); } @@ -603,10 +604,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..afae4f3 100644 --- a/lib/Screens/plans/list_plans.dart +++ b/lib/Screens/plans/list_plans.dart @@ -106,6 +106,7 @@ class _ListPlansState extends State { (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); }).toList(); + currentPage = 0; }); print("filteredPlans: $filteredPlans"); } @@ -827,6 +828,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 +847,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 +860,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 +870,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 +883,7 @@ class _ListPlansState extends State { color: Color(0xFF475569), size: 11, ), + tooltip: 'Trip Comments', onPressed: () { showDialog( context: context, @@ -1023,6 +1029,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 +1048,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 +1061,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 +1071,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 +1084,7 @@ class _ListPlansState extends State { color: Color(0xFF475569), size: 11, ), + tooltip: 'Trip Comments', onPressed: () { showDialog( context: context, diff --git a/lib/Screens/policy/policyListBackup.dart b/lib/Screens/policy/policyListBackup.dart new file mode 100644 index 0000000..2abe9b9 --- /dev/null +++ b/lib/Screens/policy/policyListBackup.dart @@ -0,0 +1,428 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:frontend/Screens/group/group.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 '../../config/apiUrl.dart'; +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; + +class PolicyListBackup extends StatefulWidget { + @override + _PolicyListBackupState createState() => _PolicyListBackupState(); +} + +class _PolicyListBackupState extends State { + final ApiService apiService = ApiService(); + + List? apiAllGroups; + Color? layoutColor; + Color? bodyColor; + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + loadAllGroups(); + loadInitialData(); + }); + } + + void loadInitialData() async { + String? layoutString = await getLayoutColor(); + String? bodyStringColor = await getBodyColor(); + + setState(() { + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; + + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; + }); + } + + Future loadAllGroups() 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(() { + apiAllGroups = result; + }); + print("Fetched services: $apiAllGroups"); + } catch (e) { + print('Error fetching role list: $e'); + } + } + + void handleActiveStatus( + Map policyData, + String policyId, + String currentStatus, + ) async { + print("Toggling user status - $policyId (Current: $currentStatus)"); + + final String apiUrlData = + '$apiUrl/api/policy/createOrUpdate'; // 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"); + + final int? selectedPolicyId; + + if (policyId.isNotEmpty) { + selectedPolicyId = int.tryParse(policyId); + policyData['policy_id'] = selectedPolicyId; // Add only if updating + policyData['is_active'] = newStatus; // Add only if updating + } + + try { + final response = await http.post( + Uri.parse(apiUrlData), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode(policyData), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("policyData submitted successfully!"); + print("Response: ${response.body}"); + + loadAllGroups(); + } else { + print("Failed to submit policyData. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print(" Error submitting policyData: $e"); + } + } + + void deletePolicy(Map policydata, policyId, status) { + print("policyId : $policyId"); + print("policystatus: $status"); + print("policysData: $policydata"); + + // handleActiveStatus(groupdata, groupId, status); + print("Calling handleActiveStatus with: id=$policyId, status=$status"); + 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) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + 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 + ) + : EdgeInsets.all(0), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildGroupList(isDesktop)), + ], + ), + ), + ); + }, + ); + } + + 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), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), + // decoration: BoxDecoration( + // // color: Colors.amber, + // color: Color(0xFFE1F5FE), + // // color: bodyColor, + // border: Border.all( + // // color: Color(0xFFF7F7FB), + // color: Colors.white, + // width: 3.5)), + child: buildGroupListLayout(isDesktop), + ); + } + + Widget buildGroupListLayout(bool isDesktop) { + return Container( + // margin: isDesktop + // ? EdgeInsets.all(10.0) + // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), + // padding: const EdgeInsets.all(10), + 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 Policy 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: 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( + "${policy['name']}", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + Expanded( + flex: 1, + child: Text( + policy['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 = policy['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: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + SizedBox(width: 5), + GestureDetector( + onTap: () { + final idStr = policy['policy_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = policy['is_active']; + + deletePolicy(policy, id, status); + }, + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/Screens/policy/policy_list.dart b/lib/Screens/policy/policy_list.dart index 99c0d1e..4c8ece3 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: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'; + class PolicyList extends StatefulWidget { @override @@ -19,20 +21,45 @@ class PolicyList extends StatefulWidget { } class _PolicyListState extends State { - final ApiService apiService = ApiService(); - List? apiAllGroups; + final ApiService apiService = ApiService(); + late Future> futurePolicy; + + late Map userSingleData; + List? apiCountryData; + String? selectedPolicyId; + String? orgId; + Color? layoutColor; Color? bodyColor; + List allPolicy = []; + List filteredPolicy = []; + TextEditingController searchController = TextEditingController(); + + + int currentPage = 0; + int itemsPerPage = 10; + + List? apiAllPolicy; + @override void initState() { super.initState(); + futurePolicy = fetchPolicy(); + + futurePolicy.then((object) { + setState(() { + allPolicy = object; + }); + }); WidgetsBinding.instance.addPostFrameCallback((_) { - loadAllGroups(); loadInitialData(); + fetchPolicy(); }); + + // futurePlans = fetchPlans(); } void loadInitialData() async { @@ -41,42 +68,60 @@ class _PolicyListState extends State { setState(() { layoutColor = - layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; bodyColor = - bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } - Future loadAllGroups() 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(() { - apiAllGroups = result; - }); - print("Fetched services: $apiAllGroups"); - } catch (e) { - print('Error fetching role list: $e'); - } + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); } + + + Future> fetchPolicy() async { + final data = await apiService.fetchAllPolicy(); + return data; // Returning raw JSON list + } + + // Refresh user list after update + void refreshPolicyList() { + setState(() { + futurePolicy = fetchPolicy(); // Re-fetch users after status update + // Wait for futurePlans to be fetched and update allPlans + }); + } + + void filterPolicy(String query) { + print("allPolicy before filtering: $query"); + final lowerQuery = query.toLowerCase(); + setState(() { + filteredPolicy = + allPolicy.where((object) { + return (object['name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (object['policy_type']?.toLowerCase().contains(lowerQuery) ?? + false) || + (object['is_active']?.toLowerCase().contains(lowerQuery) ?? false);}).toList(); + currentPage = 0; + }); + print("filtered: $filteredPolicy"); + } + + + 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 = @@ -135,14 +180,26 @@ 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'); - // } - // }' + Future loadAllGroups() 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; + }); + print("Fetched services: $futurePolicy"); + } catch (e) { + print('Error fetching role list: $e'); + } + } + @override Widget build(BuildContext context) { @@ -153,23 +210,26 @@ 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 - ) - : EdgeInsets.all(0), + 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 + ) + : EdgeInsets.all(0), child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), + // const Expanded(child: Center(child: Text("User Page Content"))), Expanded(child: buildGroupList(isDesktop)), ], ), @@ -182,247 +242,713 @@ 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) // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), // padding: const EdgeInsets.all(10), 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, + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, + + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + color: Colors.white, + padding: const EdgeInsets.all(10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Policy List', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), + Row( + children: [ + Text( + 'Policy', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ], ), - IconButton( - icon: const Icon(Icons.keyboard_arrow_down), - onPressed: () {}, + 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: filterPolicy, + decoration: InputDecoration( + hintText: "Search for a Policy", + 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), + Spacer(), + + 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 policyData = await futurePolicy; + + // Print the resolved value + print("CREATELIAS - $policyData"); + context.go('/Policy'); + + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add New 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, + ), + ], + ), ), ], ), - 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', + + if (!isDesktop) SizedBox(height: 5), + isDesktop + ? SizedBox.shrink() + : Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.8, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterPolicy, + decoration: InputDecoration( + hintText: "Search for a Policy", + 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: 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)]), ), - ), + // SizedBox(width: 16), + ], + ), + const SizedBox(height: 10), + FutureBuilder>( + future: futurePolicy, + 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 SizedBox(height: 8), + Text( + "No Policy Found", + 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), + ], + ), + ), + ); + } + + List policyData = + filteredPolicy.isNotEmpty ? filteredPolicy : allPolicy; + + policyData.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 = + policyData + .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((policy) { + String policyId = + policy['policy_id'].toString(); // Get policy ID + bool isSelected = selectedPolicyId == policyId; + + return DataRow( + cells: [ + DataCell( + Text( + "${policy['name'] ?? ''}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + policy['domestic'] == "1" + ? "Domestic" + : "International", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + policy['is_active'] == "1" + ? "Active" + : "Inactive", + style: TextStyle( + color: + policy['is_active'] == "1" + ? Colors.green + : Colors.grey, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), + DataCell( + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + GestureDetector( + onTap: () async { + final rawId = policy['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 = policy['policy_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = policy['is_active']; + + deletePolicy(policy, 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 object = 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 1: Policy Name and Actions + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "Policy Name: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: "${object['name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal + ), + ), + ], + ), + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () async { + final rawId = object['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 = object['policy_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("policy_id is null"); + return; + } + final status = object['is_active']; + + deletePolicy(object, id, status); + }, + child: Tooltip( + message: 'Delete Policy Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), + ), + ], + ), + ], + ), + + SizedBox(height: 8), // Spacing + + // Row 2: Policy Type + RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "Policy Type: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: object['domestic'] == "1" + ? "Domestic" + : "International", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ); + } + + + Widget buildMobileCardView2(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final object = 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: [ + Column( + children: [ + + 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), + Column( + children: [ + Expanded( + flex: 1, + child: Text( + "${object['name']}", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + Expanded( + flex: 1, + child: Text( + object['domestic'] == "1" + ? "Domestic" + : "International", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + // Expanded(flex: 1, child: Text("${policy['created_by']}")), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () async { + final rawId = object['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 = object['policy_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = object['is_active']; + + deletePolicy(object, 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: policyData.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, ), ], ), - ], + ), ), ); } - - // Widget buildGroupListView(bool isDesktop) { - // return Container( - // child: Text("DAta"), - // ); - // } - - Widget buildGroupListView(bool isDesktop) { - if (apiAllGroups == null || apiAllGroups!.isEmpty) { - return Center(child: Text("No Policy 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: 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( - "${policy['name']}", - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - Expanded( - flex: 1, - child: Text( - policy['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 = policy['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: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, - ), - ), - SizedBox(width: 5), - GestureDetector( - onTap: () { - final idStr = policy['policy_id']; - final id = int.tryParse(idStr.toString()); - - if (id == null) { - print("group_id is null"); - return; - } - final status = policy['is_active']; - - deletePolicy(policy, id, status); - }, - child: Image.asset( - 'assets/images/IconsImg/delete.png', - width: 20, - height: 15, - ), - ), - ], - ), - ], - ), - ), - ); - }, - ); - } -} +} \ No newline at end of file diff --git a/lib/Screens/traveller/travellerList.dart b/lib/Screens/traveller/travellerList.dart index 1076c5a..7cb3ecd 100644 --- a/lib/Screens/traveller/travellerList.dart +++ b/lib/Screens/traveller/travellerList.dart @@ -153,6 +153,7 @@ class TravellerListState extends State { false) || (isActiveStatus.contains(lowerQuery)); }).toList(); + currentPage = 0; }); print("filteredTraveller: $filteredTraveller"); } diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index 9cdff8d..e8cca5e 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -1012,6 +1012,7 @@ class _CreateUserFormDetialsState extends State { return OfficeDetails( isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, + userIdApi:userIdApi, controllers: controllers, errorMessages: errorMessages, selectedLevel: selectedLevel, @@ -1062,12 +1063,14 @@ class _CreateUserFormDetialsState extends State { errorMessages: errorMessages, travelDetails: travelDetailsDataFromAPI, // 👈 Pass this down passportFileUrlFromApi: passportFileUrlFromApi, + userIdApi:userIdApi, ); default: return PersonalDetails( personalDetailsKey: personalDetailsKey, isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, + userIdApi:userIdApi, controllers: controllers, errorMessages: errorMessages, selectedGender: selectedGender, diff --git a/lib/Screens/userManagement/create_user/office_details.dart b/lib/Screens/userManagement/create_user/office_details.dart index 6a73593..923f5fc 100644 --- a/lib/Screens/userManagement/create_user/office_details.dart +++ b/lib/Screens/userManagement/create_user/office_details.dart @@ -15,6 +15,8 @@ class OfficeDetails extends StatefulWidget { final Map errorMessages; final bool isDesktop; final bool isViewMode; + final String? userIdApi; + final ValueChanged? onLevelChanged; final ValueChanged? onDepartmentChanged; @@ -48,6 +50,7 @@ class OfficeDetails extends StatefulWidget { this.onThirdApproverChanged, this.selectedSubstituteApprover, this.onFirstSubsApproverChanged, + this.userIdApi, }) : super(key: key); @override @@ -154,6 +157,28 @@ class _OfficeDetailsState extends State { fetchFindGroup(); } + Future apiCheckDuplicate(String label, String field, String value, String? userId) async { + try { + + if (field == "employeeCode") { + field = "employee_code"; + } + + final response = await apiService.CheckDuplicate(label, field, value, userId); + if (response.isNotEmpty) { + _clearError(field); + widget.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) { setState(() { widget.errorMessages.remove(field); @@ -401,6 +426,7 @@ class _OfficeDetailsState extends State { enabled: !widget.isViewMode, onChanged: (value) { _clearError("employeeCode"); + apiCheckDuplicate("Employee Code", "employeeCode",value,widget.userIdApi); }, decoration: InputDecoration( labelText: "Employee Code", diff --git a/lib/Screens/userManagement/create_user/personal_details.dart b/lib/Screens/userManagement/create_user/personal_details.dart index 68b7c03..2f9113e 100644 --- a/lib/Screens/userManagement/create_user/personal_details.dart +++ b/lib/Screens/userManagement/create_user/personal_details.dart @@ -157,6 +157,34 @@ class PersonalDetailsState extends State { }); } + Future apiCheckDuplicate(String label, String field, String value, String? userId) async { + try { + // Basic validation: Check mobile number length + if (field == "mobile_no" && value.length != 10) { + _clearError(field); + widget.errorMessages[field] = "$label is invalid"; + print("Validation failed: $label is too short."); + return; // Skip the API call if input is invalid + } + + final response = await apiService.CheckDuplicate(label, field, value, userId); + if (response.isNotEmpty) { + _clearError(field); + widget.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) { setState(() { widget.errorMessages.remove(field); @@ -1004,6 +1032,7 @@ class PersonalDetailsState extends State { controller: widget.controllers["email"], onChanged: (value) { _clearError("email"); + apiCheckDuplicate("Email", "email",value,widget.userIdApi); }, enabled: !widget.isViewMode, decoration: InputDecoration( @@ -1054,6 +1083,7 @@ class PersonalDetailsState extends State { enabled: !widget.isViewMode, onChanged: (value) { _clearError("mobile_no"); + apiCheckDuplicate("Mobile Number", "mobile_no",value,widget.userIdApi); }, keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ diff --git a/lib/Screens/userManagement/create_user/traveller_details.dart b/lib/Screens/userManagement/create_user/traveller_details.dart index df12175..b1e39de 100644 --- a/lib/Screens/userManagement/create_user/traveller_details.dart +++ b/lib/Screens/userManagement/create_user/traveller_details.dart @@ -31,6 +31,8 @@ class TravellerDetails extends StatefulWidget { final bool isViewMode; final Map? travelDetails; final String? passportFileUrlFromApi; + final String? userIdApi; + const TravellerDetails({ Key? key, @@ -40,6 +42,7 @@ class TravellerDetails extends StatefulWidget { required this.isViewMode, this.travelDetails, this.passportFileUrlFromApi, + this.userIdApi, }) : super(key: key); @override TravellerDetailsState createState() => TravellerDetailsState(); @@ -146,6 +149,26 @@ class TravellerDetailsState extends State { }); } + Future apiCheckDuplicate(String label, String field, String value, String? userId) async { + try { + if(field == "forex_card_num"){ + field = "forex_pre_paid_card_number"; + } + final response = await apiService.CheckDuplicate(label, field, value, userId); + if (response.isNotEmpty) { + _clearError(field); + widget.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) { setState(() { widget.errorMessages.remove(field); @@ -2308,6 +2331,7 @@ class TravellerDetailsState extends State { enabled: !widget.isViewMode, onChanged: (value) { _clearError("forex_card_num"); + apiCheckDuplicate("Forex Pre-Paid Card Number", "forex_card_num",value,widget.userIdApi); }, decoration: InputDecoration( labelText: "Card Number", @@ -2439,14 +2463,24 @@ class TravellerDetailsState extends State { SizedBox(width: 25), buildFrequentFlierInformation(entry), SizedBox(width: 15), - IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), - onPressed: () { - setState(() { - removeFrequentFlierEntry(entry); - }); - }, + SizedBox( + height: 60, + child: Center( + child: Padding( + padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here + child: IconButton( + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', + onPressed: () { + setState(() { + removeFrequentFlierEntry(entry); + }); + }, + ), + ), + ), ), + ], ) : Column( @@ -2457,7 +2491,8 @@ class TravellerDetailsState extends State { buildFrequentFlierInformation(entry), SizedBox(width: 15), IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', onPressed: () { setState(() { removeFrequentFlierEntry(entry); @@ -2738,22 +2773,30 @@ class TravellerDetailsState extends State { child: widget.isDesktop ? Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - buildHotel(entry), - SizedBox(width: 25), - buildHotelMembershipNum(entry), - IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), - onPressed: () { - setState(() { - // removeHotelLoyaltyEntry(localId); - removeHotelLoyaltyEntry(entry); - }); - }, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildHotel(entry), + SizedBox(width: 25), + buildHotelMembershipNum(entry), + SizedBox( + height: 60, + child: Center( + child: Padding( + padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here + child: IconButton( + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', + onPressed: () { + setState(() { + removeHotelLoyaltyEntry(entry); + }); + }, + ), ), - ], - ) + ), + ), + ], + ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -2761,7 +2804,8 @@ class TravellerDetailsState extends State { SizedBox(height: 8), buildHotelMembershipNum(entry), IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', onPressed: () { setState(() { // removeHotelLoyaltyEntry(localId); @@ -2987,13 +3031,22 @@ class TravellerDetailsState extends State { buildVisaValidFrom(entry), SizedBox(width: 15), buildVisaValidUpTo(entry), - IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), - onPressed: () { - setState(() { - removeVisaEntry(entry); - }); - }, + SizedBox( + height: 60, + child: Center( + child: Padding( + padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here + child: IconButton( + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', + onPressed: () { + setState(() { + removeVisaEntry(entry); + }); + }, + ), + ), + ), ), ], ) @@ -3008,7 +3061,8 @@ class TravellerDetailsState extends State { SizedBox(height: 8), buildVisaValidUpTo(entry), IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', onPressed: () { setState(() { // visaRowIds.remove(id); diff --git a/lib/routes/organizationSetting.dart b/lib/routes/organizationSetting.dart index 01567f7..8ede904 100644 --- a/lib/routes/organizationSetting.dart +++ b/lib/routes/organizationSetting.dart @@ -88,9 +88,9 @@ class OrganizationSettingState extends State { }, { 'value': '/forexTexmplate', - 'icon': Icons.group_add_outlined, + 'icon': Icons.attach_money, 'label': 'Forex Template', - 'description': 'Create and Edit Template', + 'description': 'Edit Template', }, { 'value': '/templateList', diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index 0ee9888..5a63f9b 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -1355,4 +1355,55 @@ class ApiService { throw Exception('Failed to load Hotel details'); } } + + // ----------------------- User Management - CheckDuplicate --------------- + Future> CheckDuplicate( + String label, + String field, + String value, + String? userId, + ) async { + String apiUrldata; + if (userId == null || userId.isEmpty) { + apiUrldata = '$apiUrl/api/checkDuplicate?$field=$value'; + } else { + apiUrldata = '$apiUrl/api/checkDuplicate?$field=$value&user_id=$userId'; + } + + 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); + + // Updated to match actual response structure + if (data['status'] == "exists") { + print("${data['field']} - $value already exists"); + return {"message": "$label already exists", "field": data['field']}; + } else { + print("$label - $value is a new value"); + return {}; + } + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else { + throw Exception( + 'Failed to load checkDuplicate data. Status code: ${response.statusCode}', + ); + } + } + + // ----------------------- User Management - CheckDuplicate end here ---------------------------------- } 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']);