placeholders removidos e todos os dados reais colocados, com conquistas e tudo

This commit is contained in:
2026-05-17 17:29:47 +01:00
parent 6ba5c837ce
commit 49a7a6fe02
17 changed files with 4688 additions and 142 deletions

View File

@@ -1,15 +1,95 @@
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../../../../core/theme/app_theme_extension.dart';
import 'package:flutter_animate/flutter_animate.dart';
import '../../../../core/services/auth_service.dart';
import '../../../../core/services/gamification_service.dart';
import '../../../../core/models/class_stats.dart';
/// Analytics preview section for teacher dashboard
class TeacherAnalyticsPreviewWidget extends StatelessWidget {
class TeacherAnalyticsPreviewWidget extends StatefulWidget {
const TeacherAnalyticsPreviewWidget({super.key});
@override
State<TeacherAnalyticsPreviewWidget> createState() => _TeacherAnalyticsPreviewWidgetState();
}
class _TeacherAnalyticsPreviewWidgetState extends State<TeacherAnalyticsPreviewWidget> {
List<ClassStats> _classStats = [];
List<StudentRanking> _topStudents = [];
bool _loading = true;
@override
void initState() {
super.initState();
_loadAnalyticsData();
}
Future<void> _loadAnalyticsData() async {
try {
final user = AuthService.currentUser;
if (user == null) return;
// Obter turmas do professor
final classesSnapshot = await FirebaseFirestore.instance
.collection('classes')
.where('teacherId', isEqualTo: user.uid)
.get();
final classStatsList = <ClassStats>[];
for (final classDoc in classesSnapshot.docs) {
final classId = classDoc.id;
final stats = await GamificationService.getClassStats(classId);
if (stats != null) {
classStatsList.add(stats);
}
}
// Obter melhores alunos de todas as turmas
final allTopStudents = <StudentRanking>[];
for (final classStats in classStatsList) {
final ranking = await GamificationService.getClassRanking(classStats.classId);
allTopStudents.addAll(ranking.take(3)); // Top 3 de cada turma
}
// Ordenar por score e pegar os melhores
allTopStudents.sort((a, b) => b.overallScore.compareTo(a.overallScore));
final topStudents = allTopStudents.take(4).toList();
if (mounted) {
setState(() {
_classStats = classStatsList;
_topStudents = topStudents;
_loading = false;
});
}
} catch (e) {
print('Error loading analytics data: $e');
if (mounted) {
setState(() {
_loading = false;
});
}
}
}
int get totalStudents => _classStats.fold(0, (sum, stats) => sum + stats.totalStudents);
int get activeStudents => _classStats.fold(0, (sum, stats) => sum + stats.activeStudents);
double get averageProgress {
if (_classStats.isEmpty) return 0.0;
final totalProgress = _classStats.fold(0.0, (sum, stats) => sum + stats.averageProgress);
return totalProgress / _classStats.length;
}
int get studentsNeedingSupport => _classStats.fold(0, (sum, stats) => sum + stats.studentsNeedingSupport.length);
@override
Widget build(BuildContext context) {
if (_loading) {
return const Center(child: CircularProgressIndicator());
}
final user = AuthService.currentUser;
final userName = user?.displayName ?? 'Professor';
final userEmail = user?.email ?? '';
@@ -109,24 +189,24 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
_buildQuickStat(
icon: Icons.check_circle,
label: 'Alunos Ativos',
value: '18/24',
value: '$activeStudents/$totalStudents',
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 12),
_buildQuickStat(
icon: Icons.warning_amber,
label: 'Precisam Apoio',
value: '3',
value: '$studentsNeedingSupport',
color: Theme.of(context).colorScheme.secondary,
),
const SizedBox(width: 12),
_buildQuickStat(
icon: Icons.emoji_events,
label: 'Média Turma',
value: '72%',
value: '${(averageProgress * 100).toInt()}%',
color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
).colorScheme.primary.withValues(alpha: 0.8),
),
],
),
@@ -154,33 +234,17 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
const SizedBox(height: 12),
// Student List Preview
_buildStudentPerformanceItem(
context,
'Ana Silva',
95,
Theme.of(context).colorScheme.tertiary,
),
const SizedBox(height: 8),
_buildStudentPerformanceItem(
context,
'João Costa',
88,
Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 8),
_buildStudentPerformanceItem(
context,
'Maria Santos',
82,
Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 8),
_buildStudentPerformanceItem(
context,
'Pedro Lima',
45,
Theme.of(context).colorScheme.secondary,
),
..._topStudents.map((student) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: _buildStudentPerformanceItem(
context,
student.studentName,
student.overallScore.toInt(),
_getScoreColor(student.overallScore),
),
);
}),
const SizedBox(height: 20),
@@ -218,7 +282,7 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
),
const SizedBox(height: 2),
Text(
'12 conteúdos verificados • 2 pendentes de revisão',
'${_classStats.fold(0, (sum, stats) => sum + stats.totalContent)} conteúdos • $studentsNeedingSupport alunos precisam de apoio',
style: TextStyle(
color: Theme.of(
context,
@@ -336,4 +400,11 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
],
);
}
Color _getScoreColor(double score) {
if (score >= 80) return Theme.of(context).colorScheme.tertiary;
if (score >= 60) return Theme.of(context).colorScheme.primary;
if (score >= 40) return Theme.of(context).colorScheme.secondary;
return Theme.of(context).colorScheme.error;
}
}