From 7aa6637cbabf2b39ace2aec23cbd77159106da91 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 18 Jun 2026 16:14:18 +0300 Subject: [PATCH] dashboard multiple filters added. --- .../providers/api/all_requests_provider.dart | 52 ++++++++ lib/dashboard_latest/dashboard_provider.dart | 118 +++++++++++++++++- .../widgets/request_category_fragment.dart | 117 +++++++++++++---- .../widgets/request_item_view_list.dart | 6 +- 4 files changed, 258 insertions(+), 35 deletions(-) diff --git a/lib/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index 24f62800..61806a60 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -21,6 +21,7 @@ class AllRequestsProvider extends ChangeNotifier { bool isHighPriorityLoading = false; bool isCalendarLoading = false; bool isLoading = false; + bool isMultiFilterLoading = false; bool _isFilterRequestLoading = false; bool _isRequestCategoryLoading = false; @@ -720,6 +721,57 @@ class AllRequestsProvider extends ChangeNotifier { notifyListeners(); } + Future getDashboardMultifilterRequests( + BuildContext context, { + bool pagination = false, + List? statusTransaction, + List? priority, + List? displayData, + }) async { + if (isMultiFilterLoading == true) return -2; + if (_allRequestsAndCount == null || pagination) { + isMultiFilterLoading = true; + notifyListeners(); + } + Response response; + try { + Map body = {"typeTransaction": getStatues(context), "statusTransaction": statusTransaction ?? [], "priority": priority ?? [], "displayData": displayData ?? []}; + if (pagination) { + body["pageNumber"] = 0; + body["pageSize"] = 0; + } else { + body["pageNumber"] = pageNum++; + body["pageSize"] = pageItemNumber; + } + response = await ApiManager.instance.post(URLs.getAllRequestsAndCount, body: body); + + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + if (_allRequestsAndCount == null) { + _allRequestsAndCount = (await AllRequestsAndCount.fromJson(json.decode(response.body)["data"][0])); + } else { + _allRequestsAndCount!.requestsDetails!.addAll((await AllRequestsAndCount.fromJson(json.decode(response.body)["data"][0])).requestsDetails!); + } + if (_allRequestsAndCount!.requestsDetails!.length >= pageItemNumber) { + nextPage = true; + } else { + nextPage = false; + } + notifyListeners(); + } else { + _allRequestsAndCount = null; + } + isMultiFilterLoading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + isMultiFilterLoading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } + // Future getCloseRequests() async { // if (isCloseLoading == true) return -2; // isCloseLoading = true; diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index de306bfb..90c651ae 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -53,15 +53,52 @@ class DashBoardProvider extends ChangeNotifier { int? get currentListIndex => _currentListIndex; + // Helper method to check if a tab index is UnAssigned + bool isUnassignedTab(int index) { + if (index >= 0 && index < tabs.length) { + return tabs[index].requestType == RequestTypeEnum.unassigned; + } + return false; + } + + // Helper method to get the UnAssigned tab index (returns -1 if not found) + int getUnassignedTabIndex() { + for (int i = 0; i < tabs.length; i++) { + if (tabs[i].requestType == RequestTypeEnum.unassigned) { + return i; + } + } + return -1; + } + set currentListIndex(int? value) { _currentListIndex = value; if (value == null) { listIndex.clear(); - } - else if (listIndex.contains(value)) { - listIndex.remove(value); } else { - listIndex.add(value); + // Check if the selected tab is UnAssigned by its requestType + bool isUnassignedClicked = isUnassignedTab(value); + int unassignedIndex = getUnassignedTabIndex(); + bool hasUnassignedSelected = unassignedIndex != -1 && listIndex.contains(unassignedIndex); + + if (isUnassignedClicked) { + // UnAssigned is exclusive - clear all and select only UnAssigned + listIndex.clear(); + listIndex.add(value); + } else { + // Other category clicked + if (hasUnassignedSelected) { + // If UnAssigned was selected, remove it first + listIndex.remove(unassignedIndex); + } + + // Toggle the selected category + if (listIndex.contains(value)) { + listIndex.remove(value); + } else { + listIndex.add(value); + } + } } notifyListeners(); } @@ -130,8 +167,12 @@ class DashBoardProvider extends ChangeNotifier { Provider.of(context, listen: false) ..reset() ..getAllRequests(context); + } else if (listIndex.length == 1 && isUnassignedTab(listIndex[0])) { + // UnAssigned only - check by requestType, not by index + getRequestDetail(usersType: userType, status: tabs[listIndex[0]].tag, tabId: tabs[listIndex[0]].id); } else { - getRequestDetail(usersType: userType, status: tabs[currentListIndex!].tag, tabId: tabs[currentListIndex!].id); + // Multiple categories selected + getMultiSelectCategoryRequests(usersType: userType, context: context); } // notifyListeners(); } @@ -242,6 +283,73 @@ class DashBoardProvider extends ChangeNotifier { getRequestDetail(showLoader: showLoader, usersType: usersType, status: 4); } + void getMultiSelectCategoryRequests({ + bool showLoader = true, + required UsersTypes usersType, + required BuildContext context, + bool? isAscending, + }) { + // Build filter arrays based on selected tabs + List statusTransaction = []; + List priority = []; + List displayData = []; + + for (int index in listIndex) { + if (index >= 0 && index < tabs.length) { + CategoryTabs tab = tabs[index]; + + // High Priority + if (tab.isHighPriority) { + if (!priority.contains(1)) priority.add(1); + } + + // Overdue + if (tab.isOverDue) { + if (!displayData.contains(1)) displayData.add(1); + } + + // Status categories (Open, InProgress, Completed, Closed) + if (!tab.isHighPriority && !tab.isOverDue && tab.requestType != RequestTypeEnum.unassigned) { + // Map requestType to status value + switch (tab.requestType) { + case RequestTypeEnum.open: + if (!statusTransaction.contains(1)) statusTransaction.add(1); + break; + case RequestTypeEnum.inProgress: + if (!statusTransaction.contains(2)) statusTransaction.add(2); + break; + case RequestTypeEnum.completed: + if (!statusTransaction.contains(4)) statusTransaction.add(4); + break; + case RequestTypeEnum.closed: + if (!statusTransaction.contains(4)) statusTransaction.add(4); + break; + case RequestTypeEnum.canceled: + if (!statusTransaction.contains(5)) statusTransaction.add(5); + break; + case RequestTypeEnum.pendingAcknowledgment: + if (!statusTransaction.contains(3)) statusTransaction.add(3); + break; + case RequestTypeEnum.unassigned: + case RequestTypeEnum.highPriority: + case RequestTypeEnum.overdue: + // These are handled separately above + break; + } + } + } + } + + // Call the multi-filter API + Provider.of(context, listen: false).getDashboardMultifilterRequests( + context, + pagination: false, + statusTransaction: statusTransaction, + priority: priority, + displayData: displayData, + ); + } + Future makeSwipe({required Swipe model}) async { isLoading = true; SwipeModel swipeResponse = SwipeModel(data: false, message: '', responseCode: 0, isSuccess: false); diff --git a/lib/dashboard_latest/widgets/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart index 1b8e99e0..472151c6 100644 --- a/lib/dashboard_latest/widgets/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -5,6 +5,7 @@ import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; import 'package:test_sa/dashboard_latest/widgets/request_category_list.dart'; import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/enum_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; @@ -78,13 +79,26 @@ class _RequestCategoryFragmentState extends State with // Reload data with new sort order dashboardProvider.resetRequestDataList(); - if (dashboardProvider.currentListIndex == null) { - } else { + + if (dashboardProvider.listIndex.isEmpty) { + // No category selected - do nothing or could reload default + return; + } else if (dashboardProvider.listIndex.length == 1 && dashboardProvider.isUnassignedTab(dashboardProvider.listIndex[0])) { + // UnAssigned only - check by requestType, not by index + int index = dashboardProvider.listIndex[0]; dashboardProvider.getRequestDetail( usersType: userType ?? UsersTypes.normal_user, showLoader: true, - status: tabs[dashboardProvider.currentListIndex!].tag, - tabId: tabs[dashboardProvider.currentListIndex!].id, + status: tabs[index].tag, + tabId: tabs[index].id, + isAscending: isAscending, + ); + } else { + // Multiple categories - call multi-filter API + dashboardProvider.getMultiSelectCategoryRequests( + usersType: userType ?? UsersTypes.normal_user, + showLoader: true, + context: context, isAscending: isAscending, ); } @@ -180,26 +194,51 @@ class _RequestCategoryFragmentState extends State with // ], // ).paddingOnly(start: 16, end: 16) // : context.translation.allWorkOrder.heading4(context).paddingOnly(start: 16, end: 16), - (dashboardProvider.currentListIndex == null) + (dashboardProvider.listIndex.isEmpty) ? Consumer(builder: (context, snapshot, _) { return RequestItemViewList( snapshot.allRequestsAndCount?.requestsDetails ?? [], snapshot.isAllLoading, + disableScroll: true, key: ValueKey('request_list_'), // Key changes on every tab click ); }) - : dashboardProvider.isDetailLoading - ? Column( - mainAxisSize: MainAxisSize.max, - children: List.generate(3, (index) { - return Padding( - padding: EdgeInsets.symmetric(vertical: 10.toScreenHeight, horizontal: 0), - child: const SizedBox().toRequestShimmer(context, dashboardProvider.isDetailLoading), - ); - })).paddingOnly(start: 16, end: 16, top: 16) - : (dashboardProvider.requestDetailList == null || dashboardProvider.requestDetailList?.data?.isEmpty == true) - ? const NoDataFound().paddingOnly(top: 80).center - : RequestCategoryList(dashboardProvider.requestDetailList?.data ?? [], dashboardProvider.isDetailLoading, dashboardProvider.requestDetailList?.totalRows ?? 0), + : dashboardProvider.listIndex.length == 1 && dashboardProvider.isUnassignedTab(dashboardProvider.listIndex[0]) + // UnAssigned selected (check by requestType, not by index) - show requestDetailList + ? dashboardProvider.isDetailLoading + ? Column( + mainAxisSize: MainAxisSize.max, + children: List.generate(3, (index) { + return Padding( + padding: EdgeInsets.symmetric(vertical: 10.toScreenHeight, horizontal: 0), + child: const SizedBox().toRequestShimmer(context, dashboardProvider.isDetailLoading), + ); + })).paddingOnly(start: 16, end: 16, top: 16) + : (dashboardProvider.requestDetailList == null || dashboardProvider.requestDetailList?.data?.isEmpty == true) + ? const NoDataFound().paddingOnly(top: 80).center + : RequestCategoryList(dashboardProvider.requestDetailList?.data ?? [], dashboardProvider.isDetailLoading, dashboardProvider.requestDetailList?.totalRows ?? 0) + // Multi-select categories - show allRequestsAndCount + : Consumer(builder: (context, snapshot, _) { + if (snapshot.isMultiFilterLoading) { + return Column( + mainAxisSize: MainAxisSize.max, + children: List.generate(3, (index) { + return Padding( + padding: EdgeInsets.symmetric(vertical: 10.toScreenHeight, horizontal: 0), + child: const SizedBox().toRequestShimmer(context, snapshot.isMultiFilterLoading), + ); + })).paddingOnly(start: 16, end: 16, top: 16); + } + if (snapshot.allRequestsAndCount == null || snapshot.allRequestsAndCount?.requestsDetails?.isEmpty == true) { + return const NoDataFound().paddingOnly(top: 80).center; + } + return RequestItemViewList( + snapshot.allRequestsAndCount?.requestsDetails ?? [], + snapshot.isMultiFilterLoading, + disableScroll: true, + key: ValueKey('multi_filter_list_${dashboardProvider.listIndex.join("_")}'), + ); + }), ]); }); } @@ -277,23 +316,47 @@ class _RequestCategoryFragmentState extends State with width: itemWidth, // Use responsive width child: RequestTypeButton( label: tabs[index].label, - loading: requestsProvider.isDetailLoading && requestsProvider.currentListIndex == index, - isSelected: requestsProvider.currentListIndex == index, + loading: requestsProvider.isDetailLoading && requestsProvider.listIndex.contains(index), + isSelected: requestsProvider.listIndex.contains(index), count: requestsProvider.getStatusCount(tabs[index].requestType), onPressed: () async { setState(() { isAscending = false; }); + + // Check if this is UnAssigned tab (index 0 for engineer) + bool isUnassignedTab = tabs[index].requestType == RequestTypeEnum.unassigned; + + // Update selection state requestsProvider.currentListIndex = index; + + // Reset data requestsProvider.resetRequestDataList(); - requestsProvider.getRequestDetail( - usersType: userType ?? UsersTypes.normal_user, - showLoader: true, - status: tabs[index].tag, - tabId: tabs[index].id, - isHighPriority: tabs[index].isHighPriority, - isOverdue: tabs[index].isOverDue, - ); + Provider.of(context, listen: false).reset(); + + // If deselected (listIndex is now empty), show default data + if (requestsProvider.listIndex.isEmpty) { + Provider.of(context, listen: false).getAllRequests(context); + return; + } + + // If UnAssigned is selected, call its separate API + if (isUnassignedTab) { + requestsProvider.getRequestDetail( + usersType: userType ?? UsersTypes.normal_user, + showLoader: true, + status: tabs[index].tag, + tabId: tabs[index].id, + ); + } else { + // Multiple categories selected - call multi-filter API + requestsProvider.getMultiSelectCategoryRequests( + usersType: userType ?? UsersTypes.normal_user, + showLoader: true, + context: context, + isAscending: isAscending, + ); + } }, icon: tabs[index].icon, iconColor: tabs[index].iconColor, diff --git a/lib/new_views/pages/land_page/widgets/request_item_view_list.dart b/lib/new_views/pages/land_page/widgets/request_item_view_list.dart index 6a52e813..9d7dbed8 100644 --- a/lib/new_views/pages/land_page/widgets/request_item_view_list.dart +++ b/lib/new_views/pages/land_page/widgets/request_item_view_list.dart @@ -25,8 +25,9 @@ import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; class RequestItemViewList extends StatefulWidget { final List list; final bool isLoading; + final bool disableScroll; - const RequestItemViewList(this.list, this.isLoading, {Key? key}) : super(key: key); + const RequestItemViewList(this.list, this.isLoading, {Key? key, this.disableScroll = false}) : super(key: key); @override State createState() => _RequestItemViewListState(); @@ -58,6 +59,7 @@ class _RequestItemViewListState extends State with SingleTi : ListView.separated( padding: const EdgeInsets.all(16), shrinkWrap: true, + physics: widget.disableScroll ? const NeverScrollableScrollPhysics() : null, itemBuilder: (cxt, index) { if (widget.isLoading) return const SizedBox().toRequestShimmer(cxt, widget.isLoading); @@ -158,5 +160,3 @@ class _RequestItemViewListState extends State with SingleTi itemCount: widget.isLoading ? 6 : widget.list.length); } } - -