TIF_E41221079/lib/features/attendance/attendance_screen.dart

381 lines
11 KiB
Dart

// lib/features/attendance/attendance_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smpn1_lengkong/core/models/attendance_log_model.dart';
import 'package:smpn1_lengkong/core/models/student_model.dart';
import 'package:smpn1_lengkong/core/constants/app_colors.dart';
import 'package:smpn1_lengkong/core/service/student_service.dart';
import 'package:smpn1_lengkong/features/attendance/widgets/attendance_history_card.dart';
class AttendanceScreen extends StatefulWidget {
const AttendanceScreen({super.key});
@override
State<AttendanceScreen> createState() => _AttendanceScreenState();
}
class _AttendanceScreenState extends State<AttendanceScreen> {
// Simpan student di state agar data bisa diupdate saat refresh
StudentModel? _studentData;
String?
_currentClassFilter; // Variabel pengunci kelas agar tidak reset ke default
String _filter = 'Semua';
int? _selectedMonthIndex;
bool _isInitialized = false;
final List<String> _months = [
'Januari',
'Februari',
'Maret',
'April',
'Mei',
'Juni',
'Juli',
'Agustus',
'September',
'Oktober',
'November',
'Desember',
];
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Inisialisasi data awal dari arguments
if (!_isInitialized) {
final args = ModalRoute.of(context)!.settings.arguments as StudentModel;
_studentData = args;
_currentClassFilter =
args.className; // Mengunci kelas berdasarkan argumen
_isInitialized = true;
}
}
/// Fungsi untuk menarik data terbaru dari API/Firestore
Future<void> _handleRefresh() async {
if (_studentData == null) return;
try {
// Mengambil data student terbaru menggunakan StudentService
final updatedStudent = await StudentService().getCurrentStudentData();
setState(() {
// PERBAIKAN: Gunakan copyWith agar className tetap pada kelas yang dipilih user
_studentData = updatedStudent.copyWith(className: _currentClassFilter);
});
if (mounted) {}
} catch (e) {
if (mounted) {}
}
}
List<AttendanceLog> _getFilteredLogs(
List<AttendanceLog> allLogs,
String viewingClass,
) {
// 1. Filter berdasarkan kelas yang sedang dikunci
final classFiltered =
allLogs.where((log) => log.className == viewingClass).toList();
// 2. Sort data terbaru paling atas
classFiltered.sort((a, b) => b.date.compareTo(a.date));
// 3. Filter berdasarkan periode/bulan
return classFiltered.where((log) {
if (_filter.startsWith('Bulan:')) {
return log.date.month == _selectedMonthIndex;
}
return true;
}).toList();
}
Map<String, int> _calculateSummary(List<AttendanceLog> filteredLogs) {
int present = 0, sick = 0, permit = 0, alpha = 0;
for (var log in filteredLogs) {
if (log.status == 'Hadir') present++;
if (log.status == 'Sakit') sick++;
if (log.status == 'Izin') permit++;
if (log.status == 'Alpha') alpha++;
}
return {'Hadir': present, 'Sakit': sick, 'Izin': permit, 'Alpha': alpha};
}
void _showMonthPicker(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Pilih Bulan"),
content: SizedBox(
width: double.maxFinite,
child: GridView.builder(
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: _months.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
setState(() {
_selectedMonthIndex = index + 1;
_filter = "Bulan: ${_months[index]}";
});
Navigator.pop(context);
},
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(
color: AppColors.primaryBlue.withOpacity(0.3),
),
borderRadius: BorderRadius.circular(8.r),
),
child: Text(
_months[index],
style: TextStyle(fontSize: 12.sp),
),
),
);
},
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
// Pastikan data tersedia
if (_studentData == null) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));
}
// Menggunakan variabel pengunci sebagai acuan kelas yang dilihat
final viewingClass = _currentClassFilter!;
final filteredLogs = _getFilteredLogs(
_studentData!.attendanceLogs,
viewingClass,
);
final summaryCounts = _calculateSummary(filteredLogs);
return Scaffold(
backgroundColor: AppColors.backgroundWhite,
appBar: AppBar(
title: Text(
"Kehadiran Kelas $viewingClass",
style: const TextStyle(color: Colors.white),
),
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
elevation: 0,
),
body: RefreshIndicator(
onRefresh: _handleRefresh,
color: AppColors.primaryBlue,
child: Column(
children: [
_buildSummaryHeader(summaryCounts),
// Filter Buttons
Padding(
padding: EdgeInsets.symmetric(vertical: 16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildFilterButton("Semua"),
SizedBox(width: 12.w),
_buildMonthFilterTrigger(),
],
),
),
Expanded(
child:
filteredLogs.isEmpty
? _buildEmptyStateScrollable()
: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
padding: EdgeInsets.symmetric(horizontal: 16.w),
itemCount: filteredLogs.length,
itemBuilder: (context, index) {
return AttendanceHistoryCard(
log: filteredLogs[index],
);
},
),
),
],
),
),
);
}
Widget _buildMonthFilterTrigger() {
final bool isMonthSelected = _filter.contains("Bulan:");
return InkWell(
onTap: () => _showMonthPicker(context),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
decoration: BoxDecoration(
color: isMonthSelected ? AppColors.primaryBlue : Colors.white,
borderRadius: BorderRadius.circular(20.r),
border: Border.all(color: Colors.grey.shade300),
),
child: Row(
children: [
Icon(
Icons.filter_list,
size: 16.sp,
color: isMonthSelected ? Colors.white : Colors.grey.shade600,
),
SizedBox(width: 6.w),
Text(
"Pilih Bulan",
style: TextStyle(
color: isMonthSelected ? Colors.white : Colors.grey.shade600,
fontSize: 12.sp,
fontWeight: FontWeight.w600,
),
),
],
),
),
);
}
Widget _buildSummaryHeader(Map<String, int> summaryCounts) {
return Container(
width: double.infinity,
padding: EdgeInsets.fromLTRB(16.w, 0, 16.w, 24.w),
decoration: const BoxDecoration(
color: AppColors.primaryBlue,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(24)),
),
child: Column(
children: [
Text(
"Periode: $_filter",
style: TextStyle(color: Colors.white70, fontSize: 12.sp),
),
SizedBox(height: 16.h),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
_buildSummaryItem(
"Hadir",
summaryCounts['Hadir']!,
AppColors.successGreen,
),
SizedBox(width: 12.w),
_buildSummaryItem(
"Sakit",
summaryCounts['Sakit']!,
Colors.orange,
),
SizedBox(width: 12.w),
_buildSummaryItem(
"Izin",
summaryCounts['Izin']!,
Colors.blueAccent,
),
SizedBox(width: 12.w),
_buildSummaryItem(
"Alpha",
summaryCounts['Alpha']!,
AppColors.dangerRed,
),
],
),
),
],
),
);
}
Widget _buildSummaryItem(String label, int count, Color color) {
return Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 12.w),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(16.r),
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: Text(
count.toString(),
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
SizedBox(height: 8.h),
Text(
label,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
],
);
}
Widget _buildFilterButton(String label) {
final isSelected = _filter == label;
return InkWell(
onTap: () => setState(() => _filter = label),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 8.h),
decoration: BoxDecoration(
color: isSelected ? AppColors.primaryBlue : Colors.white,
borderRadius: BorderRadius.circular(20.r),
border: Border.all(
color: isSelected ? AppColors.primaryBlue : Colors.grey.shade300,
),
),
child: Text(
label,
style: TextStyle(
color: isSelected ? Colors.white : Colors.grey.shade600,
fontSize: 12.sp,
fontWeight: FontWeight.w600,
),
),
),
);
}
Widget _buildEmptyStateScrollable() {
return ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: [
SizedBox(height: 100.h),
Icon(
Icons.calendar_month_outlined,
size: 60.sp,
color: Colors.grey.shade300,
),
SizedBox(height: 8.h),
Text(
"Tidak ada data absensi\npada periode ini",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey.shade500),
),
],
);
}
}