Enhance attendance functionality by adding optional markTime parameter in uploadAttendanceImage method, and refactor attendance button logic using AttendanceButtonHelper for improved readability and maintainability.
This commit is contained in:
parent
4ce22b149f
commit
23de99432a
@ -128,6 +128,7 @@ class AttendanceController extends GetxController {
|
|||||||
String comment = "Marked via mobile app",
|
String comment = "Marked via mobile app",
|
||||||
required int action,
|
required int action,
|
||||||
bool imageCapture = true,
|
bool imageCapture = true,
|
||||||
|
String? markTime,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
uploadingStates[employeeId]?.value = true;
|
uploadingStates[employeeId]?.value = true;
|
||||||
@ -170,6 +171,7 @@ class AttendanceController extends GetxController {
|
|||||||
comment: comment,
|
comment: comment,
|
||||||
action: action,
|
action: action,
|
||||||
imageCapture: imageCapture,
|
imageCapture: imageCapture,
|
||||||
|
markTime: markTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
log.i("Attendance uploaded for $employeeId, action: $action");
|
log.i("Attendance uploaded for $employeeId, action: $action");
|
||||||
|
@ -14,4 +14,7 @@ class ApiEndpoints {
|
|||||||
static const String getAllEmployees = "/employee/list";
|
static const String getAllEmployees = "/employee/list";
|
||||||
static const String getRoles = "/roles/jobrole";
|
static const String getRoles = "/roles/jobrole";
|
||||||
static const String createEmployee = "/employee/manage-mobile";
|
static const String createEmployee = "/employee/manage-mobile";
|
||||||
|
|
||||||
|
// Daily Task Screen API Endpoints
|
||||||
|
static const String getDailyTask = "/task/list";
|
||||||
}
|
}
|
||||||
|
@ -152,65 +152,66 @@ class ApiService {
|
|||||||
|
|
||||||
// ===== Upload Attendance Image =====
|
// ===== Upload Attendance Image =====
|
||||||
|
|
||||||
static Future<bool> uploadAttendanceImage(
|
static Future<bool> uploadAttendanceImage(
|
||||||
String id,
|
String id,
|
||||||
String employeeId,
|
String employeeId,
|
||||||
XFile? imageFile,
|
XFile? imageFile,
|
||||||
double latitude,
|
double latitude,
|
||||||
double longitude, {
|
double longitude, {
|
||||||
required String imageName,
|
required String imageName,
|
||||||
required String projectId,
|
required String projectId,
|
||||||
String comment = "",
|
String comment = "",
|
||||||
required int action,
|
required int action,
|
||||||
bool imageCapture = true,
|
bool imageCapture = true,
|
||||||
}) async {
|
String? markTime, // <-- Optional markTime parameter
|
||||||
final now = DateTime.now();
|
}) async {
|
||||||
final body = {
|
final now = DateTime.now();
|
||||||
"id": id,
|
final body = {
|
||||||
"employeeId": employeeId,
|
"id": id,
|
||||||
"projectId": projectId,
|
"employeeId": employeeId,
|
||||||
"markTime": DateFormat('hh:mm a').format(now),
|
"projectId": projectId,
|
||||||
"comment": comment,
|
"markTime": markTime ?? DateFormat('hh:mm a').format(now),
|
||||||
"action": action,
|
"comment": comment,
|
||||||
"date": DateFormat('yyyy-MM-dd').format(now),
|
"action": action,
|
||||||
if (imageCapture) "latitude": '$latitude',
|
"date": DateFormat('yyyy-MM-dd').format(now),
|
||||||
if (imageCapture) "longitude": '$longitude',
|
if (imageCapture) "latitude": '$latitude',
|
||||||
};
|
if (imageCapture) "longitude": '$longitude',
|
||||||
|
};
|
||||||
|
|
||||||
if (imageCapture && imageFile != null) {
|
if (imageCapture && imageFile != null) {
|
||||||
try {
|
try {
|
||||||
final bytes = await imageFile.readAsBytes();
|
final bytes = await imageFile.readAsBytes();
|
||||||
final base64Image = base64Encode(bytes);
|
final base64Image = base64Encode(bytes);
|
||||||
final fileSize = await imageFile.length();
|
final fileSize = await imageFile.length();
|
||||||
final contentType = "image/${imageFile.path.split('.').last}";
|
final contentType = "image/${imageFile.path.split('.').last}";
|
||||||
|
|
||||||
body["image"] = {
|
body["image"] = {
|
||||||
"fileName": imageName,
|
"fileName": imageName,
|
||||||
"contentType": contentType,
|
"contentType": contentType,
|
||||||
"fileSize": fileSize,
|
"fileSize": fileSize,
|
||||||
"description": "Employee attendance photo",
|
"description": "Employee attendance photo",
|
||||||
"base64Data": base64Image,
|
"base64Data": base64Image,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log("Image encoding error: $e");
|
_log("Image encoding error: $e");
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final response =
|
final response =
|
||||||
await _postRequest(ApiEndpoints.uploadAttendanceImage, body);
|
await _postRequest(ApiEndpoints.uploadAttendanceImage, body);
|
||||||
if (response == null) return false;
|
if (response == null) return false;
|
||||||
|
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
if (response.statusCode == 200 && json['success'] == true) {
|
if (response.statusCode == 200 && json['success'] == true) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
_log("Failed to upload image: ${json['message'] ?? 'Unknown error'}");
|
_log("Failed to upload image: ${json['message'] ?? 'Unknown error'}");
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ===== Utilities =====
|
// ===== Utilities =====
|
||||||
|
|
||||||
static String generateImageName(String employeeId, int count) {
|
static String generateImageName(String employeeId, int count) {
|
||||||
@ -289,4 +290,20 @@ class ApiService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ===== Daily Tasks API Calls =====
|
||||||
|
static Future<List<dynamic>?> getDailyTasks(String projectId,
|
||||||
|
{DateTime? dateFrom, DateTime? dateTo}) async {
|
||||||
|
final query = {
|
||||||
|
"projectId": projectId,
|
||||||
|
if (dateFrom != null)
|
||||||
|
"dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
|
||||||
|
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
|
||||||
|
};
|
||||||
|
|
||||||
|
final response =
|
||||||
|
await _getRequest(ApiEndpoints.getDailyTask, queryParams: query);
|
||||||
|
return response != null
|
||||||
|
? _parseResponse(response, label: 'Daily Tasks')
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
// Define action texts
|
/// Threshold for time elapsed (e.g., 48 hours)
|
||||||
|
const Duration THRESHOLD_DURATION = Duration(hours: 48);
|
||||||
|
|
||||||
|
/// Action text labels
|
||||||
class ButtonActions {
|
class ButtonActions {
|
||||||
static const String checkIn = "Check In";
|
static const String checkIn = "Check In";
|
||||||
static const String checkOut = "Check Out";
|
static const String checkOut = "Check Out";
|
||||||
@ -12,7 +15,7 @@ class ButtonActions {
|
|||||||
static const String reject = "Reject";
|
static const String reject = "Reject";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map action texts to colors
|
/// Action colors mapping
|
||||||
class AttendanceActionColors {
|
class AttendanceActionColors {
|
||||||
static const Map<String, Color> colors = {
|
static const Map<String, Color> colors = {
|
||||||
ButtonActions.checkIn: Colors.green,
|
ButtonActions.checkIn: Colors.green,
|
||||||
@ -22,6 +25,129 @@ class AttendanceActionColors {
|
|||||||
ButtonActions.approved: Colors.green,
|
ButtonActions.approved: Colors.green,
|
||||||
ButtonActions.requested: Colors.yellow,
|
ButtonActions.requested: Colors.yellow,
|
||||||
ButtonActions.approve: Colors.blueAccent,
|
ButtonActions.approve: Colors.blueAccent,
|
||||||
ButtonActions.reject: Colors.pink,
|
ButtonActions.reject: Colors.pink,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attendance button helper utilities
|
||||||
|
class AttendanceButtonHelper {
|
||||||
|
static String getUniqueKey(String employeeId, String logId) {
|
||||||
|
return '${employeeId}_$logId';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isLogFromYesterday(DateTime? checkIn, DateTime? checkOut) {
|
||||||
|
final yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||||
|
final yesterdayOnly =
|
||||||
|
DateTime(yesterday.year, yesterday.month, yesterday.day);
|
||||||
|
|
||||||
|
return checkIn != null &&
|
||||||
|
checkOut != null &&
|
||||||
|
DateUtils.isSameDay(checkIn, yesterdayOnly) &&
|
||||||
|
DateUtils.isSameDay(checkOut, yesterdayOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTodayApproved(int activity, DateTime? checkIn) {
|
||||||
|
final today = DateTime.now();
|
||||||
|
return activity == 4 && DateUtils.isSameDay(checkIn, today);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isApprovedButNotToday(int activity, bool isTodayApproved) {
|
||||||
|
return activity == 4 && !isTodayApproved;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTimeElapsed(DateTime? time,
|
||||||
|
[Duration threshold = THRESHOLD_DURATION]) {
|
||||||
|
if (time == null) return false;
|
||||||
|
return DateTime.now().difference(time).compareTo(threshold) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isButtonDisabled({
|
||||||
|
required bool isUploading,
|
||||||
|
required bool isYesterday,
|
||||||
|
required int activity,
|
||||||
|
required bool isApprovedButNotToday,
|
||||||
|
}) {
|
||||||
|
return isUploading ||
|
||||||
|
isYesterday ||
|
||||||
|
activity == 2 ||
|
||||||
|
activity == 5 ||
|
||||||
|
isApprovedButNotToday;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getActionText(
|
||||||
|
int activity, DateTime? checkIn, DateTime? checkOut) {
|
||||||
|
switch (activity) {
|
||||||
|
case 0:
|
||||||
|
return ButtonActions.checkIn;
|
||||||
|
case 1:
|
||||||
|
if (checkOut == null && isTimeElapsed(checkIn)) {
|
||||||
|
return ButtonActions.requestRegularize;
|
||||||
|
}
|
||||||
|
return ButtonActions.checkOut;
|
||||||
|
case 2:
|
||||||
|
return ButtonActions.requestRegularize;
|
||||||
|
case 4:
|
||||||
|
return ButtonActions.checkIn;
|
||||||
|
case 5:
|
||||||
|
return ButtonActions.rejected;
|
||||||
|
default:
|
||||||
|
return ButtonActions.checkIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Color getButtonColor({
|
||||||
|
required bool isYesterday,
|
||||||
|
required bool isTodayApproved,
|
||||||
|
required int activity,
|
||||||
|
}) {
|
||||||
|
if (isYesterday) return Colors.grey;
|
||||||
|
if (isTodayApproved) return Colors.green;
|
||||||
|
|
||||||
|
return AttendanceActionColors.colors[
|
||||||
|
activity == 0 ? ButtonActions.checkIn : ButtonActions.checkOut] ??
|
||||||
|
Colors.grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isOlderThanDays(DateTime? date, int days) {
|
||||||
|
if (date == null) return false;
|
||||||
|
|
||||||
|
// Get today's date with time set to midnight (ignoring the time)
|
||||||
|
final now = DateTime.now();
|
||||||
|
final today = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
|
// Compare the date part (ignore time) of the provided date with today's date
|
||||||
|
final compareDate = DateTime(date.year, date.month, date.day);
|
||||||
|
final difference = today.difference(compareDate).inDays;
|
||||||
|
|
||||||
|
return difference >=
|
||||||
|
days; // Return true if the difference is greater than or equal to 'days'
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getButtonText({
|
||||||
|
required int activity,
|
||||||
|
required DateTime? checkIn,
|
||||||
|
required DateTime? checkOut,
|
||||||
|
required bool isTodayApproved,
|
||||||
|
}) {
|
||||||
|
if (activity == 5) return ButtonActions.rejected;
|
||||||
|
if (isTodayApproved) return ButtonActions.checkIn;
|
||||||
|
if (activity == 4) return ButtonActions.approved;
|
||||||
|
if (activity == 2) return ButtonActions.requested;
|
||||||
|
|
||||||
|
if (activity == 0) {
|
||||||
|
if (isTimeElapsed(checkIn)) {
|
||||||
|
return ButtonActions.requestRegularize;
|
||||||
|
}
|
||||||
|
return ButtonActions.checkIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity == 1) {
|
||||||
|
if (checkOut == null && isTimeElapsed(checkIn)) {
|
||||||
|
return ButtonActions.requestRegularize;
|
||||||
|
}
|
||||||
|
return ButtonActions.checkOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ButtonActions.checkOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -624,30 +624,26 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
),
|
),
|
||||||
DataCell(
|
DataCell(
|
||||||
Obx(() {
|
Obx(() {
|
||||||
final uniqueLogKey = '${log.employeeId}_${log.id}';
|
final uniqueLogKey =
|
||||||
|
AttendanceButtonHelper.getUniqueKey(log.employeeId, log.id);
|
||||||
final isUploading =
|
final isUploading =
|
||||||
attendanceController.uploadingStates[uniqueLogKey]?.value ??
|
attendanceController.uploadingStates[uniqueLogKey]?.value ??
|
||||||
false;
|
false;
|
||||||
|
|
||||||
final isYesterday = log.checkIn != null &&
|
final isYesterday = AttendanceButtonHelper.isLogFromYesterday(
|
||||||
log.checkOut != null &&
|
log.checkIn, log.checkOut);
|
||||||
DateUtils.isSameDay(log.checkIn!,
|
final isTodayApproved = AttendanceButtonHelper.isTodayApproved(
|
||||||
DateTime.now().subtract(Duration(days: 1))) &&
|
log.activity, log.checkIn);
|
||||||
DateUtils.isSameDay(log.checkOut!,
|
|
||||||
DateTime.now().subtract(Duration(days: 1)));
|
|
||||||
|
|
||||||
final isTodayApproved = log.activity == 4 &&
|
|
||||||
DateUtils.isSameDay(
|
|
||||||
log.checkIn ?? DateTime(2000), DateTime.now());
|
|
||||||
|
|
||||||
final isApprovedButNotToday =
|
final isApprovedButNotToday =
|
||||||
log.activity == 4 && !isTodayApproved;
|
AttendanceButtonHelper.isApprovedButNotToday(
|
||||||
|
log.activity, isTodayApproved);
|
||||||
|
|
||||||
final isButtonDisabled = isUploading ||
|
final isButtonDisabled = AttendanceButtonHelper.isButtonDisabled(
|
||||||
isYesterday ||
|
isUploading: isUploading,
|
||||||
log.activity == 2 ||
|
isYesterday: isYesterday,
|
||||||
log.activity == 5 ||
|
activity: log.activity,
|
||||||
isApprovedButNotToday;
|
isApprovedButNotToday: isApprovedButNotToday,
|
||||||
|
);
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 90,
|
width: 90,
|
||||||
@ -662,8 +658,8 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
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")),
|
||||||
);
|
);
|
||||||
attendanceController.uploadingStates[uniqueLogKey] =
|
attendanceController.uploadingStates[uniqueLogKey] =
|
||||||
RxBool(false);
|
RxBool(false);
|
||||||
@ -674,55 +670,88 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
String actionText;
|
String actionText;
|
||||||
bool imageCapture = true;
|
bool imageCapture = true;
|
||||||
|
|
||||||
if (log.activity == 0) {
|
switch (log.activity) {
|
||||||
updatedAction = 0;
|
case 0:
|
||||||
actionText = "Check In";
|
updatedAction = 0;
|
||||||
} else if (log.activity == 1) {
|
actionText = ButtonActions.checkIn;
|
||||||
final twoDaysAgo =
|
break;
|
||||||
DateTime.now().subtract(Duration(days: 2));
|
case 1:
|
||||||
|
if (log.checkOut == null &&
|
||||||
if (log.checkOut == null &&
|
AttendanceButtonHelper.isOlderThanDays(
|
||||||
log.checkIn != null &&
|
log.checkIn, 2)) {
|
||||||
log.checkIn!.isBefore(twoDaysAgo)) {
|
updatedAction = 2;
|
||||||
|
actionText = ButtonActions.requestRegularize;
|
||||||
|
imageCapture = false;
|
||||||
|
} else if (log.checkOut != null &&
|
||||||
|
AttendanceButtonHelper.isOlderThanDays(
|
||||||
|
log.checkOut, 2)) {
|
||||||
|
updatedAction = 2;
|
||||||
|
actionText = ButtonActions.requestRegularize;
|
||||||
|
} else {
|
||||||
|
updatedAction = 1;
|
||||||
|
actionText = ButtonActions.checkOut;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
updatedAction = 2;
|
updatedAction = 2;
|
||||||
actionText = "Request Regularize";
|
actionText = ButtonActions.requestRegularize;
|
||||||
imageCapture = false;
|
break;
|
||||||
} else if (log.checkOut != null &&
|
case 4:
|
||||||
log.checkOut!.isBefore(twoDaysAgo)) {
|
updatedAction = isTodayApproved ? 0 : 0;
|
||||||
updatedAction = 2;
|
actionText = ButtonActions.checkIn;
|
||||||
actionText = "Request Regularize";
|
break;
|
||||||
} else {
|
default:
|
||||||
updatedAction = 1;
|
updatedAction = 0;
|
||||||
actionText = "Check Out";
|
actionText = "Unknown Action";
|
||||||
}
|
break;
|
||||||
} else if (log.activity == 2) {
|
|
||||||
updatedAction = 2;
|
|
||||||
actionText = "Request Regularize";
|
|
||||||
} else if (isTodayApproved) {
|
|
||||||
updatedAction = 0;
|
|
||||||
actionText = "Check In";
|
|
||||||
} else {
|
|
||||||
updatedAction = 0;
|
|
||||||
actionText = "Unknown Action";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final success = await attendanceController
|
bool success = false;
|
||||||
.captureAndUploadAttendance(
|
if (actionText == ButtonActions.requestRegularize) {
|
||||||
log.id,
|
final selectedTime =
|
||||||
log.employeeId,
|
await showTimePickerForRegularization(
|
||||||
attendanceController.selectedProjectId!,
|
context: context,
|
||||||
comment: actionText,
|
checkInTime: log.checkIn!,
|
||||||
action: updatedAction,
|
);
|
||||||
imageCapture: imageCapture,
|
if (selectedTime != null) {
|
||||||
);
|
final formattedSelectedTime =
|
||||||
|
DateFormat("hh:mm a").format(selectedTime);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
success = await attendanceController
|
||||||
SnackBar(
|
.captureAndUploadAttendance(
|
||||||
content: Text(success
|
log.id,
|
||||||
? 'Attendance marked successfully!'
|
log.employeeId,
|
||||||
: 'Failed to mark attendance.'),
|
attendanceController.selectedProjectId!,
|
||||||
),
|
comment: actionText,
|
||||||
);
|
action: updatedAction,
|
||||||
|
imageCapture: imageCapture,
|
||||||
|
markTime: formattedSelectedTime,
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(success
|
||||||
|
? '${actionText.toLowerCase()} marked successfully!'
|
||||||
|
: 'Failed to ${actionText.toLowerCase()}.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
success = await attendanceController
|
||||||
|
.captureAndUploadAttendance(
|
||||||
|
log.id,
|
||||||
|
log.employeeId,
|
||||||
|
attendanceController.selectedProjectId!,
|
||||||
|
comment: actionText,
|
||||||
|
action: updatedAction,
|
||||||
|
imageCapture: imageCapture,
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(success
|
||||||
|
? '${actionText.toLowerCase()} marked successfully!'
|
||||||
|
: 'Failed to ${actionText.toLowerCase()}.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
attendanceController.uploadingStates[uniqueLogKey] =
|
attendanceController.uploadingStates[uniqueLogKey] =
|
||||||
RxBool(false);
|
RxBool(false);
|
||||||
@ -740,13 +769,11 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: isYesterday
|
backgroundColor: AttendanceButtonHelper.getButtonColor(
|
||||||
? Colors.grey
|
isYesterday: isYesterday,
|
||||||
: isTodayApproved
|
isTodayApproved: isTodayApproved,
|
||||||
? Colors.green
|
activity: log.activity,
|
||||||
: AttendanceActionColors.colors[(log.activity == 0)
|
),
|
||||||
? ButtonActions.checkIn
|
|
||||||
: ButtonActions.checkOut],
|
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(vertical: 4, horizontal: 6),
|
const EdgeInsets.symmetric(vertical: 4, horizontal: 6),
|
||||||
textStyle: const TextStyle(fontSize: 12),
|
textStyle: const TextStyle(fontSize: 12),
|
||||||
@ -762,42 +789,12 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
log.activity == 5
|
AttendanceButtonHelper.getButtonText(
|
||||||
? ButtonActions.rejected
|
activity: log.activity,
|
||||||
: isTodayApproved
|
checkIn: log.checkIn,
|
||||||
? ButtonActions.checkIn
|
checkOut: log.checkOut,
|
||||||
: log.activity == 4
|
isTodayApproved: isTodayApproved,
|
||||||
? ButtonActions.approved
|
),
|
||||||
: log.activity == 2
|
|
||||||
? ButtonActions.requested
|
|
||||||
: (log.activity == 0 &&
|
|
||||||
!(log.checkIn != null &&
|
|
||||||
log.checkOut != null &&
|
|
||||||
!DateUtils.isSameDay(
|
|
||||||
log.checkIn!,
|
|
||||||
DateTime.now())))
|
|
||||||
? ButtonActions.checkIn
|
|
||||||
: (log.activity == 1 &&
|
|
||||||
log.checkOut != null &&
|
|
||||||
DateTime.now()
|
|
||||||
.difference(
|
|
||||||
log.checkOut!)
|
|
||||||
.inDays <=
|
|
||||||
2)
|
|
||||||
? ButtonActions.checkOut
|
|
||||||
: (log.activity == 1 &&
|
|
||||||
log.checkOut ==
|
|
||||||
null &&
|
|
||||||
log.checkIn != null &&
|
|
||||||
log.checkIn!.isBefore(
|
|
||||||
DateTime.now()
|
|
||||||
.subtract(
|
|
||||||
Duration(
|
|
||||||
days:
|
|
||||||
2))))
|
|
||||||
? ButtonActions
|
|
||||||
.requestRegularize
|
|
||||||
: ButtonActions.checkOut,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -1076,4 +1073,36 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<DateTime?> showTimePickerForRegularization({
|
||||||
|
required BuildContext context,
|
||||||
|
required 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,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure selected time is after check-in time
|
||||||
|
if (selectedDateTime.isAfter(checkInTime)) {
|
||||||
|
return selectedDateTime;
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text("Please select a time after check-in time.")),
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user