import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/controller/dashboard/attendance_screen_controller.dart'; import 'package:marco/helpers/utils/attendance_actions.dart'; import 'package:marco/controller/project_controller.dart'; class AttendanceActionButton extends StatefulWidget { final dynamic employee; final AttendanceController attendanceController; const AttendanceActionButton({ Key? key, required this.employee, required this.attendanceController, }) : super(key: key); @override State createState() => _AttendanceActionButtonState(); } Future _showCommentBottomSheet( BuildContext context, String actionText) async { final TextEditingController commentController = TextEditingController(); String? errorText; Get.find().selectedProject?.id; return showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return StatefulBuilder( builder: (context, setModalState) { return Padding( padding: EdgeInsets.only( left: 16, right: 16, top: 24, bottom: MediaQuery.of(context).viewInsets.bottom + 24, ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Add Comment for ${capitalizeFirstLetter(actionText)}', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), const SizedBox(height: 16), TextField( controller: commentController, maxLines: 4, decoration: InputDecoration( hintText: 'Type your comment here...', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), filled: true, fillColor: Colors.grey.shade100, errorText: errorText, ), onChanged: (_) { if (errorText != null) { setModalState(() => errorText = null); } }, ), const SizedBox(height: 16), Row( children: [ Expanded( child: OutlinedButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel'), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: () { final comment = commentController.text.trim(); if (comment.isEmpty) { setModalState(() { errorText = 'Comment cannot be empty.'; }); return; } Navigator.of(context).pop(comment); }, child: const Text('Submit'), ), ), ], ), ], ), ); }, ); }, ); } String capitalizeFirstLetter(String text) { if (text.isEmpty) return text; return text[0].toUpperCase() + text.substring(1); } class _AttendanceActionButtonState extends State { late final String uniqueLogKey; @override void initState() { super.initState(); uniqueLogKey = AttendanceButtonHelper.getUniqueKey( widget.employee.employeeId, widget.employee.id); WidgetsBinding.instance.addPostFrameCallback((_) { if (!widget.attendanceController.uploadingStates .containsKey(uniqueLogKey)) { widget.attendanceController.uploadingStates[uniqueLogKey] = false.obs; } }); } Future showTimePickerForRegularization({ required BuildContext context, required DateTime checkInTime, }) async { final pickedTime = await showTimePicker( context: context, initialTime: TimeOfDay.fromDateTime(DateTime.now()), ); if (pickedTime != null) { final selectedDateTime = DateTime( checkInTime.year, checkInTime.month, checkInTime.day, pickedTime.hour, pickedTime.minute, ); final now = DateTime.now(); if (selectedDateTime.isBefore(checkInTime)) { showAppSnackbar( title: "Invalid Time", message: "Time must be after check-in.", type: SnackbarType.warning, ); return null; } else if (selectedDateTime.isAfter(now)) { showAppSnackbar( title: "Invalid Time", message: "Future time is not allowed.", type: SnackbarType.warning, ); return null; } return selectedDateTime; } return null; } void _handleButtonPressed(BuildContext context) async { widget.attendanceController.uploadingStates[uniqueLogKey]?.value = true; final projectController = Get.find(); final selectedProjectId = projectController.selectedProject?.id; if (selectedProjectId == null) { showAppSnackbar( title: "Project Required", message: "Please select a project first", type: SnackbarType.error, ); widget.attendanceController.uploadingStates[uniqueLogKey]?.value = false; return; } int updatedAction; String actionText; bool imageCapture = true; switch (widget.employee.activity) { case 0: updatedAction = 0; actionText = ButtonActions.checkIn; break; case 1: if (widget.employee.checkOut == null && AttendanceButtonHelper.isOlderThanDays( widget.employee.checkIn, 2)) { updatedAction = 2; actionText = ButtonActions.requestRegularize; imageCapture = false; } else if (widget.employee.checkOut != null && AttendanceButtonHelper.isOlderThanDays( widget.employee.checkOut, 2)) { updatedAction = 2; actionText = ButtonActions.requestRegularize; } else { updatedAction = 1; actionText = ButtonActions.checkOut; } break; case 2: updatedAction = 2; actionText = ButtonActions.requestRegularize; break; case 4: updatedAction = 0; actionText = ButtonActions.checkIn; break; default: updatedAction = 0; actionText = "Unknown Action"; break; } DateTime? selectedTime; // ✅ New condition: Yesterday Check-In + CheckOut action final isYesterdayCheckIn = widget.employee.checkIn != null && DateUtils.isSameDay( widget.employee.checkIn, DateTime.now().subtract(const Duration(days: 1)), ); if (isYesterdayCheckIn && widget.employee.checkOut == null && actionText == ButtonActions.checkOut) { selectedTime = await showTimePickerForRegularization( context: context, checkInTime: widget.employee.checkIn!, ); if (selectedTime == null) { widget.attendanceController.uploadingStates[uniqueLogKey]?.value = false; return; } } final userComment = await _showCommentBottomSheet(context, actionText); if (userComment == null || userComment.isEmpty) { widget.attendanceController.uploadingStates[uniqueLogKey]?.value = false; return; } bool success = false; if (actionText == ButtonActions.requestRegularize) { final regularizeTime = selectedTime ?? await showTimePickerForRegularization( context: context, checkInTime: widget.employee.checkIn!, ); if (regularizeTime != null) { final formattedSelectedTime = DateFormat("hh:mm a").format(regularizeTime); success = await widget.attendanceController.captureAndUploadAttendance( widget.employee.id, widget.employee.employeeId, selectedProjectId, comment: userComment, action: updatedAction, imageCapture: imageCapture, markTime: formattedSelectedTime, ); } } else if (selectedTime != null) { // ✅ If selectedTime was picked in the new condition final formattedSelectedTime = DateFormat("hh:mm a").format(selectedTime); success = await widget.attendanceController.captureAndUploadAttendance( widget.employee.id, widget.employee.employeeId, selectedProjectId, comment: userComment, action: updatedAction, imageCapture: imageCapture, markTime: formattedSelectedTime, ); } else { success = await widget.attendanceController.captureAndUploadAttendance( widget.employee.id, widget.employee.employeeId, selectedProjectId, comment: userComment, action: updatedAction, imageCapture: imageCapture, ); } showAppSnackbar( title: success ? '${capitalizeFirstLetter(actionText)} Success' : 'Error', message: success ? '${capitalizeFirstLetter(actionText)} marked successfully!' : 'Failed to ${actionText.toLowerCase()}', type: success ? SnackbarType.success : SnackbarType.error, ); widget.attendanceController.uploadingStates[uniqueLogKey]?.value = false; if (success) { widget.attendanceController.fetchEmployeesByProject(selectedProjectId); widget.attendanceController.fetchAttendanceLogs(selectedProjectId); await widget.attendanceController .fetchRegularizationLogs(selectedProjectId); await widget.attendanceController.fetchProjectData(selectedProjectId); widget.attendanceController.update(); } } @override Widget build(BuildContext context) { return Obx(() { final isUploading = widget.attendanceController.uploadingStates[uniqueLogKey]?.value ?? false; final isYesterday = AttendanceButtonHelper.isLogFromYesterday( widget.employee.checkIn, widget.employee.checkOut); final isTodayApproved = AttendanceButtonHelper.isTodayApproved( widget.employee.activity, widget.employee.checkIn); final isApprovedButNotToday = AttendanceButtonHelper.isApprovedButNotToday( widget.employee.activity, isTodayApproved); final isButtonDisabled = AttendanceButtonHelper.isButtonDisabled( isUploading: isUploading, isYesterday: isYesterday, activity: widget.employee.activity, isApprovedButNotToday: isApprovedButNotToday, ); final buttonText = AttendanceButtonHelper.getButtonText( activity: widget.employee.activity, checkIn: widget.employee.checkIn, checkOut: widget.employee.checkOut, isTodayApproved: isTodayApproved, ); final buttonColor = AttendanceButtonHelper.getButtonColor( isYesterday: isYesterday, isTodayApproved: isTodayApproved, activity: widget.employee.activity, ); return AttendanceActionButtonUI( isUploading: isUploading, isButtonDisabled: isButtonDisabled, buttonText: buttonText, buttonColor: buttonColor, onPressed: isButtonDisabled ? null : () => _handleButtonPressed(context), ); }); } } class AttendanceActionButtonUI extends StatelessWidget { final bool isUploading; final bool isButtonDisabled; final String buttonText; final Color buttonColor; final VoidCallback? onPressed; const AttendanceActionButtonUI({ Key? key, required this.isUploading, required this.isButtonDisabled, required this.buttonText, required this.buttonColor, required this.onPressed, }) : super(key: key); @override Widget build(BuildContext context) { return SizedBox( height: 30, child: ElevatedButton( onPressed: isButtonDisabled ? null : onPressed, style: ElevatedButton.styleFrom( backgroundColor: buttonColor, padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), textStyle: const TextStyle(fontSize: 12), ), child: isUploading ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Row( mainAxisSize: MainAxisSize.min, children: [ if (buttonText.toLowerCase() == 'approved') ...[ const Icon(Icons.check, size: 16, color: Colors.green), const SizedBox(width: 4), ] else if (buttonText.toLowerCase() == 'rejected') ...[ const Icon(Icons.close, size: 16, color: Colors.red), const SizedBox(width: 4), ] else if (buttonText.toLowerCase() == 'requested') ...[ const Icon(Icons.hourglass_top, size: 16, color: Colors.orange), const SizedBox(width: 4), ], Flexible( child: Text( buttonText, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 12), ), ), ], ), ), ); } }