import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/controller/attendance/attendance_screen_controller.dart'; import 'package:marco/helpers/utils/date_time_utils.dart'; import 'package:marco/helpers/widgets/avatar.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/helpers/widgets/my_custom_skeleton.dart'; import 'package:marco/model/attendance/log_details_view.dart'; import 'package:marco/model/attendance/attendence_action_button.dart'; import 'package:marco/helpers/utils/attendance_actions.dart'; class AttendanceLogsTab extends StatefulWidget { final AttendanceController controller; const AttendanceLogsTab({super.key, required this.controller}); @override State createState() => _AttendanceLogsTabState(); } class _AttendanceLogsTabState extends State { Widget _buildStatusHeader() { return Obx(() { if (!widget.controller.showPendingOnly.value) { return const SizedBox.shrink(); } return Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), color: Colors.orange.shade50, child: Row( children: [ const Icon(Icons.pending_actions, color: Colors.orange, size: 18), const SizedBox(width: 8), const Expanded( child: Text( "Showing Pending Actions Only", style: TextStyle( color: Colors.orange, fontWeight: FontWeight.w600, ), ), ), InkWell( onTap: () => widget.controller.showPendingOnly.value = false, child: const Icon(Icons.close, size: 18, color: Colors.orange), ), ], ), ); }); } /// Return button text priority for sorting inside same date int _getActionPriority(employee) { final text = AttendanceButtonHelper.getButtonText( activity: employee.activity, checkIn: employee.checkIn, checkOut: employee.checkOut, isTodayApproved: AttendanceButtonHelper.isTodayApproved( employee.activity, employee.checkIn, ), ).toLowerCase(); final isYesterdayCheckIn = employee.checkIn != null && DateUtils.isSameDay( employee.checkIn, DateTime.now().subtract(const Duration(days: 1)), ); final isMissingCheckout = employee.checkOut == null; final isCheckoutAction = text.contains("checkout") || text.contains("check out"); int priority; if (isYesterdayCheckIn && isMissingCheckout && isCheckoutAction) { priority = 0; } else if (isCheckoutAction) { priority = 0; } else if (text.contains("regular")) { priority = 1; } else if (text == "requested") { priority = 2; } else if (text == "approved") { priority = 3; } else if (text == "rejected") { priority = 4; } else { priority = 5; } return priority; } @override Widget build(BuildContext context) { return Obx(() { final allLogs = List.of(widget.controller.filteredLogs); // Filter logs if "pending only" final showPendingOnly = widget.controller.showPendingOnly.value; final filteredLogs = showPendingOnly ? allLogs .where((emp) => emp.activity == 1 ) .toList() : allLogs; // Group logs by date string final groupedLogs = >{}; for (var log in filteredLogs) { final dateKey = log.checkIn != null ? DateTimeUtils.formatDate(log.checkIn!, 'dd MMM yyyy') : 'Unknown'; groupedLogs.putIfAbsent(dateKey, () => []).add(log); } // Sort dates (latest first) final sortedDates = groupedLogs.keys.toList() ..sort((a, b) { final da = DateTimeUtils.parseDate(a, 'dd MMM yyyy') ?? DateTime(0); final db = DateTimeUtils.parseDate(b, 'dd MMM yyyy') ?? DateTime(0); return db.compareTo(da); }); final dateRangeText = widget.controller.startDateAttendance != null && widget.controller.endDateAttendance != null ? '${DateTimeUtils.formatDate(widget.controller.startDateAttendance!, 'dd MMM yyyy')} - ' '${DateTimeUtils.formatDate(widget.controller.endDateAttendance!, 'dd MMM yyyy')}' : 'Select date range'; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header row Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MyText.titleMedium("Attendance Logs", fontWeight: 600), widget.controller.isLoading.value ? SkeletonLoaders.dateSkeletonLoader() : MyText.bodySmall( dateRangeText, fontWeight: 600, color: Colors.grey[700], overflow: TextOverflow.ellipsis, ), ], ), ), // Pending-only header _buildStatusHeader(), MySpacing.height(8), // Content: loader, empty, or logs if (widget.controller.isLoadingAttendanceLogs.value) SkeletonLoaders.employeeListSkeletonLoader() else if (filteredLogs.isEmpty) SizedBox( height: 120, child: Center( child: Text(showPendingOnly ? "No Pending Actions Found" : "No Attendance Logs Found for this Project"), ), ) else MyCard.bordered( paddingAll: 8, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (final date in sortedDates) ...[ Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: MyText.bodyMedium(date, fontWeight: 700), ), // Sort employees inside this date by action priority for (final emp in (groupedLogs[date]! ..sort( (a, b) => _getActionPriority(a) .compareTo(_getActionPriority(b)), ))) ...[ MyContainer( paddingAll: 8, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Avatar( firstName: emp.firstName, lastName: emp.lastName, size: 31, ), MySpacing.width(16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Flexible( child: MyText.bodyMedium( emp.name, fontWeight: 600, overflow: TextOverflow.ellipsis, ), ), MySpacing.width(6), Flexible( child: MyText.bodySmall( '(${emp.designation})', fontWeight: 600, color: Colors.grey[700], overflow: TextOverflow.ellipsis, ), ), ], ), MySpacing.height(8), if (emp.checkIn != null || emp.checkOut != null) Row( children: [ if (emp.checkIn != null) ...[ const Icon(Icons.arrow_circle_right, size: 16, color: Colors.green), MySpacing.width(4), MyText.bodySmall( DateTimeUtils.formatDate( emp.checkIn!, 'hh:mm a'), fontWeight: 600, ), MySpacing.width(16), ], if (emp.checkOut != null) ...[ const Icon(Icons.arrow_circle_left, size: 16, color: Colors.red), MySpacing.width(4), MyText.bodySmall( DateTimeUtils.formatDate( emp.checkOut!, 'hh:mm a'), fontWeight: 600, ), ], ], ), MySpacing.height(12), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ AttendanceActionButton( employee: emp, attendanceController: widget.controller, ), MySpacing.width(8), AttendanceLogViewButton( employee: emp, attendanceController: widget.controller, ), ], ), ], ), ), ], ), ), Divider(color: Colors.grey.withOpacity(0.3)), ], ], ], ), ), ], ); }); } }