From f6fa5218b623d8a42c4ba100e5d4396cbfc9cac5 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 28 Jun 2026 11:31:05 +0300 Subject: [PATCH] cleaner module api call fixed in new ui/ux ehancement. --- lib/dashboard_latest/dashboard_view.dart | 30 ++- .../widgets/request_category_fragment.dart | 1 + ...request_category_fragment_housekeeper.dart | 240 ++++++++++++++++++ lib/new_views/pages/land_page/land_page.dart | 4 +- 4 files changed, 262 insertions(+), 13 deletions(-) create mode 100644 lib/dashboard_latest/widgets/request_category_fragment_housekeeper.dart diff --git a/lib/dashboard_latest/dashboard_view.dart b/lib/dashboard_latest/dashboard_view.dart index 88db0502..ca986cc1 100644 --- a/lib/dashboard_latest/dashboard_view.dart +++ b/lib/dashboard_latest/dashboard_view.dart @@ -13,6 +13,7 @@ import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart'; import 'package:test_sa/dashboard_latest/widgets/progress_fragment.dart'; +import 'package:test_sa/dashboard_latest/widgets/request_category_fragment_housekeeper.dart'; import 'package:test_sa/dashboard_latest/widgets/requests_fragment.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; @@ -58,17 +59,22 @@ class _DashboardViewState extends State { _dashBoardProvider.setTabs(userType: userProvider.user!.type!, context: context); _dashBoardProvider.getDashBoardCount(usersType: userProvider.user!.type!); _dashBoardProvider.resetRequestDataList(); - _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); + + if (userProvider.isHouseKeeper) { + _dashBoardProvider.currentListIndex = 0; + _dashBoardProvider.getRequestDetail( + usersType: userProvider.user!.type!, + showLoader: true, + status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].tag, + tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].id, + // isAscending: _dashBoardProvider.isAscending, + ); + } else { + _dashBoardProvider.currentListIndex = null; + Provider.of(context, listen: false) + ..reset() + ..getAllRequests(context); + } } void getInitialData() { @@ -145,7 +151,7 @@ class _DashboardViewState extends State { children: [ 16.height, ProgressFragment(), 16.height, // const RequestsFragment(), - const RequestCategoryFragment() + context.userProvider.isHouseKeeper ? const RequestCategoryFragmentHouseKeeper() : const RequestCategoryFragment() ], ), ), diff --git a/lib/dashboard_latest/widgets/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart index c91797ee..c452d306 100644 --- a/lib/dashboard_latest/widgets/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -80,6 +80,7 @@ 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 diff --git a/lib/dashboard_latest/widgets/request_category_fragment_housekeeper.dart b/lib/dashboard_latest/widgets/request_category_fragment_housekeeper.dart new file mode 100644 index 00000000..38f1e95a --- /dev/null +++ b/lib/dashboard_latest/widgets/request_category_fragment_housekeeper.dart @@ -0,0 +1,240 @@ +import 'package:flutter/material.dart'; +import 'package:provider/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/string_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/enums/user_types.dart'; +import 'package:test_sa/models/new_models/dashboard_detail.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/tab_button.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; + +/// Separate component for HouseKeeper role +/// HouseKeeper has simpler requirements: +/// - Single-select only (no multi-select) +/// - Uses getRequestDetail API only +/// - Default shows first category (In Progress) +class RequestCategoryFragmentHouseKeeper extends StatefulWidget { + const RequestCategoryFragmentHouseKeeper({Key? key}) : super(key: key); + + @override + State createState() => _RequestCategoryFragmentHouseKeeperState(); +} + +class _RequestCategoryFragmentHouseKeeperState extends State with SingleTickerProviderStateMixin { + bool isAscending = false; + late AnimationController _animationController; + late Animation _headerFadeAnimation; + late Animation _headerSlideAnimation; + final ScrollController _scrollController = ScrollController(); + // int _selectedIndex = 0; // Default first tab + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + ); + + // Header animation (appears first, faster) + _headerFadeAnimation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: const Interval(0.0, 0.4, curve: Curves.easeOut), + ), + ); + + _headerSlideAnimation = Tween( + begin: const Offset(0.0, -0.3), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: _animationController, + curve: const Interval(0.0, 0.5, curve: Curves.easeOutCubic), + ), + ); + + _animationController.forward(); + + // Load first category data on init + WidgetsBinding.instance.addPostFrameCallback((_) { + _loadCategoryData(0); + }); + } + + @override + void dispose() { + _animationController.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + + void _loadCategoryData(int index) { + final dashboardProvider = Provider.of(context, listen: false); + List tabs = CategoryTabs.getTabs(userType: UsersTypes.houseKeeper, context: context); + + if (index >= 0 && index < tabs.length) { + dashboardProvider.resetRequestDataList(); + dashboardProvider.getRequestDetail( + usersType: UsersTypes.houseKeeper, + showLoader: true, + status: tabs[index].tag, + tabId: tabs[index].id, + isAscending: isAscending, + ); + } + } + + void _toggleSort(DashBoardProvider dashboardProvider) { + setState(() { + isAscending = !isAscending; + }); + + // Reload current category with new sort order + _loadCategoryData(dashboardProvider.currentListIndex!); + } + + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, dashboardProvider, child) { + List tabs = CategoryTabs.getTabs(userType: UsersTypes.houseKeeper, context: context); + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Tabs + _buildTabs(tabs, dashboardProvider), + + // Header with sort + FadeTransition( + opacity: _headerFadeAnimation, + child: SlideTransition( + position: _headerSlideAnimation, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + 'All Tasks'.addTranslation.heading4(context), + Transform.flip( + flipY: isAscending, + child: 'dashboard/sort_descending'.toSvgAsset( + width: 24, + height: 24, + color: AppColor.headingTextColor(context), + ).onPress(()=>_toggleSort(dashboardProvider)), + ) + ], + ).paddingOnly(start: 16, end: 16), + ), + ), + + // Data list + 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, + ), + ], + ); + }); + } + + Widget _buildTabs(List tabs, DashBoardProvider dashboardProvider) { + // Calculate item width + final screenWidth = MediaQuery.of(context).size.width; + final isTablet = context.isTablet(); + final visibleItemsWithPeek = isTablet ? 4.5 : 3.5; + const spacing = 4.0; + const horizontalPadding = 16.0; + + final availableWidth = screenWidth - (horizontalPadding * 2); + final fullItems = visibleItemsWithPeek.floor(); + final totalSpacing = (fullItems - 1) * spacing + (spacing * 0.5); + final itemWidth = (availableWidth - totalSpacing) / visibleItemsWithPeek; + + return SizedBox( + height: itemWidth + 16, + child: ListView.separated( + controller: _scrollController, + scrollDirection: Axis.horizontal, + separatorBuilder: (cxt, index) => const SizedBox(width: 4), + padding: const EdgeInsets.only(bottom: 16, left: horizontalPadding, right: horizontalPadding), + itemBuilder: (cxt, index) { + // Animation + final delay = index * 0.08; + final animation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.8), + (delay + 0.3).clamp(0.0, 1.0), + curve: Curves.easeOut, + ), + ), + ); + + final slideAnimation = Tween( + begin: const Offset(-0.3, 0.0), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.8), + (delay + 0.3).clamp(0.0, 1.0), + curve: Curves.easeOutCubic, + ), + ), + ); + + return FadeTransition( + opacity: animation, + child: SlideTransition( + position: slideAnimation, + child: SizedBox( + width: itemWidth, + child: RequestTypeButton( + label: tabs[index].label, + loading: dashboardProvider.isDetailLoading && dashboardProvider.currentListIndex == index, + isSelected: dashboardProvider.currentListIndex == index, + count: dashboardProvider.getStatusCount(tabs[index].requestType), + onPressed: () { + setState(() { + isAscending = false; + // _selectedIndex = index; + }); + dashboardProvider.currentListIndex = index; + _loadCategoryData(index); + }, + icon: tabs[index].icon, + iconColor: tabs[index].iconColor, + ), + ), + ), + ); + }, + itemCount: tabs.length, + ), + ); + } +} + diff --git a/lib/new_views/pages/land_page/land_page.dart b/lib/new_views/pages/land_page/land_page.dart index 60af1bc4..bd475186 100644 --- a/lib/new_views/pages/land_page/land_page.dart +++ b/lib/new_views/pages/land_page/land_page.dart @@ -115,7 +115,9 @@ class _LandPageState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { _userProvider!.getSwipeLastTransaction(userId: _userProvider!.user!.userID!); _userProvider!.getSiteContactInfo(); - Provider.of(context, listen: false).getData(); + if(!_userProvider!.isHouseKeeper) { + Provider.of(context, listen: false).getData(); + } }); } _pages = [