refactor: Clean up AttendanceActionButton and AttendanceFilterBottomSheet code by removing unused comment bottom sheet function and filter button properties for improved readability and maintainability.
This commit is contained in:
parent
fe66f35be7
commit
9d9afe37b8
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/controller/dashboard/attendance_screen_controller.dart';
|
||||
import 'package:marco/helpers/utils/attendance_actions.dart';
|
||||
@ -21,76 +22,6 @@ class AttendanceActionButton extends StatefulWidget {
|
||||
State<AttendanceActionButton> createState() => _AttendanceActionButtonState();
|
||||
}
|
||||
|
||||
Future<String?> _showCommentBottomSheet(
|
||||
BuildContext context, String actionText) async {
|
||||
final TextEditingController commentController = TextEditingController();
|
||||
String? errorText;
|
||||
|
||||
return showModalBottomSheet<String>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setModalState) {
|
||||
void submit() {
|
||||
final comment = commentController.text.trim();
|
||||
if (comment.isEmpty) {
|
||||
setModalState(() => errorText = 'Comment cannot be empty.');
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop(comment);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: BaseBottomSheet(
|
||||
title: 'Add Comment for ${capitalizeFirstLetter(actionText)}',
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
onSubmit: submit,
|
||||
isSubmitting: false,
|
||||
submitText: 'Submit',
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: commentController,
|
||||
maxLines: 4,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Type your comment here...',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.grey.shade100,
|
||||
errorText: errorText,
|
||||
),
|
||||
onChanged: (_) {
|
||||
if (errorText != null) {
|
||||
setModalState(() => errorText = null);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String capitalizeFirstLetter(String text) {
|
||||
if (text.isEmpty) return text;
|
||||
return text[0].toUpperCase() + text.substring(1);
|
||||
}
|
||||
|
||||
class _AttendanceActionButtonState extends State<AttendanceActionButton> {
|
||||
late final String uniqueLogKey;
|
||||
|
||||
@ -110,51 +41,45 @@ class _AttendanceActionButtonState extends State<AttendanceActionButton> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<DateTime?> showTimePickerForRegularization({
|
||||
required BuildContext context,
|
||||
required DateTime checkInTime,
|
||||
}) async {
|
||||
Future<DateTime?> _pickRegularizationTime(DateTime checkInTime) async {
|
||||
final pickedTime = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.fromDateTime(DateTime.now()),
|
||||
);
|
||||
|
||||
if (pickedTime != null) {
|
||||
final selectedDateTime = DateTime(
|
||||
checkInTime.year,
|
||||
checkInTime.month,
|
||||
checkInTime.day,
|
||||
pickedTime.hour,
|
||||
pickedTime.minute,
|
||||
if (pickedTime == null) return null;
|
||||
|
||||
final selected = DateTime(
|
||||
checkInTime.year,
|
||||
checkInTime.month,
|
||||
checkInTime.day,
|
||||
pickedTime.hour,
|
||||
pickedTime.minute,
|
||||
);
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
if (selected.isBefore(checkInTime)) {
|
||||
showAppSnackbar(
|
||||
title: "Invalid Time",
|
||||
message: "Time must be after check-in.",
|
||||
type: SnackbarType.warning,
|
||||
);
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
if (selectedDateTime.isBefore(checkInTime)) {
|
||||
showAppSnackbar(
|
||||
title: "Invalid Time",
|
||||
message: "Time must be after check-in.",
|
||||
type: SnackbarType.warning,
|
||||
);
|
||||
return null;
|
||||
} else if (selectedDateTime.isAfter(now)) {
|
||||
showAppSnackbar(
|
||||
title: "Invalid Time",
|
||||
message: "Future time is not allowed.",
|
||||
type: SnackbarType.warning,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return selectedDateTime;
|
||||
return null;
|
||||
} else if (selected.isAfter(now)) {
|
||||
showAppSnackbar(
|
||||
title: "Invalid Time",
|
||||
message: "Future time is not allowed.",
|
||||
type: SnackbarType.warning,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return selected;
|
||||
}
|
||||
|
||||
void _handleButtonPressed(BuildContext context) async {
|
||||
widget.attendanceController.uploadingStates[uniqueLogKey]?.value = true;
|
||||
|
||||
Future<void> _handleButtonPressed() async {
|
||||
final controller = widget.attendanceController;
|
||||
final projectController = Get.find<ProjectController>();
|
||||
final selectedProjectId = projectController.selectedProject?.id;
|
||||
|
||||
@ -164,46 +89,43 @@ class _AttendanceActionButtonState extends State<AttendanceActionButton> {
|
||||
message: "Please select a project first",
|
||||
type: SnackbarType.error,
|
||||
);
|
||||
widget.attendanceController.uploadingStates[uniqueLogKey]?.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int updatedAction;
|
||||
controller.uploadingStates[uniqueLogKey]?.value = true;
|
||||
|
||||
int action;
|
||||
String actionText;
|
||||
bool imageCapture = true;
|
||||
|
||||
switch (widget.employee.activity) {
|
||||
case 0:
|
||||
updatedAction = 0;
|
||||
case 4:
|
||||
action = 0;
|
||||
actionText = ButtonActions.checkIn;
|
||||
break;
|
||||
case 1:
|
||||
if (widget.employee.checkOut == null &&
|
||||
AttendanceButtonHelper.isOlderThanDays(
|
||||
widget.employee.checkIn, 2)) {
|
||||
updatedAction = 2;
|
||||
final isOld = AttendanceButtonHelper.isOlderThanDays(widget.employee.checkIn, 2);
|
||||
final isOldCheckout = AttendanceButtonHelper.isOlderThanDays(widget.employee.checkOut, 2);
|
||||
|
||||
if (widget.employee.checkOut == null && isOld) {
|
||||
action = 2;
|
||||
actionText = ButtonActions.requestRegularize;
|
||||
imageCapture = false;
|
||||
} else if (widget.employee.checkOut != null &&
|
||||
AttendanceButtonHelper.isOlderThanDays(
|
||||
widget.employee.checkOut, 2)) {
|
||||
updatedAction = 2;
|
||||
} else if (widget.employee.checkOut != null && isOldCheckout) {
|
||||
action = 2;
|
||||
actionText = ButtonActions.requestRegularize;
|
||||
} else {
|
||||
updatedAction = 1;
|
||||
action = 1;
|
||||
actionText = ButtonActions.checkOut;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
updatedAction = 2;
|
||||
action = 2;
|
||||
actionText = ButtonActions.requestRegularize;
|
||||
break;
|
||||
case 4:
|
||||
updatedAction = 0;
|
||||
actionText = ButtonActions.checkIn;
|
||||
break;
|
||||
default:
|
||||
updatedAction = 0;
|
||||
action = 0;
|
||||
actionText = "Unknown Action";
|
||||
break;
|
||||
}
|
||||
@ -219,67 +141,41 @@ class _AttendanceActionButtonState extends State<AttendanceActionButton> {
|
||||
if (isYesterdayCheckIn &&
|
||||
widget.employee.checkOut == null &&
|
||||
actionText == ButtonActions.checkOut) {
|
||||
selectedTime = await showTimePickerForRegularization(
|
||||
context: context,
|
||||
checkInTime: widget.employee.checkIn!,
|
||||
);
|
||||
|
||||
selectedTime = await _pickRegularizationTime(widget.employee.checkIn!);
|
||||
if (selectedTime == null) {
|
||||
widget.attendanceController.uploadingStates[uniqueLogKey]?.value =
|
||||
false;
|
||||
controller.uploadingStates[uniqueLogKey]?.value = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final userComment = await _showCommentBottomSheet(context, actionText);
|
||||
if (userComment == null || userComment.isEmpty) {
|
||||
widget.attendanceController.uploadingStates[uniqueLogKey]?.value = false;
|
||||
final comment = await _showCommentBottomSheet(context, actionText);
|
||||
if (comment == null || comment.isEmpty) {
|
||||
controller.uploadingStates[uniqueLogKey]?.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
String? markTime;
|
||||
|
||||
if (actionText == ButtonActions.requestRegularize) {
|
||||
final regularizeTime = selectedTime ??
|
||||
await showTimePickerForRegularization(
|
||||
context: context,
|
||||
checkInTime: widget.employee.checkIn!,
|
||||
);
|
||||
|
||||
if (regularizeTime != null) {
|
||||
final formattedTime = DateFormat("hh:mm a").format(regularizeTime);
|
||||
success = await widget.attendanceController.captureAndUploadAttendance(
|
||||
widget.employee.id,
|
||||
widget.employee.employeeId,
|
||||
selectedProjectId,
|
||||
comment: userComment,
|
||||
action: updatedAction,
|
||||
imageCapture: imageCapture,
|
||||
markTime: formattedTime,
|
||||
);
|
||||
selectedTime ??= await _pickRegularizationTime(widget.employee.checkIn!);
|
||||
if (selectedTime != null) {
|
||||
markTime = DateFormat("hh:mm a").format(selectedTime);
|
||||
}
|
||||
} else if (selectedTime != null) {
|
||||
final formattedTime = DateFormat("hh:mm a").format(selectedTime);
|
||||
success = await widget.attendanceController.captureAndUploadAttendance(
|
||||
widget.employee.id,
|
||||
widget.employee.employeeId,
|
||||
selectedProjectId,
|
||||
comment: userComment,
|
||||
action: updatedAction,
|
||||
imageCapture: imageCapture,
|
||||
markTime: formattedTime,
|
||||
);
|
||||
} else {
|
||||
success = await widget.attendanceController.captureAndUploadAttendance(
|
||||
widget.employee.id,
|
||||
widget.employee.employeeId,
|
||||
selectedProjectId,
|
||||
comment: userComment,
|
||||
action: updatedAction,
|
||||
imageCapture: imageCapture,
|
||||
);
|
||||
markTime = DateFormat("hh:mm a").format(selectedTime);
|
||||
}
|
||||
|
||||
success = await controller.captureAndUploadAttendance(
|
||||
widget.employee.id,
|
||||
widget.employee.employeeId,
|
||||
selectedProjectId,
|
||||
comment: comment,
|
||||
action: action,
|
||||
imageCapture: imageCapture,
|
||||
markTime: markTime,
|
||||
);
|
||||
|
||||
showAppSnackbar(
|
||||
title: success ? '${capitalizeFirstLetter(actionText)} Success' : 'Error',
|
||||
message: success
|
||||
@ -288,57 +184,47 @@ class _AttendanceActionButtonState extends State<AttendanceActionButton> {
|
||||
type: success ? SnackbarType.success : SnackbarType.error,
|
||||
);
|
||||
|
||||
widget.attendanceController.uploadingStates[uniqueLogKey]?.value = false;
|
||||
controller.uploadingStates[uniqueLogKey]?.value = false;
|
||||
|
||||
if (success) {
|
||||
widget.attendanceController.fetchEmployeesByProject(selectedProjectId);
|
||||
widget.attendanceController.fetchAttendanceLogs(selectedProjectId);
|
||||
await widget.attendanceController.fetchRegularizationLogs(selectedProjectId);
|
||||
await widget.attendanceController.fetchProjectData(selectedProjectId);
|
||||
widget.attendanceController.update();
|
||||
controller.fetchEmployeesByProject(selectedProjectId);
|
||||
controller.fetchAttendanceLogs(selectedProjectId);
|
||||
await controller.fetchRegularizationLogs(selectedProjectId);
|
||||
await controller.fetchProjectData(selectedProjectId);
|
||||
controller.update();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final isUploading =
|
||||
widget.attendanceController.uploadingStates[uniqueLogKey]?.value ??
|
||||
false;
|
||||
final controller = widget.attendanceController;
|
||||
|
||||
final isYesterday = AttendanceButtonHelper.isLogFromYesterday(
|
||||
widget.employee.checkIn,
|
||||
widget.employee.checkOut,
|
||||
);
|
||||
final isUploading = controller.uploadingStates[uniqueLogKey]?.value ?? false;
|
||||
final emp = widget.employee;
|
||||
|
||||
final isTodayApproved = AttendanceButtonHelper.isTodayApproved(
|
||||
widget.employee.activity,
|
||||
widget.employee.checkIn,
|
||||
);
|
||||
|
||||
final isApprovedButNotToday = AttendanceButtonHelper.isApprovedButNotToday(
|
||||
widget.employee.activity,
|
||||
isTodayApproved,
|
||||
);
|
||||
final isYesterday = AttendanceButtonHelper.isLogFromYesterday(emp.checkIn, emp.checkOut);
|
||||
final isTodayApproved = AttendanceButtonHelper.isTodayApproved(emp.activity, emp.checkIn);
|
||||
final isApprovedButNotToday = AttendanceButtonHelper.isApprovedButNotToday(emp.activity, isTodayApproved);
|
||||
|
||||
final isButtonDisabled = AttendanceButtonHelper.isButtonDisabled(
|
||||
isUploading: isUploading,
|
||||
isYesterday: isYesterday,
|
||||
activity: widget.employee.activity,
|
||||
activity: emp.activity,
|
||||
isApprovedButNotToday: isApprovedButNotToday,
|
||||
);
|
||||
|
||||
final buttonText = AttendanceButtonHelper.getButtonText(
|
||||
activity: widget.employee.activity,
|
||||
checkIn: widget.employee.checkIn,
|
||||
checkOut: widget.employee.checkOut,
|
||||
activity: emp.activity,
|
||||
checkIn: emp.checkIn,
|
||||
checkOut: emp.checkOut,
|
||||
isTodayApproved: isTodayApproved,
|
||||
);
|
||||
|
||||
final buttonColor = AttendanceButtonHelper.getButtonColor(
|
||||
isYesterday: isYesterday,
|
||||
isTodayApproved: isTodayApproved,
|
||||
activity: widget.employee.activity,
|
||||
activity: emp.activity,
|
||||
);
|
||||
|
||||
return AttendanceActionButtonUI(
|
||||
@ -346,7 +232,7 @@ class _AttendanceActionButtonState extends State<AttendanceActionButton> {
|
||||
isButtonDisabled: isButtonDisabled,
|
||||
buttonText: buttonText,
|
||||
buttonColor: buttonColor,
|
||||
onPressed: isButtonDisabled ? null : () => _handleButtonPressed(context),
|
||||
onPressed: isButtonDisabled ? null : _handleButtonPressed,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -391,17 +277,14 @@ class AttendanceActionButtonUI extends StatelessWidget {
|
||||
: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (buttonText.toLowerCase() == 'approved') ...[
|
||||
if (buttonText.toLowerCase() == 'approved')
|
||||
const Icon(Icons.check, size: 16, color: Colors.green),
|
||||
const SizedBox(width: 4),
|
||||
] else if (buttonText.toLowerCase() == 'rejected') ...[
|
||||
if (buttonText.toLowerCase() == 'rejected')
|
||||
const Icon(Icons.close, size: 16, color: Colors.red),
|
||||
if (buttonText.toLowerCase() == 'requested')
|
||||
const Icon(Icons.hourglass_top, size: 16, color: Colors.orange),
|
||||
if (['approved', 'rejected', 'requested'].contains(buttonText.toLowerCase()))
|
||||
const SizedBox(width: 4),
|
||||
] else if (buttonText.toLowerCase() == 'requested') ...[
|
||||
const Icon(Icons.hourglass_top,
|
||||
size: 16, color: Colors.orange),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
Flexible(
|
||||
child: Text(
|
||||
buttonText,
|
||||
@ -415,3 +298,68 @@ class AttendanceActionButtonUI extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> _showCommentBottomSheet(BuildContext context, String actionText) async {
|
||||
final commentController = TextEditingController();
|
||||
String? errorText;
|
||||
|
||||
return showModalBottomSheet<String>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setModalState) {
|
||||
void submit() {
|
||||
final comment = commentController.text.trim();
|
||||
if (comment.isEmpty) {
|
||||
setModalState(() => errorText = 'Comment cannot be empty.');
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop(comment);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: BaseBottomSheet(
|
||||
title: 'Add Comment for ${capitalizeFirstLetter(actionText)}',
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
onSubmit: submit,
|
||||
isSubmitting: false,
|
||||
submitText: 'Submit',
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: commentController,
|
||||
maxLines: 4,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Type your comment here...',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.grey.shade100,
|
||||
errorText: errorText,
|
||||
),
|
||||
onChanged: (_) {
|
||||
if (errorText != null) {
|
||||
setModalState(() => errorText = null);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String capitalizeFirstLetter(String text) =>
|
||||
text.isEmpty ? text : text[0].toUpperCase() + text.substring(1);
|
||||
|
@ -137,9 +137,6 @@ class _AttendanceFilterBottomSheetState
|
||||
onSubmit: () => Navigator.pop(context, {
|
||||
'selectedTab': tempSelectedTab,
|
||||
}),
|
||||
submitText: "Apply Filter",
|
||||
submitIcon: Icons.filter_alt_outlined,
|
||||
submitColor: const Color.fromARGB(255, 95, 132, 255),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: buildMainFilters(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user