diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 2d9509b..b2138e0 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -63,13 +63,15 @@ class _ApprovalListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -239,8 +241,10 @@ class _ApprovalListState extends State { // print("Error fetching plan: $e"); // } - bool confirmed = - await apiService.showCancelConfirmationDialog(context, layoutColor); + bool confirmed = await apiService.showCancelConfirmationDialog( + context, + layoutColor, + ); if (confirmed) { try { @@ -282,17 +286,23 @@ class _ApprovalListState extends State { } } - void viewPlanforApprover(String planId, - {bool isViewMode = false, bool isApprover = true}) async { + void viewPlanforApprover( + String planId, { + bool isViewMode = false, + bool isApprover = true, + }) async { try { Map planData = await getViewPlan(planId); print("ViewAAA - $planData"); - context.go('/createPlan', extra: { - 'planData': planData, - 'isViewMode': isViewMode, - 'isApprover': isApprover - }); + context.go( + '/createPlan', + extra: { + 'planData': planData, + 'isViewMode': isViewMode, + 'isApprover': isApprover, + }, + ); } catch (e) { print("Error fetching plan: $e"); } @@ -314,49 +324,58 @@ class _ApprovalListState extends State { print("allPlans before filtering: $allPlans"); final lowerQuery = query.toLowerCase(); setState(() { - filteredPlans = allPlans.where((plan) { - return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); - }).toList(); + filteredPlans = + allPlans.where((plan) { + return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); currentPage = 0; }); print("filteredPlans: $filteredPlans"); } 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: Color(0xFFf5f5f5), - // 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), + // 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)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupListLayout(bool isDesktop) { @@ -383,11 +402,14 @@ class _ApprovalListState extends State { // } // } + final adjHgt = MediaQuery.of(context).size.height; + String _formatDate(String rawDate) { try { final dateTime = DateTime.parse(rawDate); - return DateFormat('dd, MMM yyyy HH:mm') - .format(dateTime); // 24-hour format + return DateFormat( + 'dd, MMM yyyy HH:mm', + ).format(dateTime); // 24-hour format } catch (e) { return rawDate; // fallback if parsing fails } @@ -396,17 +418,19 @@ class _ApprovalListState extends State { return Container( margin: isDesktop ? EdgeInsets.all(10.0) : null, padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20), - height: isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, + 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, + border: + isDesktop + ? Border.all( + width: 2, + color: Colors.white, + // color: Color(0xFFF7F7FB), + ) + : null, color: isDesktop ? Colors.white : Color(0xFFFCFCFC), // color: Color(0xFFF7F7FB), @@ -444,9 +468,7 @@ class _ApprovalListState extends State { ], ), if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.23, - ), + SizedBox(width: MediaQuery.of(context).size.width * 0.23), if (isDesktop) Container( @@ -457,8 +479,10 @@ class _ApprovalListState extends State { onChanged: filterPlans, decoration: InputDecoration( hintText: "Search...", - hintStyle: - TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), @@ -470,69 +494,72 @@ class _ApprovalListState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade200, width: 0.5), + color: Colors.grey.shade200, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), + color: Colors.grey.shade300, + width: 1, + ), ), ), - style: GoogleFonts.poppins( - fontSize: 12, - ), + style: GoogleFonts.poppins(fontSize: 12), ), ), // SizedBox(width: 16), ], ), - if (!isDesktop) - SizedBox( - height: 5, - ), + 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: filterPlans, - decoration: InputDecoration( - hintText: "Search...", - 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), + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.8, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterPlans, + decoration: InputDecoration( + hintText: "Search...", + 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, ), ), - style: GoogleFonts.poppins( - fontSize: 12, + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, + ), ), ), + style: GoogleFonts.poppins(fontSize: 12), ), - // SizedBox(width: 16), - ], - ), + ), + // SizedBox(width: 16), + ], + ), const SizedBox(height: 10), FutureBuilder>( @@ -548,7 +575,9 @@ class _ApprovalListState extends State { padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, - children: const [ + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ // Icon(Icons.error_outline, // color: Colors.redAccent, size: 60), // SizedBox(height: 1), @@ -557,14 +586,18 @@ class _ApprovalListState extends State { // fontSize: 22, // fontWeight: FontWeight.bold, // color: Colors.redAccent)), - SizedBox(height: 15), - Text("No Trips Pending For Your Approvals", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20, - fontFamily: "Inter", - fontWeight: FontWeight.w600, - color: Colors.black54)), + SizedBox(height: adjHgt / 4), + Text( + " No Trips Pending For Your Approvals", + + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + // fontFamily: "Inter", + fontWeight: FontWeight.w600, + color: Colors.black54, + ), + ), ], ), ), @@ -580,308 +613,412 @@ class _ApprovalListState extends State { List plans = searchController.text.isEmpty ? allPlans : filteredPlans; - plans.sort((a, b) => - int.parse(b.planId).compareTo(int.parse(a.planId))); + plans.sort( + (a, b) => + int.parse(b.planId).compareTo(int.parse(a.planId)), + ); - List paginatedPlans = plans - .skip(currentPage * itemsPerPage) - .take(itemsPerPage) - .toList(); + List paginatedPlans = + plans + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); Widget table = LayoutBuilder( builder: (context, constraints) { double minWidth = isDesktop ? constraints.maxWidth : 1300; return ConstrainedBox( - constraints: BoxConstraints( - minWidth: minWidth, - ), + 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), + width: 0.5, + color: Colors.grey.shade200, + ), ), columns: [ DataColumn( - label: Text( - 'Trip ID', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Trip Name', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Emp Code', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Traveller', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Trip Type', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Created On', - 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: paginatedPlans.map((plan) { - return DataRow(cells: [ - DataCell(Text(plan.planId, - style: TextStyle( - fontSize: 13, - fontFamily: "Archivo", - ))), - - DataCell(Text(plan.tripTitle, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell(Text(plan.employeeCode ?? " - ", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text( - plan.userName.isNotEmpty - ? plan.userName - : plan.travellerName, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(plan.tripType, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - - DataCell(Text( - - // plan.createdOn, - _formatDate(plan.createdOn), - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell( - Container( - width: double - .infinity, // Set your desired fixed size (equal width and height) - height: 25, - alignment: Alignment.center, - decoration: BoxDecoration( - color: getStatusColor(plan.statusValue), - borderRadius: BorderRadius.circular(8), - ), - - child: Text( - plan.statusValue, - textAlign: TextAlign.center, - style: TextStyle( - color: - getStatusTextColor(plan.statusValue), - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.w400, - ), - ), + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, ), ), - - DataCell( - Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, + ), + DataColumn( + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Created On', + 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: + paginatedPlans.map((plan) { + return DataRow( + cells: [ + DataCell( + Text( + plan.planId, + style: TextStyle( + fontSize: 13, + fontFamily: "Archivo", + ), ), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 8, vertical: 8), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - IconButton( - icon: Icon( - Icons.remove_red_eye, - color: - Color(0xFF475569), - size: 18), - tooltip: 'View The Trip Details', - onPressed: () { - Navigator.pop( - context); // Close popup manually - viewPlanforApprover( - plan.planId, - isViewMode: true, - isApprover: true); - }), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - tooltip: 'Edit The Trip Details', - onPressed: () { - print( - "Approver Edit : ${plan.approverId}"); - print( - "Approver Edit2 : ${plan.delegaterId}"); - Navigator.pop(context); - ApiService - .viewPlanForApprover( - context, - plan.planId, - plan.approverId, - plan.delegaterId, - isViewMode: false, - isApprover: true); - }, - ), - IconButton( - icon: Icon( - Icons.cancel_rounded, - size: 18), - tooltip: 'Cancellation The Trip Details', - onPressed: () { - Navigator.pop(context); - deletePlan(plan.planId); - }, - ), - IconButton( - icon: Icon(Icons.download, - color: Color(0xFF114D8B), - size: 18), - tooltip: 'Download The Trip Details', - onPressed: () { - Navigator.pop(context); - apiService.getPdfDownload( - plan.planId); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color(0xFF475569), - size: 11, - ), - tooltip: 'Approver Comment', - onPressed: () { - showDialog( - context: context, - builder: (context) => - CommentModalList( - // planId: plan.planId, - planId: plan - .planId - .toString(), - layoutColorForUser: - layoutColor!, - role: - "Approver"), - ); - }), - ], - ), + ), + + DataCell( + Text( + plan.tripTitle, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + plan.employeeCode ?? " - ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + + DataCell( + Text( + // plan.createdOn, + _formatDate(plan.createdOn), + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Container( + width: + double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 8, ), ), - ], + + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: getStatusTextColor( + plan.statusValue, + ), + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), ), + + DataCell( + Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + viewPlanforApprover( + plan.planId, + isViewMode: + true, + isApprover: + true, + ); + }, + ), + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + print( + "Approver Edit : ${plan.approverId}", + ); + print( + "Approver Edit2 : ${plan.delegaterId}", + ); + Navigator.pop( + context, + ); + ApiService.viewPlanForApprover( + context, + plan.planId, + plan.approverId, + plan.delegaterId, + isViewMode: + false, + isApprover: + true, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Approver Comment', + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "Approver", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ), + // DataCell(Row(children: [ + // IconButton( + // icon: const Icon( + // Icons.remove_red_eye, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () => viewPlanforApprover( + // plan.planId, + // isViewMode: true, + // isApprover: true)), + // + // GestureDetector( + // onTap: () => ApiService.viewPlanForApprover( + // context, plan.planId, + // isViewMode: false, isApprover: true), + // child: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // ), + // + // // IconButton( + // // icon: const Icon(Icons.edit, + // // color: Colors.green), + // // onPressed: () => viewPlanforApprover(plan.planId, + // // isViewMode: false) ), + // + // SizedBox( + // width: 5, + // ), + // GestureDetector( + // onTap: () => deletePlan(plan.planId), + // child: Image.asset( + // 'assets/images/IconsImg/delete.png', + // width: 20, + // height: 15), + // ), + // + // IconButton( + // icon: const Icon( + // Icons.download, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () { + // apiService.getPdfDownload(plan.planId); + // }), + // ])), ], - ), - ), - // DataCell(Row(children: [ - // IconButton( - // icon: const Icon( - // Icons.remove_red_eye, - // color: Color(0xFF475569), - // size: 18, - // ), - // onPressed: () => viewPlanforApprover( - // plan.planId, - // isViewMode: true, - // isApprover: true)), - // - // GestureDetector( - // onTap: () => ApiService.viewPlanForApprover( - // context, plan.planId, - // isViewMode: false, isApprover: true), - // child: Image.asset( - // 'assets/images/IconsImg/edit.png', - // width: 20, - // height: 15), - // ), - // - // // IconButton( - // // icon: const Icon(Icons.edit, - // // color: Colors.green), - // // onPressed: () => viewPlanforApprover(plan.planId, - // // isViewMode: false) ), - // - // SizedBox( - // width: 5, - // ), - // GestureDetector( - // onTap: () => deletePlan(plan.planId), - // child: Image.asset( - // 'assets/images/IconsImg/delete.png', - // width: 20, - // height: 15), - // ), - // - // IconButton( - // icon: const Icon( - // Icons.download, - // color: Color(0xFF475569), - // size: 18, - // ), - // onPressed: () { - // apiService.getPdfDownload(plan.planId); - // }), - // ])), - ]); - }).toList(), + ); + }).toList(), ), ); }, @@ -894,8 +1031,10 @@ class _ApprovalListState extends State { final plan = paginatedPlans[index]; return Card( color: Colors.white, - margin: - EdgeInsets.symmetric(horizontal: 12, vertical: 6), + margin: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), @@ -912,7 +1051,9 @@ class _ApprovalListState extends State { children: [ Container( padding: EdgeInsets.symmetric( - horizontal: 8, vertical: 4), + horizontal: 8, + vertical: 4, + ), decoration: BoxDecoration( color: getStatusColor(plan.statusValue), borderRadius: BorderRadius.circular(8), @@ -921,7 +1062,8 @@ class _ApprovalListState extends State { plan.statusValue, style: TextStyle( color: getStatusTextColor( - plan.statusValue), + plan.statusValue, + ), fontSize: 10, fontWeight: FontWeight.w600, ), @@ -936,97 +1078,124 @@ class _ApprovalListState extends State { color: Color(0xFF475569), size: 14, ), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 8, vertical: 8), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - IconButton( - icon: Icon( + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon( Icons.remove_red_eye, - color: - Color(0xFF475569), - size: 18), - tooltip: 'View The Trip Details', - onPressed: () { - Navigator.pop( - context); // Close popup manually - viewPlanforApprover( + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + viewPlanforApprover( plan.planId, isViewMode: true, - isApprover: true); - }), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - tooltip: 'Edit The Trip Details', - onPressed: () { - Navigator.pop(context); - ApiService - .viewPlanForApprover( - context, - plan.planId, - plan.approverId, - plan.delegaterId, - isViewMode: false, - isApprover: true); - }, - ), - IconButton( - icon: Icon( - Icons.cancel_rounded, - size: 18), - tooltip: 'Cancellation The Trip Details', - onPressed: () { - Navigator.pop(context); - deletePlan(plan.planId); - }, - ), - IconButton( - icon: Icon(Icons.download, - color: Color(0xFF114D8B), - size: 18), - tooltip: 'Download The Trip Details', - onPressed: () { - Navigator.pop(context); - apiService.getPdfDownload( - plan.planId); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color(0xFF475569), - size: 11, + isApprover: true, + ); + }, ), - tooltip: 'Trip Comments', - onPressed: () { - showDialog( - context: context, - builder: (context) => - CommentModalList( + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop(context); + ApiService.viewPlanForApprover( + context, + plan.planId, + plan.approverId, + plan.delegaterId, + isViewMode: false, + isApprover: true, + ); + }, + ), + IconButton( + icon: Icon( + Icons.cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop(context); + deletePlan(plan.planId); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The Trip Details', + onPressed: () { + Navigator.pop(context); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: 'Trip Comments', + onPressed: () { + showDialog( + context: context, + builder: + ( + context, + ) => CommentModalList( // planId: plan.planId, - planId: plan - .planId - .toString(), + planId: + plan.planId + .toString(), layoutColorForUser: layoutColor!, role: - "Approver"), - ); - }), - ], + "Approver", + ), + ); + }, + ), + ], + ), + ), ), - ), - ), - ], + ], ), ], ), @@ -1039,11 +1208,14 @@ class _ApprovalListState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(' ${plan.tripTitle}', - style: TextStyle( - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.bold)), + Text( + ' ${plan.tripTitle}', + style: TextStyle( + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.bold, + ), + ), ], ), ], @@ -1058,24 +1230,28 @@ class _ApprovalListState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(' ${plan.tripType}', - style: TextStyle( - fontSize: 9, - color: Colors.black87, - fontFamily: "Inter", - )), + Text( + ' ${plan.tripType}', + style: TextStyle( + fontSize: 9, + color: Colors.black87, + fontFamily: "Inter", + ), + ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('${_formatDate(plan.createdOn)}', - style: TextStyle( - fontSize: 9, - color: Colors.black87, - fontFamily: "Inter", - )), + Text( + '${_formatDate(plan.createdOn)}', + style: TextStyle( + fontSize: 9, + color: Colors.black87, + fontFamily: "Inter", + ), + ), ], ), ], @@ -1093,9 +1269,10 @@ class _ApprovalListState extends State { ? '${plan.userName}' : '${plan.travellerName}', style: TextStyle( - fontSize: 9, - fontFamily: "Inter", - color: Colors.black87), + fontSize: 9, + fontFamily: "Inter", + color: Colors.black87, + ), ), ], ), @@ -1107,9 +1284,10 @@ class _ApprovalListState extends State { Text( plan.employeeCode ?? 'No Data', style: TextStyle( - fontSize: 9, - fontFamily: "Inter", - color: Colors.black87), + fontSize: 9, + fontFamily: "Inter", + color: Colors.black87, + ), ), ], ), @@ -1130,30 +1308,35 @@ class _ApprovalListState extends State { // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: isDesktop - ? (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), - ), - ) - : buildMobileCardView(paginatedPlans)), + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedPlans)), // child: isDesktop // ? SingleChildScrollView( @@ -1183,7 +1366,7 @@ class _ApprovalListState extends State { ), ); }, - ) + ), ], ), ), diff --git a/lib/Screens/authentication/login/login_widget.dart b/lib/Screens/authentication/login/login_widget.dart index a16201b..f704dda 100644 --- a/lib/Screens/authentication/login/login_widget.dart +++ b/lib/Screens/authentication/login/login_widget.dart @@ -156,6 +156,8 @@ class _LoginWidgetState extends State { backgroundColor: Colors.red, ), ); + _emailController.clear(); + _passwordController.clear(); // Fluttertoast.showToast( // msg: "Login Failed: $errorMessage", // toastLength: Toast.LENGTH_LONG, diff --git a/lib/Screens/costCenter/costCenter_list.dart b/lib/Screens/costCenter/costCenter_list.dart index 3d1e878..43cf86a 100644 --- a/lib/Screens/costCenter/costCenter_list.dart +++ b/lib/Screens/costCenter/costCenter_list.dart @@ -25,7 +25,7 @@ class CostCenterList extends StatefulWidget { class CostCenterListState extends State { final GlobalKey costCenterListKey = - GlobalKey(); + GlobalKey(); final ApiService apiService = ApiService(); late Future> futureCostCenter; @@ -67,13 +67,15 @@ class CostCenterListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -97,7 +99,6 @@ class CostCenterListState extends State { } Future> fetchGetCostCenter() async { - final String apiUrlData = '$apiUrl/api/getCostCenterMaster'; final String? token = await getToken(); @@ -143,52 +144,60 @@ class CostCenterListState extends State { print("all before filtering: $query"); final lowerQuery = query.toLowerCase(); setState(() { - filteredCostCenter = allCostCenter.where((object) { - final isActiveStatus = - object['is_active'] == "1" ? "active" : "inactive"; - return (object['cost_center_id']?.toLowerCase().contains(lowerQuery) ?? - false) || - (object['name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (object['description']?.toLowerCase().contains(lowerQuery) ?? false) || - (isActiveStatus.contains(lowerQuery)); - }).toList(); - currentPage = 0; + filteredCostCenter = + allCostCenter.where((object) { + final isActiveStatus = + object['is_active'] == "1" ? "active" : "inactive"; + return (object['cost_center_id']?.toLowerCase().contains( + lowerQuery, + ) ?? + false) || + (object['name']?.toLowerCase().contains(lowerQuery) ?? false) || + (object['description']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); + currentPage = 0; }); print("filteredCostCenter: $filteredCostCenter"); - } @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: 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), - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - // const Expanded(child: Center(child: Text("User Page Content"))), - Expanded(child: buildGroupList(isDesktop)), - ], + 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), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + // const Expanded(child: Center(child: Text("User Page Content"))), + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupList(bool isDesktop) { @@ -216,98 +225,103 @@ class CostCenterListState extends State { // ? 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, + height: + 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: Container( - color: Colors.white, - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - 'CostCenter Details', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ], + Text( + 'Cost Center Details', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), ), - if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.16, - ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.16), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterCostCenter, - decoration: InputDecoration( - hintText: "Search ...", - 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), - ), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterCostCenter, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), - style: GoogleFonts.poppins( - fontSize: 12, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, ), ), ), - // SizedBox(width: 16), - Spacer(), + 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 { - showDialog( - context: context, - builder: (context) => CostCenterData( + 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 { + showDialog( + context: context, + builder: + (context) => CostCenterData( isDesktop: isDesktop, layoutColor: layoutColor!, fetchGetCostCenter: refreshData, @@ -315,37 +329,34 @@ class CostCenterListState extends State { // role: // "Travel Agent" ), - ); - }, - child: Row( - mainAxisSize: + ); + }, + child: Row( + mainAxisSize: MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add CostCenter", - 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, - ), - ], + children: [ + Text( + "Add CostCenter", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), ), - ), - ], - ), - - if (!isDesktop) - SizedBox( - height: 5, + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], ), - isDesktop - ? SizedBox.shrink() - : Row( + ), + ], + ), + + if (!isDesktop) SizedBox(height: 5), + isDesktop + ? SizedBox.shrink() + : Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( @@ -357,7 +368,9 @@ class CostCenterListState extends State { decoration: InputDecoration( hintText: "Search ...", hintStyle: TextStyle( - fontSize: 12, color: Color(0xFF9E9DBD)), + fontSize: 12, + color: Color(0xFF9E9DBD), + ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), @@ -369,172 +382,295 @@ class CostCenterListState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade200, - width: 0.5), + color: Colors.grey.shade200, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), + color: Colors.grey.shade300, + width: 1, + ), ), ), - style: GoogleFonts.poppins( - fontSize: 12, - ), + style: GoogleFonts.poppins(fontSize: 12), ), ), // SizedBox(width: 16), ], ), - const SizedBox(height: 10), - FutureBuilder>( - future: futureCostCenter, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 16), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // ), - const SizedBox(height: 8), - Text( - "No CostCenter Available ", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey), - ), - const SizedBox(height: 20), - Text( - "Please Create CostCenter Details", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, color: Colors.grey), - ), - const SizedBox(height: 20), - ], + const SizedBox(height: 10), + FutureBuilder>( + future: futureCostCenter, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 16), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // ), + const SizedBox(height: 8), + Text( + "No CostCenter Available ", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), ), - ), - ); - } - /* Here collect the list to displayed the data in table or card Used */ - List object = - filteredCostCenter.isNotEmpty ? filteredCostCenter : allCostCenter; + const SizedBox(height: 20), + Text( + "Please Create CostCenter Details", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ); + } + /* Here collect the list to displayed the data in table or card Used */ + List object = + filteredCostCenter.isNotEmpty + ? filteredCostCenter + : allCostCenter; - /* List is Sorting here */ - object.sort((a, b) { - DateTime dateA = DateTime.parse(a['created_on']); - DateTime dateB = DateTime.parse(b['created_on']); + /* List is Sorting here */ + object.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); - return dateB - .compareTo(dateA); // Descending: newest first - }); + return dateB.compareTo(dateA); // Descending: newest first + }); - /* For pagination for list ... */ - List paginatedCostCenter = object + /* For pagination for list ... */ + List paginatedCostCenter = + object .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); - /* Table ... */ - Widget table = LayoutBuilder( - builder: (context, constraints) { - double minWidth = - isDesktop ? constraints.maxWidth : 1300; + /* Table ... */ + 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), + 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( + 'Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), ), - columns: [ - DataColumn( - label: Text( - 'Name', - 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: paginatedCostCenter.map((tableObject) { - String costcenterId = tableObject['cost_center_id'] - .toString(); // Get user ID - bool isSelected = selectedCostCenterId == costcenterId; + ), + 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: + paginatedCostCenter.map((tableObject) { + String costcenterId = + tableObject['cost_center_id'] + .toString(); // Get user ID + bool isSelected = + selectedCostCenterId == costcenterId; - return DataRow(cells: [ - DataCell(Text(tableObject['name'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(tableObject['description'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", + return DataRow( + cells: [ + DataCell( + Text( + tableObject['name'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell( - Text( - tableObject['is_active'] == "1" - ? 'Active' - : 'Inactive', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - color: tableObject['is_active'] == "1" ? Colors.green : Colors.red, - ), - softWrap: true, - overflow: TextOverflow.ellipsis, ), - ), - DataCell( - // UserActionsMenu( - // user: forex, - // getUserDetails: (id) => - // apiService.getSingleUser(id), - // ), + DataCell( + Text( + tableObject['description'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + tableObject['is_active'] == "1" + ? 'Active' + : 'Inactive', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + color: + tableObject['is_active'] == "1" + ? Colors.green + : Colors.red, + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + // UserActionsMenu( + // user: forex, + // getUserDetails: (id) => + // apiService.getSingleUser(id), + // ), + GestureDetector( + child: Tooltip( + message: 'Edit CostCenter Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + onTap: () async { + // final userId = getUserId(user['user_id']); + // final usersData = await getUserDetails(userId); + // + final costcenterId = int.tryParse( + tableObject['cost_center_id'] + .toString(), + ); + + if (costcenterId != null) { + print( + "Table cell - costcenter Id -- $costcenterId", + ); + final data = await apiService + .getCostCenterDetailsFind( + costcenterId, + ); + print("CostCenterId -- $data"); + + showDialog( + context: context, + builder: + (context) => CostCenterData( + isDesktop: isDesktop, + costcenterId: + costcenterId, // Pass the ID + costcenterData: data, + layoutColor: layoutColor!, + // fetchGetForex: fetchGetForex, + fetchGetCostCenter: + refreshData, + // role: + // "Travel Agent" + ), + ); + } else { + print("Invalid ID"); + } + }, + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + /* Card ... */ + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final cardObject = 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: [ + // Status and Employee Code + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + cardObject['name'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700, + ), + ), + GestureDetector( child: Tooltip( message: 'Edit CostCenter Details', @@ -549,298 +685,232 @@ class CostCenterListState extends State { // final usersData = await getUserDetails(userId); // final costcenterId = int.tryParse( - tableObject['cost_center_id'] - .toString()); + cardObject['cost_center_id'] + .toString(), + ); if (costcenterId != null) { - print("Table cell - costcenter Id -- $costcenterId"); - final data = await apiService.getCostCenterDetailsFind(costcenterId); + print( + "costcenterId -- $costcenterId", + ); + final data = await apiService + .getCostCenterDetailsFind( + costcenterId, + ); print("CostCenterId -- $data"); showDialog( context: context, - builder: (context) => CostCenterData( - isDesktop: isDesktop, - costcenterId: costcenterId, // Pass the ID - costcenterData: data, - layoutColor: layoutColor!, - // fetchGetForex: fetchGetForex, - fetchGetCostCenter: refreshData, - // role: - // "Travel Agent" - ), + builder: + (context) => CostCenterData( + isDesktop: isDesktop, + costcenterId: + costcenterId, // Pass the ID + costcenterData: data, + layoutColor: layoutColor!, + // fetchGetCostCenter: fetchGetCostCenter, + fetchGetCostCenter: + refreshData, + // role: + // "Travel Agent" + ), ); } else { print("Invalid ID"); } }, ), - ), - ]); - }).toList(), - ), - ); - }, - ); - - /* Card ... */ - Widget buildMobileCardView(List paginatedUser) { - return ListView.builder( - itemCount: paginatedUser.length, - itemBuilder: (context, index) { - final cardObject = 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: [ - // Status and Employee Code - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - cardObject['name'] ?? 'N/A', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.w700), - ), - - GestureDetector( - child: Tooltip( - message: 'Edit CostCenter Details', - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, - ), - ), - onTap: () async { - // final userId = getUserId(user['user_id']); - // final usersData = await getUserDetails(userId); - // - final costcenterId = int.tryParse( - cardObject['cost_center_id'] - .toString()); - - if (costcenterId != null) { - print("costcenterId -- $costcenterId"); - final data = await apiService - .getCostCenterDetailsFind(costcenterId); - print("CostCenterId -- $data"); - - showDialog( - context: context, - builder: (context) => CostCenterData( - isDesktop: isDesktop, - costcenterId:costcenterId, // Pass the ID - costcenterData:data, - layoutColor:layoutColor!, - // fetchGetCostCenter: fetchGetCostCenter, - fetchGetCostCenter: refreshData, - // role: - // "Travel Agent" - ), - ); - } else { - print("Invalid ID"); - } - }, - ), - // PopupMenuButton( - // color: Colors.white, - // padding: EdgeInsets.zero, - // offset: Offset(0, 30), - // icon: Icon( - // Icons.more_vert, - // color: Color(0xFF475569), - // size: 14, - // ), - // itemBuilder: (context) => [ - // CustomPopupMenuEntry( - // child: Container( - // padding: EdgeInsets.symmetric( - // horizontal: 8, vertical: 8), - // child: Row( - // mainAxisSize: - // MainAxisSize.min, - // mainAxisAlignment: - // MainAxisAlignment.center, - // children: [ - // IconButton( - // icon: Icon( - // Icons - // .remove_red_eye, - // color: Color( - // 0xFF475569), - // size: 18), - // onPressed: () { - // print( - // "USerDAta - $user"); - // // dynamic usersData = apiService - // // .getSingleUser(user[ - // // 'user_id'] - // // is String - // // ? int.parse(user[ - // // 'user_id']) - // // : user[ - // // 'user_id']); - // // - // // print( - // // "USerDAta - $usersData"); - // - // context.go( - // "/CreateUserDetails", - // extra: { - // "selectedUser": - // user, - // "isViewMode": true - // }, - // ); - // }), - // IconButton( - // icon: Image.asset( - // 'assets/images/IconsImg/edit.png', - // width: 20, - // height: 15), - // onPressed: () { - // context.go( - // "/CreateUserDetails", - // extra: { - // "selectedUser": - // user, - // "isViewMode": false - // }, - // ); - // }, - // ), - // ], - // ), - // ), - // ), - // ], - // ), - ], - ), - - SizedBox(height: 2), - // Trip Id and Trip Name - // Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cardObject['description'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - SizedBox( - width: 10, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cardObject['description'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - ], - ), - // Actions - // Actions + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, vertical: 8), + // child: Row( + // mainAxisSize: + // MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment.center, + // children: [ + // IconButton( + // icon: Icon( + // Icons + // .remove_red_eye, + // color: Color( + // 0xFF475569), + // size: 18), + // onPressed: () { + // print( + // "USerDAta - $user"); + // // dynamic usersData = apiService + // // .getSingleUser(user[ + // // 'user_id'] + // // is String + // // ? int.parse(user[ + // // 'user_id']) + // // : user[ + // // 'user_id']); + // // + // // print( + // // "USerDAta - $usersData"); + // + // context.go( + // "/CreateUserDetails", + // extra: { + // "selectedUser": + // user, + // "isViewMode": true + // }, + // ); + // }), + // IconButton( + // icon: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // onPressed: () { + // context.go( + // "/CreateUserDetails", + // extra: { + // "selectedUser": + // user, + // "isViewMode": false + // }, + // ); + // }, + // ), + // ], + // ), + // ), + // ), + // ], + // ), ], ), - ), - ); - }, - ); - } - - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: isDesktop - ? (searchController.text.isNotEmpty && - filteredCostCenter.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredCostCenter.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), - ), - ) - : buildMobileCardView(paginatedCostCenter)), - ), - // Expanded( - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedCostCenter), - // ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: object.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, + SizedBox(height: 2), + // Trip Id and Trip Name + // Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + cardObject['description'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + SizedBox(width: 10), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + cardObject['description'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + // Actions + // Actions + ], ), - ], + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredCostCenter.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredCostCenter.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView( + paginatedCostCenter, + )), ), - ); - }, - ) - ]), - )), + // Expanded( + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedCostCenter), + // ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: object.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), ); } -} \ No newline at end of file +} diff --git a/lib/Screens/department/department_list.dart b/lib/Screens/department/department_list.dart index a6c0b5e..bdc821d 100644 --- a/lib/Screens/department/department_list.dart +++ b/lib/Screens/department/department_list.dart @@ -67,13 +67,15 @@ class DepartmentListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -141,16 +143,19 @@ class DepartmentListState extends State { print("all before filtering: $query"); final lowerQuery = query.toLowerCase(); setState(() { - filteredDepartment = allDepartment.where((object) { - final isActiveStatus = - object['is_active'] == "1" ? "active" : "inactive"; - return (object['department_id']?.toLowerCase().contains(lowerQuery) ?? - false) || - (object['name']?.toLowerCase().contains(lowerQuery) ?? false) || - (object['description']?.toLowerCase().contains(lowerQuery) ?? - false) || - (isActiveStatus.contains(lowerQuery)); - }).toList(); + filteredDepartment = + allDepartment.where((object) { + final isActiveStatus = + object['is_active'] == "1" ? "active" : "inactive"; + return (object['department_id']?.toLowerCase().contains( + lowerQuery, + ) ?? + false) || + (object['name']?.toLowerCase().contains(lowerQuery) ?? false) || + (object['description']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); currentPage = 0; }); print("filteredDepartment: $filteredDepartment"); @@ -158,34 +163,40 @@ class DepartmentListState extends State { @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: 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), - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - // const Expanded(child: Center(child: Text("User Page Content"))), - Expanded(child: buildGroupList(isDesktop)), - ], + 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), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + // const Expanded(child: Center(child: Text("User Page Content"))), + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupList(bool isDesktop) { @@ -213,98 +224,103 @@ class DepartmentListState extends State { // ? 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, + height: + 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: Container( - color: Colors.white, - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - 'Department Details', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ], + Text( + 'Department Details', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), ), - if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.16, - ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.16), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterDepartment, - decoration: InputDecoration( - hintText: "Search ...", - 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), - ), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterDepartment, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), - style: GoogleFonts.poppins( - fontSize: 12, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, ), ), ), - // SizedBox(width: 16), - Spacer(), + 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 { - showDialog( - context: context, - builder: (context) => DepartmentData( + 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 { + showDialog( + context: context, + builder: + (context) => DepartmentData( isDesktop: isDesktop, layoutColor: layoutColor!, fetchGetDepartment: refreshData, @@ -312,342 +328,380 @@ class DepartmentListState extends State { // role: // "Travel Agent" ), - ); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add Department", - style: GoogleFonts.poppins( - fontSize: isDesktop ? 13 : 11, + ); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add Department", + 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: filterDepartment, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, + 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: futureDepartment, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 16), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // ), + const SizedBox(height: 8), + Text( + "No Department Available ", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + Text( + "Please Create Department Details", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), ], ), ), - ], - ), - - 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: filterDepartment, - decoration: InputDecoration( - hintText: "Search ...", - 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: futureDepartment, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 16), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // ), - const SizedBox(height: 8), - Text( - "No Department Available ", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey), - ), - const SizedBox(height: 20), - Text( - "Please Create Department Details", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, color: Colors.grey), - ), - const SizedBox(height: 20), - ], - ), - ), - ); - } - /* Here collect the list to displayed the data in table or card Used */ - List object = filteredDepartment.isNotEmpty + ); + } + /* Here collect the list to displayed the data in table or card Used */ + List object = + filteredDepartment.isNotEmpty ? filteredDepartment : allDepartment; - /* List is Sorting here */ - object.sort((a, b) { - DateTime dateA = DateTime.parse(a['created_on']); - DateTime dateB = DateTime.parse(b['created_on']); + /* List is Sorting here */ + object.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); - return dateB - .compareTo(dateA); // Descending: newest first - }); + return dateB.compareTo(dateA); // Descending: newest first + }); - /* For pagination for list ... */ - List paginatedDepartment = object + /* For pagination for list ... */ + List paginatedDepartment = + object .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); - /* Table ... */ - Widget table = LayoutBuilder( - builder: (context, constraints) { - double minWidth = - isDesktop ? constraints.maxWidth : 1300; + /* Table ... */ + 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), + 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( + 'Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), ), - columns: [ - DataColumn( - label: Text( - 'Name', - 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: paginatedDepartment.map((tableObject) { + ), + 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: + paginatedDepartment.map((tableObject) { String departmentId = tableObject['department_id'] .toString(); // Get user ID bool isSelected = selectedDepartmentId == departmentId; - return DataRow(cells: [ - DataCell(Text(tableObject['name'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell( - Text(tableObject['description'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell( - Text( - tableObject['is_active'] == "1" - ? 'Active' - : 'Inactive', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - color: tableObject['is_active'] == "1" - ? Colors.green - : Colors.red, + return DataRow( + cells: [ + DataCell( + Text( + tableObject['name'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), ), - softWrap: true, - overflow: TextOverflow.ellipsis, ), - ), - DataCell( - // UserActionsMenu( - // user: forex, - // getUserDetails: (id) => - // apiService.getSingleUser(id), - // ), + DataCell( + Text( + tableObject['description'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + tableObject['is_active'] == "1" + ? 'Active' + : 'Inactive', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + color: + tableObject['is_active'] == "1" + ? Colors.green + : Colors.red, + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + // UserActionsMenu( + // user: forex, + // getUserDetails: (id) => + // apiService.getSingleUser(id), + // ), + GestureDetector( + child: Tooltip( + message: 'Edit Department Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + onTap: () async { + // final userId = getUserId(user['user_id']); + // final usersData = await getUserDetails(userId); + // + final departmentId = int.tryParse( + tableObject['department_id'] + .toString(), + ); + + if (departmentId != null) { + print( + "Table cell - department Id -- $departmentId", + ); + final data = await apiService + .getDepartmentDetailsFind( + departmentId, + ); + print("DepartmentId -- $data"); + + showDialog( + context: context, + builder: + (context) => DepartmentData( + isDesktop: isDesktop, + departmentId: + departmentId, // Pass the ID + departmentData: data, + layoutColor: layoutColor!, + // fetchGetForex: fetchGetForex, + fetchGetDepartment: + refreshData, + // role: + // "Travel Agent" + ), + ); + } else { + print("Invalid ID"); + } + }, + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + /* Card ... */ + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final cardObject = 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: [ + // Status and Employee Code + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + cardObject['name'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700, + ), + ), + 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); // final departmentId = int.tryParse( - tableObject['department_id'] - .toString()); + cardObject['department_id'] + .toString(), + ); if (departmentId != null) { print( - "Table cell - department Id -- $departmentId"); + "departmentId -- $departmentId", + ); final data = await apiService .getDepartmentDetailsFind( - departmentId); + departmentId, + ); print("DepartmentId -- $data"); showDialog( context: context, - builder: (context) => - DepartmentData( - isDesktop: isDesktop, - departmentId: - departmentId, // Pass the ID - departmentData: data, - layoutColor: layoutColor!, - // fetchGetForex: fetchGetForex, - fetchGetDepartment: refreshData, - // role: - // "Travel Agent" - ), - ); - } else { - print("Invalid ID"); - } - }, - ), - ), - ]); - }).toList(), - ), - ); - }, - ); - - /* Card ... */ - Widget buildMobileCardView(List paginatedUser) { - return ListView.builder( - itemCount: paginatedUser.length, - itemBuilder: (context, index) { - final cardObject = 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: [ - // Status and Employee Code - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - cardObject['name'] ?? 'N/A', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.w700), - ), - - GestureDetector( - child: Tooltip( - message: 'Edit Department Details', - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, - ), - ), - onTap: () async { - // final userId = getUserId(user['user_id']); - // final usersData = await getUserDetails(userId); - // - final departmentId = int.tryParse( - cardObject['department_id'] - .toString()); - - if (departmentId != null) { - print( - "departmentId -- $departmentId"); - final data = await apiService - .getDepartmentDetailsFind( - departmentId); - print("DepartmentId -- $data"); - - showDialog( - context: context, - builder: (context) => - DepartmentData( + builder: + (context) => DepartmentData( isDesktop: isDesktop, departmentId: departmentId, // Pass the ID @@ -659,197 +713,203 @@ class DepartmentListState extends State { // role: // "Travel Agent" ), - ); - } else { - print("Invalid ID"); - } - }, - ), - // PopupMenuButton( - // color: Colors.white, - // padding: EdgeInsets.zero, - // offset: Offset(0, 30), - // icon: Icon( - // Icons.more_vert, - // color: Color(0xFF475569), - // size: 14, - // ), - // itemBuilder: (context) => [ - // CustomPopupMenuEntry( - // child: Container( - // padding: EdgeInsets.symmetric( - // horizontal: 8, vertical: 8), - // child: Row( - // mainAxisSize: - // MainAxisSize.min, - // mainAxisAlignment: - // MainAxisAlignment.center, - // children: [ - // IconButton( - // icon: Icon( - // Icons - // .remove_red_eye, - // color: Color( - // 0xFF475569), - // size: 18), - // onPressed: () { - // print( - // "USerDAta - $user"); - // // dynamic usersData = apiService - // // .getSingleUser(user[ - // // 'user_id'] - // // is String - // // ? int.parse(user[ - // // 'user_id']) - // // : user[ - // // 'user_id']); - // // - // // print( - // // "USerDAta - $usersData"); - // - // context.go( - // "/CreateUserDetails", - // extra: { - // "selectedUser": - // user, - // "isViewMode": true - // }, - // ); - // }), - // IconButton( - // icon: Image.asset( - // 'assets/images/IconsImg/edit.png', - // width: 20, - // height: 15), - // onPressed: () { - // context.go( - // "/CreateUserDetails", - // extra: { - // "selectedUser": - // user, - // "isViewMode": false - // }, - // ); - // }, - // ), - // ], - // ), - // ), - // ), - // ], - // ), - ], + ); + } else { + print("Invalid ID"); + } + }, ), - - SizedBox(height: 2), - // Trip Id and Trip Name - // Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cardObject['description'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - SizedBox( - width: 10, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cardObject['description'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - ], - ), - // Actions - // Actions + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, vertical: 8), + // child: Row( + // mainAxisSize: + // MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment.center, + // children: [ + // IconButton( + // icon: Icon( + // Icons + // .remove_red_eye, + // color: Color( + // 0xFF475569), + // size: 18), + // onPressed: () { + // print( + // "USerDAta - $user"); + // // dynamic usersData = apiService + // // .getSingleUser(user[ + // // 'user_id'] + // // is String + // // ? int.parse(user[ + // // 'user_id']) + // // : user[ + // // 'user_id']); + // // + // // print( + // // "USerDAta - $usersData"); + // + // context.go( + // "/CreateUserDetails", + // extra: { + // "selectedUser": + // user, + // "isViewMode": true + // }, + // ); + // }), + // IconButton( + // icon: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // onPressed: () { + // context.go( + // "/CreateUserDetails", + // extra: { + // "selectedUser": + // user, + // "isViewMode": false + // }, + // ); + // }, + // ), + // ], + // ), + // ), + // ), + // ], + // ), ], ), - ), - ); - }, - ); - } - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: isDesktop + SizedBox(height: 2), + // Trip Id and Trip Name + // Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + cardObject['description'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + SizedBox(width: 10), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + cardObject['description'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + // Actions + // Actions + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop ? (searchController.text.isNotEmpty && filteredDepartment.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) + scrollDirection: Axis.vertical, + child: table, + )) : (searchController.text.isNotEmpty && filteredDepartment.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : buildMobileCardView( - paginatedDepartment)), - ), - // Expanded( - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedDepartment), - // ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: object.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, - ), - ], + paginatedDepartment, + )), ), - ); - }, - ) - ]), - )), + // Expanded( + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedDepartment), + // ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: object.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), ); } } diff --git a/lib/Screens/forex/forex_list.dart b/lib/Screens/forex/forex_list.dart index bf64c2c..49052a7 100644 --- a/lib/Screens/forex/forex_list.dart +++ b/lib/Screens/forex/forex_list.dart @@ -83,13 +83,15 @@ class ForexDataListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -151,7 +153,8 @@ class ForexDataListState extends State { if (!data.containsKey('data') || data['data'] is! List) { throw Exception( - "Invalid response format: 'data' field is missing or not a List"); + "Invalid response format: 'data' field is missing or not a List", + ); } List plansJson = data['data']; // 'data' is a Map, not a List @@ -180,7 +183,10 @@ class ForexDataListState extends State { } Future createUserData( - Map userData, String userId, String newStatus) async { + Map userData, + String userId, + String newStatus, + ) async { final uri = Uri.parse('$apiUrl/api/users/update/$userId'); final String? token = await getToken(); @@ -230,8 +236,11 @@ class ForexDataListState extends State { } } - void handleToggleUserStatus(String userId, String currentStatus, - Map userData) async { + void handleToggleUserStatus( + String userId, + String currentStatus, + Map userData, + ) async { print("Toggling user status - $userId (Current: $currentStatus)"); final String apiUrlData = @@ -274,7 +283,7 @@ class ForexDataListState extends State { // } } -// Refresh user list after update + // Refresh user list after update void refreshUserList() { setState(() { futureForex = fetchGetForex(); // Re-fetch users after status update @@ -282,74 +291,65 @@ class ForexDataListState extends State { }); } - void filterForex1(String query) { - print("allUsers before filtering: $query"); - final lowerQuery = query.toLowerCase(); - setState(() { - filteredForex = allForex.where((forex) { - return (forex['country_code']?.toLowerCase().contains(lowerQuery) ?? - false) || - (forex['country_name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (forex['currency']?.toLowerCase().contains(lowerQuery) ?? false) || - (forex['perdiem_amount']?.toLowerCase().contains(lowerQuery) ?? - false); - }).toList(); - currentPage = 0; - }); - print("filteredPlans: $filteredForex"); - } - void filterForex(String query) { print("allForex before filtering: $query"); final lowerQuery = query.toLowerCase(); setState(() { - filteredForex = allForex.where((forex) { - final isActiveStatus = - forex['is_active'] == "1" ? "active" : "inactive"; - return (forex['country_code']?.toLowerCase().contains(lowerQuery) ?? - false) || - (forex['country_name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (forex['currency']?.toLowerCase().contains(lowerQuery) ?? false) || - (forex['perdiem_amount']?.toLowerCase().contains(lowerQuery) ?? - false) || - (isActiveStatus.contains(lowerQuery)); - }).toList(); + filteredForex = + allForex.where((forex) { + final isActiveStatus = + forex['is_active'] == "1" ? "active" : "inactive"; + return (forex['country_code']?.toLowerCase().contains(lowerQuery) ?? + false) || + (forex['country_name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (forex['currency']?.toLowerCase().contains(lowerQuery) ?? + false) || + (forex['perdiem_amount']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); + currentPage = 0; }); print("filteredForex: $filteredForex"); } @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: 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), - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - // const Expanded(child: Center(child: Text("User Page Content"))), - Expanded(child: buildGroupList(isDesktop)), - ], + 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), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + // const Expanded(child: Center(child: Text("User Page Content"))), + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupList(bool isDesktop) { @@ -377,354 +377,479 @@ class ForexDataListState extends State { // ? 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, + height: + 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: Container( - color: Colors.white, - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - 'Perdiem Amount Details', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ], + Text( + 'Perdiem Amount Details', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), ), - if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.16, - ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.16), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterForex, - decoration: InputDecoration( - hintText: "Search ...", - 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), - ), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterForex, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), - style: GoogleFonts.poppins( - fontSize: 12, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, ), ), ), - // SizedBox(width: 16), - Spacer(), + 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 { - showDialog( - context: context, - builder: (context) => ForexData( + 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 { + showDialog( + context: context, + builder: + (context) => ForexData( isDesktop: isDesktop, layoutColor: layoutColor!, fetchGetForex: refreshData, // role: // "Travel Agent" ), - ); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add Perdiem", - style: GoogleFonts.poppins( - fontSize: isDesktop ? 13 : 11, + ); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add Perdiem", + 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: filterForex, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, + 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: futureForex, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 16), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // ), + const SizedBox(height: 8), + Text( + "No Perdiem Available ", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + Text( + "Please Create Perdiem Amount", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), ], ), ), - ], - ), + ); + } - 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: filterForex, - decoration: InputDecoration( - hintText: "Search ...", - 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: futureForex, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 16), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // ), - const SizedBox(height: 8), - Text( - "No Perdiem Available ", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey), - ), - const SizedBox(height: 20), - Text( - "Please Create Perdiem Amount", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, color: Colors.grey), - ), - const SizedBox(height: 20), - ], - ), - ), - ); - } + List forex = + filteredForex.isNotEmpty ? filteredForex : allForex; - List forex = - filteredForex.isNotEmpty ? filteredForex : allForex; + forex.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); - forex.sort((a, b) { - DateTime dateA = DateTime.parse(a['created_on']); - DateTime dateB = DateTime.parse(b['created_on']); + return dateB.compareTo(dateA); // Descending: newest first + }); - return dateB - .compareTo(dateA); // Descending: newest first - }); - - List paginatedForex = forex + List paginatedForex = + forex .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); - Widget table = LayoutBuilder( - builder: (context, constraints) { - double minWidth = - isDesktop ? constraints.maxWidth : 1300; + 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), + 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( + 'Country Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), ), - columns: [ - DataColumn( - label: Text( - 'Country Code', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Country', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Currency', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Perdiem Amount', - 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: paginatedForex.map((forex) { - String forexId = forex['forex_perdiem_id'] - .toString(); // Get user ID + ), + DataColumn( + label: Text( + 'Country', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Currency', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Perdiem Amount', + 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: + paginatedForex.map((forex) { + String forexId = + forex['forex_perdiem_id'] + .toString(); // Get user ID bool isSelected = selectedUserId == forexId; - return DataRow(cells: [ - DataCell( - Text("${forex['country_code'] ?? ''}", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(forex['country_name'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(forex['currency'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", + return DataRow( + cells: [ + DataCell( + Text( + "${forex['country_code'] ?? ''}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell( - Text(forex['perdiem_amount'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell( - Text( - forex['is_active'] == "1" - ? 'Active' - : 'Inactive', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - // color: forex['is_active'] == "1" - // ? Colors.green - // : Colors.grey, - ), - softWrap: true, - overflow: TextOverflow.ellipsis, ), - ), - DataCell( - // UserActionsMenu( - // user: forex, - // getUserDetails: (id) => - // apiService.getSingleUser(id), - // ), + DataCell( + Text( + forex['country_name'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + forex['currency'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + forex['perdiem_amount'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + forex['is_active'] == "1" + ? 'Active' + : 'Inactive', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + // color: forex['is_active'] == "1" + // ? Colors.green + // : Colors.grey, + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + // UserActionsMenu( + // user: forex, + // getUserDetails: (id) => + // apiService.getSingleUser(id), + // ), + GestureDetector( + child: Tooltip( + message: 'Edit ForEx Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + onTap: () async { + // final userId = getUserId(user['user_id']); + // final usersData = await getUserDetails(userId); + // + final forexId = int.tryParse( + forex['forex_perdiem_id'] + .toString(), + ); + + if (forexId != null) { + print("ForexId -- $forexId"); + final data = await apiService + .getForexDetailsFind(forexId); + print("ForexId -- $data"); + + showDialog( + context: context, + builder: + (context) => ForexData( + isDesktop: isDesktop, + forexId: + forexId, // Pass the ID + forexData: data, + layoutColor: layoutColor!, + // fetchGetForex: fetchGetForex, + fetchGetForex: refreshData, + // role: + // "Travel Agent" + ), + ); + } else { + print("Invalid Forex ID"); + } + }, + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final forex = 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: [ + // Status and Employee Code + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + forex['country_name'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700, + ), + ), + GestureDetector( child: Tooltip( - message: 'Edit ForEx Details', + message: 'Edit Forex Details', child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, @@ -736,8 +861,8 @@ class ForexDataListState extends State { // final usersData = await getUserDetails(userId); // final forexId = int.tryParse( - forex['forex_perdiem_id'] - .toString()); + forex['forex_perdiem_id'].toString(), + ); if (forexId != null) { print("ForexId -- $forexId"); @@ -747,87 +872,8 @@ class ForexDataListState extends State { showDialog( context: context, - builder: (context) => ForexData( - isDesktop: isDesktop, - forexId: forexId, // Pass the ID - forexData: data, - layoutColor: layoutColor!, - // fetchGetForex: fetchGetForex, - fetchGetForex: refreshData, - // role: - // "Travel Agent" - ), - ); - } else { - print("Invalid Forex ID"); - } - }, - ), - ), - ]); - }).toList(), - ), - ); - }, - ); - - Widget buildMobileCardView(List paginatedUser) { - return ListView.builder( - itemCount: paginatedUser.length, - itemBuilder: (context, index) { - final forex = 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: [ - // Status and Employee Code - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - forex['country_name'] ?? 'N/A', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.w700), - ), - - GestureDetector( - child: Tooltip( - message: 'Edit Forex Details', - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, - ), - ), - onTap: () async { - // final userId = getUserId(user['user_id']); - // final usersData = await getUserDetails(userId); - // - final forexId = int.tryParse( - forex['forex_perdiem_id'] - .toString()); - - if (forexId != null) { - print("ForexId -- $forexId"); - final data = await apiService - .getForexDetailsFind(forexId); - print("ForexId -- $data"); - - showDialog( - context: context, - builder: (context) => ForexData( + builder: + (context) => ForexData( isDesktop: isDesktop, forexId: forexId, // Pass the ID @@ -838,217 +884,221 @@ class ForexDataListState extends State { // role: // "Travel Agent" ), - ); - } else { - print("Invalid Forex ID"); - } - }, - ), - // PopupMenuButton( - // color: Colors.white, - // padding: EdgeInsets.zero, - // offset: Offset(0, 30), - // icon: Icon( - // Icons.more_vert, - // color: Color(0xFF475569), - // size: 14, - // ), - // itemBuilder: (context) => [ - // CustomPopupMenuEntry( - // child: Container( - // padding: EdgeInsets.symmetric( - // horizontal: 8, vertical: 8), - // child: Row( - // mainAxisSize: - // MainAxisSize.min, - // mainAxisAlignment: - // MainAxisAlignment.center, - // children: [ - // IconButton( - // icon: Icon( - // Icons - // .remove_red_eye, - // color: Color( - // 0xFF475569), - // size: 18), - // onPressed: () { - // print( - // "USerDAta - $user"); - // // dynamic usersData = apiService - // // .getSingleUser(user[ - // // 'user_id'] - // // is String - // // ? int.parse(user[ - // // 'user_id']) - // // : user[ - // // 'user_id']); - // // - // // print( - // // "USerDAta - $usersData"); - // - // context.go( - // "/CreateUserDetails", - // extra: { - // "selectedUser": - // user, - // "isViewMode": true - // }, - // ); - // }), - // IconButton( - // icon: Image.asset( - // 'assets/images/IconsImg/edit.png', - // width: 20, - // height: 15), - // onPressed: () { - // context.go( - // "/CreateUserDetails", - // extra: { - // "selectedUser": - // user, - // "isViewMode": false - // }, - // ); - // }, - // ), - // ], - // ), - // ), - // ), - // ], - // ), - ], + ); + } else { + print("Invalid Forex ID"); + } + }, ), - - SizedBox(height: 2), - // Trip Id and Trip Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${forex['country_code'] ?? ''} ", - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.w500), - ), - ], - ), - ], - ), - SizedBox(height: 2), - - // Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - forex['currency'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - SizedBox( - width: 10, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - forex['perdiem_amount'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - ], - ), - // Actions - // Actions + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, vertical: 8), + // child: Row( + // mainAxisSize: + // MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment.center, + // children: [ + // IconButton( + // icon: Icon( + // Icons + // .remove_red_eye, + // color: Color( + // 0xFF475569), + // size: 18), + // onPressed: () { + // print( + // "USerDAta - $user"); + // // dynamic usersData = apiService + // // .getSingleUser(user[ + // // 'user_id'] + // // is String + // // ? int.parse(user[ + // // 'user_id']) + // // : user[ + // // 'user_id']); + // // + // // print( + // // "USerDAta - $usersData"); + // + // context.go( + // "/CreateUserDetails", + // extra: { + // "selectedUser": + // user, + // "isViewMode": true + // }, + // ); + // }), + // IconButton( + // icon: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // onPressed: () { + // context.go( + // "/CreateUserDetails", + // extra: { + // "selectedUser": + // user, + // "isViewMode": false + // }, + // ); + // }, + // ), + // ], + // ), + // ), + // ), + // ], + // ), ], ), - ), - ); - }, - ); - } - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: isDesktop + SizedBox(height: 2), + // Trip Id and Trip Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "${forex['country_code'] ?? ''} ", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + SizedBox(height: 2), + + // Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + forex['currency'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + SizedBox(width: 10), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + forex['perdiem_amount'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + // Actions + // Actions + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop ? (searchController.text.isNotEmpty && filteredForex.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) + scrollDirection: Axis.vertical, + child: table, + )) : (searchController.text.isNotEmpty && filteredForex.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : buildMobileCardView(paginatedForex)), - ), - // Expanded( - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedUser), - // ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: forex.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, - ), - ], ), - ); - }, - ) - ]), - )), + // Expanded( + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedUser), + // ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: forex.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), ); } - - } diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index 17e205e..919b0b0 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -15,8 +15,6 @@ import '../../utils/auth_utils.dart'; import '../../utils/pagination.dart'; import 'groupDetails.dart'; - - class GroupList extends StatefulWidget { @override _GroupListState createState() => _GroupListState(); @@ -39,7 +37,6 @@ class _GroupListState extends State { List filteredGroups = []; TextEditingController searchController = TextEditingController(); - int currentPage = 0; int itemsPerPage = 10; @@ -71,14 +68,14 @@ class _GroupListState 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; }); } @@ -87,15 +84,11 @@ class _GroupListState extends State { 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(); @@ -116,14 +109,21 @@ class _GroupListState extends State { 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['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(); + (group['is_active']?.toLowerCase().contains(lowerQuery) ?? + false); + }).toList(); currentPage = 0; }); @@ -131,10 +131,10 @@ class _GroupListState extends State { } void handleActiveStatus( - Map groupData, - String groupId, - String currentStatus, - ) async { + Map groupData, + String groupId, + String currentStatus, + ) async { print("Toggling group status - $groupId (Current: $currentStatus)"); final String apiUrlData = @@ -159,7 +159,7 @@ class _GroupListState extends State { 'Content-Type': 'application/json', }, body: jsonEncode({ - "is_active": newStatus // Set new status dynamically + "is_active": newStatus, // Set new status dynamically }), ); @@ -189,7 +189,6 @@ class _GroupListState extends State { loadAllGroups(); } - @override Widget build(BuildContext context) { return ResponsiveBuilder( @@ -203,21 +202,17 @@ class _GroupListState extends State { 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)), - ], - ), + 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))]), ), ); }, @@ -250,9 +245,9 @@ class _GroupListState extends State { // : 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, + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, child: Padding( padding: const EdgeInsets.all(10.0), @@ -342,23 +337,22 @@ class _GroupListState extends State { ), ), onPressed: () async { - // List groups = await futureGroups; - // context.go('/CreateGroup'); - showDialog( + // List groups = await futureGroups; + // context.go('/CreateGroup'); + showDialog( context: context, - builder: (context) => GroupData( - isDesktop: isDesktop, - groupId: null, - layoutColor: layoutColor!, - fetchGetGroup: refreshData - ), - ); - - + builder: + (context) => GroupData( + isDesktop: isDesktop, + groupId: null, + layoutColor: layoutColor!, + fetchGetGroup: refreshData, + ), + ); }, child: Row( mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely + MainAxisSize.min, // Ensures content fits nicely children: [ Text( "Add New Group", @@ -382,49 +376,49 @@ class _GroupListState extends State { 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, + 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), ), ), - style: GoogleFonts.poppins(fontSize: 12), - ), + // SizedBox(width: 16), + ], ), - // SizedBox(width: 16), - ], - ), const SizedBox(height: 10), FutureBuilder>( future: futureGroups, @@ -467,7 +461,7 @@ class _GroupListState extends State { } List groups = - filteredGroups.isNotEmpty ? filteredGroups : allGroups; + filteredGroups.isNotEmpty ? filteredGroups : allGroups; groups.sort((a, b) { DateTime dateA = DateTime.parse(a['created_on']); @@ -477,10 +471,10 @@ class _GroupListState extends State { }); List paginatedGroup = - groups - .skip(currentPage * itemsPerPage) - .take(itemsPerPage) - .toList(); + groups + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); Widget table = LayoutBuilder( builder: (context, constraints) { @@ -554,128 +548,146 @@ class _GroupListState extends State { ), ], rows: - paginatedGroup.map((group) { - String groupId = - group['group_id'].toString(); // Get group ID - bool isSelected = selectedGroupId == groupId; + 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", + 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['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['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['description'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), ), - ), - ), - DataCell( - Text( - group['is_active'] == "1" - ? "Active" - : "Inactive", - style: TextStyle( - color: + DataCell( + Text( group['is_active'] == "1" - ? Colors.green - : Colors.grey, - fontFamily: "Inter", - fontWeight: FontWeight.w400, + ? "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 - ), + 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(), ); - } - } 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), ), + 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, + ), + ), + ), + ], ), - 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(), + ), + ], + ); + }).toList(), ), ); }, @@ -688,7 +700,10 @@ class _GroupListState extends State { return Card( color: Colors.white, - margin: EdgeInsets.symmetric(horizontal: 12, vertical: 6), + margin: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), @@ -700,7 +715,8 @@ class _GroupListState extends State { children: [ // Row 1: Policy Name and Actions Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ Expanded( child: RichText( @@ -718,11 +734,12 @@ class _GroupListState extends State { ), ), TextSpan( - text: "${object['name'] ?? 'N/A'}", + text: + "${object['name'] ?? 'N/A'}", style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.normal + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal, ), ), ], @@ -736,42 +753,54 @@ class _GroupListState extends State { onTap: () async { if (object['group_id'] != null) { final newGroupID = int.tryParse( - object['group_id'].toString()); + object['group_id'].toString(), + ); if (newGroupID != null) { - final data = await apiService.getGroupDetailsFind( - newGroupID); // ✅ Always an int + 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 + builder: + (context) => GroupData( + isDesktop: isDesktop, + groupId: newGroupID, + // Pass the ID + groupData: data, + layoutColor: + layoutColor!, + fetchGetGroup: + refreshData, ), ); } } else { - print("something went wrong check properly"); + 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, + 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()); + final id = int.tryParse( + idStr.toString(), + ); if (id == null) { print("group_id is null"); @@ -783,8 +812,12 @@ class _GroupListState extends State { }, child: Tooltip( message: 'Edit Group Details', - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15),), + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), @@ -792,7 +825,6 @@ class _GroupListState extends State { ), SizedBox(height: 8), // Spacing - // Row 2: RichText( text: TextSpan( @@ -809,18 +841,19 @@ class _GroupListState extends State { ), ), TextSpan( - text: "${object['domestic_policy_name'] ?? 'N/A'}", + text: + "${object['domestic_policy_name'] ?? 'N/A'}", + style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.normal + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal, ), ), ], ), ), SizedBox(height: 8), // Spacing - // Row 3: RichText( text: TextSpan( @@ -837,18 +870,18 @@ class _GroupListState extends State { ), ), TextSpan( - text: "${object['international_policy_name']}", + text: + "${object['international_policy_name'] ?? 'N/A'}", style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.normal + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal, ), ), ], ), ), SizedBox(height: 8), // Spacing - // Row 4: RichText( text: TextSpan( @@ -865,11 +898,12 @@ class _GroupListState extends State { ), ), TextSpan( - text: "${object['description'] ?? 'N/A'}", + text: + "${object['description'] ?? 'N/A'}", style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.normal + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal, ), ), ], @@ -889,12 +923,12 @@ class _GroupListState extends State { children: [ Expanded( child: - isDesktop - ? SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, // <-- your existing table - ) - : buildMobileCardView(paginatedGroup), + isDesktop + ? SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, // <-- your existing table + ) + : buildMobileCardView(paginatedGroup), ), PaginationControls( currentPage: currentPage, @@ -924,4 +958,4 @@ class _GroupListState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/Screens/hotels/hotels_list.dart b/lib/Screens/hotels/hotels_list.dart index c23a904..7db65d8 100644 --- a/lib/Screens/hotels/hotels_list.dart +++ b/lib/Screens/hotels/hotels_list.dart @@ -83,13 +83,15 @@ class HotelsDataListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -150,7 +152,8 @@ class HotelsDataListState extends State { if (!data.containsKey('data') || data['data'] is! List) { throw Exception( - "Invalid response format: 'data' field is missing or not a List"); + "Invalid response format: 'data' field is missing or not a List", + ); } List plansJson = data['data']; // 'data' is a Map, not a List @@ -174,8 +177,7 @@ class HotelsDataListState extends State { } } - -// Refresh user list after update + // Refresh user list after update void refreshUserList() { setState(() { futureHotels = fetchGetHotels(); // Re-fetch users after status update @@ -187,19 +189,23 @@ class HotelsDataListState extends State { print("allHotels before filtering: $query"); final lowerQuery = query.toLowerCase(); setState(() { - filteredHotels = allHotels.where((hotels) { - final isActiveStatus = - hotels['is_active'] == "1" ? "active" : "inactive"; - return (hotels['country_code']?.toLowerCase().contains(lowerQuery) ?? - false) || - (hotels['country_name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (hotels['city']?.toLowerCase().contains(lowerQuery) ?? false) || - (hotels['hotel_chain']?.toLowerCase().contains(lowerQuery) ?? false) || - (hotels['hotel_name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (isActiveStatus.contains(lowerQuery)); - }).toList(); + filteredHotels = + allHotels.where((hotels) { + final isActiveStatus = + hotels['is_active'] == "1" ? "active" : "inactive"; + return (hotels['country_code']?.toLowerCase().contains( + lowerQuery, + ) ?? + false) || + (hotels['country_name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (hotels['city']?.toLowerCase().contains(lowerQuery) ?? false) || + (hotels['hotel_chain']?.toLowerCase().contains(lowerQuery) ?? + false) || + (hotels['hotel_name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); currentPage = 0; }); print("filteredHotels: $filteredHotels"); @@ -207,34 +213,40 @@ class HotelsDataListState extends State { @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: 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), - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - // const Expanded(child: Center(child: Text("User Page Content"))), - Expanded(child: buildGroupList(isDesktop)), - ], + 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), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + // const Expanded(child: Center(child: Text("User Page Content"))), + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupList(bool isDesktop) { @@ -262,352 +274,481 @@ class HotelsDataListState extends State { // ? 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, + height: + 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: Container( - color: Colors.white, - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - 'Hotel Details', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ], + Text( + 'Hotel Details', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), ), - if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.16, - ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.16), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterHotels, - decoration: InputDecoration( - hintText: "Search ...", - 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), - ), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterHotels, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), - style: GoogleFonts.poppins( - fontSize: 12, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, ), ), ), - // SizedBox(width: 16), - Spacer(), + 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 { - showDialog( - context: context, - builder: (context) => HotelsData( + 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 { + showDialog( + context: context, + builder: + (context) => HotelsData( isDesktop: isDesktop, layoutColor: layoutColor!, fetchGetHotels: refreshData, // role: // "Travel Agent" ), - ); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add Hotels", - style: GoogleFonts.poppins( - fontSize: isDesktop ? 13 : 11, + ); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add Hotels", + 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: filterHotels, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, + 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: futureHotels, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 16), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // ), + const SizedBox(height: 8), + Text( + "No Hotels Available ", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + Text( + "Please Create Hotels", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), ], ), ), - ], - ), + ); + } - 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: filterHotels, - decoration: InputDecoration( - hintText: "Search ...", - 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: futureHotels, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 16), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // ), - const SizedBox(height: 8), - Text( - "No Hotels Available ", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey), - ), - const SizedBox(height: 20), - Text( - "Please Create Hotels", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, color: Colors.grey), - ), - const SizedBox(height: 20), - ], - ), - ), - ); - } + List hotels = + filteredHotels.isNotEmpty ? filteredHotels : allHotels; - List hotels = - filteredHotels.isNotEmpty ? filteredHotels : allHotels; + hotels.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); - hotels.sort((a, b) { - DateTime dateA = DateTime.parse(a['created_on']); - DateTime dateB = DateTime.parse(b['created_on']); + return dateB.compareTo(dateA); // Descending: newest first + }); - return dateB - .compareTo(dateA); // Descending: newest first - }); - - List paginatedHotels = hotels + List paginatedHotels = + hotels .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); - Widget table = LayoutBuilder( - builder: (context, constraints) { - double minWidth = - isDesktop ? constraints.maxWidth : 1300; + 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), + 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( + 'Hotel Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), ), - columns: [ - DataColumn( - label: Text( - 'Hotel Name', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Hotel Chain', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'City', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Country', - 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: paginatedHotels.map((hotels) { - String hotelsId = hotels['hotel_id'] - .toString(); // Get user ID + ), + DataColumn( + label: Text( + 'Hotel Chain', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'City', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Country', + 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: + paginatedHotels.map((hotels) { + String hotelsId = + hotels['hotel_id'] + .toString(); // Get user ID bool isSelected = selectedUserId == hotelsId; - return DataRow(cells: [ - DataCell(Text(hotels['hotel_name'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", + return DataRow( + cells: [ + DataCell( + Text( + hotels['hotel_name'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell(Text(hotels['hotel_chain'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(hotels['city'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell(Text(hotels['country_name'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell( - Text( - hotels['is_active'] == "1" - ? 'Active' - : 'Inactive', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - color: hotels['is_active'] == "1" ? Colors.green : Colors.red, - ), - softWrap: true, - overflow: TextOverflow.ellipsis, ), - ), - DataCell( - // UserActionsMenu( - // user: hotels, - // getUserDetails: (id) => - // apiService.getSingleUser(id), - // ), + DataCell( + Text( + hotels['hotel_chain'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + hotels['city'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + hotels['country_name'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + hotels['is_active'] == "1" + ? 'Active' + : 'Inactive', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + color: + hotels['is_active'] == "1" + ? Colors.green + : Colors.red, + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + // UserActionsMenu( + // user: hotels, + // getUserDetails: (id) => + // apiService.getSingleUser(id), + // ), + GestureDetector( + child: Tooltip( + message: 'Edit Hotel Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + onTap: () async { + // final userId = getUserId(user['user_id']); + // final usersData = await getUserDetails(userId); + // + final hotelsId = int.tryParse( + hotels['hotel_id'].toString(), + ); + + if (hotelsId != null) { + print("HotelsId -- $hotelsId"); + final data = await apiService + .getHotelsDetailsFind(hotelsId); + print("HotelsId -- $data"); + + showDialog( + context: context, + builder: + (context) => HotelsData( + isDesktop: isDesktop, + hotelsId: + hotelsId, // Pass the ID + hotelsData: data, + layoutColor: layoutColor!, + // fetchGetHotels: fetchGetHotels, + fetchGetHotels: refreshData, + // role: + // "Travel Agent" + ), + ); + } else { + print("Invalid Hotels ID"); + } + }, + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final hotels = 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: [ + // Status and Employee Code + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + hotels['hotel_name'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700, + ), + ), + GestureDetector( child: Tooltip( - message: 'Delete Hotel Details', + message: 'Edit Hotel Details', child: Image.asset( - 'assets/images/IconsImg/delete.png', + 'assets/images/IconsImg/edit.png', width: 20, height: 15, ), @@ -617,8 +758,8 @@ class HotelsDataListState extends State { // final usersData = await getUserDetails(userId); // final hotelsId = int.tryParse( - hotels['hotel_id'] - .toString()); + hotels['hotel_id'].toString(), + ); if (hotelsId != null) { print("HotelsId -- $hotelsId"); @@ -628,83 +769,8 @@ class HotelsDataListState extends State { showDialog( context: context, - builder: (context) => HotelsData( - isDesktop: isDesktop, - hotelsId: hotelsId, // Pass the ID - hotelsData: data, - layoutColor: layoutColor!, - // fetchGetHotels: fetchGetHotels, - fetchGetHotels: refreshData, - // role: - // "Travel Agent" - ), - ); - } else { - print("Invalid Hotels ID"); - } - }, - ), - ), - ]); - }).toList(), - ), - ); - }, - ); - - Widget buildMobileCardView(List paginatedUser) { - return ListView.builder( - itemCount: paginatedUser.length, - itemBuilder: (context, index) { - final hotels = 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: [ - // Status and Employee Code - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - hotels['hotel_name'] ?? 'N/A', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.w700), - ), - - GestureDetector( - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onTap: () async { - // final userId = getUserId(user['user_id']); - // final usersData = await getUserDetails(userId); - // - final hotelsId = int.tryParse( - hotels['hotel_id'] - .toString()); - - if (hotelsId != null) { - print("HotelsId -- $hotelsId"); - final data = await apiService - .getHotelsDetailsFind(hotelsId); - print("HotelsId -- $data"); - - showDialog( - context: context, - builder: (context) => HotelsData( + builder: + (context) => HotelsData( isDesktop: isDesktop, hotelsId: hotelsId, // Pass the ID @@ -715,142 +781,150 @@ class HotelsDataListState extends State { // role: // "Travel Agent" ), - ); - } else { - print("Invalid Hotels ID"); - } - }, - ), - ], + ); + } else { + print("Invalid Hotels ID"); + } + }, ), - - SizedBox(height: 2), - // Trip Id and Trip Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${hotels['hotel_chain'] ?? ''} ", - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.w500), - ), - ], - ), - ], - ), - SizedBox(height: 2), - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${hotels['city'] ?? ''} ", - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.w500), - ), - ], - ), - ], - ), - SizedBox(height: 2), - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - hotels['country_name'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - ], - ), - // ** ], ), - ), - ); - }, - ); - } - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: isDesktop + SizedBox(height: 2), + // Trip Id and Trip Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "${hotels['hotel_chain'] ?? ''} ", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + SizedBox(height: 2), + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "${hotels['city'] ?? ''} ", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + SizedBox(height: 2), + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + hotels['country_name'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + // ** + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop ? (searchController.text.isNotEmpty && filteredHotels.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) + scrollDirection: Axis.vertical, + child: table, + )) : (searchController.text.isNotEmpty && filteredHotels.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : buildMobileCardView(paginatedHotels)), - ), - // Expanded( - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedUser), - // ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: hotels.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, - ), - ], ), - ); - }, - ) - ]), - )), + // Expanded( + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedUser), + // ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: hotels.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), ); } } diff --git a/lib/Screens/itnerary/taxi.dart b/lib/Screens/itnerary/taxi.dart index 6549298..969ba4e 100644 --- a/lib/Screens/itnerary/taxi.dart +++ b/lib/Screens/itnerary/taxi.dart @@ -14,12 +14,13 @@ class TaxiScreen extends StatefulWidget { final Map? selectedItem; final String? loginUser; - TaxiScreen( - {required this.onClose, - this.apiData, - required this.onSavetaxi, - required this.selectedItem, - required this.loginUser}); + TaxiScreen({ + required this.onClose, + this.apiData, + required this.onSavetaxi, + required this.selectedItem, + required this.loginUser, + }); @override _TaxiScreenState createState() => _TaxiScreenState(); @@ -97,13 +98,17 @@ class _TaxiScreenState extends State { super.initState(); _addFocusListener( - _destinationFocusNode, (focus) => _destinationFocus = focus); + _destinationFocusNode, + (focus) => _destinationFocus = focus, + ); _addFocusListener(_locationFocusNode, (focus) => _locationFocus = focus); _addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus); _addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus); _addFocusListener(_taxiReqFocusNode, (focus) => _taxiReqFocused = focus); _addFocusListener( - _numPassengerFocusNode, (focus) => _numPassengerFocus = focus); + _numPassengerFocusNode, + (focus) => _numPassengerFocus = focus, + ); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _destinationController = initController("destination_city"); @@ -168,7 +173,7 @@ class _TaxiScreenState extends State { "location_of_pickup", "no_of_passengers", "date", - "time" + "time", ]; // Check validation for each field @@ -199,30 +204,33 @@ class _TaxiScreenState extends State { @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isMobile = sizingInfo.isMobile; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isMobile = sizingInfo.isMobile; + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Container( - // color: Color(0xFFF4F4FB), - child: Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(28.0), - child: Center( - child: Column(children: _buildAccomadtionForm(isDesktop)), + return Container( + // color: Color(0xFFF4F4FB), + child: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(28.0), + child: Center( + child: Column(children: _buildAccomadtionForm(isDesktop)), + ), ), - ) - ], + ], + ), ), ), - ), - ); - }); + ); + }, + ); } List _buildAccomadtionForm(bool isDesktop) { @@ -235,7 +243,7 @@ class _TaxiScreenState extends State { List> rowBuilders = [ // _builClassType(isDesktop), - _buildSecondRow(isDesktop) + _buildSecondRow(isDesktop), ]; return [ @@ -253,19 +261,24 @@ class _TaxiScreenState extends State { List _buildFirstRow(isDesktop) { List purposeList = widget.apiData?['taxt_car_type'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -281,31 +294,28 @@ class _TaxiScreenState extends State { Text( "Taxi Required For", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), isDesktop ? Row(children: _buildTripType(isDesktop)) - : Column(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Number of Passenger", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -319,8 +329,9 @@ class _TaxiScreenState extends State { style: const TextStyle(fontSize: 12), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + FilteringTextInputFormatter.allow( + RegExp(r'^\d*\.?\d*$'), + ), // Allow only positive numbers with optional decimal ], decoration: const InputDecoration( labelText: "Number of Passenger", @@ -334,28 +345,21 @@ class _TaxiScreenState extends State { ), if (errorMessages["no_of_passengers"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Car Type", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -369,18 +373,21 @@ class _TaxiScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), // Proper padding ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedCarType = newValue; - }); - print( - "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - } - : null, + onChanged: + purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedCarType = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}", + ); + } + : null, items: dropdownItems, ), @@ -388,31 +395,31 @@ class _TaxiScreenState extends State { ), ], ), - if (isDesktop) - SizedBox.shrink() - else - SizedBox( - height: 8, - ), + if (isDesktop) SizedBox.shrink() else SizedBox(height: 8), ]; } List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['taxi_car_required_for'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -425,9 +432,10 @@ class _TaxiScreenState extends State { CustomTextFieldWrapper( isFocused: _taxiReqFocused, isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownButtonFormField( @@ -436,18 +444,21 @@ class _TaxiScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), // Proper padding ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedReqTaxi = newValue; - }); - print( - "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - } - : null, + onChanged: + purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedReqTaxi = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}", + ); + } + : null, items: dropdownItems, ), @@ -466,10 +477,11 @@ class _TaxiScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - initialDate: _selectedCheckOutDate != null && - _selectedCheckOutDate!.isAfter(today) - ? _selectedCheckOutDate! - : today, + initialDate: + _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) + ? _selectedCheckOutDate! + : today, firstDate: today, lastDate: DateTime(2100), ); @@ -494,8 +506,13 @@ class _TaxiScreenState extends State { // Formatting time to HH:mm (24-hour format) final now = DateTime.now(); final formattedTime = DateFormat('HH:mm').format( - DateTime(now.year, now.month, now.day, pickedTime.hour, - pickedTime.minute), + DateTime( + now.year, + now.month, + now.day, + pickedTime.hour, + pickedTime.minute, + ), ); _timeController.text = formattedTime; }); @@ -509,9 +526,10 @@ class _TaxiScreenState extends State { Text( "City", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -535,28 +553,21 @@ class _TaxiScreenState extends State { ), if (errorMessages["destination_city"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Location of Pickup", + "Pickup Location", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -569,7 +580,7 @@ class _TaxiScreenState extends State { controller: _locationController, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( - labelText: "Location of Pickup", + labelText: "Pickup Location", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -580,28 +591,21 @@ class _TaxiScreenState extends State { ), if (errorMessages["location_of_pickup"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Date", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -622,8 +626,11 @@ class _TaxiScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -632,28 +639,21 @@ class _TaxiScreenState extends State { ), if (errorMessages["date"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Time", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -674,8 +674,11 @@ class _TaxiScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -684,10 +687,7 @@ class _TaxiScreenState extends State { ), if (errorMessages["time"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), @@ -702,17 +702,19 @@ class _TaxiScreenState extends State { Text( "Comments", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, @@ -733,9 +735,7 @@ class _TaxiScreenState extends State { ], ), if (isDesktop) Spacer(), - SizedBox( - height: 5, - ), + SizedBox(height: 5), Column( children: [ Row( @@ -756,9 +756,7 @@ class _TaxiScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( @@ -767,7 +765,6 @@ class _TaxiScreenState extends State { ), ), SizedBox(width: 10), // Space between buttons - // Save Changes Button ElevatedButton( onPressed: () { @@ -775,9 +772,7 @@ class _TaxiScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( diff --git a/lib/Screens/itnerary_list/flight_list.dart b/lib/Screens/itnerary_list/flight_list.dart index f064d3b..aaa5b84 100644 --- a/lib/Screens/itnerary_list/flight_list.dart +++ b/lib/Screens/itnerary_list/flight_list.dart @@ -85,7 +85,8 @@ class _FlightListWidgetState extends State { return AlertDialog( title: const Text('Select Trip Type'), content: const Text( - 'Please select a trip type before adding a flight.'), + 'Please select a trip type before adding a flight.', + ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), @@ -118,16 +119,18 @@ class _FlightListWidgetState extends State { // style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), // ), MouseRegion( - cursor: widget.isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + cursor: + widget.isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: GestureDetector( - onTap: widget.isViewMode - ? null - : () { - checkClass(); - print("New data"); - }, + onTap: + widget.isViewMode + ? null + : () { + checkClass(); + print("New data"); + }, child: Row( mainAxisSize: MainAxisSize.min, // Ensures content fits nicely @@ -141,7 +144,7 @@ class _FlightListWidgetState extends State { Icons.add_circle_sharp, size: 30, color: Color(0xFF114D8B), - ) + ), ], ), ), @@ -149,9 +152,9 @@ class _FlightListWidgetState extends State { ], ), const SizedBox(height: 16), - // Scroll behavior based on device - _buildData(context, isDesktop) + // Scroll behavior based on device + _buildData(context, isDesktop), ], ), ), @@ -224,8 +227,11 @@ class _FlightListWidgetState extends State { if (hour == 24 && minute == 0) { // 24:00 is treated as 00:00 on the next day - dateTime = DateTime(now.year, now.month, now.day) - .add(const Duration(days: 1)); + dateTime = DateTime( + now.year, + now.month, + now.day, + ).add(const Duration(days: 1)); } else { if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { throw FormatException("Invalid hour or minute"); @@ -245,7 +251,21 @@ class _FlightListWidgetState extends State { itemCount: filteredList.length, itemBuilder: (context, index) { final item = filteredList[index]; - + String? tripTypeName = ''; + switch (item["trip_type"]?.toString()) { + case "Roundtrip": + tripTypeName = "Round Trip"; + break; + case "Multitrip": + tripTypeName = "Multi-Trip"; + break; + case "Oneway": + tripTypeName = "One-Way"; + break; + default: + tripTypeName = item["trip_type"]?.toString(); + break; + } return Container( margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), decoration: BoxDecoration( @@ -281,7 +301,7 @@ class _FlightListWidgetState extends State { Row( children: [ Text( - item["trip_type"]?.toString() ?? "N/A", + tripTypeName ?? "N/A", style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w800, @@ -313,59 +333,53 @@ class _FlightListWidgetState extends State { ), ], ), - Divider( - color: Colors.blueGrey.shade50, - ), + Divider(color: Colors.blueGrey.shade50), SizedBox(height: 4), if (isDesktop) Row( children: [ Expanded( - flex: 2, - child: Text( - "Class", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), + flex: 2, + child: Text( + "Class", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), Expanded( - flex: 4, - child: Text( - "Sector", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), + flex: 4, + child: Text( + "Sector", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), Expanded( - flex: 2, - child: Text( - "Date", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), + flex: 2, + child: Text( + "Date", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), Expanded( - flex: 2, - child: Text( - "Time", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), + flex: 2, + child: Text( + "Time", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), ], ), SizedBox(height: 4), - // Trip Rows + // Trip Rows if (isDesktop) if (item["trips"] != null && item["trips"].isNotEmpty) ...item["trips"].map((trip) { String fromPlaceCountry = countryMap[trip["from_place"]?.toString()] ?? - "Unknown Country"; + "Unknown Country"; String toPlaceCountry = countryMap[trip["to_place"]?.toString()] ?? - "Unknown Country"; + "Unknown Country"; return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), @@ -385,7 +399,7 @@ class _FlightListWidgetState extends State { Expanded( flex: 4, child: Text( - "$fromPlaceCountry (from) - (to) $toPlaceCountry", + "$fromPlaceCountry (From) - (To) $toPlaceCountry", // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", style: GoogleFonts.poppins( fontSize: 12, @@ -435,15 +449,22 @@ class _FlightListWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildKeyValueRow( - "Class", - getRequestForClass(trip["class"].toString()) ?? - "N/A"), - _buildKeyValueRow("Sector", - "${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}"), + "Class", + getRequestForClass(trip["class"].toString()) ?? + "N/A", + ), _buildKeyValueRow( - "Date", formatDate(trip["date"] ?? "")), + "Sector", + "${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}", + ), _buildKeyValueRow( - "Time", formatTime(trip["time"] ?? "")), + "Date", + formatDate(trip["date"] ?? ""), + ), + _buildKeyValueRow( + "Time", + formatTime(trip["time"] ?? ""), + ), ], ), ), @@ -474,12 +495,7 @@ class _FlightListWidgetState extends State { ), ), Expanded( - child: Text( - value, - style: GoogleFonts.poppins( - fontSize: 12, - ), - ), + child: Text(value, style: GoogleFonts.poppins(fontSize: 12)), ), ], ), diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index e8cca5e..74aff24 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -131,7 +131,7 @@ class _CreateUserFormDetialsState extends State { "delegationEndDate", // "dateOfIssue", // "dateOfExpiry", - "changePassword" + "changePassword", ]; Color? layoutColor; @@ -202,7 +202,7 @@ class _CreateUserFormDetialsState extends State { print("API Selected User Has Data - $apiselectedUser"); } - userIdApi = apiselectedUser?["user_id"] ?? ""; + userIdApi = apiselectedUser?["user_id"] ?? ""; controllers["Fname"]?.text = apiselectedUser?["first_name"] ?? ""; controllers["Lname"]?.text = apiselectedUser?["last_name"] ?? ""; controllers["email"]?.text = apiselectedUser?["email"] ?? ""; @@ -286,7 +286,8 @@ class _CreateUserFormDetialsState extends State { if (apiselectedUser?["delegated_to_user_id"] != null) { print( - "UPDADele- ${apiselectedUser?["delegated_to_user_id"]?.toString()}"); + "UPDADele- ${apiselectedUser?["delegated_to_user_id"]?.toString()}", + ); selectedSubstituteApprover = apiselectedUser?["delegated_to_user_id"]?.toString() ?? ""; @@ -303,12 +304,11 @@ class _CreateUserFormDetialsState extends State { List decodedList = jsonDecode(fixedJson); - selectedServiceIds = decodedList.map>((item) { - final map = Map.from(item); - return { - "service_id": map['service_id'].toString(), - }; - }).toList(); + selectedServiceIds = + decodedList.map>((item) { + final map = Map.from(item); + return {"service_id": map['service_id'].toString()}; + }).toList(); } catch (e) { print("❌ Error decoding fixed agent_supported_service_ids: $e"); selectedServiceIds = []; @@ -380,8 +380,9 @@ class _CreateUserFormDetialsState extends State { // userIdsApi = userMap.keys.toList(); // Handle selectedUser as a Map (not a List) - apiselectedUser = extraData['selectedUser'] - as Map?; // Cast it as a Map + apiselectedUser = + extraData['selectedUser'] + as Map?; // Cast it as a Map isViewMode = extraData['isViewMode'] ?? false; isEditProfile = extraData['isEditProfile'] ?? false; }); @@ -443,7 +444,7 @@ class _CreateUserFormDetialsState extends State { userMap = { for (var user in userList) user['user_id'].toString(): - "${user['first_name']} ${user['last_name']}" + "${user['first_name']} ${user['last_name']}", }; userIdsApi = userMap.keys.toList(); }); @@ -481,13 +482,15 @@ class _CreateUserFormDetialsState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -538,6 +541,11 @@ class _CreateUserFormDetialsState extends State { } } + void handleGoBack() async { + print("hello, please Go Back"); + printFormData(); + } + void handleNext() async { print("USR Detail Next"); printFormData(); @@ -552,35 +560,67 @@ class _CreateUserFormDetialsState extends State { print("USERDETAILS : $data"); - if (!isValidData(data)) { - print("USERDETAILS : $userDetials"); - print("Validation Failed: Required fields are missing."); - setState(() {}); - return; // Stop execution if validation fails + final tabs = { + "personal": "Personal Details", + "office": "Office Details", + "travel": "Travel Details", + }; + + final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"] + + final currentIndex = tabKeys.indexOf(selectedTab ?? "personal"); + + print("currentIndex - $currentIndex"); + + bool isValid = false; + + final currentTab = tabKeys[currentIndex]; + if (currentTab == "personal") { + isValid = isValidData(userDetials); + } else if (currentTab == "office") { + isValid = isValidDataTwo(userDetials); } else { - print("USERDETAILS : $userDetials"); - - final tabs = { - "personal": "Personal Details", - "office": "Office Details", - "travel": "Travel Details", - }; - - final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"] - - final currentIndex = tabKeys.indexOf(selectedTab ?? "personal"); - - if (currentIndex < tabKeys.length - 1) { - // Move to next tab - setState(() { - selectedTab = tabKeys[currentIndex + 1]; - }); - } else { - // Already at last tab (travel), maybe submit form or show done message - print("All tabs completed!"); - // You can trigger full form submit here - } + isValid = true; // Travel tab might not need validation at this point } + + if (!isValid) { + print("Validation Failed on $currentTab: $userDetials"); + setState(() {}); // To trigger UI update showing errors + return; + } + + if (currentIndex < tabKeys.length - 1) { + // Move to next tab + setState(() { + selectedTab = tabKeys[currentIndex + 1]; + }); + } else { + // Final step — submit or show done + print("All tabs completed!"); + // Submit the full form here + } + + // if (!isValidData(data)) + // { + // print("USERDETAILS : $userDetials"); + // print("Validation Failed: Required fields are missing."); + // setState(() {}); + // return; // Stop execution if validation fails + // } else + // { + // print("USERDETAILS : $userDetials"); + // + // if (currentIndex < tabKeys.length - 1) { + // // Move to next tab + // setState(() { + // selectedTab = tabKeys[currentIndex + 1]; + // }); + // } else { + // // Already at last tab (travel), maybe submit form or show done message + // print("All tabs completed!"); + // // You can trigger full form submit here + // } + // } } void handleSubmit() async { @@ -601,7 +641,7 @@ class _CreateUserFormDetialsState extends State { // Map data = userDetials; - if (!isValidData(data)) { + if (!isValidData(data) && isValidDataTwo(data)) { print("USERDETAILS : $userDetials"); print("Validation Failed: Required fields are missing."); setState(() {}); @@ -623,7 +663,7 @@ class _CreateUserFormDetialsState extends State { "last_name", "email", "mobile_no", - // "employeeCode" + // "employeeCode", ]; if (apiselectedUser == null) { @@ -647,8 +687,9 @@ class _CreateUserFormDetialsState extends State { if (data["alternate_mobile_no"] != null && data["alternate_mobile_no"].toString().isNotEmpty) { - if (!RegExp(r"^\d{10}$") - .hasMatch(data["alternate_mobile_no"].toString())) { + if (!RegExp( + r"^\d{10}$", + ).hasMatch(data["alternate_mobile_no"].toString())) { errorMessages["alternate_mobile_no"] = "Enter 10 digits"; // Invalid mobile number format } @@ -656,14 +697,31 @@ class _CreateUserFormDetialsState extends State { // Email validation if (data["email"] != null && data["email"].toString().isNotEmpty) { - if (!RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") - .hasMatch(data["email"].toString())) { + if (!RegExp( + r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", + ).hasMatch(data["email"].toString())) { errorMessages["email"] = "Invalid email format"; // Invalid email format } } return errorMessages.isEmpty; // Valid if there are no errors } + bool isValidDataTwo(Map data) { + errorMessages.clear(); // Reset errors + + // Required fields that must not be empty + List requiredFields = ["employee_code"]; + + // Check validation for each field + for (String field in requiredFields) { + if (data[field] == null || data[field].toString().trim().isEmpty) { + errorMessages[field] = "Required"; + } + } + + return errorMessages.isEmpty; // Valid if there are no errors + } + void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { @@ -814,8 +872,10 @@ class _CreateUserFormDetialsState extends State { print("enteredPassword - $enteredPassword "); if (hashedPassword != null && hashedPassword.isNotEmpty) { - bool isMatch = - BCrypt.checkpw(enteredPassword, hashedPassword); // Compare passwords + bool isMatch = BCrypt.checkpw( + enteredPassword, + hashedPassword, + ); // Compare passwords setState(() { // ✅ Ensure UI updates @@ -825,8 +885,9 @@ class _CreateUserFormDetialsState extends State { print(" Password match!"); } else { print(" Password NOT match!"); - errorMessages - .remove("password"); // Clear error if password is different + errorMessages.remove( + "password", + ); // Clear error if password is different } }); } else { @@ -836,32 +897,36 @@ class _CreateUserFormDetialsState extends State { @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: Color(0xFFf5f5f5), - // appBar: isDesktop ? null : const CustomAppBar(title: 'Create User '), - // 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: [ - Expanded(child: buildData(isDesktop, context)), - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'Create User '), + // 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: [Expanded(child: buildData(isDesktop, context))], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildData(bool isDesktop, context) { @@ -875,9 +940,10 @@ class _CreateUserFormDetialsState extends State { children: [ Expanded( child: Container( - height: isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, child: Padding( padding: EdgeInsets.all(0.0), child: _buildUserDetails(isDesktop), @@ -887,25 +953,32 @@ class _CreateUserFormDetialsState extends State { Container( color: Colors.white, child: Padding( - padding: const EdgeInsets.all(8.0), - child: isDesktop - ? Row( + padding: const EdgeInsets.all(8.0), + child: + isDesktop + ? Row( mainAxisAlignment: MainAxisAlignment.end, - children: (selectedTab == "travel" || - selectedRole == "5" || - setSelectesUserType == true) - ? _buildSubmit(isDesktop, layoutColor!) - : _buildNext(isDesktop, layoutColor!), + children: + (selectedTab == "travel" || + selectedRole == "5" || + setSelectesUserType == true) + ? _buildSubmit(isDesktop, layoutColor!) + : _buildNext( + isDesktop, + layoutColor!, + ), // _buildGoBack(isDesktop, layoutColor!), ) - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: (selectedTab == "travel" || - selectedRole == "5" || - setSelectesUserType == true) - ? _buildSubmit(isDesktop, layoutColor!) - : _buildNext(isDesktop, layoutColor!), - )), - ) + : Row( + mainAxisAlignment: MainAxisAlignment.end, + children: + (selectedTab == "travel" || + selectedRole == "5" || + setSelectesUserType == true) + ? _buildSubmit(isDesktop, layoutColor!) + : _buildNext(isDesktop, layoutColor!), + ), + ), + ), ], ), ); @@ -944,25 +1017,22 @@ class _CreateUserFormDetialsState extends State { // }) ], ), - SizedBox( - height: 18, - ), + SizedBox(height: 18), isDesktop ? buildTabsForUser() : SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: buildTabsForUser(), - ), + scrollDirection: Axis.horizontal, + child: buildTabsForUser(), + ), Container( // color: Colors.yellow.shade50, - height: MediaQuery.of(context).size.height * 0.64, child: Row( children: [ Expanded(child: buildTabContents(isDesktop, isViewMode)), ], ), - ) + ), ], ), ), @@ -977,7 +1047,7 @@ class _CreateUserFormDetialsState extends State { personalDetailsKey: personalDetailsKey, isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, - userIdApi:userIdApi, + userIdApi: userIdApi, controllers: controllers, errorMessages: errorMessages, selectedGender: selectedGender, @@ -1012,7 +1082,7 @@ class _CreateUserFormDetialsState extends State { return OfficeDetails( isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, - userIdApi:userIdApi, + userIdApi: userIdApi, controllers: controllers, errorMessages: errorMessages, selectedLevel: selectedLevel, @@ -1063,14 +1133,14 @@ class _CreateUserFormDetialsState extends State { errorMessages: errorMessages, travelDetails: travelDetailsDataFromAPI, // 👈 Pass this down passportFileUrlFromApi: passportFileUrlFromApi, - userIdApi:userIdApi, + userIdApi: userIdApi, ); default: return PersonalDetails( personalDetailsKey: personalDetailsKey, isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, - userIdApi:userIdApi, + userIdApi: userIdApi, controllers: controllers, errorMessages: errorMessages, selectedGender: selectedGender, @@ -1101,9 +1171,7 @@ class _CreateUserFormDetialsState extends State { Map getTabs(bool setSelectesUserType) { if (setSelectesUserType || selectedRole == "5") { - return { - "personal": "Personal Details", - }; + return {"personal": "Personal Details"}; } else { return allTabs; } @@ -1113,50 +1181,102 @@ class _CreateUserFormDetialsState extends State { return Row( crossAxisAlignment: CrossAxisAlignment.end, // important - children: tabs.entries.map((entry) { - final isSelected = selectedTab == entry.key; - return GestureDetector( - onTap: () { - setState(() { - selectedTab = entry.key; - }); - }, - child: Padding( - padding: const EdgeInsets.only(right: 24.0), // space between tabs - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - entry.value, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: isSelected ? Color(0xFF114D8B) : Color(0xFF475569), - ), + children: + tabs.entries.map((entry) { + final isSelected = selectedTab == entry.key; + return GestureDetector( + onTap: () { + setState(() { + bool isValid = false; + + final currentTab = selectedTab; + if (currentTab == "personal") { + isValid = isValidData(userDetials); + + if (isValid) { + selectedTab = entry.key; + } + } else if (currentTab == "office") { + isValid = isValidDataTwo(userDetials); + if (isValid) { + selectedTab = entry.key; + } + } else { + isValid = + true; // Travel tab might not need validation at this point + selectedTab = entry.key; + } + }); + }, + child: Padding( + padding: const EdgeInsets.only( + right: 24.0, + ), // space between tabs + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + entry.value, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: + isSelected ? Color(0xFF114D8B) : Color(0xFF475569), + ), + ), + const SizedBox(height: 8), + AnimatedContainer( + duration: Duration(milliseconds: 300), + height: 2, + width: isSelected ? 50 : 0, // small line + color: Color(0xFF114D8B), + ), + ], ), - const SizedBox(height: 8), - AnimatedContainer( - duration: Duration(milliseconds: 300), - height: 2, - width: isSelected ? 50 : 0, // small line - color: Color(0xFF114D8B), - ), - ], - ), - ), - ); - }).toList(), + ), + ); + }).toList(), ); } -// ---- Submit --------------------------------------- + // ---- Submit --------------------------------------- + + List _buildGoBack(isDesktop, Color layoutColor) { + return [ + MouseRegion( + cursor: + isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + isViewMode ? layoutColor : layoutColor, // Keep original color + foregroundColor: + isViewMode ? Colors.white : Colors.white, // Keep original color + disabledBackgroundColor: + layoutColor, // Ensure color remains when disabled + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: handleGoBack, + child: Text("Back"), + ), + ), + ]; + } List _buildNext(isDesktop, Color layoutColor) { return [ MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + cursor: + isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: @@ -1177,41 +1297,42 @@ class _CreateUserFormDetialsState extends State { // isViewMode ? null : handleNext, // Disable when in view mode child: Text("Next"), ), - ) + ), ]; } List _buildSubmit(isDesktop, Color layoutColor) { return [ ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: layoutColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: layoutColor, width: 2), - ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: layoutColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor, width: 2), ), - onPressed: () { - isEditProfile ? context.go('/listPlan') : context.go('/listUser'); - }, - child: Text("Cancel")), - SizedBox( - width: 20, + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: () { + isEditProfile ? context.go('/listPlan') : context.go('/listUser'); + }, + child: Text("Cancel"), ), + SizedBox(width: 20), if (!isViewMode) MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + cursor: + isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: isViewMode ? layoutColor : layoutColor, // Keep original color - foregroundColor: isViewMode - ? Colors.white - : Colors.white, // Keep original color + foregroundColor: + isViewMode + ? Colors.white + : Colors.white, // Keep original color disabledBackgroundColor: layoutColor, // Ensure color remains when disabled disabledForegroundColor: Colors.white, @@ -1225,7 +1346,7 @@ class _CreateUserFormDetialsState extends State { isViewMode ? null : handleSubmit, // Disable when in view mode child: Text("Submit"), ), - ) + ), ]; } } diff --git a/lib/Screens/userManagement/create_user/office_details.dart b/lib/Screens/userManagement/create_user/office_details.dart index 923f5fc..02f80f7 100644 --- a/lib/Screens/userManagement/create_user/office_details.dart +++ b/lib/Screens/userManagement/create_user/office_details.dart @@ -17,7 +17,6 @@ class OfficeDetails extends StatefulWidget { final bool isViewMode; final String? userIdApi; - final ValueChanged? onLevelChanged; final ValueChanged? onDepartmentChanged; final ValueChanged? onFirstApproverChanged; @@ -157,17 +156,25 @@ class _OfficeDetailsState extends State { fetchFindGroup(); } - Future apiCheckDuplicate(String label, String field, String value, String? userId) async { + Future apiCheckDuplicate( + String label, + String field, + String value, + String? userId, + ) async { try { + // Basic validation: Check mobile number length - if (field == "employeeCode") { - field = "employee_code"; - } - - final response = await apiService.CheckDuplicate(label, field, value, userId); + final response = await apiService.CheckDuplicate( + label, + field, + value, + userId, + ); if (response.isNotEmpty) { _clearError(field); - widget.errorMessages[field] = response['message'] ?? "$label already exists"; + widget.errorMessages[field] = + response['message'] ?? "$label Already Exists"; print("Duplicate found: ${response['message']}"); return; } else { @@ -290,7 +297,7 @@ class _OfficeDetailsState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox(height: 10), + SizedBox(height: 20), _buildFirstRow(widget.isDesktop), if (widget.isDesktop) SizedBox(height: 10), _buildSecondRow(widget.isDesktop), @@ -407,7 +414,7 @@ class _OfficeDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Employee Code", + "Employee Code *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -425,8 +432,13 @@ class _OfficeDetailsState extends State { controller: widget.controllers["employeeCode"], enabled: !widget.isViewMode, onChanged: (value) { - _clearError("employeeCode"); - apiCheckDuplicate("Employee Code", "employeeCode",value,widget.userIdApi); + _clearError("employee_code"); + apiCheckDuplicate( + "Employee Code", + "employee_code", + value, + widget.userIdApi, + ); }, decoration: InputDecoration( labelText: "Employee Code", @@ -441,10 +453,10 @@ class _OfficeDetailsState extends State { ), ), ), - if (widget.errorMessages["employeeCode"] != null) ...[ + if (widget.errorMessages["employee_code"] != null) ...[ SizedBox(height: 5), // Space before error message Text( - widget.errorMessages["employeeCode"]!, + widget.errorMessages["employee_code"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], @@ -452,7 +464,71 @@ class _OfficeDetailsState extends State { ); } + // Widget buildDepartmentFieldOld() { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Department", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74), + // ), + // ), + // SizedBox(height: 5), + // CustomTextFieldUserWrapper( + // isFocused: false, // Dropdown doesn't use focus + // isDesktop: widget.isDesktop, + // child: SizedBox( + // height: 45, // Set appropriate height + // child: DropdownButtonFormField( + // value: selectedDepartment, + // // value: widget.isViewMode ? null : selectedDepartment, + // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), + // decoration: InputDecoration( + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // horizontal: 10, + // ), // Proper padding + // ), + // onChanged: + // widget.isViewMode + // ? null + // : (newValue) { + // setState(() { + // selectedDepartment = newValue; + // }); + // widget.onDepartmentChanged?.call(newValue); + // }, + // + // items: + // apiCostData?.map>((item) { + // return DropdownMenuItem( + // + // value: item['department_id'], // ID as value + // child: Text(item['name'] ?? "Unknown"), + // ); + // }).toList(), + // hint: Text("Select"), + // disabledHint: Text( + // selectedDepartment ?? "Select Department", + // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), + // ), + // ), + // ), + // ), + // ], + // ); + // } + Widget buildDepartmentField() { + // Map department_id to department_name + Map departmentMap = { + for (var item in apiCostData ?? []) + item['department_id'] as String: item['name'] as String, + }; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -466,41 +542,74 @@ class _OfficeDetailsState extends State { ), SizedBox(height: 5), CustomTextFieldUserWrapper( - isFocused: false, // Dropdown doesn't use focus + isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( - height: 45, // Set appropriate height - child: DropdownButtonFormField( - value: selectedDepartment, - // value: widget.isViewMode ? null : selectedDepartment, - style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - ), // Proper padding + height: 40, + child: DropdownSearch( + selectedItem: departmentMap[selectedDepartment], + enabled: !widget.isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: + (context, item, isSelected) => Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), + constraints: BoxConstraints(maxHeight: 200), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Department...", + contentPadding: EdgeInsets.symmetric(horizontal: 10), + ), + ), ), + items: departmentMap.values.toList(), + + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Department", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ), onChanged: widget.isViewMode ? null - : (newValue) { + : (String? newValue) { + if (newValue == null) return; + + final departmentId = + departmentMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; + setState(() { - selectedDepartment = newValue; + selectedDepartment = departmentId; }); - widget.onDepartmentChanged?.call(newValue); + + widget.onDepartmentChanged?.call( + departmentId, + ); // Send department_id }, - items: - apiCostData?.map>((item) { - return DropdownMenuItem( - value: item['department_id'], // ID as value - child: Text(item['name'] ?? "Unknown"), - ); - }).toList(), - hint: Text("Select"), - disabledHint: Text( - selectedDepartment ?? "Select Department", - style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), - ), ), ), ), @@ -585,6 +694,17 @@ class _OfficeDetailsState extends State { popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit.loose, // Allows flexible height + itemBuilder: + (context, item, isSelected) => Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), constraints: BoxConstraints(maxHeight: 250), searchFieldProps: TextFieldProps( decoration: InputDecoration( @@ -686,6 +806,22 @@ class _OfficeDetailsState extends State { popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit.loose, // Allows flexible height + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: + (context, item, isSelected) => Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: 11.5, + ), + ), + ), constraints: BoxConstraints(maxHeight: 250), searchFieldProps: TextFieldProps( decoration: InputDecoration( @@ -806,6 +942,22 @@ class _OfficeDetailsState extends State { popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit.loose, // Allows flexible height + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: + (context, item, isSelected) => Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: 11.5, + ), + ), + ), constraints: BoxConstraints(maxHeight: 250), searchFieldProps: TextFieldProps( decoration: InputDecoration( @@ -925,6 +1077,22 @@ class _OfficeDetailsState extends State { popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit.loose, // Allows flexible height + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: + (context, item, isSelected) => Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: 11.5, + ), + ), + ), constraints: BoxConstraints(maxHeight: 250), searchFieldProps: TextFieldProps( decoration: InputDecoration( @@ -1047,6 +1215,22 @@ class _OfficeDetailsState extends State { popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit.loose, // Allows flexible height + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: + (context, item, isSelected) => Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: 11.5, + ), + ), + ), constraints: BoxConstraints(maxHeight: 250), searchFieldProps: TextFieldProps( decoration: InputDecoration( diff --git a/lib/Screens/userManagement/create_user/personal_details.dart b/lib/Screens/userManagement/create_user/personal_details.dart index 2f9113e..0f757e3 100644 --- a/lib/Screens/userManagement/create_user/personal_details.dart +++ b/lib/Screens/userManagement/create_user/personal_details.dart @@ -157,7 +157,12 @@ class PersonalDetailsState extends State { }); } - Future apiCheckDuplicate(String label, String field, String value, String? userId) async { + 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) { @@ -167,10 +172,16 @@ class PersonalDetailsState extends State { return; // Skip the API call if input is invalid } - final response = await apiService.CheckDuplicate(label, field, value, userId); + final response = await apiService.CheckDuplicate( + label, + field, + value, + userId, + ); if (response.isNotEmpty) { _clearError(field); - widget.errorMessages[field] = response['message'] ?? "$label already exists"; + widget.errorMessages[field] = + response['message'] ?? "$label Already Exists"; print("Duplicate found: ${response['message']}"); return; } else { @@ -182,9 +193,6 @@ class PersonalDetailsState extends State { } } - - - void _clearError(String field) { setState(() { widget.errorMessages.remove(field); @@ -305,11 +313,11 @@ class PersonalDetailsState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox(height: 10), + SizedBox(height: 20), _buildFirstRow(widget.isDesktop), SizedBox(height: 10), _buildSecondRow(widget.isDesktop), - // SizedBox(height: 10), + SizedBox(height: 10), _buildThirdRow(widget.isDesktop), SizedBox(height: 10), _buildForthRow(widget.isDesktop), @@ -731,7 +739,7 @@ class PersonalDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "First Name", + "First Name *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -781,7 +789,7 @@ class PersonalDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Last Name", + "Last Name*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -822,7 +830,107 @@ class PersonalDetailsState extends State { ); } + // Widget buildGenderFieldOld() { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Gender", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74), + // ), + // ), + // SizedBox(height: 5), + // CustomTextFieldUserWrapper( + // width: + // widget.isDesktop + // ? MediaQuery.of(context).size.width * 0.12 + // : null, + // isFocused: false, + // isDesktop: widget.isDesktop, + // child: SizedBox( + // height: 40, + // child: DropdownButtonFormField( + // // value: selectedGender, + // value: widget.isViewMode ? null : selectedGender, + // onChanged: + // widget.isViewMode + // ? null + // : (String? newValue) { + // setState(() { + // selectedGender = newValue; + // print("selectedGender - $selectedGender"); + // }); + // + // widget.onGenderChanged?.call(newValue); + // }, + // decoration: InputDecoration( + // border: InputBorder.none, + // enabled: !widget.isViewMode, // Disables input when in view mode + // ), + // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), + // items: [ + // DropdownMenuItem(value: "Male", child: Text("Male")), + // DropdownMenuItem(value: "Female", child: Text("Female")), + // ], + // hint: Text( + // selectedGender ?? "Select Gender", + // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), + // ), + // disabledHint: Text( + // selectedGender ?? "Select Gender", + // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), + // ), + // ), + // + // // child: DropdownSearch( + // // selectedItem: selectedGender, + // // // key: ValueKey(selectedGender), + // // popupProps: PopupProps.menu( + // // fit: FlexFit.loose, // Allows flexible height + // // constraints: BoxConstraints(maxHeight: 250), + // // + // // ), + // // items: ["Male", "Female", "Others"], + // // dropdownDecoratorProps: DropDownDecoratorProps( + // // dropdownSearchDecoration: InputDecoration( + // // border: InputBorder.none, + // // contentPadding: EdgeInsets.symmetric(horizontal: 1,), + // // ), + // // ), + // // dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item + // // alignment: Alignment.centerLeft, + // // child: Text( + // // selectedItem ?? "Select", + // // style: TextStyle(fontSize: 12), + // // ), + // // ), + // // onChanged: isViewMode + // // ? null + // // : (String? newValue) { + // // setState(() { + // // // Find the country_code based on selected country_name + // // selectedGender = newValue; + // // print("selectedGender - $selectedGender"); + // // // if (selectedCountry!.isNotEmpty) { + // // // errorMessages.remove("country_code"); + // // // } + // // }); + // // }, + // // + // // + // // ), + // ), + // ), + // ], + // ); + // } + Widget buildGenderField() { + List genderOptions = ["Male", "Female"]; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -844,9 +952,45 @@ class PersonalDetailsState extends State { isDesktop: widget.isDesktop, child: SizedBox( height: 40, - child: DropdownButtonFormField( - // value: selectedGender, - value: widget.isViewMode ? null : selectedGender, + child: DropdownSearch( + selectedItem: selectedGender, + enabled: !widget.isViewMode, + + popupProps: PopupProps.menu( + showSearchBox: false, // Set true if you want search + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 200), + itemBuilder: + (context, item, isSelected) => Container( + color: Colors.white, + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), + ), + items: genderOptions, + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Gender", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ), onChanged: widget.isViewMode ? null @@ -855,65 +999,9 @@ class PersonalDetailsState extends State { selectedGender = newValue; print("selectedGender - $selectedGender"); }); - widget.onGenderChanged?.call(newValue); }, - decoration: InputDecoration( - border: InputBorder.none, - enabled: !widget.isViewMode, // Disables input when in view mode - ), - style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), - items: [ - DropdownMenuItem(value: "Male", child: Text("Male")), - DropdownMenuItem(value: "Female", child: Text("Female")), - ], - hint: Text( - selectedGender ?? "Select Gender", - style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), - ), - disabledHint: Text( - selectedGender ?? "Select Gender", - style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), - ), ), - - // child: DropdownSearch( - // selectedItem: selectedGender, - // // key: ValueKey(selectedGender), - // popupProps: PopupProps.menu( - // fit: FlexFit.loose, // Allows flexible height - // constraints: BoxConstraints(maxHeight: 250), - // - // ), - // items: ["Male", "Female", "Others"], - // dropdownDecoratorProps: DropDownDecoratorProps( - // dropdownSearchDecoration: InputDecoration( - // border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(horizontal: 1,), - // ), - // ), - // dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item - // alignment: Alignment.centerLeft, - // child: Text( - // selectedItem ?? "Select", - // style: TextStyle(fontSize: 12), - // ), - // ), - // onChanged: isViewMode - // ? null - // : (String? newValue) { - // setState(() { - // // Find the country_code based on selected country_name - // selectedGender = newValue; - // print("selectedGender - $selectedGender"); - // // if (selectedCountry!.isNotEmpty) { - // // errorMessages.remove("country_code"); - // // } - // }); - // }, - // - // - // ), ), ), ], @@ -1014,7 +1102,7 @@ class PersonalDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Email", + "Email *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -1032,7 +1120,7 @@ class PersonalDetailsState extends State { controller: widget.controllers["email"], onChanged: (value) { _clearError("email"); - apiCheckDuplicate("Email", "email",value,widget.userIdApi); + apiCheckDuplicate("Email", "email", value, widget.userIdApi); }, enabled: !widget.isViewMode, decoration: InputDecoration( @@ -1064,7 +1152,7 @@ class PersonalDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Mobile Number", + "Mobile Number *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -1083,7 +1171,12 @@ class PersonalDetailsState extends State { enabled: !widget.isViewMode, onChanged: (value) { _clearError("mobile_no"); - apiCheckDuplicate("Mobile Number", "mobile_no",value,widget.userIdApi); + apiCheckDuplicate( + "Mobile Number", + "mobile_no", + value, + widget.userIdApi, + ); }, keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ @@ -1208,6 +1301,18 @@ class PersonalDetailsState extends State { popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality fit: FlexFit.loose, // Allows flexible height + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: + (context, item, isSelected) => Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), constraints: BoxConstraints(maxHeight: 200), searchFieldProps: TextFieldProps( decoration: InputDecoration( @@ -1323,12 +1428,79 @@ class PersonalDetailsState extends State { ); } + // Widget buildRoleOld() { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Role ", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74), + // ), + // ), + // SizedBox(height: 5), + // CustomTextFieldUserWrapper( + // isFocused: false, // Dropdown doesn't use focus + // isDesktop: widget.isDesktop, + // child: SizedBox( + // height: 45, // Set appropriate height + // child: DropdownButtonFormField( + // // value: widget.isViewMode ? null : selectedRole, + // value: selectedRole, + // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), + // decoration: InputDecoration( + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // horizontal: 10, + // ), // Proper padding + // ), + // onChanged: + // widget.isViewMode + // ? null + // : (newValue) { + // setState(() { + // selectedRole = newValue; + // isTravelAgent = selectedRole == "5"; + // // Pass the result back to parent + // widget.onUserTypeChanged?.call(isTravelAgent); + // }); + // widget.onRoleChanged?.call(newValue); + // }, + // items: + // apiRoleData?.map>((item) { + // return DropdownMenuItem( + // value: item['dropdown_key'], // ID as value + // child: Text(item['dropdown_value'] ?? "Select Role"), + // ); + // }).toList(), + // hint: Text("Select Role"), + // disabledHint: Text( + // selectedRole ?? "Select Role", + // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), + // ), + // ), + // ), + // ), + // ], + // ); + // } + Widget buildRole() { + // Map dropdown_key (ID) -> dropdown_value (Name) + Map roleMap = { + for (var item in apiRoleData ?? []) + item['dropdown_key'] as String: item['dropdown_value'] as String, + }; + + List roleNames = roleMap.values.toList(); + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Role ", + "Role", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -1337,44 +1509,73 @@ class PersonalDetailsState extends State { ), SizedBox(height: 5), CustomTextFieldUserWrapper( - isFocused: false, // Dropdown doesn't use focus + isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( - height: 45, // Set appropriate height - child: DropdownButtonFormField( - // value: widget.isViewMode ? null : selectedRole, - value: selectedRole, - style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - ), // Proper padding + height: 40, + child: DropdownSearch( + selectedItem: selectedRole != null ? roleMap[selectedRole] : null, + enabled: !widget.isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: + (context, item, isSelected) => Container( + color: Colors.white, + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Role...", + contentPadding: EdgeInsets.symmetric(horizontal: 10), + ), + ), ), + items: roleNames, + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Role", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ), onChanged: widget.isViewMode ? null - : (newValue) { + : (String? newValue) { + if (newValue == null) return; + final selectedKey = + roleMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; + setState(() { - selectedRole = newValue; - isTravelAgent = selectedRole == "5"; - // Pass the result back to parent - widget.onUserTypeChanged?.call(isTravelAgent); + selectedRole = selectedKey; + isTravelAgent = selectedKey == "5"; }); - widget.onRoleChanged?.call(newValue); + + widget.onUserTypeChanged?.call(isTravelAgent); + widget.onRoleChanged?.call(selectedKey); }, - items: - apiRoleData?.map>((item) { - return DropdownMenuItem( - value: item['dropdown_key'], // ID as value - child: Text(item['dropdown_value'] ?? "Select Role"), - ); - }).toList(), - hint: Text("Select Role"), - disabledHint: Text( - selectedRole ?? "Select Role", - style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), - ), ), ), ), @@ -1429,7 +1630,7 @@ class PersonalDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Password", + "Password *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, diff --git a/lib/Screens/userManagement/create_user/traveller_details.dart b/lib/Screens/userManagement/create_user/traveller_details.dart index b1e39de..c952059 100644 --- a/lib/Screens/userManagement/create_user/traveller_details.dart +++ b/lib/Screens/userManagement/create_user/traveller_details.dart @@ -33,7 +33,6 @@ class TravellerDetails extends StatefulWidget { final String? passportFileUrlFromApi; final String? userIdApi; - const TravellerDetails({ Key? key, required this.controllers, @@ -149,15 +148,24 @@ class TravellerDetailsState extends State { }); } - Future apiCheckDuplicate(String label, String field, String value, String? userId) async { + Future apiCheckDuplicate( + String label, + String field, + String value, + String? userId, + ) async { + var newField = ""; try { - if(field == "forex_card_num"){ - field = "forex_pre_paid_card_number"; - } - final response = await apiService.CheckDuplicate(label, field, value, userId); + final response = await apiService.CheckDuplicate( + label, + newField, + value, + userId, + ); if (response.isNotEmpty) { _clearError(field); - widget.errorMessages[field] = response['message'] ?? "$label already exists"; + widget.errorMessages[field] = + response['message'] ?? "$label already exists"; print("Duplicate found: ${response['message']}"); return; } else { @@ -705,6 +713,7 @@ class TravellerDetailsState extends State { Widget build(BuildContext context) { return Container( height: MediaQuery.of(context).size.height, + padding: const EdgeInsets.only(top: 8), decoration: BoxDecoration( border: Border( top: BorderSide( @@ -1107,7 +1116,13 @@ class TravellerDetailsState extends State { controller: controllers["passportNumber"], enabled: !widget.isViewMode, onChanged: (value) { - _clearError("last_name"); + _clearError("passport_number"); + apiCheckDuplicate( + "Passport Number", + "passport_number", + value, + widget.userIdApi, + ); }, decoration: InputDecoration( labelText: "Passport Number", @@ -1122,6 +1137,13 @@ class TravellerDetailsState extends State { ), ), ), + if (widget.errorMessages["passport_number"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + widget.errorMessages["passport_number"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], ], ); } @@ -1181,7 +1203,8 @@ class TravellerDetailsState extends State { _selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today) ? _selectedDateOfIssue! : today, - firstDate: today, + // firstDate: today, + firstDate: DateTime(1900), lastDate: DateTime(2100), ); @@ -1271,7 +1294,8 @@ class TravellerDetailsState extends State { _selectedDateOfExpiry!.isAfter(today) ? _selectedDateOfExpiry! : today, - firstDate: today, + // firstDate: today, + firstDate: DateTime(1900), lastDate: DateTime(2100), ); @@ -2330,8 +2354,13 @@ class TravellerDetailsState extends State { controller: controllers["forex_card_num"], enabled: !widget.isViewMode, onChanged: (value) { - _clearError("forex_card_num"); - apiCheckDuplicate("Forex Pre-Paid Card Number", "forex_card_num",value,widget.userIdApi); + _clearError("forex_pre_paid_card_number"); + apiCheckDuplicate( + "Forex Pre-Paid Card Number", + "forex_pre_paid_card_number", + value, + widget.userIdApi, + ); }, decoration: InputDecoration( labelText: "Card Number", @@ -2467,9 +2496,16 @@ class TravellerDetailsState extends State { height: 60, child: Center( child: Padding( - padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here + padding: EdgeInsets.only( + top: 20, + left: 8, + ), // 👈 Add top padding here child: IconButton( - icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + icon: Icon( + Icons.remove_circle_sharp, + color: Colors.red, + size: 15, + ), tooltip: 'Cancel the Details', onPressed: () { setState(() { @@ -2480,7 +2516,6 @@ class TravellerDetailsState extends State { ), ), ), - ], ) : Column( @@ -2491,7 +2526,11 @@ class TravellerDetailsState extends State { buildFrequentFlierInformation(entry), SizedBox(width: 15), IconButton( - icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + icon: Icon( + Icons.remove_circle_sharp, + color: Colors.red, + size: 15, + ), tooltip: 'Cancel the Details', onPressed: () { setState(() { @@ -2773,30 +2812,37 @@ class TravellerDetailsState extends State { child: widget.isDesktop ? Row( - 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); - }); - }, + 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.remove_circle_sharp, + color: Colors.red, + size: 15, + ), + tooltip: 'Cancel the Details', + onPressed: () { + setState(() { + removeHotelLoyaltyEntry(entry); + }); + }, + ), + ), ), ), - ), - ), - ], - ) + ], + ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -2804,7 +2850,11 @@ class TravellerDetailsState extends State { SizedBox(height: 8), buildHotelMembershipNum(entry), IconButton( - icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + icon: Icon( + Icons.remove_circle_sharp, + color: Colors.red, + size: 15, + ), tooltip: 'Cancel the Details', onPressed: () { setState(() { @@ -3035,9 +3085,16 @@ class TravellerDetailsState extends State { height: 60, child: Center( child: Padding( - padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here + padding: EdgeInsets.only( + top: 20, + left: 8, + ), // 👈 Add top padding here child: IconButton( - icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + icon: Icon( + Icons.remove_circle_sharp, + color: Colors.red, + size: 15, + ), tooltip: 'Cancel the Details', onPressed: () { setState(() { @@ -3061,7 +3118,11 @@ class TravellerDetailsState extends State { SizedBox(height: 8), buildVisaValidUpTo(entry), IconButton( - icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + icon: Icon( + Icons.remove_circle_sharp, + color: Colors.red, + size: 15, + ), tooltip: 'Cancel the Details', onPressed: () { setState(() { @@ -3187,6 +3248,116 @@ class TravellerDetailsState extends State { ); } + List purposeList = apiData?['visa_type_of_visa']; + + // Map for id => name + Map visaTypeMap = { + for (var item in purposeList) + item['dropdown_key'].toString(): item['dropdown_value'].toString(), + }; + + // Selected value from entry + String? selectedPurpose = entry['visa_type_id']?.toString(); + + if (!visaTypeMap.containsKey(selectedPurpose)) { + selectedPurpose = null; + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Visa Type", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldUserTravellerWrapper( + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.17 + : null, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: DropdownSearch( + selectedItem: visaTypeMap[selectedPurpose], + enabled: purposeList.isNotEmpty, + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + constraints: BoxConstraints(maxHeight: 200), + itemBuilder: + (context, item, isSelected) => Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Visa Type...", + hintStyle: GoogleFonts.poppins(fontSize: 11.5), + contentPadding: EdgeInsets.symmetric(horizontal: 10), + ), + ), + ), + items: visaTypeMap.values.toList(), + + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Visa Type", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ), + onChanged: (newValue) { + if (newValue == null) return; + + final selectedId = + visaTypeMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; + + setState(() { + entry['visa_type_id'] = selectedId; + }); + + print("Updating form data: visa_type_id -> $selectedId"); + }, + ), + ), + ), + ], + ); + } + + Widget buildVisaType3(entry) { + if (apiData == null || apiData?['visa_type_of_visa'] == null) { + return Center( + child: Transform.scale(scale: 0.5, child: CircularProgressIndicator()), + ); + } + // List purposeList = apiData?['visa_type_of_visa'] ?? []; // List purposeList = []; List purposeList = apiData?['visa_type_of_visa']; @@ -3402,8 +3573,9 @@ class TravellerDetailsState extends State { final pickedDate = await showDatePicker( context: context, - initialDate: initialDate, - firstDate: initialDate, + // initialDate: initialDate, + // firstDate: initialDate, + firstDate: DateTime(1900), lastDate: DateTime(2100), ); @@ -3490,8 +3662,9 @@ class TravellerDetailsState extends State { final pickedDate = await showDatePicker( context: context, - initialDate: initialDate, - firstDate: initialDate, + // initialDate: initialDate, + // firstDate: initialDate, + firstDate: DateTime(1900), lastDate: DateTime(2100), ); diff --git a/lib/app.dart b/lib/app.dart index ff0f116..988c9f1 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -// import 'package:flutter/rendering.dart'; +import 'package:flutter/rendering.dart'; import 'dart:html' as html; import 'package:frontend/config/apiUrl.dart'; // 1 newly added import 'package:frontend/services/apiService.dart'; @@ -31,7 +31,7 @@ class _MyAppState extends State { @override void initState() { super.initState(); - // SemanticsBinding.instance.ensureSemantics(); // ✅ Safe here + SemanticsBinding.instance.ensureSemantics(); // ✅ Safe here if (kIsWeb) { final uri = Uri.parse(html.window.location.href); if (uri.path == '/authredirection' && diff --git a/lib/config/apiUrl.dart b/lib/config/apiUrl.dart index b6b5695..145911c 100644 --- a/lib/config/apiUrl.dart +++ b/lib/config/apiUrl.dart @@ -1,3 +1,3 @@ //api url -const String apiUrl = 'http://apitest.tripapprovaltool.com'; +const String apiUrl = 'http://apitest.tripapprovaltool.com/tstat_be'; // const String apiUrl = 'https://uat.tripapprovaltool.com';