From 21fe8f7d6331763b9e9247ded9fb99b4120a1d27 Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Mon, 28 Apr 2025 13:23:54 +0530 Subject: [PATCH] added the refresh for attendence tab --- devtools_options.yaml | 3 + lib/helpers/services/api_service.dart | 4 +- lib/helpers/services/auth_service.dart | 2 +- lib/helpers/utils/attendance_actions.dart | 23 + lib/model/attendance_log_model.dart | 13 +- lib/model/attendance_log_view_model.dart | 26 +- lib/view/dashboard/attendanceScreen.dart | 715 +++++++++++++++------- 7 files changed, 538 insertions(+), 248 deletions(-) create mode 100644 devtools_options.yaml create mode 100644 lib/helpers/utils/attendance_actions.dart diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/helpers/services/api_service.dart b/lib/helpers/services/api_service.dart index b23a487..3e16533 100644 --- a/lib/helpers/services/api_service.dart +++ b/lib/helpers/services/api_service.dart @@ -6,7 +6,7 @@ import 'package:intl/intl.dart'; class ApiService { - static const String baseUrl = "https://api.marcoaiot.com/api"; + static const String baseUrl = "https://stageapi.marcoaiot.com/api"; // ===== Common Helpers ===== @@ -82,7 +82,7 @@ class ApiService { }; final response = - await _getRequest("/attendance/project/team", queryParams: query); + await _getRequest("/attendance/project/log", queryParams: query); return response != null ? _parseResponse(response, label: 'Attendance Logs') : null; diff --git a/lib/helpers/services/auth_service.dart b/lib/helpers/services/auth_service.dart index c9ef4d5..fde383e 100644 --- a/lib/helpers/services/auth_service.dart +++ b/lib/helpers/services/auth_service.dart @@ -8,7 +8,7 @@ class AuthService { static Future?> loginUser(Map data) async { try { final response = await http.post( - Uri.parse('https://api.marcoaiot.com/api/auth/login'), + Uri.parse('https://stageapi.marcoaiot.com/api/auth/login'), headers: {'Content-Type': 'application/json'}, body: jsonEncode(data), ); diff --git a/lib/helpers/utils/attendance_actions.dart b/lib/helpers/utils/attendance_actions.dart new file mode 100644 index 0000000..955517d --- /dev/null +++ b/lib/helpers/utils/attendance_actions.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +// Define action texts +class ButtonActions { + static const String checkIn = "Check In"; + static const String checkOut = "Check Out"; + static const String requestRegularize = " Request Regularize"; + static const String rejected = "Rejected"; + static const String approved = "Approved"; + static const String requested = "Requested"; +} + +// Map action texts to colors +class AttendanceActionColors { + static const Map colors = { + ButtonActions.checkIn: Colors.green, + ButtonActions.checkOut: Colors.red, + ButtonActions.requestRegularize: Colors.blue, + ButtonActions.rejected: Colors.orange, + ButtonActions.approved: Colors.green, + ButtonActions.requested: Colors.yellow, + }; +} diff --git a/lib/model/attendance_log_model.dart b/lib/model/attendance_log_model.dart index 9f965ff..ef6fed9 100644 --- a/lib/model/attendance_log_model.dart +++ b/lib/model/attendance_log_model.dart @@ -1,5 +1,6 @@ class AttendanceLogModel { final String name; + final int employeeId; final String role; final DateTime? checkIn; final DateTime? checkOut; @@ -13,16 +14,22 @@ class AttendanceLogModel { this.checkOut, required this.activity, required this.id, + required this.employeeId, }); factory AttendanceLogModel.fromJson(Map json) { return AttendanceLogModel( name: "${json['firstName'] ?? ''} ${json['lastName'] ?? ''}".trim(), role: json['jobRoleName'] ?? '', - checkIn: json['checkInTime'] != null ? DateTime.tryParse(json['checkInTime']) : null, - checkOut: json['checkOutTime'] != null ? DateTime.tryParse(json['checkOutTime']) : null, + checkIn: json['checkInTime'] != null + ? DateTime.tryParse(json['checkInTime']) + : null, + checkOut: json['checkOutTime'] != null + ? DateTime.tryParse(json['checkOutTime']) + : null, activity: json['activity'] ?? 0, - id: json['id'] != null ? json['id'] : null, + id: json['id'] != null ? json['id'] : null, + employeeId: json['employeeId'] != null ? json['employeeId'] : null, ); } } diff --git a/lib/model/attendance_log_view_model.dart b/lib/model/attendance_log_view_model.dart index 6380b5c..941d492 100644 --- a/lib/model/attendance_log_view_model.dart +++ b/lib/model/attendance_log_view_model.dart @@ -1,24 +1,36 @@ import 'package:intl/intl.dart'; + class AttendanceLogViewModel { final DateTime? activityTime; final String? imageUrl; - final String? description; + final String? comment; + final String? thumbPreSignedUrl; + final String? preSignedUrl; AttendanceLogViewModel({ this.activityTime, this.imageUrl, - this.description, + this.comment, + this.thumbPreSignedUrl, + this.preSignedUrl, }); factory AttendanceLogViewModel.fromJson(Map json) { return AttendanceLogViewModel( - activityTime: json['activityTime'] != null ? DateTime.tryParse(json['activityTime']) : null, + activityTime: json['activityTime'] != null + ? DateTime.tryParse(json['activityTime']) + : null, imageUrl: json['imageUrl'], - description: json['description'], + comment: json['comment'], + thumbPreSignedUrl: json['thumbPreSignedUrl'], + preSignedUrl: json['preSignedUrl'], ); } - String? get formattedDate => activityTime != null ? DateFormat('yyyy-MM-dd').format(activityTime!) : null; + String? get formattedDate => activityTime != null + ? DateFormat('yyyy-MM-dd').format(activityTime!) + : null; - String? get formattedTime => activityTime != null ? DateFormat('hh:mm a').format(activityTime!) : null; -} \ No newline at end of file + String? get formattedTime => + activityTime != null ? DateFormat('hh:mm a').format(activityTime!) : null; +} diff --git a/lib/view/dashboard/attendanceScreen.dart b/lib/view/dashboard/attendanceScreen.dart index 08562a3..da78578 100644 --- a/lib/view/dashboard/attendanceScreen.dart +++ b/lib/view/dashboard/attendanceScreen.dart @@ -16,6 +16,10 @@ import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/view/layouts/layout.dart'; import 'package:marco/controller/dashboard/attendance_screen_controller.dart'; import 'package:intl/intl.dart'; +import 'package:marco/controller/permission_controller.dart'; +import 'package:marco/helpers/utils/permission_constants.dart'; // Make sure this import is correct +import 'package:marco/helpers/utils/attendance_actions.dart'; +import 'package:marco/helpers/widgets/my_refresh_wrapper.dart'; class AttendanceScreen extends StatefulWidget { const AttendanceScreen({super.key}); @@ -26,163 +30,186 @@ class AttendanceScreen extends StatefulWidget { class _AttendanceScreenState extends State with UIMixin { AttendanceController attendanceController = Get.put(AttendanceController()); + PermissionController permissionController = Get.find(); @override Widget build(BuildContext context) { return Layout( - child: GetBuilder( - init: attendanceController, - tag: 'attendance_dashboard_controller', - builder: (controller) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: MySpacing.x(flexSpacing), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MyText.titleMedium("Attendance", - fontSize: 18, fontWeight: 600), - MyBreadcrumb( - children: [ - MyBreadcrumbItem(name: 'Dashboard'), - MyBreadcrumbItem(name: 'Attendance', active: true), - ], - ), - ], + child: MyRefreshWrapper( + onRefresh: () async { + // Call your controller's refresh logic + if (attendanceController.selectedProjectId != null) { + await attendanceController.fetchEmployeesByProject( + attendanceController.selectedProjectId!); + await attendanceController + .fetchAttendanceLogs(attendanceController.selectedProjectId!); + } else { + await attendanceController.fetchProjects(); + } + }, + child: GetBuilder( + init: attendanceController, + tag: 'attendance_dashboard_controller', + builder: (controller) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: MySpacing.x(flexSpacing), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + MyText.titleMedium("Attendance", + fontSize: 18, fontWeight: 600), + MyBreadcrumb( + children: [ + MyBreadcrumbItem(name: 'Dashboard'), + MyBreadcrumbItem(name: 'Attendance', active: true), + ], + ), + ], + ), ), - ), - MySpacing.height(flexSpacing), - Padding( - padding: MySpacing.x(flexSpacing / 2), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MySpacing.height(flexSpacing), - // Move project selection here - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: MyContainer.bordered( - padding: MySpacing.xy(8, 8), - child: PopupMenuButton( - onSelected: (value) { - setState(() { - attendanceController.selectedProjectId = - value; - attendanceController - .fetchEmployeesByProject(value); - attendanceController - .fetchAttendanceLogs(value); - attendanceController - .fetchAttendanceLogs(value); - }); - }, - itemBuilder: (BuildContext context) { - if (attendanceController.projects.isEmpty) { - return [ - PopupMenuItem( - value: '', - child: MyText.bodySmall('No Data', - fontWeight: 600), - ) - ]; - } - return attendanceController.projects - .map((project) { - return PopupMenuItem( - value: project.id.toString(), - height: 32, - child: MyText.bodySmall( - project.name, + MySpacing.height(flexSpacing), + Padding( + padding: MySpacing.x(flexSpacing / 2), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MySpacing.height(flexSpacing), + // Project selection dropdown + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: MyContainer.bordered( + padding: MySpacing.xy(8, 8), + child: PopupMenuButton( + onSelected: (value) { + setState(() { + attendanceController.selectedProjectId = + value; + attendanceController + .fetchEmployeesByProject(value); + attendanceController + .fetchAttendanceLogs(value); + }); + }, + itemBuilder: (BuildContext context) { + if (attendanceController.projects.isEmpty) { + return [ + PopupMenuItem( + value: '', + child: MyText.bodySmall('No Data', + fontWeight: 600), + ) + ]; + } + return attendanceController.projects + .map((project) { + return PopupMenuItem( + value: project.id.toString(), + height: 32, + child: MyText.bodySmall( + project.name, + color: theme.colorScheme.onSurface, + fontWeight: 600, + ), + ); + }).toList(); + }, + color: theme.cardTheme.color, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + MyText.labelSmall( + attendanceController.selectedProjectId != + null + ? attendanceController.projects + .firstWhereOrNull((proj) => + proj.id.toString() == + attendanceController + .selectedProjectId) + ?.name ?? + 'Select a Project' + : 'Select a Project', color: theme.colorScheme.onSurface, - fontWeight: 600, ), - ); - }).toList(); - }, - color: theme.cardTheme.color, - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - MyText.labelSmall( - attendanceController.selectedProjectId != - null - ? attendanceController.projects - .firstWhereOrNull((proj) => - proj.id.toString() == - attendanceController - .selectedProjectId) - ?.name ?? - 'Select a Project' - : 'Select a Project', - color: theme.colorScheme.onSurface, - ), - Icon(LucideIcons.chevron_down, - size: 20, - color: theme.colorScheme.onSurface), - ], + Icon(LucideIcons.chevron_down, + size: 20, + color: theme.colorScheme.onSurface), + ], + ), ), ), ), - ), - ], - ), - MySpacing.height(flexSpacing), - MyFlex( - children: [ - MyFlexItem( - child: DefaultTabController( - length: 3, - child: MyCard.bordered( - borderRadiusAll: 4, - border: - Border.all(color: Colors.grey.withAlpha(50)), - shadow: MyShadow( - elevation: 1, - position: MyShadowPosition.bottom), - paddingAll: 10, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TabBar( - labelColor: theme.colorScheme.primary, - unselectedLabelColor: theme - .colorScheme.onSurface - .withAlpha(150), - tabs: const [ - Tab(text: 'Employee List'), - Tab(text: 'Logs'), - Tab(text: 'Regularization'), + ], + ), + MySpacing.height(flexSpacing), + MyFlex( + children: [ + MyFlexItem( + child: Obx(() { + bool hasRegularizationPermission = + permissionController.hasPermission( + Permissions.regularizeAttendance); + + final tabs = [ + const Tab(text: 'Employee List'), + const Tab(text: 'Logs'), + if (hasRegularizationPermission) + const Tab(text: 'Regularization'), + ]; + + final views = [ + employeeListTab(), + reportsTab(context), + if (hasRegularizationPermission) + regularizationTab(context), + ]; + + return DefaultTabController( + length: tabs.length, + child: MyCard.bordered( + borderRadiusAll: 4, + border: Border.all( + color: Colors.grey.withAlpha(50)), + shadow: MyShadow( + elevation: 1, + position: MyShadowPosition.bottom), + paddingAll: 10, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + TabBar( + labelColor: theme.colorScheme.primary, + unselectedLabelColor: theme + .colorScheme.onSurface + .withAlpha(150), + tabs: tabs, + ), + MySpacing.height(16), + SizedBox( + height: 500, + child: TabBarView(children: views), + ), ], ), - MySpacing.height(16), - SizedBox( - height: 500, - child: TabBarView( - children: [ - employeeListTab(), - reportsTab(context), - regularizationTab(context), - ], - ), - ), - ], - ), - ), + ), + ); + }), ), - ), - ], - ), - ], + ], + ), + ], + ), ), - ), - ], - ); - }, + ], + ); + }, + ), ), ); } @@ -228,11 +255,10 @@ class _AttendanceScreenState extends State with UIMixin { // Set action text based on employee's activity value if (activity == 1) { actionText = "Check In"; - } else if (activity == 0) { + } else if (activity == 0 || activity == 4) { actionText = "Check Out"; - } else if (activity == 4) { - // Activity 4 logic - actionText = "Check In"; + } else if (activity == 2) { + actionText = "Request Regularize"; } return DataRow(cells: [ @@ -252,28 +278,22 @@ class _AttendanceScreenState extends State with UIMixin { 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"; + actionText = ButtonActions.checkIn; } else { - // The user is currently checked out (activity == 1), so they need to check in updatedAction = 1; - actionText = "Check Out"; + actionText = ButtonActions.checkOut; } - // Call the method to capture attendance with the updated action final success = await attendanceController.captureAndUploadAttendance( - employee.id, // Pass the employee's ID + employee.id, employee.employeeId, - - int.parse(attendanceController - .selectedProjectId!), // Pass the selected project ID - comment: actionText, // Action text (Check In / Check Out) + int.parse(attendanceController.selectedProjectId!), + comment: actionText, action: updatedAction, ); - // Show success or failure message ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( @@ -284,23 +304,25 @@ class _AttendanceScreenState extends State with UIMixin { ), ); 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 + backgroundColor: AttendanceActionColors.colors[ + (activity == 0 || activity == 4) + ? ButtonActions.checkIn + : ButtonActions.checkOut], + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + minimumSize: const Size(60, 20), + textStyle: const TextStyle(fontSize: 12), ), - child: Text( - activity == 0 || activity == 4 ? 'Check In' : 'Check Out'), - )), + child: Text((activity == 0 || activity == 4) + ? ButtonActions.checkIn + : ButtonActions.checkOut), + )) ]); }).toList(), ), @@ -360,6 +382,9 @@ class _AttendanceScreenState extends State with UIMixin { DataColumn( label: MyText.labelLarge('Check-Out', color: contentTheme.primary)), + DataColumn( + label: MyText.labelLarge('View', + color: contentTheme.primary)), DataColumn( label: MyText.labelLarge('Action', color: contentTheme.primary)), @@ -370,36 +395,57 @@ class _AttendanceScreenState extends State with UIMixin { MyText.bodyMedium(log.name, fontWeight: 600)), DataCell( MyText.bodyMedium(log.role, fontWeight: 600)), - DataCell(MyText.bodyMedium( - log.checkIn != null - ? DateFormat('dd MMM yyyy hh:mm a') - .format(log.checkIn!) - : '-', - fontWeight: 600, - )), - DataCell(MyText.bodyMedium( - log.checkOut != null - ? DateFormat('dd MMM yyyy hh:mm a') - .format(log.checkOut!) - : '-', - fontWeight: 600, - )), 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 - ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium( + log.checkIn != null + ? DateFormat('dd MMM yyyy') + .format(log.checkIn!) + : '-', + fontWeight: 600, + ), + MyText.bodyMedium( + log.checkIn != null + ? DateFormat('hh:mm a') + .format(log.checkIn!) + : '', + fontWeight: 600, + ), + ], + ), + ), + DataCell( + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium( + log.checkOut != null + ? DateFormat('dd MMM yyyy') + .format(log.checkOut!) + : '-', + fontWeight: 600, + ), + MyText.bodyMedium( + log.checkOut != null + ? DateFormat('hh:mm a') + .format(log.checkOut!) + : '', + fontWeight: 600, + ), + ], + ), + ), + + DataCell( + IconButton( + icon: const Icon(Icons.visibility, size: 18), onPressed: () async { - // Call fetchLogsView to load the log data - await attendanceController.fetchLogsView(log.id - .toString()); // Assuming `log.id` is available - // Open the bottom sheet to display the log details + await attendanceController + .fetchLogsView(log.id.toString()); showModalBottomSheet( context: context, shape: const RoundedRectangleBorder( @@ -419,33 +465,118 @@ class _AttendanceScreenState extends State with UIMixin { "Attendance Log Details", fontWeight: 700), const SizedBox(height: 16), - // Display the log details + if (attendanceController + .attendenceLogsView.isNotEmpty) + Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Expanded( + child: MyText.bodyMedium( + "Date", + fontWeight: 600)), + Expanded( + child: MyText.bodyMedium( + "Time", + fontWeight: 600)), + Expanded( + child: MyText.bodyMedium( + "Description", + fontWeight: 600)), + Expanded( + child: MyText.bodyMedium( + "Image", + fontWeight: 600)), + ], + ), + const Divider( + thickness: 1, height: 24), if (attendanceController .attendenceLogsView.isNotEmpty) ...attendanceController .attendenceLogsView - .map((log) => Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - MyText.bodyMedium( - "Date: ${log.formattedDate ?? '-'}", - fontWeight: 600, - ), - MyText.bodyMedium( - "Time: ${log.formattedTime ?? '-'}", - fontWeight: 600, - ), - MyText.bodyMedium( - "Description: ${log.description ?? '-'}", - fontWeight: 600, - ), - const Divider( - thickness: 1, - height: 24), - ], - )), + .map((log) { + return Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Expanded( + child: MyText.bodyMedium( + log.formattedDate ?? + '-', + fontWeight: 600)), + Expanded( + child: MyText.bodyMedium( + log.formattedTime ?? + '-', + fontWeight: 600)), + Expanded( + child: MyText.bodyMedium( + log.comment ?? '-', + fontWeight: 600)), + Expanded( + child: GestureDetector( + onTap: () { + if (log.preSignedUrl != + null) { + showDialog( + context: context, + builder: (_) => + Dialog( + child: + Image.network( + log.preSignedUrl!, + fit: BoxFit + .cover, + height: 400, + errorBuilder: + (context, + error, + stackTrace) { + return Icon( + Icons + .broken_image, + size: 50, + color: Colors + .grey); + }, + ), + ), + ); + } + }, + child: log.thumbPreSignedUrl != + null + ? Image.network( + log.thumbPreSignedUrl!, + fit: BoxFit.cover, + height: 40, + width: 40, + errorBuilder: + (context, + error, + stackTrace) { + return Icon( + Icons + .broken_image, + size: 40, + color: Colors + .grey); + }, + ) + : Icon( + Icons + .broken_image, + size: 40, + color: + Colors.grey), + ), + ), + ], + ); + }), Align( alignment: Alignment.centerRight, child: ElevatedButton( @@ -453,16 +584,100 @@ class _AttendanceScreenState extends State with UIMixin { Navigator.pop(context), child: const Text("Close"), ), - ) + ), ], ), ); }, ); }, - child: const Text('View'), ), - ) + ), + + // The action button + 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 the employee's activity + // Determine the updated action based on current activity + int updatedAction; + String actionText; + + if (log.activity == 0 || log.activity == 4) { + // Needs to Check In + updatedAction = 0; + actionText = "Check In"; + } else if (log.activity == 1) { + // Needs to Check Out + updatedAction = 1; + actionText = "Check Out"; + } else if (log.activity == 2) { + // Needs to Request Regularize + updatedAction = 2; + actionText = "Request Regularize"; + } else { + // Default case (fallback) + updatedAction = 0; + actionText = "Unknown Action"; + } + + // Call the method to capture and upload attendance + final success = await attendanceController + .captureAndUploadAttendance( + log.id, + log.employeeId, + int.parse(attendanceController + .selectedProjectId!), // Pass the selected project ID + comment: + actionText, // Action text (Check In/Check Out) + action: updatedAction, // Updated action + ); + + // Show success or failure message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + success + ? 'Attendance marked successfully!' + : 'Image upload failed.', + ), + ), + ); + if (success) { + // Fetch updated logs and employees + attendanceController.fetchEmployeesByProject( + attendanceController.selectedProjectId!); + attendanceController.fetchAttendanceLogs( + attendanceController.selectedProjectId!); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: AttendanceActionColors.colors[ + (log.activity == 0 || log.activity == 4) + ? ButtonActions.checkIn + : ButtonActions.checkOut], + padding: EdgeInsets.symmetric( + vertical: 4, horizontal: 6), + minimumSize: Size(60, 20), + textStyle: TextStyle(fontSize: 12), + ), + child: Text( + (log.activity == 0 || log.activity == 4) + ? ButtonActions.checkIn + : (log.activity == 2) + ? ButtonActions.requestRegularize + : ButtonActions.checkOut, + ), + )), ])) .toList(), ), @@ -529,20 +744,50 @@ class _AttendanceScreenState extends State with UIMixin { MyText.bodyMedium(log.name, fontWeight: 600)), DataCell( MyText.bodyMedium(log.role, fontWeight: 600)), - DataCell(MyText.bodyMedium( - log.checkIn != null - ? DateFormat('dd MMM yyyy hh:mm a') - .format(log.checkIn!) - : '-', - fontWeight: 600, - )), - DataCell(MyText.bodyMedium( - log.checkOut != null - ? DateFormat('dd MMM yyyy hh:mm a') - .format(log.checkOut!) - : '-', - fontWeight: 600, - )), + DataCell( + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium( + log.checkIn != null + ? DateFormat('dd/MMM/yyyy') + .format(log.checkIn!) + : '-', + fontWeight: 600, + ), + MyText.bodyMedium( + log.checkIn != null + ? DateFormat('hh:mm a') + .format(log.checkIn!) + : '', + fontWeight: 600, + ), + ], + ), + ), + DataCell( + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium( + log.checkOut != null + ? DateFormat('dd MMM yyyy') + .format(log.checkOut!) + : '-', + fontWeight: 600, + ), + MyText.bodyMedium( + log.checkOut != null + ? DateFormat('hh:mm a') + .format(log.checkOut!) + : '', + fontWeight: 600, + ), + ], + ), + ), DataCell(IconButton( icon: Icon(Icons.info_outline, color: contentTheme.primary),