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