From 75221bf61fde4c782fef75d7349ddaeedf0178b7 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 8 Jun 2026 10:26:45 +0300 Subject: [PATCH 1/4] dashboard ui enhancement --- lib/dashboard_latest/dashboard_provider.dart | 5 +- .../widgets/request_category_fragment.dart | 114 ++++++++++-------- 2 files changed, 65 insertions(+), 54 deletions(-) diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index 84997ec0..55fb0f06 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -438,12 +438,13 @@ class CategoryTabs { static List getTabs({required UsersTypes userType, required BuildContext context}) { List tabs = []; if (userType == UsersTypes.engineer) { + // Order: 1-UnAssigned, 2-HighPriority, 3-Overdue, 4-Open, 5-InProgress, 6-Completed tabs.add(CategoryTabs(label: 'UnAssigned', tag: 1, id: 1, requestType: RequestTypeEnum.unassigned, icon: "dashboard/unassigned", iconColor: AppColor.unassignedStatus(context))); + tabs.add(CategoryTabs(label: context.translation.highPriority, tag: 3, id: 4, requestType: RequestTypeEnum.highPriority, icon: "high_priority", iconColor: AppColor.red30, isHighPriority: true)); + tabs.add(CategoryTabs(label: context.translation.overdue, tag: 3, id: 4, requestType: RequestTypeEnum.overdue, icon: "overdue", iconColor: AppColor.yellowIcon(context), isOverDue: true)); tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2, requestType: RequestTypeEnum.open, icon: "dashboard/open_icon", iconColor: AppColor.openColor)); tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3, requestType: RequestTypeEnum.inProgress, icon: "dashboard/inprogress_icon", iconColor: AppColor.primary10)); tabs.add(CategoryTabs(label: 'Completed', tag: 3, id: 4, requestType: RequestTypeEnum.completed, icon: "complete_request", iconColor: AppColor.completedColor)); - tabs.add(CategoryTabs(label: context.translation.overdue, tag: 3, id: 4, requestType: RequestTypeEnum.overdue, icon: "overdue", iconColor: AppColor.yellowIcon(context), isOverDue: true)); - tabs.add(CategoryTabs(label: context.translation.highPriority, tag: 3, id: 4, requestType: RequestTypeEnum.highPriority, icon: "high_priority", iconColor: AppColor.red30, isHighPriority: true)); return tabs; } diff --git a/lib/dashboard_latest/widgets/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart index 08adc55c..d362aec8 100644 --- a/lib/dashboard_latest/widgets/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -27,6 +27,7 @@ class _RequestCategoryFragmentState extends State with late AnimationController _animationController; late Animation _headerFadeAnimation; late Animation _headerSlideAnimation; + final ScrollController _scrollController = ScrollController(); @override void initState() { @@ -60,6 +61,7 @@ class _RequestCategoryFragmentState extends State with @override void dispose() { _animationController.dispose(); + _scrollController.dispose(); super.dispose(); } @@ -176,36 +178,41 @@ class _RequestCategoryFragmentState extends State with Widget getTabs({required BuildContext context, required DashBoardProvider requestsProvider, required UsersTypes? userType}) { List tabs = CategoryTabs.getTabs(userType: userType ?? UsersTypes.normal_user, context: context); - // SizedBox( - // height: 44 + (context.isTablet() ? 36 : 16).toScreenHeight, - // child: ListView.separated( - // scrollDirection: Axis.horizontal, - // separatorBuilder: (cxt, index) => 12.width, - // padding: const EdgeInsets.only(top: 6 + 16, left: 16, right: 16), - // itemBuilder: (cxt, index) { - // return TabButton( - // label: tabs[index].label, - // loading: requestsProvider.isDetailLoading, - // isSelected: requestsProvider.currentListIndex == index, - // count: requestsProvider.requestDetailList != null ? requestsProvider.requestDetailList!.count ?? 0 : 0, - // onPressed: () async { - // setState(() { - // isAscending = false; - // }); - // requestsProvider.currentListIndex = index; - // requestsProvider.resetRequestDataList(); - // requestsProvider.getRequestDetail(usersType: userType ?? UsersTypes.normal_user, showLoader: true, status: tabs[index].tag, tabId: tabs[index].id); - // }, - // ); - // }, - // itemCount: tabs.length), - // ); - return GridView.builder( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: context.isTablet() ? 5 : 4, childAspectRatio: 85 / 85, crossAxisSpacing: 8, mainAxisSpacing: 8), + // Calculate item width to show X.5 items (last visible item is exactly 50% visible) + final screenWidth = MediaQuery.of(context).size.width; + final isTablet = context.isTablet(); + + // Show 3.5 items on phone, 4.5 on tablet (perfect 50% peek) + final visibleItemsWithPeek = isTablet ? 4.5 : 3.5; + const spacing = 4.0; + const horizontalPadding = 16.0; + + // Calculate item width: divide available space by visible items including the half item + final availableWidth = screenWidth - (horizontalPadding * 2); + final fullItems = visibleItemsWithPeek.floor(); // 3 or 4 + final totalSpacing = (fullItems - 1) * spacing + (spacing * 0.5); // Full gaps + half gap for peek + final itemWidth = (availableWidth - totalSpacing) / visibleItemsWithPeek; + + // Check if scrolling is needed + final needsScrolling = tabs.length > fullItems; + + return SizedBox( + height: itemWidth + 16, // Height matches item width + bottom padding + child: _buildHorizontalScrollableList(tabs, requestsProvider, userType, itemWidth, needsScrolling), + ); + } + + // Horizontal scrollable list + Widget _buildHorizontalScrollableList(List tabs, DashBoardProvider requestsProvider, UsersTypes? userType, double itemWidth, bool needsScrolling) { + const leftPadding = 16.0; + const rightPadding = 16.0; // Equal padding - peek is created by item sizing + + return ListView.separated( + controller: _scrollController, + scrollDirection: Axis.horizontal, + separatorBuilder: (cxt, index) => const SizedBox(width: 4), + padding: const EdgeInsets.only(bottom: 16, left: leftPadding, right: rightPadding), itemBuilder: (cxt, index) { // Calculate staggered animation delay final delay = index * 0.08; @@ -221,7 +228,7 @@ class _RequestCategoryFragmentState extends State with ); final slideAnimation = Tween( - begin: const Offset(0.0, 0.3), + begin: const Offset(-0.3, 0.0), end: Offset.zero, ).animate( CurvedAnimation( @@ -238,28 +245,31 @@ class _RequestCategoryFragmentState extends State with opacity: animation, child: SlideTransition( position: slideAnimation, - child: RequestTypeButton( - label: tabs[index].label, - loading: requestsProvider.isDetailLoading && requestsProvider.currentListIndex == index, - isSelected: requestsProvider.currentListIndex == index, - count: requestsProvider.getStatusCount(tabs[index].requestType), - onPressed: () async { - setState(() { - isAscending = false; - }); - requestsProvider.currentListIndex = index; - 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, - ); - }, - icon: tabs[index].icon, - iconColor: tabs[index].iconColor, + child: SizedBox( + width: itemWidth, // Use responsive width + child: RequestTypeButton( + label: tabs[index].label, + loading: requestsProvider.isDetailLoading && requestsProvider.currentListIndex == index, + isSelected: requestsProvider.currentListIndex == index, + count: requestsProvider.getStatusCount(tabs[index].requestType), + onPressed: () async { + setState(() { + isAscending = false; + }); + requestsProvider.currentListIndex = index; + 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, + ); + }, + icon: tabs[index].icon, + iconColor: tabs[index].iconColor, + ), ), ), ); From 164daa7d58d073ca5af9cc7027e74dae95603234 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 17 Jun 2026 14:33:12 +0300 Subject: [PATCH 2/4] dashboard multiple filter cont --- lib/dashboard_latest/dashboard_provider.dart | 26 ++- lib/dashboard_latest/dashboard_view.dart | 29 +++- .../widgets/app_bar_widget.dart | 155 +++++++++--------- .../widgets/request_category_fragment.dart | 150 ++++++++++------- 4 files changed, 210 insertions(+), 150 deletions(-) diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index 55fb0f06..de306bfb 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -3,8 +3,10 @@ import 'dart:developer'; import 'package:flutter/widgets.dart'; import 'package:http/http.dart'; +import 'package:provider/provider.dart'; import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/controllers/providers/api/all_requests_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/enum_extensions.dart'; import 'package:test_sa/models/enums/user_types.dart'; @@ -45,12 +47,22 @@ class DashBoardProvider extends ChangeNotifier { notifyListeners(); } - int _currentListIndex = 0; + List listIndex = []; - int get currentListIndex => _currentListIndex; + int? _currentListIndex; - set currentListIndex(int value) { + int? get currentListIndex => _currentListIndex; + + set currentListIndex(int? value) { _currentListIndex = value; + if (value == null) { + listIndex.clear(); + } + else if (listIndex.contains(value)) { + listIndex.remove(value); + } else { + listIndex.add(value); + } notifyListeners(); } @@ -114,7 +126,13 @@ class DashBoardProvider extends ChangeNotifier { setTabs(userType: userType, context: context); getDashBoardCount(usersType: userType); resetRequestDataList(); - getRequestDetail(usersType: userType, status: tabs[currentListIndex].tag, tabId: tabs[currentListIndex].id); + if (listIndex.isEmpty) { + Provider.of(context, listen: false) + ..reset() + ..getAllRequests(context); + } else { + getRequestDetail(usersType: userType, status: tabs[currentListIndex!].tag, tabId: tabs[currentListIndex!].id); + } // notifyListeners(); } diff --git a/lib/dashboard_latest/dashboard_view.dart b/lib/dashboard_latest/dashboard_view.dart index f5f0abfe..88db0502 100644 --- a/lib/dashboard_latest/dashboard_view.dart +++ b/lib/dashboard_latest/dashboard_view.dart @@ -6,6 +6,7 @@ import 'package:nfc_manager/nfc_manager.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/notification/firebase_notification_manger.dart'; import 'package:test_sa/controllers/notification/notification_manger.dart'; +import 'package:test_sa/controllers/providers/api/all_requests_provider.dart'; import 'package:test_sa/controllers/providers/api/notifications_provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; @@ -57,13 +58,17 @@ class _DashboardViewState extends State { _dashBoardProvider.setTabs(userType: userProvider.user!.type!, context: context); _dashBoardProvider.getDashBoardCount(usersType: userProvider.user!.type!); _dashBoardProvider.resetRequestDataList(); - _dashBoardProvider.currentListIndex = 0; - _dashBoardProvider.getRequestDetail( - usersType: userProvider.user!.type!, - status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].tag, - tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].id, - isHighPriority: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].isHighPriority, - isOverdue: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].isOverDue); + _dashBoardProvider.currentListIndex = null; + Provider.of(context, listen: false) + ..reset() + ..getAllRequests(context); + + // _dashBoardProvider.getRequestDetail( + // usersType: userProvider.user!.type!, + // status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].tag, + // tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].id, + // isHighPriority: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].isHighPriority, + // isOverdue: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].isOverDue); } void getInitialData() { @@ -94,8 +99,14 @@ class _DashboardViewState extends State { _scrollController.addListener(() async { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !_dashBoardProvider.isDetailLoading) { _dashBoardProvider.pageNum = _dashBoardProvider.pageNum + 1; - _dashBoardProvider.getRequestDetail( - usersType: user.type!, showLoader: false, status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].tag, tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].id); + if (_dashBoardProvider.currentListIndex == null) { + } else { + _dashBoardProvider.getRequestDetail( + usersType: user.type!, + showLoader: false, + status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].tag, + tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].id); + } } }); } diff --git a/lib/dashboard_latest/widgets/app_bar_widget.dart b/lib/dashboard_latest/widgets/app_bar_widget.dart index f130d70c..c2865647 100644 --- a/lib/dashboard_latest/widgets/app_bar_widget.dart +++ b/lib/dashboard_latest/widgets/app_bar_widget.dart @@ -157,84 +157,87 @@ class _AppBarWidgetState extends State with TickerProviderStateMix child: FadeTransition( opacity: _rightFadeAnimation, child: Consumer(builder: (context, settingProvider, child) { - return MenuAnchor( - style: MenuStyle( - backgroundColor: WidgetStateProperty.all(AppColor.background(context)), - shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: const BorderRadius.all(Radius.circular(20.0)), - side: BorderSide(color: AppColor.borderColor(context), width: 1), - ), - ), - padding: WidgetStateProperty.all( - EdgeInsets.symmetric( - horizontal: 0.toScreenWidth, - vertical: 8.toScreenHeight, + return MenuAnchor( + style: MenuStyle( + backgroundColor: WidgetStateProperty.all(AppColor.background(context)), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(20.0)), + side: BorderSide(color: AppColor.borderColor(context), width: 1), + ), + ), + padding: WidgetStateProperty.all( + EdgeInsets.symmetric( + horizontal: 0.toScreenWidth, + vertical: 8.toScreenHeight, + ), + ), + elevation: WidgetStateProperty.all(0), ), - ), - elevation: WidgetStateProperty.all(0), - ), - menuChildren: Provider.of(context, listen: false).user?.assetGroups?.map((value) { - return MenuItemButton( - onPressed: () async { - if (settingProvider.assetGroup != value) { - AssetGroup? tempGroup = ApiManager.instance.assetGroup; - ApiManager.instance.assetGroup = value; + menuChildren: Provider.of(context, listen: false).user?.assetGroups?.map((value) { + return MenuItemButton( + onPressed: () async { + if (settingProvider.assetGroup != value) { + AssetGroup? tempGroup = ApiManager.instance.assetGroup; + ApiManager.instance.assetGroup = value; - Utils.showLoading(context); - bool isSuccess = await context.userProvider.userChangeAssetGroup(); - Utils.hideLoading(context); - if (isSuccess) { - await context.userProvider.getMobilePermissionByUser(); - settingProvider.setAssetGroup(value); - WidgetsBinding.instance.addPostFrameCallback((_) { - context.settingProvider.setUser(ApiManager.instance.user!); - context.userProvider.setUser(ApiManager.instance.user!); - DashBoardProvider dashBoardProvider = Provider.of(context, listen: false); - dashBoardProvider.setTabs(userType: settingProvider.user!.type!, context: context); - dashBoardProvider.getDashBoardCount(usersType: settingProvider.user!.type!); - dashBoardProvider.resetRequestDataList(); - dashBoardProvider.getRequestDetail(usersType: settingProvider.user!.type!, status: dashBoardProvider.tabs[dashBoardProvider.currentListIndex].tag); - }); - } else { - ApiManager.instance.assetGroup = tempGroup; - context.showConfirmDialog("Failed, Please Try again"); - } - } - }, - child: Text(value.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))), - ); - }).toList() ?? - [], - builder: (_, MenuController controller, Widget? child) { - return Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - color: AppColor.assetGroupBackground(context), - border: Border.all(color: AppColor.white936.withValues(alpha: .03), width: 1), - boxShadow: [ - BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 4, offset: const Offset(0, 0), spreadRadius: 0), - ], - ), - child: Row( - mainAxisSize: MainAxisSize.min, - spacing: 16, - children: [ - Text(settingProvider.assetGroup?.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))), - Icon(Icons.keyboard_arrow_down, color: AppColor.headingTextColor(context)), - ], - ), - ).onPress(() { - if (controller.isOpen) { - controller.close(); - } else { - controller.open(); - } - }); - }, - ); - }), + Utils.showLoading(context); + bool isSuccess = await context.userProvider.userChangeAssetGroup(); + Utils.hideLoading(context); + if (isSuccess) { + await context.userProvider.getMobilePermissionByUser(); + settingProvider.setAssetGroup(value); + WidgetsBinding.instance.addPostFrameCallback((_) { + context.settingProvider.setUser(ApiManager.instance.user!); + context.userProvider.setUser(ApiManager.instance.user!); + DashBoardProvider dashBoardProvider = Provider.of(context, listen: false); + dashBoardProvider.setTabs(userType: settingProvider.user!.type!, context: context); + dashBoardProvider.getDashBoardCount(usersType: settingProvider.user!.type!); + dashBoardProvider.resetRequestDataList(); + if (dashBoardProvider.currentListIndex == null) { + } else { + dashBoardProvider.getRequestDetail(usersType: settingProvider.user!.type!, status: dashBoardProvider.tabs[dashBoardProvider.currentListIndex!].tag); + } + }); + } else { + ApiManager.instance.assetGroup = tempGroup; + context.showConfirmDialog("Failed, Please Try again"); + } + } + }, + child: Text(value.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))), + ); + }).toList() ?? + [], + builder: (_, MenuController controller, Widget? child) { + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: AppColor.assetGroupBackground(context), + border: Border.all(color: AppColor.white936.withValues(alpha: .03), width: 1), + boxShadow: [ + BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 4, offset: const Offset(0, 0), spreadRadius: 0), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + spacing: 16, + children: [ + Text(settingProvider.assetGroup?.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))), + Icon(Icons.keyboard_arrow_down, color: AppColor.headingTextColor(context)), + ], + ), + ).onPress(() { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }); + }, + ); + }), ), ), 16.width, diff --git a/lib/dashboard_latest/widgets/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart index d362aec8..1b8e99e0 100644 --- a/lib/dashboard_latest/widgets/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/all_requests_provider.dart'; 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'; @@ -15,6 +16,8 @@ import 'package:test_sa/new_views/common_widgets/tab_button.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; +import '../../new_views/pages/land_page/widgets/request_item_view_list.dart'; + class RequestCategoryFragment extends StatefulWidget { const RequestCategoryFragment({Key? key}) : super(key: key); @@ -75,13 +78,16 @@ class _RequestCategoryFragmentState extends State with // Reload data with new sort order dashboardProvider.resetRequestDataList(); - dashboardProvider.getRequestDetail( - usersType: userType ?? UsersTypes.normal_user, - showLoader: true, - status: tabs[dashboardProvider.currentListIndex].tag, - tabId: tabs[dashboardProvider.currentListIndex].id, - isAscending: isAscending, - ); + if (dashboardProvider.currentListIndex == null) { + } else { + dashboardProvider.getRequestDetail( + usersType: userType ?? UsersTypes.normal_user, + showLoader: true, + status: tabs[dashboardProvider.currentListIndex!].tag, + tabId: tabs[dashboardProvider.currentListIndex!].id, + isAscending: isAscending, + ); + } } @override @@ -104,47 +110,62 @@ class _RequestCategoryFragmentState extends State with opacity: _headerFadeAnimation, child: SlideTransition( position: _headerSlideAnimation, - child: !isNurse && dashboardProvider.tabs.isNotEmpty && dashboardProvider.tabs[dashboardProvider.currentListIndex].tag == 0 - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // context.translation.allWorkOrder.heading4(context), - // GestureDetector( - // onTap: () { - // dashboardProvider.isShowDashBoardCalendar = !dashboardProvider.isShowDashBoardCalendar; - // }, - // child: dashboardProvider.isShowDashBoardCalendar ? AppAsset.listIcon.toSvgAsset() : AppAsset.calendarIcon.toSvgAsset()) - // ], - // ), - dashboardProvider.isShowDashBoardCalendar - ? Padding( - padding: EdgeInsets.only(top: 12.toScreenHeight), - child: const WeeklyCalendarFragment(), - ) - : const SizedBox(), - ], - ).paddingOnly(start: 14, end: 12) - : Row( - spacing: 16, - children: [ - Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))).expanded, - // todo commenting this code, for FM - // if (!isFMSEngineer) - // 'search'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() { - // Navigator.of(context).pushNamed('/cleaner_search'); - // }), - Transform.flip( - flipY: isAscending, - child: 'dashboard/sort_descending'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() { - _toggleSort(dashboardProvider, user?.type); - }), - ) - ], - ).paddingOnly(start: 16, end: 16), + child: + // !isNurse && dashboardProvider.tabs.isNotEmpty + // //&& dashboardProvider.tabs[dashboardProvider.currentListIndex].tag == 0 + // ? Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))), + // // Row( + // // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // // children: [ + // // context.translation.allWorkOrder.heading4(context), + // // GestureDetector( + // // onTap: () { + // // dashboardProvider.isShowDashBoardCalendar = !dashboardProvider.isShowDashBoardCalendar; + // // }, + // // child: dashboardProvider.isShowDashBoardCalendar ? AppAsset.listIcon.toSvgAsset() : AppAsset.calendarIcon.toSvgAsset()) + // // ], + // // ), + // dashboardProvider.isShowDashBoardCalendar + // ? Padding( + // padding: EdgeInsets.only(top: 12.toScreenHeight), + // child: const WeeklyCalendarFragment(), + // ) + // : const SizedBox(), + // ], + // ).paddingOnly(start: 14, end: 12) + // : + Row( + spacing: 16, + children: [ + Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))).expanded, + // todo commenting this code, for FM + // if (!isFMSEngineer) + // 'search'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() { + // Navigator.of(context).pushNamed('/cleaner_search'); + // }), + + if (dashboardProvider.listIndex.isNotEmpty) + Text( + 'Clear Filters', + style: AppTextStyles.bodyText2.copyWith(color: AppColor.red30), + ).onPress(() { + dashboardProvider.resetRequestDataList(); + dashboardProvider.currentListIndex = null; + Provider.of(context, listen: false) + ..reset() + ..getAllRequests(context); + }), + Transform.flip( + flipY: isAscending, + child: 'dashboard/sort_descending'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() { + _toggleSort(dashboardProvider, user?.type); + }), + ) + ], + ).paddingOnly(start: 16, end: 16), ), ), @@ -159,19 +180,26 @@ class _RequestCategoryFragmentState extends State with // ], // ).paddingOnly(start: 16, end: 16) // : context.translation.allWorkOrder.heading4(context).paddingOnly(start: 16, end: 16), - - 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.currentListIndex == null) + ? Consumer(builder: (context, snapshot, _) { + return RequestItemViewList( + snapshot.allRequestsAndCount?.requestsDetails ?? [], + snapshot.isAllLoading, + 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), ]); }); } From 7aa6637cbabf2b39ace2aec23cbd77159106da91 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 18 Jun 2026 16:14:18 +0300 Subject: [PATCH 3/4] 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); } } - - From 2a49a0dbcb019b674ff8fd6f78477b30284baa39 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Tue, 23 Jun 2026 15:06:15 +0300 Subject: [PATCH 4/4] bug 2004, sorting fixed with dashboard multi filter. --- lib/controllers/providers/api/all_requests_provider.dart | 2 ++ lib/dashboard_latest/dashboard_provider.dart | 1 + lib/dashboard_latest/widgets/request_category_fragment.dart | 5 +++-- lib/views/widgets/dialogs/dialog.dart | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index 61806a60..894d7089 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -727,6 +727,7 @@ class AllRequestsProvider extends ChangeNotifier { List? statusTransaction, List? priority, List? displayData, + bool? isAscending, }) async { if (isMultiFilterLoading == true) return -2; if (_allRequestsAndCount == null || pagination) { @@ -743,6 +744,7 @@ class AllRequestsProvider extends ChangeNotifier { body["pageNumber"] = pageNum++; body["pageSize"] = pageItemNumber; } + if (isAscending != null) body["isAscending"] = isAscending; response = await ApiManager.instance.post(URLs.getAllRequestsAndCount, body: body); stateCode = response.statusCode; diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index 90c651ae..c449f25d 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -347,6 +347,7 @@ class DashBoardProvider extends ChangeNotifier { statusTransaction: statusTransaction, priority: priority, displayData: displayData, + isAscending: isAscending, ); } diff --git a/lib/dashboard_latest/widgets/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart index 472151c6..9798882b 100644 --- a/lib/dashboard_latest/widgets/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -79,10 +79,11 @@ class _RequestCategoryFragmentState extends State with // Reload data with new sort order dashboardProvider.resetRequestDataList(); + Provider.of(context, listen: false).reset(); if (dashboardProvider.listIndex.isEmpty) { - // No category selected - do nothing or could reload default - return; + // No category selected - reload default data with sorting + Provider.of(context, listen: false).getAllRequests(context, isAscending: isAscending); } else if (dashboardProvider.listIndex.length == 1 && dashboardProvider.isUnassignedTab(dashboardProvider.listIndex[0])) { // UnAssigned only - check by requestType, not by index int index = dashboardProvider.listIndex[0]; diff --git a/lib/views/widgets/dialogs/dialog.dart b/lib/views/widgets/dialogs/dialog.dart index 0cdfa87c..e7132eb1 100644 --- a/lib/views/widgets/dialogs/dialog.dart +++ b/lib/views/widgets/dialogs/dialog.dart @@ -37,6 +37,7 @@ class AAlertDialog extends StatelessWidget { ) : AlertDialog( backgroundColor: AppColor.background(context), + actionsPadding: const EdgeInsets.only(bottom: 12, left: 24, right: 24), title: title != null ? Text(title!) : null, content: content != null ? Text(content!) : null, // contentTextStyle: AppTextStyles.bodyText.copyWith(color: (context.isDark ? AppColor.neutral30 : AppColor.neutral50)),