diff --git a/lib/helpers/services/notification_action_handler.dart b/lib/helpers/services/notification_action_handler.dart index 40c13c6..cbb57f0 100644 --- a/lib/helpers/services/notification_action_handler.dart +++ b/lib/helpers/services/notification_action_handler.dart @@ -414,12 +414,17 @@ class NotificationActionHandler { required String notFoundMessage, required String successMessage, }) { + if (!Get.isRegistered()) { + _logger.w(notFoundMessage); + return; + } + try { final controller = Get.find(); onFound(controller); _logger.i(successMessage); } catch (e) { - _logger.w(notFoundMessage); + _logger.w('⚠️ Error updating controller: $e'); } } } diff --git a/lib/view/service_project/service_project_details_screen.dart b/lib/view/service_project/service_project_details_screen.dart index ac528f7..9a3d6d5 100644 --- a/lib/view/service_project/service_project_details_screen.dart +++ b/lib/view/service_project/service_project_details_screen.dart @@ -12,12 +12,14 @@ import 'package:on_field_work/helpers/widgets/avatar.dart'; import 'package:on_field_work/model/service_project/service_project_allocation_bottomsheet.dart'; import 'package:on_field_work/model/employees/employee_model.dart'; import 'package:on_field_work/view/service_project/jobs_tab.dart'; +import 'package:on_field_work/helpers/widgets/my_refresh_indicator.dart'; class ServiceProjectDetailsScreen extends StatefulWidget { final String projectId; final String? projectName; - const ServiceProjectDetailsScreen({super.key, required this.projectId , this.projectName}); + const ServiceProjectDetailsScreen( + {super.key, required this.projectId, this.projectName}); @override State createState() => @@ -174,13 +176,20 @@ class _ServiceProjectDetailsScreenState Widget _buildProfileTab() { final project = controller.projectDetail.value; + if (project == null) { return Center(child: MyText.bodyMedium("No project data")); } - return Padding( - padding: MySpacing.all(12), + return MyRefreshIndicator( + onRefresh: () async { + await controller.fetchProjectDetail(); + }, + backgroundColor: Colors.indigo, + color: Colors.white, child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: MySpacing.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -335,77 +344,85 @@ class _ServiceProjectDetailsScreenState return Center(child: MyText.bodyMedium("No team members found")); } - // Group team members by their role ID + // Group team members by role final Map roleGroups = {}; for (var team in controller.teamList) { roleGroups.putIfAbsent(team.teamRole.id, () => []).add(team); } - return ListView.separated( - padding: const EdgeInsets.all(12), - itemCount: roleGroups.keys.length, - separatorBuilder: (_, __) => const SizedBox(height: 12), - itemBuilder: (context, index) { - final roleId = roleGroups.keys.elementAt(index); - final teamMembers = roleGroups[roleId]!; - final roleName = teamMembers.first.teamRole.name; - - return Card( - shape: - RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), - elevation: 3, - shadowColor: Colors.black26, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Role header - MyText.bodyLarge( - roleName, - fontWeight: 700, - color: Colors.black87, - ), - const Divider(height: 20, thickness: 1), - // List of team members inside this role card - ...teamMembers.map((team) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Row( - children: [ - Avatar( - firstName: team.employee.firstName, - lastName: team.employee.lastName, - size: 32, - imageUrl: (team.employee.photo?.isNotEmpty ?? false) - ? team.employee.photo - : null, - ), - MySpacing.width(12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.titleMedium( - "${team.employee.firstName} ${team.employee.lastName}", - fontWeight: 600, - ), - MyText.bodySmall( - "Status: ${team.isActive ? 'Active' : 'Inactive'}", - color: Colors.grey[700], - ), - ], - ), - ), - ], - ), - ); - }).toList(), - ], - ), - ), - ); + return MyRefreshIndicator( + onRefresh: () async { + await controller.fetchProjectTeams(); }, + backgroundColor: Colors.indigo, + color: Colors.white, + child: ListView.separated( + padding: const EdgeInsets.all(12), + itemCount: roleGroups.keys.length, + separatorBuilder: (_, __) => const SizedBox(height: 12), + itemBuilder: (context, index) { + final roleId = roleGroups.keys.elementAt(index); + final teamMembers = roleGroups[roleId]!; + final roleName = teamMembers.first.teamRole.name; + + return Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8)), + elevation: 3, + shadowColor: Colors.black26, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Role header + MyText.bodyLarge( + roleName, + fontWeight: 700, + color: Colors.black87, + ), + const Divider(height: 20, thickness: 1), + // List of team members inside this role card + ...teamMembers.map((team) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + children: [ + Avatar( + firstName: team.employee.firstName, + lastName: team.employee.lastName, + size: 32, + imageUrl: + (team.employee.photo?.isNotEmpty ?? false) + ? team.employee.photo + : null, + ), + MySpacing.width(12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.titleMedium( + "${team.employee.firstName} ${team.employee.lastName}", + fontWeight: 600, + ), + MyText.bodySmall( + "Status: ${team.isActive ? 'Active' : 'Inactive'}", + color: Colors.grey[700], + ), + ], + ), + ), + ], + ), + ); + }).toList(), + ], + ), + ), + ); + }, + ), ); }); } @@ -457,7 +474,10 @@ class _ServiceProjectDetailsScreenState controller: _tabController, children: [ _buildProfileTab(), - JobsTab(scrollController: _jobScrollController, projectName: widget.projectName ?? '',), + JobsTab( + scrollController: _jobScrollController, + projectName: widget.projectName ?? '', + ), _buildTeamsTab(), ], );