Merge branch 'refs/heads/export_module_ui_ux_enhancement' into export_module_ui_ux_enhancement_signup

export_module_ui_ux_enhancement_signup
Sikander Saleem 2 weeks ago
commit 7046e1b4cf

@ -21,6 +21,7 @@ class AllRequestsProvider extends ChangeNotifier {
bool isHighPriorityLoading = false; bool isHighPriorityLoading = false;
bool isCalendarLoading = false; bool isCalendarLoading = false;
bool isLoading = false; bool isLoading = false;
bool isMultiFilterLoading = false;
bool _isFilterRequestLoading = false; bool _isFilterRequestLoading = false;
bool _isRequestCategoryLoading = false; bool _isRequestCategoryLoading = false;
@ -720,6 +721,59 @@ class AllRequestsProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<int> getDashboardMultifilterRequests(
BuildContext context, {
bool pagination = false,
List<int>? statusTransaction,
List<int>? priority,
List<int>? displayData,
bool? isAscending,
}) async {
if (isMultiFilterLoading == true) return -2;
if (_allRequestsAndCount == null || pagination) {
isMultiFilterLoading = true;
notifyListeners();
}
Response response;
try {
Map<String, dynamic> 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;
}
if (isAscending != null) body["isAscending"] = isAscending;
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<int> getCloseRequests() async { // Future<int> getCloseRequests() async {
// if (isCloseLoading == true) return -2; // if (isCloseLoading == true) return -2;
// isCloseLoading = true; // isCloseLoading = true;

@ -3,8 +3,10 @@ import 'dart:developer';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:http/http.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/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.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/context_extension.dart';
import 'package:test_sa/extensions/enum_extensions.dart'; import 'package:test_sa/extensions/enum_extensions.dart';
import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/enums/user_types.dart';
@ -45,12 +47,59 @@ class DashBoardProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
int _currentListIndex = 0; List<int> listIndex = [];
int get currentListIndex => _currentListIndex; int? _currentListIndex;
set currentListIndex(int value) { 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; _currentListIndex = value;
if (value == null) {
listIndex.clear();
} else {
// 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(); notifyListeners();
} }
@ -114,7 +163,17 @@ class DashBoardProvider extends ChangeNotifier {
setTabs(userType: userType, context: context); setTabs(userType: userType, context: context);
getDashBoardCount(usersType: userType); getDashBoardCount(usersType: userType);
resetRequestDataList(); resetRequestDataList();
getRequestDetail(usersType: userType, status: tabs[currentListIndex].tag, tabId: tabs[currentListIndex].id); if (listIndex.isEmpty) {
Provider.of<AllRequestsProvider>(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 {
// Multiple categories selected
getMultiSelectCategoryRequests(usersType: userType, context: context);
}
// notifyListeners(); // notifyListeners();
} }
@ -224,6 +283,74 @@ class DashBoardProvider extends ChangeNotifier {
getRequestDetail(showLoader: showLoader, usersType: usersType, status: 4); 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<int> statusTransaction = [];
List<int> priority = [];
List<int> 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<AllRequestsProvider>(context, listen: false).getDashboardMultifilterRequests(
context,
pagination: false,
statusTransaction: statusTransaction,
priority: priority,
displayData: displayData,
isAscending: isAscending,
);
}
Future<SwipeModel> makeSwipe({required Swipe model}) async { Future<SwipeModel> makeSwipe({required Swipe model}) async {
isLoading = true; isLoading = true;
SwipeModel swipeResponse = SwipeModel(data: false, message: '', responseCode: 0, isSuccess: false); SwipeModel swipeResponse = SwipeModel(data: false, message: '', responseCode: 0, isSuccess: false);
@ -438,12 +565,13 @@ class CategoryTabs {
static List<CategoryTabs> getTabs({required UsersTypes userType, required BuildContext context}) { static List<CategoryTabs> getTabs({required UsersTypes userType, required BuildContext context}) {
List<CategoryTabs> tabs = []; List<CategoryTabs> tabs = [];
if (userType == UsersTypes.engineer) { 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: '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: '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: '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: '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; return tabs;
} }

@ -6,6 +6,7 @@ import 'package:nfc_manager/nfc_manager.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:test_sa/controllers/notification/firebase_notification_manger.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/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/notifications_provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.dart';
@ -57,13 +58,17 @@ class _DashboardViewState extends State<DashboardView> {
_dashBoardProvider.setTabs(userType: userProvider.user!.type!, context: context); _dashBoardProvider.setTabs(userType: userProvider.user!.type!, context: context);
_dashBoardProvider.getDashBoardCount(usersType: userProvider.user!.type!); _dashBoardProvider.getDashBoardCount(usersType: userProvider.user!.type!);
_dashBoardProvider.resetRequestDataList(); _dashBoardProvider.resetRequestDataList();
_dashBoardProvider.currentListIndex = 0; _dashBoardProvider.currentListIndex = null;
_dashBoardProvider.getRequestDetail( Provider.of<AllRequestsProvider>(context, listen: false)
usersType: userProvider.user!.type!, ..reset()
status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].tag, ..getAllRequests(context);
tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].id,
isHighPriority: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].isHighPriority, // _dashBoardProvider.getRequestDetail(
isOverdue: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].isOverDue); // 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() { void getInitialData() {
@ -94,8 +99,14 @@ class _DashboardViewState extends State<DashboardView> {
_scrollController.addListener(() async { _scrollController.addListener(() async {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !_dashBoardProvider.isDetailLoading) { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !_dashBoardProvider.isDetailLoading) {
_dashBoardProvider.pageNum = _dashBoardProvider.pageNum + 1; _dashBoardProvider.pageNum = _dashBoardProvider.pageNum + 1;
_dashBoardProvider.getRequestDetail( if (_dashBoardProvider.currentListIndex == null) {
usersType: user.type!, showLoader: false, status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].tag, tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].id); } else {
_dashBoardProvider.getRequestDetail(
usersType: user.type!,
showLoader: false,
status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].tag,
tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].id);
}
} }
}); });
} }

