From b81ac33b2d41fefbb314b741ac5591e8ee554feb Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Wed, 11 Jun 2025 21:55:15 +0530 Subject: [PATCH] feat: Enhance project selection handling across various screens and controllers --- lib/controller/project_controller.dart | 12 +- .../attendance/attendence_filter_sheet.dart | 73 +------- .../daily_progress_report_filter.dart | 168 ++++++------------ .../Attendence/attendance_screen.dart | 32 +++- lib/view/layouts/layout.dart | 3 +- lib/view/taskPlaning/daily_progress.dart | 25 +++ lib/view/taskPlaning/daily_task_planing.dart | 89 +++------- 7 files changed, 135 insertions(+), 267 deletions(-) diff --git a/lib/controller/project_controller.dart b/lib/controller/project_controller.dart index 805e3d9..18205f4 100644 --- a/lib/controller/project_controller.dart +++ b/lib/controller/project_controller.dart @@ -32,17 +32,18 @@ class ProjectController extends GetxController { if (response != null && response.isNotEmpty) { projects.assignAll( - response.map((json) => ProjectModel.fromJson(json)).toList()); + response.map((json) => ProjectModel.fromJson(json)).toList()); String? savedId = LocalStorage.getString('selectedProjectId'); if (savedId != null && projects.any((p) => p.id == savedId)) { selectedProjectId = RxString(savedId); } else { selectedProjectId = RxString(projects.first.id.toString()); - LocalStorage.saveString('selectedProjectId', projects.first.id.toString()); + LocalStorage.saveString( + 'selectedProjectId', projects.first.id.toString()); } - isProjectSelectionExpanded.value = false; + isProjectSelectionExpanded.value = false; log.i("Projects fetched: ${projects.length}"); } else { log.w("No projects found or API call failed."); @@ -53,8 +54,9 @@ class ProjectController extends GetxController { update(['dashboard_controller']); } - void updateSelectedProject(String projectId) { + Future updateSelectedProject(String projectId) async { selectedProjectId?.value = projectId; - LocalStorage.saveString('selectedProjectId', projectId); + await LocalStorage.saveString('selectedProjectId', projectId); + update(); } } diff --git a/lib/model/attendance/attendence_filter_sheet.dart b/lib/model/attendance/attendence_filter_sheet.dart index 6d6ea57..e51f5d9 100644 --- a/lib/model/attendance/attendence_filter_sheet.dart +++ b/lib/model/attendance/attendence_filter_sheet.dart @@ -24,14 +24,11 @@ class AttendanceFilterBottomSheet extends StatefulWidget { class _AttendanceFilterBottomSheetState extends State { - late String? tempSelectedProjectId; late String tempSelectedTab; - bool showProjectList = false; @override void initState() { super.initState(); - tempSelectedProjectId = widget.controller.selectedProjectId; tempSelectedTab = widget.selectedTab; } @@ -46,55 +43,7 @@ class _AttendanceFilterBottomSheetState return "Date Range"; } - List buildProjectList() { - final accessibleProjects = widget.controller.projects - .where((project) => - widget.permissionController.isUserAssignedToProject( - project.id.toString())) - .toList(); - - if (accessibleProjects.isEmpty) { - return [ - const Padding( - padding: EdgeInsets.all(12.0), - child: Center(child: Text('No Projects Assigned')), - ), - ]; - } - - return accessibleProjects.map((project) { - final isSelected = tempSelectedProjectId == project.id.toString(); - return ListTile( - dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - title: Text(project.name), - trailing: isSelected ? const Icon(Icons.check) : null, - onTap: () { - setState(() { - tempSelectedProjectId = project.id.toString(); - showProjectList = false; - }); - }, - ); - }).toList(); - } - List buildMainFilters() { - final accessibleProjects = widget.controller.projects - .where((project) => - widget.permissionController.isUserAssignedToProject( - project.id.toString())) - .toList(); - - final selectedProject = accessibleProjects.isNotEmpty - ? accessibleProjects.firstWhere( - (p) => p.id.toString() == tempSelectedProjectId, - orElse: () => accessibleProjects[0], - ) - : null; - - final selectedProjectName = selectedProject?.name ?? "Select Project"; - final hasRegularizationPermission = widget.permissionController .hasPermission(Permissions.regularizeAttendance); @@ -112,24 +61,6 @@ class _AttendanceFilterBottomSheetState }).toList(); List widgets = [ - Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), - child: Align( - alignment: Alignment.centerLeft, - child: MyText.titleSmall( - "Project", - fontWeight: 600, - ), - ), - ), - ListTile( - dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - title: Text(selectedProjectName), - trailing: const Icon(Icons.arrow_drop_down), - onTap: () => setState(() => showProjectList = true), - ), - const Divider(), Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), child: Align( @@ -216,7 +147,6 @@ class _AttendanceFilterBottomSheetState child: Column( mainAxisSize: MainAxisSize.min, children: [ - // Drag handle Padding( padding: const EdgeInsets.only(top: 12, bottom: 8), child: Center( @@ -230,7 +160,7 @@ class _AttendanceFilterBottomSheetState ), ), ), - if (showProjectList) ...buildProjectList() else ...buildMainFilters(), + ...buildMainFilters(), const Divider(), Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), @@ -247,7 +177,6 @@ class _AttendanceFilterBottomSheetState child: const Text('Apply Filter'), onPressed: () { Navigator.pop(context, { - 'projectId': tempSelectedProjectId, 'selectedTab': tempSelectedTab, }); }, diff --git a/lib/model/dailyTaskPlaning/daily_progress_report_filter.dart b/lib/model/dailyTaskPlaning/daily_progress_report_filter.dart index 8012977..457bd97 100644 --- a/lib/model/dailyTaskPlaning/daily_progress_report_filter.dart +++ b/lib/model/dailyTaskPlaning/daily_progress_report_filter.dart @@ -20,13 +20,9 @@ class DailyProgressReportFilter extends StatefulWidget { } class _DailyProgressReportFilterState extends State { - late String? tempSelectedProjectId; - bool showProjectList = false; - @override void initState() { super.initState(); - tempSelectedProjectId = widget.controller.selectedProjectId; } String getLabelText() { @@ -42,116 +38,6 @@ class _DailyProgressReportFilterState extends State { @override Widget build(BuildContext context) { - final accessibleProjects = widget.controller.projects - .where((project) => widget.permissionController - .isUserAssignedToProject(project.id.toString())) - .toList(); - - List filterWidgets; - - if (showProjectList) { - filterWidgets = accessibleProjects.isEmpty - ? [ - const Padding( - padding: EdgeInsets.all(12.0), - child: Center(child: Text('No Projects Assigned')), - ), - ] - : accessibleProjects.map((project) { - final isSelected = tempSelectedProjectId == project.id.toString(); - return ListTile( - dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - title: Text(project.name), - trailing: isSelected ? const Icon(Icons.check) : null, - onTap: () { - setState(() { - tempSelectedProjectId = project.id.toString(); - showProjectList = false; - }); - }, - ); - }).toList(); - } else { - final selectedProject = accessibleProjects.isNotEmpty - ? accessibleProjects.firstWhere( - (p) => p.id.toString() == tempSelectedProjectId, - orElse: () => accessibleProjects[0], - ) - : null; - - final selectedProjectName = selectedProject?.name ?? "Select Project"; - - filterWidgets = [ - Padding( - padding: EdgeInsets.fromLTRB(16, 12, 16, 4), - child: Align( - alignment: Alignment.centerLeft, - child: MyText.titleSmall( - 'Select Project', - fontWeight: 600, - ), - ), - ), - ListTile( - dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - title: Text(selectedProjectName), - trailing: const Icon(Icons.arrow_drop_down), - onTap: () => setState(() => showProjectList = true), - ), - ]; - - filterWidgets.addAll([ - const Divider(), - Padding( - padding: EdgeInsets.fromLTRB(16, 12, 16, 4), - child: Align( - alignment: Alignment.centerLeft, - child: MyText.titleSmall( - "Select Date Range", - fontWeight: 600, - )), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: InkWell( - borderRadius: BorderRadius.circular(10), - onTap: () => widget.controller.selectDateRangeForTaskData( - context, - widget.controller, - ), - child: Ink( - decoration: BoxDecoration( - color: Colors.grey.shade100, - border: Border.all(color: Colors.grey.shade400), - borderRadius: BorderRadius.circular(10), - ), - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), - child: Row( - children: [ - Icon(Icons.date_range, color: Colors.blue.shade600), - const SizedBox(width: 12), - Expanded( - child: Text( - getLabelText(), - style: const TextStyle( - fontSize: 16, - color: Colors.black87, - fontWeight: FontWeight.w500, - ), - overflow: TextOverflow.ellipsis, - ), - ), - const Icon(Icons.arrow_drop_down, color: Colors.grey), - ], - ), - ), - ), - ), - ]); - } - return SafeArea( child: Padding( padding: EdgeInsets.only( @@ -174,7 +60,55 @@ class _DailyProgressReportFilterState extends State { ), ), ), - ...filterWidgets, + const Divider(), + Padding( + padding: EdgeInsets.fromLTRB(16, 12, 16, 4), + child: Align( + alignment: Alignment.centerLeft, + child: MyText.titleSmall( + "Select Date Range", + fontWeight: 600, + ), + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: InkWell( + borderRadius: BorderRadius.circular(10), + onTap: () => widget.controller.selectDateRangeForTaskData( + context, + widget.controller, + ), + child: Ink( + decoration: BoxDecoration( + color: Colors.grey.shade100, + border: Border.all(color: Colors.grey.shade400), + borderRadius: BorderRadius.circular(10), + ), + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 14), + child: Row( + children: [ + Icon(Icons.date_range, color: Colors.blue.shade600), + const SizedBox(width: 12), + Expanded( + child: Text( + getLabelText(), + style: const TextStyle( + fontSize: 16, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const Icon(Icons.arrow_drop_down, color: Colors.grey), + ], + ), + ), + ), + ), const Divider(), Padding( padding: @@ -190,9 +124,7 @@ class _DailyProgressReportFilterState extends State { ), child: const Text('Apply Filter'), onPressed: () { - Navigator.pop(context, { - 'projectId': tempSelectedProjectId, - }); + Navigator.pop(context, {}); }, ), ), diff --git a/lib/view/dashboard/Attendence/attendance_screen.dart b/lib/view/dashboard/Attendence/attendance_screen.dart index 8de61e9..18c5a91 100644 --- a/lib/view/dashboard/Attendence/attendance_screen.dart +++ b/lib/view/dashboard/Attendence/attendance_screen.dart @@ -20,6 +20,7 @@ import 'package:marco/model/attendance/log_details_view.dart'; import 'package:marco/model/attendance/attendence_action_button.dart'; import 'package:marco/model/attendance/regualrize_action_button.dart'; import 'package:marco/model/attendance/attendence_filter_sheet.dart'; +import 'package:marco/controller/project_controller.dart'; // adjust if needed class AttendanceScreen extends StatefulWidget { AttendanceScreen({super.key}); @@ -35,6 +36,24 @@ class _AttendanceScreenState extends State with UIMixin { Get.put(PermissionController()); String selectedTab = 'todaysAttendance'; + @override + void initState() { + super.initState(); + final projectController = Get.find(); + ever(projectController.selectedProjectId!, (projectId) async { + if (projectId != null && projectId.isNotEmpty) { + try { + await attendanceController.fetchEmployeesByProject(projectId); + await attendanceController.fetchAttendanceLogs(projectId); + await attendanceController.fetchRegularizationLogs(projectId); + await attendanceController.fetchProjectData(projectId); + attendanceController.update(['attendance_dashboard_controller']); + } catch (e) { + debugPrint("Error updating data on project change: $e"); + } + } + }); + } @override Widget build(BuildContext context) { @@ -93,7 +112,10 @@ class _AttendanceScreenState extends State with UIMixin { if (result != null) { final selectedProjectId = - result['projectId'] as String?; + Get.find() + .selectedProjectId + ?.value; + final selectedView = result['selectedTab'] as String?; if (selectedProjectId != null && @@ -146,8 +168,10 @@ class _AttendanceScreenState extends State with UIMixin { child: InkWell( borderRadius: BorderRadius.circular(24), onTap: () async { - final projectId = - attendanceController.selectedProjectId; + final projectId = Get.find() + .selectedProjectId + ?.value; + if (projectId != null && projectId.isNotEmpty) { try { await attendanceController @@ -181,7 +205,7 @@ class _AttendanceScreenState extends State with UIMixin { ], ), Padding( - padding: MySpacing.x(flexSpacing / 2), + padding: MySpacing.x(0), child: MyFlex(children: [ MyFlexItem( sizes: 'lg-12 md-12 sm-12', diff --git a/lib/view/layouts/layout.dart b/lib/view/layouts/layout.dart index ff144e3..ee546d2 100644 --- a/lib/view/layouts/layout.dart +++ b/lib/view/layouts/layout.dart @@ -51,7 +51,8 @@ class _LayoutState extends State { Expanded( child: SingleChildScrollView( key: controller.scrollKey, - padding: EdgeInsets.all(isMobile ? 16 : 32), + padding: EdgeInsets.symmetric(horizontal: 0, vertical: isMobile ? 16 : 32), + child: widget.child, ), ), diff --git a/lib/view/taskPlaning/daily_progress.dart b/lib/view/taskPlaning/daily_progress.dart index 0447d97..da557b2 100644 --- a/lib/view/taskPlaning/daily_progress.dart +++ b/lib/view/taskPlaning/daily_progress.dart @@ -17,6 +17,7 @@ import 'package:marco/model/dailyTaskPlaning/daily_progress_report_filter.dart'; import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/model/dailyTaskPlaning/comment_task_bottom_sheet.dart'; import 'package:marco/model/dailyTaskPlaning/report_task_bottom_sheet.dart'; +import 'package:marco/controller/project_controller.dart'; class DailyProgressReportScreen extends StatefulWidget { const DailyProgressReportScreen({super.key}); @@ -40,6 +41,30 @@ class _DailyProgressReportScreenState extends State Get.put(DailyTaskController()); final PermissionController permissionController = Get.put(PermissionController()); + final ProjectController projectController = Get.find(); + + @override + void initState() { + super.initState(); + + final initialProjectId = projectController.selectedProjectId?.value; + if (initialProjectId != null) { + dailyTaskController.selectedProjectId = initialProjectId; + dailyTaskController.fetchTaskData(initialProjectId); + } + + ever( + projectController.selectedProjectId!, + (newProjectId) async { + if (newProjectId != null && + newProjectId != dailyTaskController.selectedProjectId) { + dailyTaskController.selectedProjectId = newProjectId; + await dailyTaskController.fetchTaskData(newProjectId); + dailyTaskController.update(['daily_progress_report_controller']); + } + }, + ); + } @override Widget build(BuildContext context) { diff --git a/lib/view/taskPlaning/daily_task_planing.dart b/lib/view/taskPlaning/daily_task_planing.dart index 110a363..21c8afd 100644 --- a/lib/view/taskPlaning/daily_task_planing.dart +++ b/lib/view/taskPlaning/daily_task_planing.dart @@ -10,10 +10,10 @@ import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/view/layouts/layout.dart'; import 'package:marco/controller/permission_controller.dart'; -import 'package:marco/model/dailyTaskPlaning/daily_task_planing_filter.dart'; import 'package:marco/controller/task_planing/daily_task_planing_controller.dart'; -import 'package:marco/model/dailyTaskPlaning/assign_task_bottom_sheet .dart'; +import 'package:marco/controller/project_controller.dart'; import 'package:percent_indicator/percent_indicator.dart'; +import 'package:marco/model/dailyTaskPlaning/assign_task_bottom_sheet .dart'; class DailyTaskPlaningScreen extends StatefulWidget { DailyTaskPlaningScreen({super.key}); @@ -28,6 +28,25 @@ class _DailyTaskPlaningScreenState extends State Get.put(DailyTaskPlaningController()); final PermissionController permissionController = Get.put(PermissionController()); + final ProjectController projectController = Get.find(); + + @override + void initState() { + super.initState(); + + // Initial fetch + final projectId = projectController.selectedProjectId?.value; + if (projectId != null) { + dailyTaskPlaningController.fetchTaskData(projectId); + } + + // Reactive fetch on project ID change + ever(projectController.selectedProjectId!, (projectId) { + if (projectId != null) { + dailyTaskPlaningController.fetchTaskData(projectId); + } + }); + } @override Widget build(BuildContext context) { @@ -62,70 +81,6 @@ class _DailyTaskPlaningScreenState extends State child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - MyText.bodyMedium( - "Filter", - fontWeight: 600, - ), - Tooltip( - message: 'Project', - child: InkWell( - borderRadius: BorderRadius.circular(24), - onTap: () async { - final result = - await showModalBottomSheet>( - context: context, - isScrollControlled: true, - backgroundColor: Colors.white, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(12)), - ), - builder: (context) => DailyTaskPlaningFilter( - controller: dailyTaskPlaningController, - permissionController: permissionController, - ), - ); - - if (result != null) { - final selectedProjectId = - result['projectId'] as String?; - - if (selectedProjectId != null && - selectedProjectId != - dailyTaskPlaningController - .selectedProjectId) { - // Update the controller's selected project ID - dailyTaskPlaningController.selectedProjectId = - selectedProjectId; - - try { - // Fetch tasks for the new project - await dailyTaskPlaningController - .fetchTaskData(selectedProjectId); - } catch (e) { - debugPrint( - 'Error fetching task data: ${e.toString()}'); - } - - // Update the UI - dailyTaskPlaningController - .update(['daily_task_planing_controller']); - } - } - }, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon( - Icons.filter_list_alt, - color: Colors.blueAccent, - size: 28, - ), - ), - ), - ), - ), const SizedBox(width: 8), MyText.bodyMedium( "Refresh", @@ -137,7 +92,7 @@ class _DailyTaskPlaningScreenState extends State borderRadius: BorderRadius.circular(24), onTap: () async { final projectId = - dailyTaskPlaningController.selectedProjectId; + projectController.selectedProjectId?.value; if (projectId != null) { try { await dailyTaskPlaningController