TIF_E41222181/lib/feature_teacher/dashboard/presentation/teacher_dashboard_controlle...

314 lines
11 KiB
Dart

import 'dart:developer';
import 'package:absensi_qr/configs/api_constant.dart';
import 'package:absensi_qr/features/others/main_controller.dart';
import 'package:absensi_qr/models/model_merging/schedule_report_item.dart';
import 'package:absensi_qr/models/model_merging/schedule_student_attendance_report.dart';
import 'package:absensi_qr/services/endpoint_service.dart';
import 'package:absensi_qr/services/geolocation_service.dart';
import 'package:absensi_qr/utils/app_util.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:get/get.dart';
class TeacherDashboardController extends GetxController {
// service controller
final EndpointService _httpService = Get.find<EndpointService>();
final MainController mainController = Get.find<MainController>();
final GeolocationService _geolocationService = Get.find<GeolocationService>();
final RxBool isLoading = false.obs;
final RxBool isLoadingStatistic = false.obs;
final RxList<ScheduleReportItem> dataSchedule = <ScheduleReportItem>[].obs;
/// data profile
final RxString name = ''.obs;
final RxString firstName = ''.obs;
final RxString email = ''.obs;
final RxString subject = ''.obs;
final RxString nip = ''.obs;
final RxString profileImageURL = ''.obs;
/// attendance history
final RxBool isLoadingAttendanceHistory = false.obs;
// data result store.
final RxList<ScheduleStudentAttendanceReport> attendanceHistoryResult =
<ScheduleStudentAttendanceReport>[].obs;
final Rx<BigInt> selectedClassId = BigInt.from(-1).obs;
// final DateTime selectedDate = DateTime.now();
final DateTime selectedDate = DateTime(2026, 7, 9); // testing only - Thursday
final Rx<ScheduleStudentAttendanceReport?> statiscticAttendanceToday =
Rx<ScheduleStudentAttendanceReport?>(null);
/// data geolocation
String get placemark => _geolocationService.outputPlacemark.value;
// kota
String get placemarkCity =>
_geolocationService.placemarkResult.value?.subAdministrativeArea ??
'(No Data)';
// jalan
String get placemarkStreet =>
_geolocationService.placemarkResult.value?.street ?? '(No Data)';
// kecamatan
String get placemarkLocality =>
_geolocationService.placemarkResult.value?.locality ?? '';
// desa
String get placemarkVillage =>
_geolocationService.placemarkResult.value?.subLocality ?? '';
/// data geolocation END
DateTime dateNow = DateTime.now();
// DateTime dateNow = DateTime(2026, 7, 9, 9, 20, 00); // testing only - Thursday
String get dateNowFormatted => AppUtil.formatDateIndonesia(dateNow);
@override
void onInit() async {
// TODO: implement onInit
super.onInit();
// selectedDate.value = dateNow;
_setProfileData();
ever(mainController.triggerUpdateProfile, (_) {
_setProfileData();
});
await fetchTodayScheduleTeacher();
await fetchAttendanceHistory();
}
// === SERVICE ZONE ===
_setProfileData() {
final String? emailService = _httpService.userData != null
? (_httpService.userData!['email'] as String?)
: null;
name.value = _httpService.teacherData?.name ?? '';
firstName.value = _httpService.teacherData?.name != null
? _httpService.teacherData!.name.split(' ').first
: '';
email.value = emailService ?? '';
subject.value = _httpService.teacherData?.subject ?? '';
nip.value = _httpService.teacherData?.nip ?? '';
final String? profilePicture = _httpService.userModel?.profilePicture;
if (profilePicture != null) {
profileImageURL.value =
ApiConstant.getProfilePictureURL(profilePicture, 'teacher');
}
}
Future<void> fetchTodayScheduleTeacher() async {
isLoading.value = true;
final result = await _httpService.teacherSchedulePersonal();
if (result.success) {
final List<dynamic>? raw = result.data;
if (raw != null) {
final items = raw
.map((e) => ScheduleReportItem.fromMap(e as Map<String, dynamic>))
.toList();
// sort by schedule.startTime
items.sort((a, b) {
return a.schedule.startTime.compareTo(b.schedule.startTime);
});
dataSchedule.assignAll(items);
log('Loaded ${items.length} schedule items');
} else {
dataSchedule.clear();
}
} else {
dataSchedule.clear();
}
isLoading.value = false;
}
Future<void> fetchAttendanceHistory() async {
// final now = DateTime.now();
isLoadingAttendanceHistory.value = true;
// use dummy, change later to get from student data class id.
// get filtered schedule for the current time and get the class id from there.
final filteredSchedule = dataSchedule.where((item) {
final normalizedStartTime = DateTime(
dateNow.year,
dateNow.month,
dateNow.day,
item.schedule.startTime.hour,
item.schedule.startTime.minute,
);
final normalizedEndTime = DateTime(
dateNow.year,
dateNow.month,
dateNow.day,
item.schedule.endTime.hour,
item.schedule.endTime.minute,
);
return normalizedStartTime.isBefore(dateNow) &&
normalizedEndTime.isAfter(dateNow);
}).toList();
if (filteredSchedule.isNotEmpty) {
final schedule = filteredSchedule.first;
selectedClassId.value = schedule.classModel?.id ?? BigInt.from(-1);
log('Auto-selected class ID: ${selectedClassId.value} based on current schedule');
} else {
// No current class found, try to get upcoming class
log('No active schedule found for current time. Looking for upcoming schedule...');
final upcomingSchedule = dataSchedule.where((item) {
final normalizedStartTime = DateTime(
dateNow.year,
dateNow.month,
dateNow.day,
item.schedule.startTime.hour,
item.schedule.startTime.minute,
);
return normalizedStartTime.isAfter(dateNow);
}).toList();
if (upcomingSchedule.isNotEmpty) {
// Sort by startTime to get the nearest upcoming schedule
upcomingSchedule.sort((a, b) {
return a.schedule.startTime.compareTo(b.schedule.startTime);
});
final schedule = upcomingSchedule.first;
selectedClassId.value = schedule.classModel?.id ?? BigInt.from(-1);
log('Auto-selected nearest upcoming class ID: ${selectedClassId.value} at ${schedule.schedule.startTime}');
} else {
log('No upcoming schedule found.');
Fluttertoast.showToast(msg: 'Tidak ada kelas yang berjalan saat ini.');
isLoadingAttendanceHistory.value = false;
isLoadingStatistic.value = false;
return;
}
}
if (selectedClassId.value == BigInt.from(-1)) {
log('Invalid class ID: ${selectedClassId.value}. Cannot fetch attendance history.');
Fluttertoast.showToast(msg: 'Jadwal kosong.');
isLoadingAttendanceHistory.value = false;
isLoadingStatistic.value = false;
return;
}
final result = await _httpService.teacherClassesAttendance(
classId: selectedClassId.value.toString(),
date: selectedDate,
);
isLoadingAttendanceHistory.value = false;
if (result.success) {
final List<dynamic>? rawList = result.data;
if (rawList != null) {
final List<ScheduleStudentAttendanceReport> attendanceList = rawList
.map((item) => ScheduleStudentAttendanceReport.fromMap(item))
.toList();
attendanceHistoryResult.value = attendanceList;
log('Loaded attendance history for class $selectedClassId on date ${selectedDate.toIso8601String()}');
// jangan lupa tambahkan filter schedule hasilnya berdasarkan range waktu upcoming / ongoing schedule. dan tampilkan di dashboard statistik.
/// filter logic here...
filterClassAttendanceBySchedule();
} else {
attendanceHistoryResult.clear();
Fluttertoast.showToast(msg: 'Data: No Data Found on This Date');
}
Fluttertoast.showToast(msg: 'msg_success_fetch_attendance');
} else {
Fluttertoast.showToast(
msg: result.message ?? 'msg_failed_fetch_attendance');
}
isLoadingAttendanceHistory.value = false;
}
// schedule student attendance filetring logic based on date now and schedule time (endtime and start time)
void filterClassAttendanceBySchedule() {
isLoadingStatistic.value = true;
// final now = DateTime.now();
final filteredSchedule = attendanceHistoryResult.where((item) {
final normalizedStartTime = DateTime(
dateNow.year,
dateNow.month,
dateNow.day,
item.schedule.startTime.hour,
item.schedule.startTime.minute,
);
final normalizedEndTime = DateTime(
dateNow.year,
dateNow.month,
dateNow.day,
item.schedule.endTime.hour,
item.schedule.endTime.minute,
);
isLoadingStatistic.value = false;
return normalizedStartTime.isBefore(dateNow) &&
normalizedEndTime.isAfter(dateNow);
}).toList();
isLoadingStatistic.value = false;
if (filteredSchedule.isNotEmpty) {
statiscticAttendanceToday.value = filteredSchedule.first;
log('Filtered attendance history to ${filteredSchedule.length} items based on current schedule');
} else {
log('No active schedule found for current time. Attendance history not filtered.');
// get upcoming schedule and filter based on that
final upcomingSchedule = attendanceHistoryResult.where((item) {
final normalizedStartTime = DateTime(
dateNow.year,
dateNow.month,
dateNow.day,
item.schedule.startTime.hour,
item.schedule.startTime.minute,
);
return normalizedStartTime.isAfter(dateNow);
}).toList();
if (upcomingSchedule.isNotEmpty) {
// Sort by startTime to get the nearest upcoming schedule
upcomingSchedule.sort((a, b) {
return a.schedule.startTime.compareTo(b.schedule.startTime);
});
statiscticAttendanceToday.value = upcomingSchedule.first;
log('Filtered attendance history to ${upcomingSchedule.length} items based on nearest upcoming schedule at ${upcomingSchedule.first.schedule.startTime}');
} else {
statiscticAttendanceToday.value = null;
log('No upcoming schedule found. Attendance history not filtered.');
}
// log statistic attendance today
log('Statistic attendance for today: ${statiscticAttendanceToday.value}');
log('Statistic attendance for today - schedule: ${statiscticAttendanceToday.value?.subject?.name ?? 'No Schedule'}');
log('Statistic attendance for today - class: ${statiscticAttendanceToday.value?.classModel ?? 'No Class'}');
log('Statistic attendance for today - class: ${statiscticAttendanceToday.value?.classModel?.name ?? 'No Class'}');
}
}
Future<void> refreshData() async {
await fetchTodayScheduleTeacher();
await fetchAttendanceHistory();
filterClassAttendanceBySchedule();
}
}