Refactor attendance button UI and improve feedback messages

This commit is contained in:
Vaibhav Surve 2025-05-13 12:06:00 +05:30
parent 5462f0aa24
commit 932cfe81e7

View File

@ -279,9 +279,10 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
.uploadingStates[employee.employeeId]?.value ?? .uploadingStates[employee.employeeId]?.value ??
false; false;
final controller = attendanceController; final controller = attendanceController;
return SizedBox( return ConstrainedBox(
width: 90, constraints: const BoxConstraints(minWidth: 100, maxWidth: 140),
height: 25, child: SizedBox(
height: 30,
child: ElevatedButton( child: ElevatedButton(
onPressed: isUploading onPressed: isUploading
? null ? null
@ -291,7 +292,8 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
if (controller.selectedProjectId == null) { if (controller.selectedProjectId == null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text("Please select a project first")), content:
Text("Please select a project first")),
); );
controller.uploadingStates[employee.employeeId] = controller.uploadingStates[employee.employeeId] =
RxBool(false); RxBool(false);
@ -336,6 +338,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AttendanceActionColors.colors[buttonText], backgroundColor: AttendanceActionColors.colors[buttonText],
textStyle: const TextStyle(fontSize: 12), textStyle: const TextStyle(fontSize: 12),
padding: const EdgeInsets.symmetric(horizontal: 12),
), ),
child: isUploading child: isUploading
? const SizedBox( ? const SizedBox(
@ -347,7 +350,15 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
AlwaysStoppedAnimation<Color>(Colors.white), AlwaysStoppedAnimation<Color>(Colors.white),
), ),
) )
: Text(buttonText), : FittedBox(
fit: BoxFit.scaleDown,
child: Text(
buttonText,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 12),
),
),
),
), ),
); );
}), }),
@ -645,9 +656,10 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
isApprovedButNotToday: isApprovedButNotToday, isApprovedButNotToday: isApprovedButNotToday,
); );
return SizedBox( return ConstrainedBox(
width: 90, constraints: const BoxConstraints(minWidth: 100, maxWidth: 150),
height: 25, child: SizedBox(
height: 30,
child: ElevatedButton( child: ElevatedButton(
onPressed: isButtonDisabled onPressed: isButtonDisabled
? null ? null
@ -655,13 +667,16 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
attendanceController.uploadingStates[uniqueLogKey] = attendanceController.uploadingStates[uniqueLogKey] =
RxBool(true); RxBool(true);
if (attendanceController.selectedProjectId == null) { if (attendanceController.selectedProjectId ==
null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: content:
Text("Please select a project first")), Text("Please select a project first"),
),
); );
attendanceController.uploadingStates[uniqueLogKey] = attendanceController
.uploadingStates[uniqueLogKey] =
RxBool(false); RxBool(false);
return; return;
} }
@ -730,7 +745,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
SnackBar( SnackBar(
content: Text(success content: Text(success
? '${actionText.toLowerCase()} marked successfully!' ? '${actionText.toLowerCase()} marked successfully!'
: 'Failed to ${actionText.toLowerCase()}.'), : 'Failed to ${actionText.toLowerCase()}'),
), ),
); );
} }
@ -748,7 +763,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
SnackBar( SnackBar(
content: Text(success content: Text(success
? '${actionText.toLowerCase()} marked successfully!' ? '${actionText.toLowerCase()} marked successfully!'
: 'Failed to ${actionText.toLowerCase()}.'), : 'Failed to ${actionText.toLowerCase()}'),
), ),
); );
} }
@ -761,7 +776,8 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
attendanceController.fetchAttendanceLogs( attendanceController.fetchAttendanceLogs(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
await attendanceController.fetchRegularizationLogs( await attendanceController
.fetchRegularizationLogs(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
await attendanceController.fetchProjectData( await attendanceController.fetchProjectData(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
@ -774,8 +790,8 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
isTodayApproved: isTodayApproved, isTodayApproved: isTodayApproved,
activity: log.activity, activity: log.activity,
), ),
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(vertical: 4, horizontal: 6), vertical: 4, horizontal: 6),
textStyle: const TextStyle(fontSize: 12), textStyle: const TextStyle(fontSize: 12),
), ),
child: isUploading child: isUploading
@ -788,18 +804,24 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
AlwaysStoppedAnimation<Color>(Colors.white), AlwaysStoppedAnimation<Color>(Colors.white),
), ),
) )
: Text( : FittedBox(
fit: BoxFit.scaleDown,
child: Text(
AttendanceButtonHelper.getButtonText( AttendanceButtonHelper.getButtonText(
activity: log.activity, activity: log.activity,
checkIn: log.checkIn, checkIn: log.checkIn,
checkOut: log.checkOut, checkOut: log.checkOut,
isTodayApproved: isTodayApproved, isTodayApproved: isTodayApproved,
), ),
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 12),
),
),
), ),
), ),
); );
}), }),
) ),
])); ]));
} }
}); });
@ -917,27 +939,34 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
Row( Row(
children: [ children: [
// Approve Button // Approve Button
ElevatedButton( ConstrainedBox(
onPressed: isUploading // Disable button if uploading constraints: const BoxConstraints(minWidth: 70, maxWidth: 120),
child: SizedBox(
height: 30,
child: ElevatedButton(
onPressed: isUploading
? null ? null
: () async { : () async {
if (attendanceController.selectedProjectId == null) { if (attendanceController.selectedProjectId ==
null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text("Please select a project first")), content:
Text("Please select a project first")),
); );
return; return;
} }
attendanceController.uploadingStates[uniqueLogKey] attendanceController
?.value = true; // Start loading .uploadingStates[uniqueLogKey]?.value = true;
final success = await attendanceController final success = await attendanceController
.captureAndUploadAttendance( .captureAndUploadAttendance(
log.id, log.id,
log.employeeId, log.employeeId,
attendanceController.selectedProjectId!, attendanceController.selectedProjectId!,
comment: "Accepted", comment: "Accepted",
action: 4, // Approve action action: 4,
imageCapture: false, imageCapture: false,
); );
@ -945,7 +974,8 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
SnackBar( SnackBar(
content: Text(success content: Text(success
? 'Approval marked successfully!' ? 'Approval marked successfully!'
: 'Failed to mark approval.')), : 'Failed to mark approval.'),
),
); );
if (success) { if (success) {
@ -953,48 +983,64 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
attendanceController.fetchAttendanceLogs( attendanceController.fetchAttendanceLogs(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
await attendanceController.fetchRegularizationLogs( await attendanceController
.fetchRegularizationLogs(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
await attendanceController.fetchProjectData( await attendanceController.fetchProjectData(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
} }
attendanceController.uploadingStates[uniqueLogKey] attendanceController
?.value = false; // End loading .uploadingStates[uniqueLogKey]?.value = false;
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor:
AttendanceActionColors.colors[ButtonActions.approve], AttendanceActionColors.colors[ButtonActions.approve],
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(vertical: 4, horizontal: 6), vertical: 4, horizontal: 6),
minimumSize: const Size(60, 20), minimumSize: const Size(60, 20),
textStyle: const TextStyle(fontSize: 12), textStyle: const TextStyle(fontSize: 12),
), ),
child: isUploading child: isUploading
? const CircularProgressIndicator( ? const SizedBox(
strokeWidth: width: 16,
2) // Show loading indicator while uploading height: 16,
: const Text("Approve"), child: CircularProgressIndicator(strokeWidth: 2),
)
: FittedBox(
fit: BoxFit.scaleDown,
child: const Text(
"Approve",
overflow: TextOverflow.ellipsis,
),
),
),
),
), ),
// Space between buttons
const SizedBox(width: 8), const SizedBox(width: 8),
// Reject Button // Reject Button
ElevatedButton( ConstrainedBox(
onPressed: isUploading // Disable button if uploading constraints: const BoxConstraints(minWidth: 70, maxWidth: 120),
child: SizedBox(
height: 30,
child: ElevatedButton(
onPressed: isUploading
? null ? null
: () async { : () async {
if (attendanceController.selectedProjectId == null) { if (attendanceController.selectedProjectId ==
null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text("Please select a project first")), content:
Text("Please select a project first")),
); );
return; return;
} }
attendanceController.uploadingStates[uniqueLogKey] attendanceController
?.value = true; // Start loading .uploadingStates[uniqueLogKey]?.value = true;
final success = await attendanceController final success = await attendanceController
.captureAndUploadAttendance( .captureAndUploadAttendance(
@ -1002,7 +1048,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
log.employeeId, log.employeeId,
attendanceController.selectedProjectId!, attendanceController.selectedProjectId!,
comment: "Rejected", comment: "Rejected",
action: 5, // Reject action action: 5,
imageCapture: false, imageCapture: false,
); );
@ -1010,7 +1056,8 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
SnackBar( SnackBar(
content: Text(success content: Text(success
? 'Attendance marked as Rejected!' ? 'Attendance marked as Rejected!'
: 'Failed to mark attendance.')), : 'Failed to mark attendance.'),
),
); );
if (success) { if (success) {
@ -1018,28 +1065,39 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
attendanceController.fetchAttendanceLogs( attendanceController.fetchAttendanceLogs(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
await attendanceController.fetchRegularizationLogs( await attendanceController
.fetchRegularizationLogs(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
await attendanceController.fetchProjectData( await attendanceController.fetchProjectData(
attendanceController.selectedProjectId!); attendanceController.selectedProjectId!);
} }
attendanceController.uploadingStates[uniqueLogKey] attendanceController
?.value = false; // End loading .uploadingStates[uniqueLogKey]?.value = false;
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor:
AttendanceActionColors.colors[ButtonActions.reject], AttendanceActionColors.colors[ButtonActions.reject],
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(vertical: 4, horizontal: 6), vertical: 4, horizontal: 6),
minimumSize: const Size(60, 20), minimumSize: const Size(60, 20),
textStyle: const TextStyle(fontSize: 12), textStyle: const TextStyle(fontSize: 12),
), ),
child: isUploading child: isUploading
? const CircularProgressIndicator( ? const SizedBox(
strokeWidth: width: 16,
2) // Show loading indicator while uploading height: 16,
: const Text("Reject"), child: CircularProgressIndicator(strokeWidth: 2),
)
: FittedBox(
fit: BoxFit.scaleDown,
child: const Text(
"Reject",
overflow: TextOverflow.ellipsis,
),
),
),
),
), ),
], ],
), ),