marco.pms.mobileapp/lib/view/Attendence/attendance_screen.dart
Vaibhav Surve f5d4ab8415 feat: Add attendance log screen and related functionalities
- Implemented AttendenceLogScreen to display employee attendance logs.
- Created RegularizationRequestsTab to manage regularization requests.
- Added TodaysAttendanceTab for viewing today's attendance.
- Removed outdated dashboard chart implementation.
- Updated dashboard screen to integrate new attendance overview and project progress charts.
- Refactored employee detail and employee screens to use updated controllers.
- Organized expense-related imports and components for better structure.
- Adjusted daily progress report to use the correct controller.
2025-08-29 15:53:19 +05:30

247 lines
8.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:marco/helpers/theme/app_theme.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/widgets/my_flex.dart';
import 'package:marco/helpers/widgets/my_flex_item.dart';
import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/controller/attendance/attendance_screen_controller.dart';
import 'package:marco/controller/permission_controller.dart';
import 'package:marco/model/attendance/attendence_filter_sheet.dart';
import 'package:marco/controller/project_controller.dart';
import 'package:marco/view/Attendence/regularization_requests_tab.dart';
import 'package:marco/view/Attendence/attendance_logs_tab.dart';
import 'package:marco/view/Attendence/todays_attendance_tab.dart';
import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
class AttendanceScreen extends StatefulWidget {
const AttendanceScreen({super.key});
@override
State<AttendanceScreen> createState() => _AttendanceScreenState();
}
class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
final attendanceController = Get.put(AttendanceController());
final permissionController = Get.put(PermissionController());
final projectController = Get.find<ProjectController>();
String selectedTab = 'todaysAttendance';
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// Listen for future project selection changes
ever<String>(projectController.selectedProjectId, (projectId) async {
if (projectId.isNotEmpty) await _loadData(projectId);
});
// Load initial data
final projectId = projectController.selectedProjectId.value;
if (projectId.isNotEmpty) _loadData(projectId);
});
}
Future<void> _loadData(String projectId) async {
try {
await attendanceController.loadAttendanceData(projectId);
attendanceController.update(['attendance_dashboard_controller']);
} catch (e) {
debugPrint("Error loading data: $e");
}
}
Future<void> _refreshData() async {
final projectId = projectController.selectedProjectId.value;
if (projectId.isNotEmpty) await _loadData(projectId);
}
Widget _buildAppBar() {
return AppBar(
backgroundColor: const Color(0xFFF5F5F5),
elevation: 0.5,
automaticallyImplyLeading: false,
titleSpacing: 0,
title: Padding(
padding: MySpacing.xy(16, 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.arrow_back_ios_new,
color: Colors.black, size: 20),
onPressed: () => Get.offNamed('/dashboard'),
),
MySpacing.width(8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.titleLarge('Attendance',
fontWeight: 700, color: Colors.black),
MySpacing.height(2),
GetBuilder<ProjectController>(
builder: (projectController) {
final projectName =
projectController.selectedProject?.name ??
'Select Project';
return Row(
children: [
const Icon(Icons.work_outline,
size: 14, color: Colors.grey),
MySpacing.width(4),
Expanded(
child: MyText.bodySmall(
projectName,
fontWeight: 600,
overflow: TextOverflow.ellipsis,
color: Colors.grey[700],
),
),
],
);
},
),
],
),
),
],
),
),
);
}
Widget _buildFilterAndRefreshRow() {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
MyText.bodyMedium("Filter", fontWeight: 600),
Tooltip(
message: 'Filter Project',
child: InkWell(
borderRadius: BorderRadius.circular(24),
onTap: () async {
final result = await showModalBottomSheet<Map<String, dynamic>>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
),
builder: (context) => AttendanceFilterBottomSheet(
controller: attendanceController,
permissionController: permissionController,
selectedTab: selectedTab,
),
);
if (result != null) {
final selectedProjectId =
projectController.selectedProjectId.value;
final selectedView = result['selectedTab'] as String?;
if (selectedProjectId.isNotEmpty) {
try {
await attendanceController
.fetchEmployeesByProject(selectedProjectId);
await attendanceController
.fetchAttendanceLogs(selectedProjectId);
await attendanceController
.fetchRegularizationLogs(selectedProjectId);
await attendanceController
.fetchProjectData(selectedProjectId);
} catch (_) {}
attendanceController
.update(['attendance_dashboard_controller']);
}
if (selectedView != null && selectedView != selectedTab) {
setState(() => selectedTab = selectedView);
}
}
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.tune, size: 18),
),
),
),
],
);
}
Widget _buildNoProjectWidget() {
return Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: MyText.titleMedium(
'No Records Found',
fontWeight: 600,
color: Colors.grey[600],
),
),
);
}
Widget _buildSelectedTabContent() {
switch (selectedTab) {
case 'attendanceLogs':
return AttendanceLogsTab(controller: attendanceController);
case 'regularizationRequests':
return RegularizationRequestsTab(controller: attendanceController);
case 'todaysAttendance':
default:
return TodaysAttendanceTab(controller: attendanceController);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(72),
child: _buildAppBar(),
),
body: SafeArea(
child: GetBuilder<AttendanceController>(
init: attendanceController,
tag: 'attendance_dashboard_controller',
builder: (controller) {
final selectedProjectId = projectController.selectedProjectId.value;
final noProjectSelected = selectedProjectId.isEmpty;
return MyRefreshIndicator(
onRefresh: _refreshData,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: MySpacing.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MySpacing.height(flexSpacing),
_buildFilterAndRefreshRow(),
MySpacing.height(flexSpacing),
MyFlex(
children: [
MyFlexItem(
sizes: 'lg-12 md-12 sm-12',
child: noProjectSelected
? _buildNoProjectWidget()
: _buildSelectedTabContent(),
),
],
),
],
),
),
);
},
),
),
);
}
}