@ -157,84 +157,87 @@ class _AppBarWidgetState extends State<AppBarWidget> with TickerProviderStateMix
child: FadeTransition( child: FadeTransition(
opacity: _rightFadeAnimation, opacity: _rightFadeAnimation,
child: Consumer<SettingProvider>(builder: (context, settingProvider, child) { child: Consumer<SettingProvider>(builder: (context, settingProvider, child) {
return MenuAnchor( return MenuAnchor(
style: MenuStyle( style: MenuStyle(
backgroundColor: WidgetStateProperty.all(AppColor.background(context)), backgroundColor: WidgetStateProperty.all(AppColor.background(context)),
shape: WidgetStateProperty.all( shape: WidgetStateProperty.all(
RoundedRectangleBorder( RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(20.0)), borderRadius: const BorderRadius.all(Radius.circular(20.0)),
side: BorderSide(color: AppColor.borderColor(context), width: 1), side: BorderSide(color: AppColor.borderColor(context), width: 1),
), ),
), ),
padding: WidgetStateProperty.all( padding: WidgetStateProperty.all(
EdgeInsets.symmetric( EdgeInsets.symmetric(
horizontal: 0.toScreenWidth, horizontal: 0.toScreenWidth,
vertical: 8.toScreenHeight, vertical: 8.toScreenHeight,
),
),
elevation: WidgetStateProperty.all(0),
), ),
), menuChildren: Provider.of<UserProvider>(context, listen: false).user?.assetGroups?.map<MenuItemButton>((value) {
elevation: WidgetStateProperty.all(0), return MenuItemButton(
), onPressed: () async {
menuChildren: Provider.of<UserProvider>(context, listen: false).user?.assetGroups?.map<MenuItemButton>((value) { if (settingProvider.assetGroup != value) {
return MenuItemButton( AssetGroup? tempGroup = ApiManager.instance.assetGroup;
onPressed: () async { ApiManager.instance.assetGroup = value;
if (settingProvider.assetGroup != value) {
AssetGroup? tempGroup = ApiManager.instance.assetGroup;
ApiManager.instance.assetGroup = value;
Utils.showLoading(context); Utils.showLoading(context);
bool isSuccess = await context.userProvider.userChangeAssetGroup(); bool isSuccess = await context.userProvider.userChangeAssetGroup();
Utils.hideLoading(context); Utils.hideLoading(context);
if (isSuccess) { if (isSuccess) {
await context.userProvider.getMobilePermissionByUser(); await context.userProvider.getMobilePermissionByUser();
settingProvider.setAssetGroup(value); settingProvider.setAssetGroup(value);
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
context.settingProvider.setUser(ApiManager.instance.user!); context.settingProvider.setUser(ApiManager.instance.user!);
context.userProvider.setUser(ApiManager.instance.user!); context.userProvider.setUser(ApiManager.instance.user!);
DashBoardProvider dashBoardProvider = Provider.of<DashBoardProvider>(context, listen: false); DashBoardProvider dashBoardProvider = Provider.of<DashBoardProvider>(context, listen: false);
dashBoardProvider.setTabs(userType: settingProvider.user!.type!, context: context); dashBoardProvider.setTabs(userType: settingProvider.user!.type!, context: context);
dashBoardProvider.getDashBoardCount(usersType: settingProvider.user!.type!); dashBoardProvider.getDashBoardCount(usersType: settingProvider.user!.type!);
dashBoardProvider.resetRequestDataList(); dashBoardProvider.resetRequestDataList();
dashBoardProvider.getRequestDetail(usersType: settingProvider.user!.type!, status: dashBoardProvider.tabs[dashBoardProvider.currentListIndex].tag); if (dashBoardProvider.currentListIndex == null) {
}); } else {
} else { dashBoardProvider.getRequestDetail(usersType: settingProvider.user!.type!, status: dashBoardProvider.tabs[dashBoardProvider.currentListIndex!].tag);
ApiManager.instance.assetGroup = tempGroup; }
context.showConfirmDialog("Failed, Please Try again"); });
} } else {
} ApiManager.instance.assetGroup = tempGroup;
}, context.showConfirmDialog("Failed, Please Try again");
child: Text(value.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))), }
); }
}).toList() ?? },
[], child: Text(value.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))),
builder: (_, MenuController controller, Widget? child) { );
return Container( }).toList() ??
padding: const EdgeInsets.all(10), [],
decoration: BoxDecoration( builder: (_, MenuController controller, Widget? child) {
borderRadius: BorderRadius.circular(8), return Container(
color: AppColor.assetGroupBackground(context), padding: const EdgeInsets.all(10),
border: Border.all(color: AppColor.white936.withValues(alpha: .03), width: 1), decoration: BoxDecoration(
boxShadow: [ borderRadius: BorderRadius.circular(8),
BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 4, offset: const Offset(0, 0), spreadRadius: 0), color: AppColor.assetGroupBackground(context),
], border: Border.all(color: AppColor.white936.withValues(alpha: .03), width: 1),
), boxShadow: [
child: Row( BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 4, offset: const Offset(0, 0), spreadRadius: 0),
mainAxisSize: MainAxisSize.min, ],
spacing: 16, ),
children: [ child: Row(
Text(settingProvider.assetGroup?.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))), mainAxisSize: MainAxisSize.min,
Icon(Icons.keyboard_arrow_down, color: AppColor.headingTextColor(context)), spacing: 16,
], children: [
), Text(settingProvider.assetGroup?.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))),
).onPress(() { Icon(Icons.keyboard_arrow_down, color: AppColor.headingTextColor(context)),
if (controller.isOpen) { ],
controller.close(); ),
} else { ).onPress(() {
controller.open(); if (controller.isOpen) {
} controller.close();
}); } else {
}, controller.open();
); }
}), });
},
);
}),
), ),
), ),
16.width, 16.width,

