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

View File

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

View File

@ -1,28 +1,34 @@
class EmployeeModel { class EmployeeModel {
final int id; final int id;
final int employeeId;
final String name; final String name;
final String designation; final String designation;
final String checkIn; final String checkIn;
final String checkOut; final String checkOut;
final int actions; final int activity;
int action;
EmployeeModel({ EmployeeModel({
required this.id, required this.id,
required this.employeeId,
required this.name, required this.name,
required this.designation, required this.designation,
required this.checkIn, required this.checkIn,
required this.checkOut, required this.checkOut,
required this.actions, required this.action,
required this.activity,
}); });
factory EmployeeModel.fromJson(Map<String, dynamic> json) { factory EmployeeModel.fromJson(Map<String, dynamic> json) {
return EmployeeModel( return EmployeeModel(
id: json['employeeId'] ?? 0, id: json['id'] ?? 0,
employeeId: json['employeeId'] ?? 0,
name: '${json['firstName']} ${json['lastName']}', name: '${json['firstName']} ${json['lastName']}',
designation: json['jobRoleName'] ?? '', designation: json['jobRoleName'] ?? '',
checkIn: json['checkIn'] ?? '-', // Make sure your API returns this field checkIn: json['checkIn'] ?? '-', // Make sure your API returns this field
checkOut: json['checkOut'] ?? '-', 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: [ children: [
Expanded( Expanded(
child: MyContainer.bordered( child: MyContainer.bordered(
padding: MySpacing.xy(4, 8), padding: MySpacing.xy(8, 8),
child: PopupMenuButton<String>( child: PopupMenuButton<String>(
onSelected: (value) { onSelected: (value) {
setState(() { setState(() {
@ -122,7 +122,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
color: theme.colorScheme.onSurface, color: theme.colorScheme.onSurface,
), ),
Icon(LucideIcons.chevron_down, Icon(LucideIcons.chevron_down,
size: 16, size: 20,
color: theme.colorScheme.onSurface), color: theme.colorScheme.onSurface),
], ],
), ),
@ -219,37 +219,90 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
DataColumn( DataColumn(
label: MyText.labelLarge('Actions', color: contentTheme.primary)), label: MyText.labelLarge('Actions', color: contentTheme.primary)),
], ],
rows: attendanceController.employees rows: attendanceController.employees.mapIndexed((index, employee) {
.mapIndexed((index, employee) => DataRow(cells: [ // Set actionText directly from employee's action
DataCell(MyText.bodyMedium(employee.name, fontWeight: 600)), String actionText = "";
DataCell( int? activity =
MyText.bodyMedium(employee.designation, fontWeight: 600)), employee.activity; // Assuming employee has an 'action' field
DataCell(
ElevatedButton(
onPressed: () async {
final success = await attendanceController
.captureAndUploadAttendance(
employee.id,
int.parse(
attendanceController.selectedProjectId ?? "0"),
comment: "Checked in via app",
);
ScaffoldMessenger.of(context).showSnackBar( // Set action text based on employee's activity value
SnackBar( if (activity == 1) {
content: Text( actionText = "Check In";
success } else if (activity == 0) {
? 'Attendence Marked successfully!' actionText = "Check Out";
: 'Image upload failed.', } else if (activity == 4) {
), // Activity 4 logic
), actionText = "Check In";
); }
},
child: const Text('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( DataCell(
ElevatedButton( 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 { onPressed: () async {
// Call fetchLogsView to load the log data // Call fetchLogsView to load the log data
await attendanceController.fetchLogsView(log.id await attendanceController.fetchLogsView(log.id