import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:marco/helpers/theme/app_theme.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/utils/my_shadow.dart'; import 'package:marco/helpers/widgets/my_breadcrumb.dart'; import 'package:marco/helpers/widgets/my_breadcrumb_item.dart'; import 'package:marco/helpers/widgets/my_card.dart'; import 'package:marco/helpers/widgets/my_container.dart'; 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/controller/dashboard/daily_task_controller.dart'; 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'; class DailyProgressReportScreen extends StatefulWidget { const DailyProgressReportScreen({super.key}); @override State createState() => _DailyProgressReportScreenState(); } class TaskChartData { final String label; final num value; final Color color; TaskChartData(this.label, this.value, this.color); } class _DailyProgressReportScreenState extends State with UIMixin { final DailyTaskController dailyTaskController = Get.put(DailyTaskController()); final PermissionController permissionController = Get.put(PermissionController()); @override Widget build(BuildContext context) { return Layout( child: GetBuilder( init: dailyTaskController, tag: 'daily_progress_report_controller', builder: (controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(), MySpacing.height(flexSpacing), _buildActionBar(), Padding( padding: MySpacing.x(flexSpacing), child: _buildDailyProgressReportTab(), ), ], ); }, ), ); } Widget _buildHeader() { return Padding( padding: MySpacing.x(flexSpacing), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MyText.titleMedium("Daily Progress Report", fontSize: 18, fontWeight: 600), MyBreadcrumb( children: [ MyBreadcrumbItem(name: 'Dashboard'), MyBreadcrumbItem(name: 'Daily Progress Report', active: true), ], ), ], ), ); } Widget _buildActionBar() { return Padding( padding: MySpacing.x(flexSpacing), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ _buildActionItem( label: "Filter", icon: Icons.filter_list_alt, tooltip: 'Filter Project', color: Colors.blueAccent, onTap: _openFilterSheet, ), const SizedBox(width: 8), _buildActionItem( label: "Refresh", icon: Icons.refresh, tooltip: 'Refresh Data', color: Colors.green, onTap: _refreshData, ), ], ), ); } Widget _buildActionItem({ required String label, required IconData icon, required String tooltip, required VoidCallback onTap, required Color color, }) { return Row( children: [ MyText.bodyMedium(label, fontWeight: 600), Tooltip( message: tooltip, child: InkWell( borderRadius: BorderRadius.circular(24), onTap: onTap, child: MouseRegion( cursor: SystemMouseCursors.click, child: Padding( padding: const EdgeInsets.all(8.0), child: Icon(icon, color: color, size: 28), ), ), ), ), ], ); } Future _openFilterSheet() async { final result = await showModalBottomSheet>( context: context, isScrollControlled: true, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(12)), ), builder: (context) => DailyProgressReportFilter( controller: dailyTaskController, permissionController: permissionController, ), ); if (result != null) { final selectedProjectId = result['projectId'] as String?; if (selectedProjectId != null && selectedProjectId != dailyTaskController.selectedProjectId) { dailyTaskController.selectedProjectId = selectedProjectId; try { await dailyTaskController.fetchProjects(); } catch (e) { debugPrint('Error fetching projects: $e'); } dailyTaskController.update(['daily_progress_report_controller']); } } } Future _refreshData() async { final projectId = dailyTaskController.selectedProjectId; if (projectId != null) { try { await dailyTaskController.fetchTaskData(projectId); } catch (e) { debugPrint('Error refreshing task data: $e'); } } } void _showTeamMembersBottomSheet(List members) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, isDismissible: true, enableDrag: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(12)), ), builder: (context) { return GestureDetector( onTap: () {}, child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), ), padding: const EdgeInsets.fromLTRB(16, 24, 16, 24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.titleMedium( 'Team Members', fontWeight: 600, ), const SizedBox(height: 8), const Divider(thickness: 1), const SizedBox(height: 8), ...members.map((member) { final firstName = member.firstName ?? 'Unnamed'; final lastName = member.lastName ?? 'User'; return ListTile( contentPadding: EdgeInsets.zero, leading: Avatar( firstName: firstName, lastName: lastName, size: 31, ), title: MyText.bodyMedium( '$firstName $lastName', fontWeight: 600, ), ); }), const SizedBox(height: 8), ], ), ), ); }, ); } Widget _buildDailyProgressReportTab() { return Obx(() { final isLoading = dailyTaskController.isLoading.value; final groupedTasks = dailyTaskController.groupedDailyTasks; if (isLoading) { return const Center(child: CircularProgressIndicator()); } if (groupedTasks.isEmpty) { return Center( child: MyText.bodySmall( "No Progress Report Found", fontWeight: 600, ), ); } final sortedDates = groupedTasks.keys.toList() ..sort((a, b) => b.compareTo(a)); return MyCard.bordered( borderRadiusAll: 4, border: Border.all(color: Colors.grey.withOpacity(0.2)), shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom), paddingAll: 8, child: ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: sortedDates.length, separatorBuilder: (_, __) => Column( children: [ const SizedBox(height: 12), Divider(color: Colors.grey.withOpacity(0.3), thickness: 1), const SizedBox(height: 12), ], ), itemBuilder: (context, dateIndex) { final dateKey = sortedDates[dateIndex]; final tasksForDate = groupedTasks[dateKey]!; final date = DateTime.tryParse(dateKey); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ GestureDetector( onTap: () => dailyTaskController.toggleDate(dateKey), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MyText.bodyMedium( date != null ? DateFormat('dd MMM yyyy').format(date) : dateKey, fontWeight: 700, ), Obx(() => Icon( dailyTaskController.expandedDates.contains(dateKey) ? Icons.remove_circle : Icons.add_circle, color: Colors.blueAccent, )), ], ), ), Obx(() { if (!dailyTaskController.expandedDates.contains(dateKey)) { return const SizedBox.shrink(); } return Column( children: tasksForDate.asMap().entries.map((entry) { final task = entry.value; final index = entry.key; final activityName = task.workItem?.activityMaster?.activityName ?? 'N/A'; final location = [ task.workItem?.workArea?.floor?.building?.name, task.workItem?.workArea?.floor?.floorName, task.workItem?.workArea?.areaName ].where((e) => e?.isNotEmpty ?? false).join(' > '); final planned = task.plannedTask; final completed = task.completedTask; final progress = (planned != 0) ? (completed / planned).clamp(0.0, 1.0) : 0.0; return Column( children: [ Padding( padding: const EdgeInsets.only(bottom: 8), child: MyContainer( paddingAll: 12, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodyMedium(activityName, fontWeight: 600), const SizedBox(height: 2), MyText.bodySmall(location, color: Colors.grey), const SizedBox(height: 8), GestureDetector( onTap: () => _showTeamMembersBottomSheet( task.teamMembers), child: Row( children: [ const Icon(Icons.group, size: 18, color: Colors.blueAccent), const SizedBox(width: 6), MyText.bodyMedium('Team', color: Colors.blueAccent, fontWeight: 600), ], ), ), const SizedBox(height: 8), MyText.bodySmall( "Completed: $completed / $planned", fontWeight: 600, color: Colors.black87, ), const SizedBox(height: 6), Stack( children: [ Container( height: 5, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(6), ), ), FractionallySizedBox( widthFactor: progress, child: Container( height: 5, decoration: BoxDecoration( color: progress >= 1.0 ? Colors.green : progress >= 0.5 ? Colors.amber : Colors.red, borderRadius: BorderRadius.circular(6), ), ), ), ], ), const SizedBox(height: 4), MyText.bodySmall( "${(progress * 100).toStringAsFixed(1)}%", fontWeight: 500, color: progress >= 1.0 ? Colors.green[700] : progress >= 0.5 ? Colors.amber[800] : Colors.red[700], ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ if (task.reportedDate == null || task.reportedDate.toString().isEmpty) OutlinedButton.icon( icon: const Icon(Icons.report, size: 18, color: Colors.blueAccent), label: const Text('Report', style: TextStyle( color: Colors.blueAccent)), style: OutlinedButton.styleFrom( side: const BorderSide( color: Colors.blueAccent), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), textStyle: const TextStyle(fontSize: 14), ), onPressed: () { final activityName = task .workItem ?.activityMaster ?.activityName ?? 'N/A'; final assigned = '${(task.plannedTask - completed)}'; final assignedBy = "${task.assignedBy.firstName} ${task.assignedBy.lastName ?? ''}"; final assignedOn = DateFormat('dd-MM-yyyy').format( task.assignmentDate); final taskId = task.id; final location = [ task.workItem?.workArea?.floor ?.building?.name, task.workItem?.workArea?.floor ?.floorName, task.workItem?.workArea?.areaName, ] .where((e) => e != null && e.isNotEmpty) .join(' > '); final teamMembers = task.teamMembers .map((e) => e.firstName) .toList(); final taskData = { 'activity': activityName, 'assigned': assigned, 'taskId': taskId, 'assignedBy': assignedBy, 'completed': completed, 'assignedOn': assignedOn, 'location': location, 'teamSize': task.teamMembers.length, 'teamMembers': teamMembers, }; showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular( 16)), ), builder: (_) => Padding( padding: MediaQuery.of(context) .viewInsets, child: ReportTaskBottomSheet( taskData: taskData), ), ); }, ), const SizedBox(width: 8), OutlinedButton.icon( icon: const Icon(Icons.comment, size: 18, color: Colors.blueAccent), label: const Text('Comment', style: TextStyle( color: Colors.blueAccent)), style: OutlinedButton.styleFrom( side: const BorderSide( color: Colors.blueAccent), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), textStyle: const TextStyle(fontSize: 14), ), onPressed: () { final activityName = task .workItem ?.activityMaster ?.activityName ?? 'N/A'; final plannedTask = task.plannedTask; final completed = task.completedTask; final assigned = '${(plannedTask - completed)}'; final plannedWork = plannedTask.toString(); final completedWork = completed.toString(); final assignedBy = "${task.assignedBy.firstName} ${task.assignedBy.lastName ?? ''}"; final assignedOn = DateFormat('yyyy-MM-dd') .format(task.assignmentDate); final taskId = task.id; final location = [ task.workItem?.workArea?.floor ?.building?.name, task.workItem?.workArea?.floor ?.floorName, task.workItem?.workArea?.areaName, ] .where((e) => e != null && e.isNotEmpty) .join(' > '); final teamMembers = task.teamMembers .map((e) => '${e.firstName} ${e.lastName}') .toList(); final taskComments = task.comments.map((comment) { final isoDate = comment.timestamp .toIso8601String(); final commenterName = comment .commentedBy .firstName .isNotEmpty ? "${comment.commentedBy.firstName} ${comment.commentedBy.lastName ?? ''}" .trim() : "Unknown"; return { 'text': comment.comment, 'date': isoDate, 'commentedBy': commenterName, }; }).toList(); final taskData = { 'activity': activityName, 'assigned': assigned, 'taskId': taskId, 'assignedBy': assignedBy, 'completedWork': completedWork, 'plannedWork': plannedWork, 'assignedOn': assignedOn, 'location': location, 'teamSize': task.teamMembers.length, 'teamMembers': teamMembers, 'taskComments': taskComments, }; showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (_) => CommentTaskBottomSheet( taskData: taskData, onCommentSuccess: () { _refreshData(); Navigator.of(context).pop(); }, ), ); }, ), ], ) ], ), ), ), if (index != tasksForDate.length - 1) Divider( color: Colors.grey.withOpacity(0.2), thickness: 1, height: 1), ], ); }).toList(), ); }) ], ); }, ), ); }); } }