@ -1,9 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.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/controllers/providers/api/user_provider.dart';
import 'package:test_sa/dashboard_latest/dashboard_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/dashboard_latest/widgets/request_category_list.dart';
import 'package:test_sa/extensions/context_extension.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/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart';
@ -15,6 +17,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/views/widgets/loaders/no_data_found.dart';
import 'package:test_sa/controllers/providers/settings/setting_provider.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 { class RequestCategoryFragment extends StatefulWidget {
const RequestCategoryFragment({Key? key}) : super(key: key); const RequestCategoryFragment({Key? key}) : super(key: key);
@ -27,6 +31,7 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
late AnimationController _animationController; late AnimationController _animationController;
late Animation<double> _headerFadeAnimation; late Animation<double> _headerFadeAnimation;
late Animation<Offset> _headerSlideAnimation; late Animation<Offset> _headerSlideAnimation;
final ScrollController _scrollController = ScrollController();
@override @override
void initState() { void initState() {
@ -60,6 +65,7 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
@override @override
void dispose() { void dispose() {
_animationController.dispose(); _animationController.dispose();
_scrollController.dispose();
super.dispose(); super.dispose();
} }
@ -73,13 +79,30 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
// Reload data with new sort order // Reload data with new sort order
dashboardProvider.resetRequestDataList(); dashboardProvider.resetRequestDataList();
dashboardProvider.getRequestDetail( Provider.of<AllRequestsProvider>(context, listen: false).reset();
usersType: userType ?? UsersTypes.normal_user,
showLoader: true, if (dashboardProvider.listIndex.isEmpty) {
status: tabs[dashboardProvider.currentListIndex].tag, // No category selected - reload default data with sorting
tabId: tabs[dashboardProvider.currentListIndex].id, Provider.of<AllRequestsProvider>(context, listen: false).getAllRequests(context, isAscending: isAscending);
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];
dashboardProvider.getRequestDetail(
usersType: userType ?? UsersTypes.normal_user,
showLoader: true,
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,
);
}
} }
@override @override
@ -102,47 +125,62 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
opacity: _headerFadeAnimation, opacity: _headerFadeAnimation,
child: SlideTransition( child: SlideTransition(
position: _headerSlideAnimation, position: _headerSlideAnimation,
child: !isNurse && dashboardProvider.tabs.isNotEmpty && dashboardProvider.tabs[dashboardProvider.currentListIndex].tag == 0 child:
? Column( // !isNurse && dashboardProvider.tabs.isNotEmpty
crossAxisAlignment: CrossAxisAlignment.start, // //&& dashboardProvider.tabs[dashboardProvider.currentListIndex].tag == 0
children: [ // ? Column(
Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))), // crossAxisAlignment: CrossAxisAlignment.start,
// Row( // children: [
// mainAxisAlignment: MainAxisAlignment.spaceBetween, // Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))),
// children: [ // // Row(
// context.translation.allWorkOrder.heading4(context), // // mainAxisAlignment: MainAxisAlignment.spaceBetween,
// GestureDetector( // // children: [
// onTap: () { // // context.translation.allWorkOrder.heading4(context),
// dashboardProvider.isShowDashBoardCalendar = !dashboardProvider.isShowDashBoardCalendar; // // GestureDetector(
// }, // // onTap: () {
// child: dashboardProvider.isShowDashBoardCalendar ? AppAsset.listIcon.toSvgAsset() : AppAsset.calendarIcon.toSvgAsset()) // // dashboardProvider.isShowDashBoardCalendar = !dashboardProvider.isShowDashBoardCalendar;
// ], // // },
// ), // // child: dashboardProvider.isShowDashBoardCalendar ? AppAsset.listIcon.toSvgAsset() : AppAsset.calendarIcon.toSvgAsset())
dashboardProvider.isShowDashBoardCalendar // // ],
? Padding( // // ),
padding: EdgeInsets.only(top: 12.toScreenHeight), // dashboardProvider.isShowDashBoardCalendar
child: const WeeklyCalendarFragment(), // ? Padding(
) // padding: EdgeInsets.only(top: 12.toScreenHeight),
: const SizedBox(), // child: const WeeklyCalendarFragment(),
], // )
).paddingOnly(start: 14, end: 12) // : const SizedBox(),
: Row( // ],
spacing: 16, // ).paddingOnly(start: 14, end: 12)
children: [ // :
Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))).expanded, Row(
// todo commenting this code, for FM spacing: 16,
// if (!isFMSEngineer) children: [
// 'search'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() { Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))).expanded,
// Navigator.of(context).pushNamed('/cleaner_search'); // todo commenting this code, for FM
// }), // if (!isFMSEngineer)
Transform.flip( // 'search'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() {
flipY: isAscending, // Navigator.of(context).pushNamed('/cleaner_search');
child: 'dashboard/sort_descending'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() { // }),
_toggleSort(dashboardProvider, user?.type);
}), if (dashboardProvider.listIndex.isNotEmpty)
) Text(
], 'Clear Filters',
).paddingOnly(start: 16, end: 16), style: AppTextStyles.bodyText2.copyWith(color: AppColor.red30),
).onPress(() {
dashboardProvider.resetRequestDataList();
dashboardProvider.currentListIndex = null;
Provider.of<AllRequestsProvider>(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),
), ),
), ),
@ -157,55 +195,92 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
// ], // ],
// ).paddingOnly(start: 16, end: 16) // ).paddingOnly(start: 16, end: 16)
// : context.translation.allWorkOrder.heading4(context).paddingOnly(start: 16, end: 16), // : context.translation.allWorkOrder.heading4(context).paddingOnly(start: 16, end: 16),
(dashboardProvider.listIndex.isEmpty)
dashboardProvider.isDetailLoading ? Consumer<AllRequestsProvider>(builder: (context, snapshot, _) {
? Column( return RequestItemViewList(
mainAxisSize: MainAxisSize.max, snapshot.allRequestsAndCount?.requestsDetails ?? [],
children: List.generate(3, (index) { snapshot.isAllLoading,
return Padding( disableScroll: true,
padding: EdgeInsets.symmetric(vertical: 10.toScreenHeight, horizontal: 0), key: ValueKey('request_list_'), // Key changes on every tab click
child: const SizedBox().toRequestShimmer(context, dashboardProvider.isDetailLoading), );
); })
})).paddingOnly(start: 16, end: 16, top: 16) : dashboardProvider.listIndex.length == 1 && dashboardProvider.isUnassignedTab(dashboardProvider.listIndex[0])
: (dashboardProvider.requestDetailList == null || dashboardProvider.requestDetailList?.data?.isEmpty == true) // UnAssigned selected (check by requestType, not by index) - show requestDetailList
? const NoDataFound().paddingOnly(top: 80).center ? dashboardProvider.isDetailLoading
: RequestCategoryList(dashboardProvider.requestDetailList?.data ?? <Data>[], dashboardProvider.isDetailLoading, dashboardProvider.requestDetailList?.totalRows ?? 0), ? 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 ?? <Data>[], dashboardProvider.isDetailLoading, dashboardProvider.requestDetailList?.totalRows ?? 0)
// Multi-select categories - show allRequestsAndCount
: Consumer<AllRequestsProvider>(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("_")}'),
);
}),
]); ]);
}); });
} }
Widget getTabs({required BuildContext context, required DashBoardProvider requestsProvider, required UsersTypes? userType}) { Widget getTabs({required BuildContext context, required DashBoardProvider requestsProvider, required UsersTypes? userType}) {
List<CategoryTabs> tabs = CategoryTabs.getTabs(userType: userType ?? UsersTypes.normal_user, context: context); List<CategoryTabs> tabs = CategoryTabs.getTabs(userType: userType ?? UsersTypes.normal_user, context: context);
// SizedBox(
// height: 44 + (context.isTablet() ? 36 : 16).toScreenHeight, // Calculate item width to show X.5 items (last visible item is exactly 50% visible)
// child: ListView.separated( final screenWidth = MediaQuery.of(context).size.width;
// scrollDirection: Axis.horizontal, final isTablet = context.isTablet();
// separatorBuilder: (cxt, index) => 12.width,
// padding: const EdgeInsets.only(top: 6 + 16, left: 16, right: 16), // Show 3.5 items on phone, 4.5 on tablet (perfect 50% peek)
// itemBuilder: (cxt, index) { final visibleItemsWithPeek = isTablet ? 4.5 : 3.5;
// return TabButton( const spacing = 4.0;
// label: tabs[index].label, const horizontalPadding = 16.0;
// loading: requestsProvider.isDetailLoading,
// isSelected: requestsProvider.currentListIndex == index, // Calculate item width: divide available space by visible items including the half item
// count: requestsProvider.requestDetailList != null ? requestsProvider.requestDetailList!.count ?? 0 : 0, final availableWidth = screenWidth - (horizontalPadding * 2);
// onPressed: () async { final fullItems = visibleItemsWithPeek.floor(); // 3 or 4
// setState(() { final totalSpacing = (fullItems - 1) * spacing + (spacing * 0.5); // Full gaps + half gap for peek
// isAscending = false; final itemWidth = (availableWidth - totalSpacing) / visibleItemsWithPeek;
// });
// requestsProvider.currentListIndex = index; // Check if scrolling is needed
// requestsProvider.resetRequestDataList(); final needsScrolling = tabs.length > fullItems;
// requestsProvider.getRequestDetail(usersType: userType ?? UsersTypes.normal_user, showLoader: true, status: tabs[index].tag, tabId: tabs[index].id);
// }, return SizedBox(
// ); height: itemWidth + 16, // Height matches item width + bottom padding
// }, child: _buildHorizontalScrollableList(tabs, requestsProvider, userType, itemWidth, needsScrolling),
// itemCount: tabs.length), );
// ); }
return GridView.builder( // Horizontal scrollable list
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), Widget _buildHorizontalScrollableList(List<CategoryTabs> tabs, DashBoardProvider requestsProvider, UsersTypes? userType, double itemWidth, bool needsScrolling) {
physics: const NeverScrollableScrollPhysics(), const leftPadding = 16.0;
shrinkWrap: true, const rightPadding = 16.0; // Equal padding - peek is created by item sizing
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: context.isTablet() ? 5 : 4, childAspectRatio: 85 / 85, crossAxisSpacing: 8, mainAxisSpacing: 8),
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) { itemBuilder: (cxt, index) {
// Calculate staggered animation delay // Calculate staggered animation delay
final delay = index * 0.08; final delay = index * 0.08;
@ -221,7 +296,7 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
); );
final slideAnimation = Tween<Offset>( final slideAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.3), begin: const Offset(-0.3, 0.0),
end: Offset.zero, end: Offset.zero,
).animate( ).animate(
CurvedAnimation( CurvedAnimation(
@ -238,28 +313,55 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
opacity: animation, opacity: animation,
child: SlideTransition( child: SlideTransition(
position: slideAnimation, position: slideAnimation,
child: RequestTypeButton( child: SizedBox(
label: tabs[index].label, width: itemWidth, // Use responsive width
loading: requestsProvider.isDetailLoading && requestsProvider.currentListIndex == index, child: RequestTypeButton(
isSelected: requestsProvider.currentListIndex == index, label: tabs[index].label,
count: requestsProvider.getStatusCount(tabs[index].requestType), loading: requestsProvider.isDetailLoading && requestsProvider.listIndex.contains(index),
onPressed: () async { isSelected: requestsProvider.listIndex.contains(index),
setState(() { count: requestsProvider.getStatusCount(tabs[index].requestType),
isAscending = false; onPressed: () async {
}); setState(() {
requestsProvider.currentListIndex = index; isAscending = false;
requestsProvider.resetRequestDataList(); });
requestsProvider.getRequestDetail(
usersType: userType ?? UsersTypes.normal_user, // Check if this is UnAssigned tab (index 0 for engineer)
showLoader: true, bool isUnassignedTab = tabs[index].requestType == RequestTypeEnum.unassigned;
status: tabs[index].tag,
tabId: tabs[index].id, // Update selection state
isHighPriority: tabs[index].isHighPriority, requestsProvider.currentListIndex = index;
isOverdue: tabs[index].isOverDue,
); // Reset data
}, requestsProvider.resetRequestDataList();
icon: tabs[index].icon, Provider.of<AllRequestsProvider>(context, listen: false).reset();
iconColor: tabs[index].iconColor,
// If deselected (listIndex is now empty), show default data
if (requestsProvider.listIndex.isEmpty) {
Provider.of<AllRequestsProvider>(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,
),
), ),
), ),
); );

@ -25,8 +25,9 @@ import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
class RequestItemViewList extends StatefulWidget { class RequestItemViewList extends StatefulWidget {
final List<RequestsDetails> list; final List<RequestsDetails> list;
final bool isLoading; 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 @override
State<RequestItemViewList> createState() => _RequestItemViewListState(); State<RequestItemViewList> createState() => _RequestItemViewListState();
@ -58,6 +59,7 @@ class _RequestItemViewListState extends State<RequestItemViewList> with SingleTi
: ListView.separated( : ListView.separated(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
shrinkWrap: true, shrinkWrap: true,
physics: widget.disableScroll ? const NeverScrollableScrollPhysics() : null,
itemBuilder: (cxt, index) { itemBuilder: (cxt, index) {
if (widget.isLoading) return const SizedBox().toRequestShimmer(cxt, widget.isLoading); if (widget.isLoading) return const SizedBox().toRequestShimmer(cxt, widget.isLoading);
@ -158,5 +160,3 @@ class _RequestItemViewListState extends State<RequestItemViewList> with SingleTi
itemCount: widget.isLoading ? 6 : widget.list.length); itemCount: widget.isLoading ? 6 : widget.list.length);
} }
} }

@ -39,6 +39,7 @@ class AAlertDialog extends StatelessWidget {
) )
: AlertDialog( : AlertDialog(
backgroundColor: AppColor.background(context), backgroundColor: AppColor.background(context),
actionsPadding: const EdgeInsets.only(bottom: 12, left: 24, right: 24),
title: title != null ? Text(title!) : null, title: title != null ? Text(title!) : null,
content: content != null ? Text(content!) : null, content: content != null ? Text(content!) : null,
// contentTextStyle: AppTextStyles.bodyText.copyWith(color: (context.isDark ? AppColor.neutral30 : AppColor.neutral50)), // contentTextStyle: AppTextStyles.bodyText.copyWith(color: (context.isDark ? AppColor.neutral30 : AppColor.neutral50)),

Loading…
Cancel
Save