diff --git a/lib/controller/dashboard/attendance_screen_controller.dart b/lib/controller/dashboard/attendance_screen_controller.dart index ff69c00..7c41018 100644 --- a/lib/controller/dashboard/attendance_screen_controller.dart +++ b/lib/controller/dashboard/attendance_screen_controller.dart @@ -260,20 +260,28 @@ class AttendanceController extends GetxController { } } - Future fetchLogsView(String? id) async { - if (id == null) return; + Future fetchLogsView(String? id) async { + if (id == null) return; - isLoading.value = true; - final response = await ApiService.getAttendanceLogView(id); - isLoading.value = false; + isLoading.value = true; + final response = await ApiService.getAttendanceLogView(id); + isLoading.value = false; - if (response != null) { - attendenceLogsView = - response.map((json) => AttendanceLogViewModel.fromJson(json)).toList(); - log.i("Attendance log view fetched for ID: $id"); - update(); - } else { - log.e("Failed to fetch attendance log view for ID $id"); - } + if (response != null) { + attendenceLogsView = response + .map((json) => AttendanceLogViewModel.fromJson(json)) + .toList(); + + // Sort by activityTime field (latest first) + attendenceLogsView.sort((a, b) { + if (a.activityTime == null || b.activityTime == null) return 0; // Handle null values if any + return b.activityTime!.compareTo(a.activityTime!); // Sort descending (latest first) + }); + + log.i("Attendance log view fetched for ID: $id"); + update(); + } else { + log.e("Failed to fetch attendance log view for ID $id"); } } +} diff --git a/lib/view/dashboard/attendanceScreen.dart b/lib/view/dashboard/attendanceScreen.dart index b2849a7..5960149 100644 --- a/lib/view/dashboard/attendanceScreen.dart +++ b/lib/view/dashboard/attendanceScreen.dart @@ -462,8 +462,19 @@ class _AttendanceScreenState extends State with UIMixin { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - MyText.titleMedium("Attendance Log Details", - fontWeight: 700), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + MyText.titleMedium( + "Attendance Log Details", + fontWeight: 700, + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () => Navigator.pop(context), + ), + ], + ), const SizedBox(height: 16), if (attendanceController .attendenceLogsView.isNotEmpty) ...[ @@ -602,14 +613,6 @@ class _AttendanceScreenState extends State with UIMixin { ], ), )), - const SizedBox(height: 16), - Align( - alignment: Alignment.centerRight, - child: ElevatedButton( - onPressed: () => Navigator.pop(context), - child: const Text("Close"), - ), - ), ], ), ), @@ -860,108 +863,114 @@ class _AttendanceScreenState extends State with UIMixin { } Widget regularizationTab(BuildContext context) { - final attendanceController = Get.find(); + final attendanceController = Get.find(); - final columns = [ - DataColumn(label: MyText.labelLarge('Name', color: contentTheme.primary)), + final columns = [ + DataColumn(label: MyText.labelLarge('Name', color: contentTheme.primary)), DataColumn( label: MyText.labelLarge('Check-In', color: contentTheme.primary)), DataColumn( label: MyText.labelLarge('Check-Out', color: contentTheme.primary)), DataColumn( label: MyText.labelLarge('Action', color: contentTheme.primary)), - ]; + ]; - final rows = attendanceController.regularizationLogs - .mapIndexed((index, log) { - final uniqueLogKey = '${log.id}-${log.employeeId}'; // Unique key for each log - final isUploading = attendanceController.uploadingStates[uniqueLogKey]?.value ?? false; // Check the upload state + final rows = + attendanceController.regularizationLogs.mapIndexed((index, log) { + final uniqueLogKey = + '${log.id}-${log.employeeId}'; // Unique key for each log + final isUploading = + attendanceController.uploadingStates[uniqueLogKey]?.value ?? + false; // Check the upload state - return DataRow(cells: [ - DataCell( - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MyText.bodyMedium(log.name, fontWeight: 600), - SizedBox(height: 2), - MyText.bodySmall(log.role, color: Colors.grey), - ], - ), + return DataRow(cells: [ + DataCell( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MyText.bodyMedium(log.name, fontWeight: 600), + SizedBox(height: 2), + MyText.bodySmall(log.role, color: Colors.grey), + ], ), - DataCell( - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.bodyMedium( - log.checkIn != null - ? DateFormat('dd/MMM/yyyy').format(log.checkIn!) - : '-', - fontWeight: 600, - ), - MyText.bodyMedium( - log.checkIn != null - ? DateFormat('hh:mm a').format(log.checkIn!) - : '', - fontWeight: 600, - ), - ], - ), + ), + DataCell( + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium( + log.checkIn != null + ? DateFormat('dd/MMM/yyyy').format(log.checkIn!) + : '-', + fontWeight: 600, + ), + MyText.bodyMedium( + log.checkIn != null + ? DateFormat('hh:mm a').format(log.checkIn!) + : '', + fontWeight: 600, + ), + ], ), - DataCell( - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.bodyMedium( - log.checkOut != null - ? DateFormat('dd MMM yyyy').format(log.checkOut!) - : '-', - fontWeight: 600, - ), - MyText.bodyMedium( - log.checkOut != null - ? DateFormat('hh:mm a').format(log.checkOut!) - : '', - fontWeight: 600, - ), - ], - ), + ), + DataCell( + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium( + log.checkOut != null + ? DateFormat('dd MMM yyyy').format(log.checkOut!) + : '-', + fontWeight: 600, + ), + MyText.bodyMedium( + log.checkOut != null + ? DateFormat('hh:mm a').format(log.checkOut!) + : '', + fontWeight: 600, + ), + ], ), - DataCell( - Row( - children: [ - // Approve Button - ElevatedButton( - onPressed: isUploading // Disable button if uploading - ? null - : () async { - if (attendanceController.selectedProjectId == null) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Please select a project first")), - ); - return; - } - - attendanceController.uploadingStates[uniqueLogKey]?.value = true; // Start loading - final success = await attendanceController.captureAndUploadAttendance( - log.id, - log.employeeId, - attendanceController.selectedProjectId!, - comment: "Accepted", - action: 4, // Approve action - imageCapture: false, - ); - + ), + DataCell( + Row( + children: [ + // Approve Button + ElevatedButton( + onPressed: isUploading // Disable button if uploading + ? null + : () async { + if (attendanceController.selectedProjectId == null) { ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Please select a project first")), + ); + return; + } + + attendanceController.uploadingStates[uniqueLogKey] + ?.value = true; // Start loading + final success = await attendanceController + .captureAndUploadAttendance( + log.id, + log.employeeId, + attendanceController.selectedProjectId!, + comment: "Accepted", + action: 4, // Approve action + imageCapture: false, + ); + + ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(success ? 'Approval marked successfully!' : 'Failed to mark approval.')), - ); + ); - if (success) { + if (success) { attendanceController.fetchEmployeesByProject( attendanceController.selectedProjectId!); attendanceController.fetchAttendanceLogs( @@ -970,57 +979,63 @@ class _AttendanceScreenState extends State with UIMixin { attendanceController.selectedProjectId!); await attendanceController.fetchProjectData( attendanceController.selectedProjectId!); - } + } - attendanceController.uploadingStates[uniqueLogKey]?.value = false; // End loading - }, - style: ElevatedButton.styleFrom( - backgroundColor: AttendanceActionColors - .colors[ButtonActions.approve], - padding: const EdgeInsets.symmetric( - vertical: 4, horizontal: 6), - minimumSize: const Size(60, 20), - textStyle: const TextStyle(fontSize: 12), - ), - child: isUploading - ? const CircularProgressIndicator(strokeWidth: 2) // Show loading indicator while uploading - : const Text("Approve"), + attendanceController.uploadingStates[uniqueLogKey] + ?.value = false; // End loading + }, + style: ElevatedButton.styleFrom( + backgroundColor: + AttendanceActionColors.colors[ButtonActions.approve], + padding: + const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + minimumSize: const Size(60, 20), + textStyle: const TextStyle(fontSize: 12), ), + child: isUploading + ? const CircularProgressIndicator( + strokeWidth: + 2) // Show loading indicator while uploading + : const Text("Approve"), + ), - // Space between buttons - const SizedBox(width: 8), - - // Reject Button - ElevatedButton( - onPressed: isUploading // Disable button if uploading - ? null - : () async { - if (attendanceController.selectedProjectId == null) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Please select a project first")), - ); - return; - } - - attendanceController.uploadingStates[uniqueLogKey]?.value = true; // Start loading - - final success = await attendanceController.captureAndUploadAttendance( - log.id, - log.employeeId, - attendanceController.selectedProjectId!, - comment: "Rejected", - action: 5, // Reject action - imageCapture: false, - ); + // Space between buttons + const SizedBox(width: 8), + // Reject Button + ElevatedButton( + onPressed: isUploading // Disable button if uploading + ? null + : () async { + if (attendanceController.selectedProjectId == null) { ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Please select a project first")), + ); + return; + } + + attendanceController.uploadingStates[uniqueLogKey] + ?.value = true; // Start loading + + final success = await attendanceController + .captureAndUploadAttendance( + log.id, + log.employeeId, + attendanceController.selectedProjectId!, + comment: "Rejected", + action: 5, // Reject action + imageCapture: false, + ); + + ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(success ? 'Attendance marked as Rejected!' : 'Failed to mark attendance.')), - ); + ); - if (success) { + if (success) { attendanceController.fetchEmployeesByProject( attendanceController.selectedProjectId!); attendanceController.fetchAttendanceLogs( @@ -1029,53 +1044,55 @@ class _AttendanceScreenState extends State with UIMixin { attendanceController.selectedProjectId!); await attendanceController.fetchProjectData( attendanceController.selectedProjectId!); - } + } - attendanceController.uploadingStates[uniqueLogKey]?.value = false; // End loading - }, - style: ElevatedButton.styleFrom( - backgroundColor: - AttendanceActionColors.colors[ButtonActions.reject], - padding: const EdgeInsets.symmetric( - vertical: 4, horizontal: 6), - minimumSize: const Size(60, 20), - textStyle: const TextStyle(fontSize: 12), - ), - child: isUploading - ? const CircularProgressIndicator(strokeWidth: 2) // Show loading indicator while uploading - : const Text("Reject"), + attendanceController.uploadingStates[uniqueLogKey] + ?.value = false; // End loading + }, + style: ElevatedButton.styleFrom( + backgroundColor: + AttendanceActionColors.colors[ButtonActions.reject], + padding: + const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + minimumSize: const Size(60, 20), + textStyle: const TextStyle(fontSize: 12), ), - ], - ), + child: isUploading + ? const CircularProgressIndicator( + strokeWidth: + 2) // Show loading indicator while uploading + : const Text("Reject"), + ), + ], ), - ]); - }) - .toList(); + ), + ]); + }).toList(); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Padding( padding: const EdgeInsets.all(8.0), ), - if (attendanceController.regularizationLogs.isEmpty) - Expanded( - child: Center( + if (attendanceController.regularizationLogs.isEmpty) + Expanded( + child: Center( child: MyText.bodySmall("No Regularization Records Found", fontWeight: 600), - ), - ) - else - Expanded( - child: SingleChildScrollView( - child: MyPaginatedTable( - columns: columns, - rows: rows, - columnSpacing: 15.0, + ), + ) + else + Expanded( + child: SingleChildScrollView( + child: MyPaginatedTable( + columns: columns, + rows: rows, + columnSpacing: 15.0, + ), ), ), - ), - ], - ); -} + ], + ); + } }