marco.pms.mobileapp/lib/view/Attendence/attendance_logs_tab.dart
Vaibhav Surve 8fb725a5cf Refactor Attendance Logs and Regularization Requests Tabs
- Changed AttendanceLogsTab from StatelessWidget to StatefulWidget to manage state for showing pending actions.
- Added a status header in AttendanceLogsTab to indicate when only pending actions are displayed.
- Updated filtering logic in AttendanceLogsTab to use filteredLogs based on the pending actions toggle.
- Refactored AttendanceScreen to include a search bar for filtering attendance logs by name.
- Introduced a new filter icon in AttendanceScreen for accessing the filter options.
- Updated RegularizationRequestsTab to use filteredRegularizationLogs for displaying requests.
- Modified TodaysAttendanceTab to utilize filteredEmployees for showing today's attendance.
- Cleaned up code formatting and improved readability across various files.
2025-09-16 18:06:19 +05:30

255 lines
10 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:marco/controller/attendance/attendance_screen_controller.dart';
import 'package:marco/helpers/utils/date_time_utils.dart';
import 'package:marco/helpers/widgets/avatar.dart';
import 'package:marco/helpers/widgets/my_card.dart';
import 'package:marco/helpers/widgets/my_container.dart';
import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
import 'package:marco/model/attendance/log_details_view.dart';
import 'package:marco/model/attendance/attendence_action_button.dart';
class AttendanceLogsTab extends StatefulWidget {
final AttendanceController controller;
const AttendanceLogsTab({super.key, required this.controller});
@override
State<AttendanceLogsTab> createState() => _AttendanceLogsTabState();
}
class _AttendanceLogsTabState extends State<AttendanceLogsTab> {
Widget _buildStatusHeader() {
return Obx(() {
final showPending = widget.controller.showPendingOnly.value;
if (!showPending) return const SizedBox.shrink();
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
color: Colors.orange.shade50,
child: Row(
children: [
const Icon(
Icons.pending_actions,
color: Colors.orange,
size: 18,
),
const SizedBox(width: 8),
const Expanded(
child: Text(
"Showing Pending Actions Only",
style: TextStyle(
color: Colors.orange,
fontWeight: FontWeight.w600,
),
),
),
InkWell(
onTap: () {
widget.controller.showPendingOnly.value = false;
},
child: const Icon(
Icons.close,
size: 18,
color: Colors.orange,
),
),
],
),
);
});
}
@override
Widget build(BuildContext context) {
return Obx(() {
final logs = List.of(widget.controller.filteredLogs);
logs.sort((a, b) {
final aDate = a.checkIn ?? DateTime(0);
final bDate = b.checkIn ?? DateTime(0);
return bDate.compareTo(aDate);
});
// Use controller's observable for pending filter
final showPendingOnly = widget.controller.showPendingOnly.value;
final filteredLogs = showPendingOnly
? logs
.where((employee) =>
employee.activity == 1 || employee.activity == 2)
.toList()
: logs;
final dateRangeText = widget.controller.startDateAttendance != null &&
widget.controller.endDateAttendance != null
? '${DateTimeUtils.formatDate(widget.controller.startDateAttendance!, 'dd MMM yyyy')} - '
'${DateTimeUtils.formatDate(widget.controller.endDateAttendance!, 'dd MMM yyyy')}'
: 'Select date range';
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header row: Title and Date Range
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.titleMedium("Attendance Logs", fontWeight: 600),
widget.controller.isLoading.value
? SkeletonLoaders.dateSkeletonLoader()
: MyText.bodySmall(
dateRangeText,
fontWeight: 600,
color: Colors.grey[700],
overflow: TextOverflow.ellipsis,
),
],
),
),
// ✅ Pending status header
_buildStatusHeader(),
MySpacing.height(8),
// Content: Skeleton, Empty, or List
if (widget.controller.isLoadingAttendanceLogs.value)
SkeletonLoaders.employeeListSkeletonLoader()
else if (filteredLogs.isEmpty)
SizedBox(
height: 120,
child: Center(
child: Text(showPendingOnly
? "No Pending Actions Found"
: "No Attendance Logs Found for this Project"),
),
)
else
MyCard.bordered(
paddingAll: 8,
child: Column(
children: List.generate(filteredLogs.length, (index) {
final employee = filteredLogs[index];
final currentDate = employee.checkIn != null
? DateTimeUtils.formatDate(
employee.checkIn!, 'dd MMM yyyy')
: '';
final previousDate =
index > 0 && filteredLogs[index - 1].checkIn != null
? DateTimeUtils.formatDate(
filteredLogs[index - 1].checkIn!, 'dd MMM yyyy')
: '';
final showDateHeader =
index == 0 || currentDate != previousDate;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (showDateHeader)
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: MyText.bodyMedium(
currentDate,
fontWeight: 700,
),
),
MyContainer(
paddingAll: 8,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Avatar(
firstName: employee.firstName,
lastName: employee.lastName,
size: 31,
),
MySpacing.width(16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: MyText.bodyMedium(
employee.name,
fontWeight: 600,
overflow: TextOverflow.ellipsis,
),
),
MySpacing.width(6),
Flexible(
child: MyText.bodySmall(
'(${employee.designation})',
fontWeight: 600,
color: Colors.grey[700],
overflow: TextOverflow.ellipsis,
),
),
],
),
MySpacing.height(8),
if (employee.checkIn != null ||
employee.checkOut != null)
Row(
children: [
if (employee.checkIn != null) ...[
const Icon(Icons.arrow_circle_right,
size: 16, color: Colors.green),
MySpacing.width(4),
MyText.bodySmall(
DateTimeUtils.formatDate(
employee.checkIn!, 'hh:mm a'),
fontWeight: 600,
),
MySpacing.width(16),
],
if (employee.checkOut != null) ...[
const Icon(Icons.arrow_circle_left,
size: 16, color: Colors.red),
MySpacing.width(4),
MyText.bodySmall(
DateTimeUtils.formatDate(
employee.checkOut!, 'hh:mm a'),
fontWeight: 600,
),
],
],
),
MySpacing.height(12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AttendanceActionButton(
employee: employee,
attendanceController: widget.controller,
),
MySpacing.width(8),
AttendanceLogViewButton(
employee: employee,
attendanceController: widget.controller,
),
],
),
],
),
),
],
),
),
if (index != filteredLogs.length - 1)
Divider(color: Colors.grey.withOpacity(0.3)),
],
);
}),
),
),
],
);
});
}
}