|
|
|
|
@ -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);
|
|
|
|
|
|
|
|
|
|
@ -27,6 +30,7 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
|
|
|
|
|
late AnimationController _animationController;
|
|
|
|
|
late Animation<double> _headerFadeAnimation;
|
|
|
|
|
late Animation<Offset> _headerSlideAnimation;
|
|
|
|
|
final ScrollController _scrollController = ScrollController();
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
@ -60,6 +64,7 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
_animationController.dispose();
|
|
|
|
|
_scrollController.dispose();
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -73,13 +78,16 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> 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
|
|
|
|
|
@ -102,47 +110,62 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> 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<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 +180,67 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> 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 ?? <Data>[], dashboardProvider.isDetailLoading, dashboardProvider.requestDetailList?.totalRows ?? 0),
|
|
|
|
|
(dashboardProvider.currentListIndex == null)
|
|
|
|
|
? Consumer<AllRequestsProvider>(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 ?? <Data>[], dashboardProvider.isDetailLoading, dashboardProvider.requestDetailList?.totalRows ?? 0),
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget getTabs({required BuildContext context, required DashBoardProvider requestsProvider, required UsersTypes? userType}) {
|
|
|
|
|
List<CategoryTabs> 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<CategoryTabs> 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 +256,7 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
final slideAnimation = Tween<Offset>(
|
|
|
|
|
begin: const Offset(0.0, 0.3),
|
|
|
|
|
begin: const Offset(-0.3, 0.0),
|
|
|
|
|
end: Offset.zero,
|
|
|
|
|
).animate(
|
|
|
|
|
CurvedAnimation(
|
|
|
|
|
@ -238,28 +273,31 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> 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,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|