import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/controller/task_planing/report_task_controller.dart'; import 'package:marco/helpers/theme/app_theme.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_button.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_text_style.dart'; import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/helpers/widgets/my_team_model_sheet.dart'; import 'package:intl/intl.dart'; class CommentTaskBottomSheet extends StatefulWidget { final Map taskData; final VoidCallback? onCommentSuccess; const CommentTaskBottomSheet({ super.key, required this.taskData, this.onCommentSuccess, }); @override State createState() => _CommentTaskBottomSheetState(); } class _Member { final String firstName; _Member(this.firstName); } class _CommentTaskBottomSheetState extends State with UIMixin { late ReportTaskController controller; final ScrollController _scrollController = ScrollController(); @override void initState() { super.initState(); controller = Get.put(ReportTaskController(), tag: widget.taskData['taskId'] ?? UniqueKey().toString()); final data = widget.taskData; controller.basicValidator.getController('assigned_date')?.text = data['assignedOn'] ?? ''; controller.basicValidator.getController('assigned_by')?.text = data['assignedBy'] ?? ''; controller.basicValidator.getController('work_area')?.text = data['location'] ?? ''; controller.basicValidator.getController('activity')?.text = data['activity'] ?? ''; controller.basicValidator.getController('planned_work')?.text = data['plannedWork'] ?? ''; controller.basicValidator.getController('completed_work')?.text = data['completedWork'] ?? ''; controller.basicValidator.getController('team_members')?.text = (data['teamMembers'] as List).join(', '); controller.basicValidator.getController('assigned')?.text = data['assigned'] ?? ''; controller.basicValidator.getController('task_id')?.text = data['taskId'] ?? ''; controller.basicValidator.getController('comment')?.clear(); WidgetsBinding.instance.addPostFrameCallback((_) { if (_scrollController.hasClients) { _scrollController.jumpTo(_scrollController.position.maxScrollExtent); } }); } String timeAgo(String dateString) { try { DateTime date = DateTime.parse(dateString + "Z").toLocal(); final now = DateTime.now(); final difference = now.difference(date); if (difference.inDays > 8) { return DateFormat('dd-MM-yyyy').format(date); } else if (difference.inDays >= 1) { return '${difference.inDays} day${difference.inDays > 1 ? 's' : ''} ago'; } else if (difference.inHours >= 1) { return '${difference.inHours} hr${difference.inHours > 1 ? 's' : ''} ago'; } else if (difference.inMinutes >= 1) { return '${difference.inMinutes} min${difference.inMinutes > 1 ? 's' : ''} ago'; } else { return 'just now'; } } catch (e) { print('Error parsing date: $e'); return ''; } } @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), ), child: SingleChildScrollView( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom + 24, left: 24, right: 24, top: 12, ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Drag handle Container( width: 40, height: 4, margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: Colors.grey.shade400, borderRadius: BorderRadius.circular(2), ), ), GetBuilder( tag: widget.taskData['taskId'] ?? '', builder: (controller) { return Form( key: controller.basicValidator.formKey, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ MyText.titleMedium( "Comment Task", fontWeight: 600, fontSize: 18, ), ], ), MySpacing.height(24), buildRow( "Assigned By", controller.basicValidator .getController('assigned_by') ?.text .trim(), icon: Icons.person_outline, ), buildRow( "Work Area", controller.basicValidator .getController('work_area') ?.text .trim(), icon: Icons.place_outlined, ), buildRow( "Activity", controller.basicValidator .getController('activity') ?.text .trim(), icon: Icons.assignment_outlined, ), buildRow( "Planned Work", controller.basicValidator .getController('planned_work') ?.text .trim(), icon: Icons.schedule_outlined, ), buildRow( "Completed Work", controller.basicValidator .getController('completed_work') ?.text .trim(), icon: Icons.done_all_outlined, ), buildTeamMembers(), Row( children: [ Icon(Icons.comment_outlined, size: 18, color: Colors.grey[700]), MySpacing.width(8), MyText.titleSmall( "Comment:", fontWeight: 600, ), ], ), MySpacing.height(8), TextFormField( validator: controller.basicValidator .getValidation('comment'), controller: controller.basicValidator .getController('comment'), keyboardType: TextInputType.text, decoration: InputDecoration( hintText: "eg: Work done successfully", hintStyle: MyTextStyle.bodySmall(xMuted: true), border: outlineInputBorder, enabledBorder: outlineInputBorder, focusedBorder: focusedInputBorder, contentPadding: MySpacing.all(16), isCollapsed: true, floatingLabelBehavior: FloatingLabelBehavior.never, ), ), MySpacing.height(24), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ MyButton.text( onPressed: () => Navigator.of(context).pop(), padding: MySpacing.xy(20, 16), splashColor: contentTheme.secondary.withAlpha(25), child: MyText.bodySmall('Cancel'), ), MySpacing.width(12), Obx(() { return MyButton( onPressed: controller.isLoading.value ? null : () async { if (controller.basicValidator .validateForm()) { await controller.commentTask( projectId: controller.basicValidator .getController('task_id') ?.text ?? '', comment: controller.basicValidator .getController('comment') ?.text ?? '', ); if (widget.onCommentSuccess != null) { widget.onCommentSuccess!(); } } }, elevation: 0, padding: MySpacing.xy(20, 16), backgroundColor: Colors.blueAccent, borderRadiusAll: AppStyle.buttonRadius.medium, child: controller.isLoading.value ? SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( contentTheme.onPrimary), ), ) : MyText.bodySmall( 'Comment', color: contentTheme.onPrimary, ), ); }), ], ), MySpacing.height(24), if ((widget.taskData['taskComments'] as List?) ?.isNotEmpty == true) ...[ Row( children: [ Icon(Icons.chat_bubble_outline, size: 18, color: Colors.grey[700]), MySpacing.width(8), MyText.titleSmall( "Comments", fontWeight: 600, ), ], ), MySpacing.height(12), Builder( builder: (context) { final comments = List>.from( widget.taskData['taskComments'] as List, ); comments.sort((a, b) { final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime.fromMillisecondsSinceEpoch(0); final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime.fromMillisecondsSinceEpoch(0); return bDate.compareTo( aDate); // descending: newest first }); return SizedBox( height: 300, child: ListView.builder( itemCount: comments.length, itemBuilder: (context, index) { final comment = comments[index]; final commentText = comment['text'] ?? '-'; final commentedBy = comment['commentedBy'] ?? 'Unknown'; final relativeTime = timeAgo(comment['date'] ?? ''); return Container( margin: EdgeInsets.symmetric( vertical: 6, horizontal: 8), padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey.shade200, borderRadius: BorderRadius.circular(12), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Avatar for commenter Avatar( firstName: commentedBy.split(' ').first, lastName: commentedBy .split(' ') .length > 1 ? commentedBy.split(' ').last : '', size: 32, ), SizedBox(width: 12), // Comment text and meta Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text( commentedBy, style: TextStyle( fontWeight: FontWeight.bold, color: Colors.black87, ), ), Text( relativeTime, style: TextStyle( fontSize: 12, color: Colors.black54, ), ) ], ), SizedBox(height: 6), Text( commentText, style: TextStyle( fontWeight: FontWeight.w500, color: Colors.black87, ), ), ], ), ), ], ), ); }, ), ); }, ), ], ], ), ), ); }, ), ], ), ), ); } Widget buildTeamMembers() { final teamMembersText = controller.basicValidator.getController('team_members')?.text ?? ''; final members = teamMembersText .split(',') .map((e) => e.trim()) .where((e) => e.isNotEmpty) .toList(); return Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ MyText.titleSmall( "Team Members:", fontWeight: 600, ), MySpacing.width(12), GestureDetector( onTap: () { TeamBottomSheet.show( context: context, teamMembers: members.map((name) => _Member(name)).toList(), ); }, child: SizedBox( height: 32, width: 100, child: Stack( children: [ for (int i = 0; i < members.length.clamp(0, 3); i++) Positioned( left: i * 24.0, child: Tooltip( message: members[i], child: Avatar( firstName: members[i], lastName: '', size: 32, ), ), ), if (members.length > 3) Positioned( left: 2 * 24.0, child: CircleAvatar( radius: 16, backgroundColor: Colors.grey.shade300, child: MyText.bodyMedium( '+${members.length - 3}', style: const TextStyle( fontSize: 12, color: Colors.black87), ), ), ), ], ), ), ), ], ), ); } Widget buildRow(String label, String? value, {IconData? icon}) { return Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (icon != null) Padding( padding: const EdgeInsets.only(right: 8.0, top: 2), child: Icon(icon, size: 18, color: Colors.grey[700]), ), MyText.titleSmall( "$label:", fontWeight: 600, ), MySpacing.width(12), Expanded( child: MyText.bodyMedium(value?.isNotEmpty == true ? value! : "-"), ), ], ), ); } }