From 488a921f4549248ea229cd249f0dd47bab7a0a4f Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Tue, 6 May 2025 17:53:57 +0530 Subject: [PATCH] added loading for regularization tab --- lib/view/dashboard/attendanceScreen.dart | 300 ++++++++++++----------- 1 file changed, 158 insertions(+), 142 deletions(-) diff --git a/lib/view/dashboard/attendanceScreen.dart b/lib/view/dashboard/attendanceScreen.dart index 308abea..b2849a7 100644 --- a/lib/view/dashboard/attendanceScreen.dart +++ b/lib/view/dashboard/attendanceScreen.dart @@ -860,103 +860,108 @@ 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) => 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), - ], + 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), + ], + ), + ), + DataCell( + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium( + log.checkIn != null + ? DateFormat('dd/MMM/yyyy').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, - ), - ], + 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, ), - ), - DataCell( - Row( - children: [ - // Approve Button - ElevatedButton( - onPressed: () async { - if (attendanceController.selectedProjectId == null) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text("Please select a project first")), + 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, ); - return; - } - final success = await attendanceController - .captureAndUploadAttendance( - log.id, - log.employeeId, - attendanceController.selectedProjectId!, - comment: "Accepted", - action: 4, // Approve action - imageCapture: false, - ); - - ScaffoldMessenger.of(context).showSnackBar( + 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( @@ -965,51 +970,57 @@ class _AttendanceScreenState extends State with UIMixin { attendanceController.selectedProjectId!); await attendanceController.fetchProjectData( attendanceController.selectedProjectId!); - } - }, - style: ElevatedButton.styleFrom( + } + + 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: const Text("Approve"), - ), + 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), + // Space between buttons + const SizedBox(width: 8), - // Reject Button - ElevatedButton( - onPressed: () async { - if (attendanceController.selectedProjectId == null) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text("Please select a project first")), + // 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, ); - return; - } - final success = await attendanceController - .captureAndUploadAttendance( - log.id, - log.employeeId, - attendanceController.selectedProjectId!, - comment: "Rejected", - action: 5, // Reject action - imageCapture: false, - ); - - ScaffoldMessenger.of(context).showSnackBar( + 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( @@ -1018,48 +1029,53 @@ class _AttendanceScreenState extends State with UIMixin { attendanceController.selectedProjectId!); await attendanceController.fetchProjectData( attendanceController.selectedProjectId!); - } - }, - style: ElevatedButton.styleFrom( + } + + 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: const Text("Reject"), - ), - ], + 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, ), ), - ], - ); - } + ), + ], + ); +} }