marco.pms.mobileapp/lib/controller/dashboard/dashboard_controller.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

202 lines
5.7 KiB
Dart

import 'package:get/get.dart';
import 'package:marco/helpers/services/app_logger.dart';
import 'package:marco/helpers/services/api_service.dart';
import 'package:marco/controller/project_controller.dart';
import 'package:marco/model/dashboard/project_progress_model.dart';
class DashboardController extends GetxController {
// =========================
// Attendance overview
// =========================
final RxList<Map<String, dynamic>> roleWiseData =
<Map<String, dynamic>>[].obs;
final RxString attendanceSelectedRange = '15D'.obs;
final RxBool attendanceIsChartView = true.obs;
final RxBool isAttendanceLoading = false.obs;
// =========================
// Project progress overview
// =========================
final RxList<ChartTaskData> projectChartData = <ChartTaskData>[].obs;
final RxString projectSelectedRange = '15D'.obs;
final RxBool projectIsChartView = true.obs;
final RxBool isProjectLoading = false.obs;
// Common ranges
final List<String> ranges = ['7D', '15D', '30D'];
// Inject ProjectController
final ProjectController projectController = Get.find<ProjectController>();
@override
void onInit() {
super.onInit();
logSafe(
'DashboardController initialized with project ID: ${projectController.selectedProjectId.value}',
level: LogLevel.info,
);
if (projectController.selectedProjectId.value.isNotEmpty) {
fetchRoleWiseAttendance();
fetchProjectProgress();
}
// React to project change
ever<String>(projectController.selectedProjectId, (id) {
if (id.isNotEmpty) {
logSafe('Project changed to $id, refreshing dashboard',
level: LogLevel.info);
fetchRoleWiseAttendance();
fetchProjectProgress();
}
});
// 👇 Separate listeners for ranges
ever(attendanceSelectedRange, (_) => fetchRoleWiseAttendance());
ever(projectSelectedRange, (_) => fetchProjectProgress());
}
// =========================
// Helpers
// =========================
int _getDaysFromRange(String range) {
switch (range) {
case '7D':
return 7;
case '15D':
return 15;
case '30D':
return 30;
case '3M':
return 90;
case '6M':
return 180;
default:
return 7;
}
}
int getAttendanceDays() => _getDaysFromRange(attendanceSelectedRange.value);
int getProjectDays() => _getDaysFromRange(projectSelectedRange.value);
void updateAttendanceRange(String range) {
attendanceSelectedRange.value = range;
logSafe('Attendance range updated to $range', level: LogLevel.debug);
}
void updateProjectRange(String range) {
projectSelectedRange.value = range;
logSafe('Project range updated to $range', level: LogLevel.debug);
}
void toggleAttendanceChartView(bool isChart) {
attendanceIsChartView.value = isChart;
logSafe('Attendance chart view toggled to: $isChart',
level: LogLevel.debug);
}
void toggleProjectChartView(bool isChart) {
projectIsChartView.value = isChart;
logSafe('Project chart view toggled to: $isChart', level: LogLevel.debug);
}
/// Manual refresh
Future<void> refreshDashboard() async {
logSafe('Manual dashboard refresh triggered.', level: LogLevel.debug);
await Future.wait([
fetchRoleWiseAttendance(),
fetchProjectProgress(),
]);
}
// =========================
// API Calls
// =========================
Future<void> fetchRoleWiseAttendance() async {
final String projectId = projectController.selectedProjectId.value;
if (projectId.isEmpty) {
logSafe('Project ID is empty, skipping attendance API call.',
level: LogLevel.warning);
return;
}
try {
isAttendanceLoading.value = true;
final List<dynamic>? response =
await ApiService.getDashboardAttendanceOverview(
projectId,
getAttendanceDays(),
);
if (response != null) {
roleWiseData.value =
response.map((e) => Map<String, dynamic>.from(e)).toList();
logSafe('Attendance overview fetched successfully.',
level: LogLevel.info);
} else {
roleWiseData.clear();
logSafe('Failed to fetch attendance overview: response is null.',
level: LogLevel.error);
}
} catch (e, st) {
roleWiseData.clear();
logSafe(
'Error fetching attendance overview',
level: LogLevel.error,
error: e,
stackTrace: st,
);
} finally {
isAttendanceLoading.value = false;
}
}
Future<void> fetchProjectProgress() async {
final String projectId = projectController.selectedProjectId.value;
if (projectId.isEmpty) {
logSafe('Project ID is empty, skipping project progress API call.',
level: LogLevel.warning);
return;
}
try {
isProjectLoading.value = true;
// Instead of using DateTime.now(), we simply pass 'days' to the API
// Let the backend decide the correct date range
final response = await ApiService.getProjectProgress(
projectId: projectId,
days: getProjectDays(),
// Remove fromDate
);
if (response != null && response.success) {
projectChartData.value =
response.data.map((d) => ChartTaskData.fromProjectData(d)).toList();
logSafe('Project progress data mapped for chart', level: LogLevel.info);
} else {
projectChartData.clear();
logSafe('Failed to fetch project progress', level: LogLevel.error);
}
} catch (e, st) {
projectChartData.clear();
logSafe(
'Error fetching project progress',
level: LogLevel.error,
error: e,
stackTrace: st,
);
} finally {
isProjectLoading.value = false;
}
}
}