import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/theme/app_theme.dart'; import '../../models/attendance.dart'; import '../../providers/attendance_provider.dart'; class AttendanceScreen extends ConsumerWidget { const AttendanceScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { const filter = AttendanceFilter(); final attendanceAsync = ref.watch(attendanceProvider(filter)); return Scaffold( appBar: AppBar( title: const Text('Attendance'), ), body: RefreshIndicator( onRefresh: () async { ref.invalidate(attendanceProvider(filter)); await ref.read(attendanceProvider(filter).future); }, child: attendanceAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (err, _) => Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline_rounded, size: 48, color: AppTheme.errorColor), const SizedBox(height: 12), const Text('Failed to load attendance', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Text(err.toString(), textAlign: TextAlign.center, style: const TextStyle(color: AppTheme.textSecondary, fontSize: 13)), const SizedBox(height: 16), ElevatedButton( onPressed: () => ref.refresh(attendanceProvider(filter)), child: const Text('Retry'), ), ], ), ), ), data: (data) => _buildAttendanceBody(context, data), ), ), ); } Widget _buildAttendanceBody(BuildContext context, AttendanceData data) { final summary = data.summary; final records = data.records; return ListView( padding: const EdgeInsets.all(16), children: [ // Attendance Overview Card Card( child: Padding( padding: const EdgeInsets.all(20), child: Column( children: [ const Text( 'Cumulative Attendance', style: TextStyle( fontSize: 14, color: AppTheme.textSecondary, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 8), Text( '${summary.percentage.toStringAsFixed(1)}%', style: TextStyle( fontSize: 44, fontWeight: FontWeight.bold, color: summary.percentage >= 75 ? AppTheme.successColor : (summary.percentage >= 60 ? AppTheme.warningColor : AppTheme.errorColor), ), ), const SizedBox(height: 6), Text( summary.percentage >= 75 ? 'Eligibility: Met (Eligible for exams)' : 'Eligibility: Shortage (< 75% required)', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: summary.percentage >= 75 ? AppTheme.successColor : AppTheme.errorColor, ), ), const Divider(height: 32), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildStatCol('Present', summary.present, AppTheme.successColor), _buildStatCol('Absent', summary.absent, AppTheme.errorColor), _buildStatCol('Late', summary.late, AppTheme.warningColor), _buildStatCol('Leave', summary.medicalLeave, Colors.purple), _buildStatCol('Duty', summary.onDuty, Colors.blue), ], ), ], ), ), ), const SizedBox(height: 20), // Session History Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Attendance History', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), Text( '${records.length} sessions recorded', style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), ], ), const SizedBox(height: 10), if (records.isEmpty) Container( padding: const EdgeInsets.symmetric(vertical: 40), alignment: Alignment.center, child: const Column( children: [ Icon(Icons.event_busy, size: 40, color: AppTheme.textMuted), SizedBox(height: 8), Text( 'No attendance sessions recorded yet', style: TextStyle(color: AppTheme.textSecondary), ), ], ), ) else ...records.map((r) => _buildRecordCard(r)), ], ); } Widget _buildStatCol(String label, int count, Color color) { return Column( children: [ Text( '$count', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: color, ), ), const SizedBox(height: 2), Text( label, style: const TextStyle( fontSize: 11, color: AppTheme.textSecondary, ), ), ], ); } Widget _buildRecordCard(AttendanceRecord record) { Color badgeColor; Color textColor; String label; switch (record.status.toLowerCase()) { case 'present': badgeColor = AppTheme.successColor.withOpacity(0.12); textColor = AppTheme.successColor; label = 'PRESENT'; break; case 'absent': badgeColor = AppTheme.errorColor.withOpacity(0.12); textColor = AppTheme.errorColor; label = 'ABSENT'; break; case 'late': badgeColor = AppTheme.warningColor.withOpacity(0.12); textColor = AppTheme.warningColor; label = 'LATE'; break; case 'medical_leave': badgeColor = Colors.purple.withOpacity(0.12); textColor = Colors.purple; label = 'MEDICAL LEAVE'; break; case 'on_duty': badgeColor = Colors.blue.withOpacity(0.12); textColor = Colors.blue; label = 'ON DUTY'; break; default: badgeColor = Colors.grey.withOpacity(0.12); textColor = Colors.grey; label = record.status.toUpperCase(); } return Card( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.calendar_today_rounded, size: 16, color: AppTheme.textSecondary), const SizedBox(width: 8), Text( record.date, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ], ), if (record.remarks.isNotEmpty) ...[ const SizedBox(height: 4), Text( record.remarks, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, fontStyle: FontStyle.italic, ), ), ], ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: badgeColor, borderRadius: BorderRadius.circular(6), ), child: Text( label, style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold, color: textColor, ), ), ), ], ), ), ); } }