import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/controller/task_planning/report_task_action_controller.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.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:marco/helpers/widgets/image_viewer_dialog.dart'; import 'package:marco/model/dailyTaskPlanning/create_task_botom_sheet.dart'; import 'package:marco/model/dailyTaskPlanning/report_action_widgets.dart'; import 'package:marco/helpers/utils/base_bottom_sheet.dart'; class ReportActionBottomSheet extends StatefulWidget { final Map taskData; final VoidCallback? onCommentSuccess; final String taskDataId; final String workAreaId; final String activityId; final VoidCallback onReportSuccess; const ReportActionBottomSheet({ super.key, required this.taskData, this.onCommentSuccess, required this.taskDataId, required this.workAreaId, required this.activityId, required this.onReportSuccess, }); @override State createState() => _ReportActionBottomSheetState(); } class _Member { final String firstName; _Member(this.firstName); } class _ReportActionBottomSheetState extends State with UIMixin { late ReportTaskActionController controller; @override void initState() { super.initState(); controller = Get.put( ReportTaskActionController(), tag: widget.taskData['taskId'] ?? '', ); controller.fetchWorkStatuses(); final data = widget.taskData; controller.basicValidator.getController('approved_task')?.text = data['approvedTask']?.toString() ?? ''; 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 = widget.taskDataId; controller.basicValidator.getController('comment')?.clear(); controller.selectedImages.clear(); } @override Widget build(BuildContext context) { return GetBuilder( tag: widget.taskData['taskId'] ?? '', builder: (controller) { return BaseBottomSheet( title: "Take Report Action", isSubmitting: controller.isLoading.value, onCancel: () => Navigator.of(context).pop(), onSubmit: () async {}, // not used since buttons moved showButtons: false, // disable internal buttons child: _buildForm(context, controller), ); }, ); } Widget _buildForm( BuildContext context, ReportTaskActionController controller) { return Form( key: controller.basicValidator.formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 📋 Task Details buildRow("Assigned By", controller.basicValidator.getController('assigned_by')?.text, icon: Icons.person_outline), buildRow("Work Area", controller.basicValidator.getController('work_area')?.text, icon: Icons.place_outlined), buildRow("Activity", controller.basicValidator.getController('activity')?.text, icon: Icons.assignment_outlined), buildRow("Planned Work", controller.basicValidator.getController('planned_work')?.text, icon: Icons.schedule_outlined), buildRow("Completed Work", controller.basicValidator.getController('completed_work')?.text, icon: Icons.done_all_outlined), buildTeamMembers(), MySpacing.height(8), // ✅ Approved Task Field Row( children: [ Icon(Icons.check_circle_outline, size: 18, color: Colors.grey[700]), MySpacing.width(8), MyText.titleSmall("Approved Task:", fontWeight: 600), ], ), MySpacing.height(10), TextFormField( controller: controller.basicValidator.getController('approved_task'), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) return 'Required'; if (int.tryParse(value) == null) return 'Must be a number'; return null; }, decoration: InputDecoration( hintText: "eg: 5", hintStyle: MyTextStyle.bodySmall(xMuted: true), border: outlineInputBorder, contentPadding: MySpacing.all(16), floatingLabelBehavior: FloatingLabelBehavior.never, ), ), MySpacing.height(10), if ((widget.taskData['reportedPreSignedUrls'] as List?) ?.isNotEmpty == true) buildReportedImagesSection( imageUrls: List.from( widget.taskData['reportedPreSignedUrls'] ?? []), context: context, ), MySpacing.height(10), MyText.titleSmall("Report Actions", fontWeight: 600), MySpacing.height(10), Obx(() { if (controller.isLoadingWorkStatus.value) return const CircularProgressIndicator(); return PopupMenuButton( onSelected: (String value) { controller.selectedWorkStatusName.value = value; controller.showAddTaskCheckbox.value = true; }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), itemBuilder: (BuildContext context) { return controller.workStatus.map((status) { return PopupMenuItem( value: status.name, child: Row( children: [ Radio( value: status.name, groupValue: controller.selectedWorkStatusName.value, onChanged: (_) => Navigator.pop(context, status.name), ), const SizedBox(width: 8), MyText.bodySmall(status.name), ], ), ); }).toList(); }, child: Container( padding: MySpacing.xy(16, 12), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade400), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MyText.bodySmall( controller.selectedWorkStatusName.value.isEmpty ? "Select Work Status" : controller.selectedWorkStatusName.value, color: Colors.black87, ), const Icon(Icons.arrow_drop_down, size: 20), ], ), ), ); }), MySpacing.height(10), Obx(() { if (!controller.showAddTaskCheckbox.value) return const SizedBox.shrink(); return Theme( data: Theme.of(context).copyWith( checkboxTheme: CheckboxThemeData( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), ), side: const BorderSide( color: Colors.black, width: 2), fillColor: MaterialStateProperty.resolveWith((states) { if (states.contains(MaterialState.selected)) { return Colors.blueAccent; } return Colors.white; }), checkColor: MaterialStateProperty.all(Colors.white), ), ), child: CheckboxListTile( title: MyText.titleSmall("Add new task", fontWeight: 600), value: controller.isAddTaskChecked.value, onChanged: (val) => controller.isAddTaskChecked.value = val ?? false, controlAffinity: ListTileControlAffinity.leading, contentPadding: EdgeInsets.zero, ), ); }), MySpacing.height(24), // ✏️ Comment Field 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, contentPadding: MySpacing.all(16), floatingLabelBehavior: FloatingLabelBehavior.never, ), ), MySpacing.height(16), // 📸 Image Attachments Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.camera_alt_outlined, size: 18, color: Colors.grey[700]), MySpacing.width(8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.titleSmall("Attach Photos:", fontWeight: 600), MySpacing.height(12), ], ), ), ], ), Obx(() { final images = controller.selectedImages; return buildImagePickerSection( images: images, onCameraTap: () => controller.pickImages(fromCamera: true), onUploadTap: () => controller.pickImages(fromCamera: false), onRemoveImage: (index) => controller.removeImageAt(index), onPreviewImage: (index) { showDialog( context: context, builder: (_) => ImageViewerDialog( imageSources: images, initialIndex: index, ), ); }, ); }), MySpacing.height(12), // ✅ Submit/Cancel Buttons moved here Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.close, color: Colors.white), label: MyText.bodyMedium("Cancel", color: Colors.white, fontWeight: 600), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), padding: const EdgeInsets.symmetric(vertical: 8), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton.icon( onPressed: controller.isLoading.value ? null : () async { if (controller.basicValidator.validateForm()) { final selectedStatusName = controller.selectedWorkStatusName.value; final selectedStatus = controller.workStatus .firstWhereOrNull( (s) => s.name == selectedStatusName); final reportActionId = selectedStatus?.id.toString() ?? ''; final approvedTaskCount = controller.basicValidator .getController('approved_task') ?.text .trim() ?? ''; final shouldShowAddTaskSheet = controller.isAddTaskChecked.value; final success = await controller.approveTask( projectId: controller.basicValidator .getController('task_id') ?.text ?? '', comment: controller.basicValidator .getController('comment') ?.text ?? '', images: controller.selectedImages, reportActionId: reportActionId, approvedTaskCount: approvedTaskCount, ); if (success) { Navigator.of(context).pop(); if (shouldShowAddTaskSheet) { await Future.delayed( const Duration(milliseconds: 100)); showCreateTaskBottomSheet( workArea: widget.taskData['location'] ?? '', activity: widget.taskData['activity'] ?? '', completedWork: widget.taskData['completedWork'] ?? '', unit: widget.taskData['unit'] ?? '', parentTaskId: widget.taskDataId, plannedTask: int.tryParse( widget.taskData['plannedWork'] ?? '0') ?? 0, activityId: widget.activityId, workAreaId: widget.workAreaId, onSubmit: () => Navigator.of(context).pop(), onCategoryChanged: (category) {}, ); } widget.onReportSuccess.call(); } } }, icon: const Icon(Icons.check_circle_outline, color: Colors.white), label: MyText.bodyMedium( controller.isLoading.value ? "Submitting..." : "Submit", color: Colors.white, fontWeight: 600, ), style: ElevatedButton.styleFrom( backgroundColor: Colors.indigo, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), padding: const EdgeInsets.symmetric(vertical: 8), ), ), ), ], ), MySpacing.height(12), // 💬 Previous Comments List (only below submit) if ((widget.taskData['taskComments'] as List?)?.isNotEmpty == true) ...[ Row( children: [ MySpacing.width(10), Icon(Icons.chat_bubble_outline, size: 18, color: Colors.grey[700]), MySpacing.width(8), MyText.titleSmall("Comments", fontWeight: 600), ], ), MySpacing.height(12), buildCommentList( List>.from( widget.taskData['taskComments'] as List), context, timeAgo, ), ], ], ), ); } 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), ), ), ), ], ), ), ), ], ), ); } }