made chnages for check in and check out

This commit is contained in:
Vaibhav Surve 2025-04-25 12:13:41 +05:30
parent 7936072b74
commit 8da8ee8371
4 changed files with 118 additions and 42 deletions

View File

@ -79,9 +79,11 @@ class AttendanceController extends GetxController {
}
Future<bool> captureAndUploadAttendance(
int id,
int employeeId,
int projectId, {
String comment = "Marked via mobile app",
required int action,
}) async {
try {
final image = await ImagePicker().pickImage(
@ -98,6 +100,7 @@ class AttendanceController extends GetxController {
ApiService.generateImageName(employeeId, employees.length + 1);
return await ApiService.uploadAttendanceImage(
id,
employeeId,
image,
position.latitude,
@ -105,6 +108,7 @@ class AttendanceController extends GetxController {
imageName: imageName,
projectId: projectId,
comment: comment,
action: action,
);
} catch (e) {
print("Error capturing or uploading attendance: $e");

View File

@ -106,6 +106,7 @@ class ApiService {
// ===== Upload Image =====
static Future<bool> uploadAttendanceImage(
int id,
int employeeId,
XFile imageFile,
double latitude,
@ -113,7 +114,7 @@ class ApiService {
required String imageName,
required int projectId,
String comment = "",
int action = 0,
required int action, // action passed here
}) async {
final token = await _getToken();
if (token == null) return false;
@ -135,7 +136,7 @@ class ApiService {
final now = DateTime.now();
final body = {
"id": null,
"id": id,
"employeeId": employeeId,
"projectId": projectId,
"markTime": DateFormat('hh:mm a').format(now),
@ -146,21 +147,24 @@ class ApiService {
"longitude": '$longitude',
"image": imageObject
};
print("Upload body Image: $body");
print("Attendance Image Upload Body: $body");
final response = await http.post(
Uri.parse("$baseUrl/attendance/record-image"),
headers: _headers(token),
body: jsonEncode(body),
);
print('Upload request: $baseUrl/attendance/record-image');
print("Attendance Image Upload Response: ${response.body}");
final json = jsonDecode(response.body);
print("Upload Image response: ${response.body}");
return response.statusCode == 200 && json['success'] == true;
if (response.statusCode == 200 && json['success'] == true) {
return true;
} else {
print("Failed to upload image. API Error: ${json['message']}");
}
} catch (e) {
print("Exception during image upload: $e");
return false;
}
return false;
}
// ===== Utilities =====

View File

@ -1,28 +1,34 @@
class EmployeeModel {
final int id;
final int employeeId;
final String name;
final String designation;
final String checkIn;
final String checkOut;
final int actions;
final int activity;
int action;
EmployeeModel({
required this.id,
required this.employeeId,
required this.name,
required this.designation,
required this.checkIn,
required this.checkOut,
required this.actions,
required this.action,
required this.activity,
});
factory EmployeeModel.fromJson(Map<String, dynamic> json) {
return EmployeeModel(
id: json['employeeId'] ?? 0,
id: json['id'] ?? 0,
employeeId: json['employeeId'] ?? 0,
name: '${json['firstName']} ${json['lastName']}',
designation: json['jobRoleName'] ?? '',
checkIn: json['checkIn'] ?? '-', // Make sure your API returns this field
checkOut: json['checkOut'] ?? '-',
actions: json['actions'] ?? 0, // Make sure your API returns this field
action: json['action'] ?? 0, // Make sure your API returns this field
activity: json['activity'] ?? 0,
);
}
}

View File

@ -66,7 +66,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
children: [
Expanded(
child: MyContainer.bordered(
padding: MySpacing.xy(4, 8),
padding: MySpacing.xy(8, 8),
child: PopupMenuButton<String>(
onSelected: (value) {
setState(() {
@ -122,7 +122,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
color: theme.colorScheme.onSurface,
),
Icon(LucideIcons.chevron_down,
size: 16,
size: 20,
color: theme.colorScheme.onSurface),
],
),
@ -219,37 +219,90 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
DataColumn(
label: MyText.labelLarge('Actions', color: contentTheme.primary)),
],
rows: attendanceController.employees
.mapIndexed((index, employee) => DataRow(cells: [
DataCell(MyText.bodyMedium(employee.name, fontWeight: 600)),
DataCell(
MyText.bodyMedium(employee.designation, fontWeight: 600)),
DataCell(
ElevatedButton(
onPressed: () async {
final success = await attendanceController
.captureAndUploadAttendance(
employee.id,
int.parse(
attendanceController.selectedProjectId ?? "0"),
comment: "Checked in via app",
);
rows: attendanceController.employees.mapIndexed((index, employee) {
// Set actionText directly from employee's action
String actionText = "";
int? activity =
employee.activity; // Assuming employee has an 'action' field
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
success
? 'Attendence Marked successfully!'
: 'Image upload failed.',
),
),
);
},
child: const Text('Check In'),
// Set action text based on employee's activity value
if (activity == 1) {
actionText = "Check In";
} else if (activity == 0) {
actionText = "Check Out";
} else if (activity == 4) {
// Activity 4 logic
actionText = "Check In";
}
return DataRow(cells: [
DataCell(MyText.bodyMedium(employee.name, fontWeight: 600)),
DataCell(MyText.bodyMedium(employee.designation, fontWeight: 600)),
DataCell(ElevatedButton(
onPressed: () async {
if (attendanceController.selectedProjectId == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Please select a project first")),
);
return;
}
// Determine the updated action based on current activity
int updatedAction;
String actionText;
if (activity == 0 || activity == 4) {
// The user is currently checked in (activity == 0), so they need to check out
updatedAction = 0;
actionText = "Check In";
} else {
// The user is currently checked out (activity == 1), so they need to check in
updatedAction = 1;
actionText = "Check Out";
}
// Call the method to capture attendance with the updated action
final success =
await attendanceController.captureAndUploadAttendance(
employee.id, // Pass the employee's ID
employee.employeeId,
int.parse(attendanceController
.selectedProjectId!), // Pass the selected project ID
comment: actionText, // Action text (Check In / Check Out)
action: updatedAction,
);
// Show success or failure message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
success
? 'Attendance marked successfully!'
: 'Image upload failed.',
),
),
]))
.toList(),
);
if (success) {
// Fetch the updated list of employees and logs after the attendance upload
attendanceController.fetchEmployeesByProject(
attendanceController.selectedProjectId!);
attendanceController.fetchAttendanceLogs(
attendanceController.selectedProjectId!);
// You can add more fetch calls if necessary, such as regularization logs.
}
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(
vertical: 4, horizontal: 6), // Adjust padding
minimumSize: Size(60, 20), // Adjust minimum size for the button
textStyle: TextStyle(fontSize: 12), // Smaller font size
),
child: Text(
activity == 0 || activity == 4 ? 'Check In' : 'Check Out'),
)),
]);
}).toList(),
),
);
}
@ -333,6 +386,15 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
)),
DataCell(
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(
vertical: 4,
horizontal: 6), // Adjust padding
minimumSize:
Size(60, 20), // Adjust minimum size
textStyle: TextStyle(
fontSize: 12), // Smaller font size
),
onPressed: () async {
// Call fetchLogsView to load the log data
await attendanceController.fetchLogsView(log.id