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

@@ -0,0 +1,334 @@
import 'package:cloud_firestore/cloud_firestore.dart';
/// Model para definição de conquistas
class Achievement {
final String id;
final String name;
final String description;
final String icon;
final String category; // 'streak', 'study_time', 'quiz', 'concept'
final AchievementRequirement requirements;
final int points;
final String rarity; // 'common', 'rare', 'epic', 'legendary'
final bool isActive;
final DateTime createdAt;
final String? createdBy; // teacherId se criada por professor
const Achievement({
required this.id,
required this.name,
required this.description,
required this.icon,
required this.category,
required this.requirements,
required this.points,
required this.rarity,
required this.isActive,
required this.createdAt,
this.createdBy,
});
factory Achievement.fromFirestore(Map<String, dynamic> data, String id) {
return Achievement(
id: id,
name: data['name'] ?? '',
description: data['description'] ?? '',
icon: data['icon'] ?? 'star',
category: data['category'] ?? 'general',
requirements: AchievementRequirement.fromFirestore(data['requirements'] ?? {}),
points: data['points'] ?? 0,
rarity: data['rarity'] ?? 'common',
isActive: data['isActive'] ?? true,
createdAt: (data['createdAt'] as Timestamp?)?.toDate() ?? DateTime.now(),
createdBy: data['createdBy'],
);
}
Map<String, dynamic> toFirestore() {
return {
'name': name,
'description': description,
'icon': icon,
'category': category,
'requirements': requirements.toFirestore(),
'points': points,
'rarity': rarity,
'isActive': isActive,
'createdAt': Timestamp.fromDate(createdAt),
if (createdBy != null) 'createdBy': createdBy,
};
}
Achievement copyWith({
String? id,
String? name,
String? description,
String? icon,
String? category,
AchievementRequirement? requirements,
int? points,
String? rarity,
bool? isActive,
String? createdBy,
}) {
return Achievement(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
icon: icon ?? this.icon,
category: category ?? this.category,
requirements: requirements ?? this.requirements,
points: points ?? this.points,
rarity: rarity ?? this.rarity,
isActive: isActive ?? this.isActive,
createdAt: createdAt,
createdBy: createdBy ?? this.createdBy,
);
}
}
/// Requisitos para desbloquear uma conquista
class AchievementRequirement {
final String type; // 'streak_days', 'study_time', 'quiz_score', 'concepts_mastered', 'quiz_completion'
final num value;
final String operator; // '>=', '==', '>'
final Map<String, dynamic>? additionalParams;
const AchievementRequirement({
required this.type,
required this.value,
required this.operator,
this.additionalParams,
});
factory AchievementRequirement.fromFirestore(Map<String, dynamic> data) {
return AchievementRequirement(
type: data['type'] ?? '',
value: data['value'] ?? 0,
operator: data['operator'] ?? '>=',
additionalParams: data['additionalParams'],
);
}
Map<String, dynamic> toFirestore() {
return {
'type': type,
'value': value,
'operator': operator,
if (additionalParams != null) 'additionalParams': additionalParams,
};
}
bool checkCondition(num currentValue) {
switch (operator) {
case '>=':
return currentValue >= value;
case '==':
return currentValue == value;
case '>':
return currentValue > value;
default:
return false;
}
}
}
/// Conquistas predefinidas do sistema
class SystemAchievements {
static List<Achievement> get defaultAchievements => [
Achievement(
id: 'first_quiz',
name: 'Primeiro Passo',
description: 'Complete seu primeiro quiz',
icon: 'emoji_events',
category: 'quiz',
requirements: AchievementRequirement(
type: 'quiz_completion',
value: 1,
operator: '>=',
),
points: 10,
rarity: 'common',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'week_streak',
name: 'Semana de Dedicação',
description: 'Mantenha uma streak de 7 dias',
icon: 'local_fire_department',
category: 'streak',
requirements: AchievementRequirement(
type: 'streak_days',
value: 7,
operator: '>=',
),
points: 50,
rarity: 'rare',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'study_marathon',
name: 'Maratona de Estudos',
description: 'Estude por 100 minutos em um dia',
icon: 'schedule',
category: 'study_time',
requirements: AchievementRequirement(
type: 'study_time',
value: 100,
operator: '>=',
additionalParams: {'period': 'daily'},
),
points: 30,
rarity: 'rare',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'first_quiz',
name: 'Primeiro Passo',
description: 'Complete seu primeiro quiz',
icon: 'emoji_events',
category: 'quiz',
requirements: AchievementRequirement(
type: 'quiz_completion',
value: 1,
operator: '>=',
),
points: 10,
rarity: 'common',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'perfect_score',
name: 'Perfeição',
description: 'Obtenha 100% em um quiz',
icon: 'star',
category: 'quiz',
requirements: AchievementRequirement(
type: 'quiz_score',
value: 100,
operator: '==',
),
points: 25,
rarity: 'rare',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'concept_master',
name: 'Mestre de Conceitos',
description: 'Domine 5 conceitos',
icon: 'school',
category: 'concept',
requirements: AchievementRequirement(
type: 'concepts_mastered',
value: 5,
operator: '>=',
),
points: 40,
rarity: 'epic',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'month_streak',
name: 'Lendário',
description: 'Mantenha uma streak de 30 dias',
icon: 'whatshot',
category: 'streak',
requirements: AchievementRequirement(
type: 'streak_days',
value: 30,
operator: '>=',
),
points: 200,
rarity: 'legendary',
isActive: true,
createdAt: DateTime.now(),
),
// Conquistas genéricas de número de quizzes
Achievement(
id: 'quiz_novice_5',
name: 'Iniciante',
description: 'Complete 5 quizzes',
icon: 'emoji_events',
category: 'quiz_count',
requirements: AchievementRequirement(
type: 'quiz_completion',
value: 5,
operator: '>=',
),
points: 15,
rarity: 'common',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'quiz_intermediate_10',
name: 'Estudante Dedicao',
description: 'Complete 10 quizzes',
icon: 'school',
category: 'quiz_count',
requirements: AchievementRequirement(
type: 'quiz_completion',
value: 10,
operator: '>=',
),
points: 30,
rarity: 'common',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'quiz_advanced_25',
name: 'Mestre dos Quizzes',
description: 'Complete 25 quizzes',
icon: 'military_tech',
category: 'quiz_count',
requirements: AchievementRequirement(
type: 'quiz_completion',
value: 25,
operator: '>=',
),
points: 75,
rarity: 'rare',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'quiz_expert_50',
name: 'Especialista',
description: 'Complete 50 quizzes',
icon: 'workspace_premium',
category: 'quiz_count',
requirements: AchievementRequirement(
type: 'quiz_completion',
value: 50,
operator: '>=',
),
points: 150,
rarity: 'epic',
isActive: true,
createdAt: DateTime.now(),
),
Achievement(
id: 'quiz_legend_100',
name: 'Lenda dos Quizzes',
description: 'Complete 100 quizzes',
icon: 'stars',
category: 'quiz_count',
requirements: AchievementRequirement(
type: 'quiz_completion',
value: 100,
operator: '>=',
),
points: 300,
rarity: 'legendary',
isActive: true,
createdAt: DateTime.now(),
),
];
}

View File

@@ -0,0 +1,195 @@
import 'package:cloud_firestore/cloud_firestore.dart';
/// Model para estatísticas da turma (professor)
class ClassStats {
final String classId;
final String teacherId;
final String className;
final int totalStudents;
final int activeStudents;
final double averageProgress;
final int totalQuizzes;
final int activeQuizzes;
final int totalContent;
final List<WeeklyStats> weeklyStats;
final List<StudentNeedingSupport> studentsNeedingSupport;
const ClassStats({
required this.classId,
required this.teacherId,
required this.className,
required this.totalStudents,
required this.activeStudents,
required this.averageProgress,
required this.totalQuizzes,
required this.activeQuizzes,
required this.totalContent,
required this.weeklyStats,
required this.studentsNeedingSupport,
});
factory ClassStats.fromFirestore(Map<String, dynamic> data, String classId) {
return ClassStats(
classId: classId,
teacherId: data['teacherId'] ?? '',
className: data['className'] ?? '',
totalStudents: data['totalStudents'] ?? 0,
activeStudents: data['activeStudents'] ?? 0,
averageProgress: (data['averageProgress'] ?? 0).toDouble(),
totalQuizzes: data['totalQuizzes'] ?? 0,
activeQuizzes: data['activeQuizzes'] ?? 0,
totalContent: data['totalContent'] ?? 0,
weeklyStats: (data['weeklyStats'] as List<dynamic>?)
?.map((w) => WeeklyStats.fromFirestore(w))
.toList() ??
[],
studentsNeedingSupport: (data['studentsNeedingSupport'] as List<dynamic>?)
?.map((s) => StudentNeedingSupport.fromFirestore(s))
.toList() ??
[],
);
}
Map<String, dynamic> toFirestore() {
return {
'teacherId': teacherId,
'className': className,
'totalStudents': totalStudents,
'activeStudents': activeStudents,
'averageProgress': averageProgress,
'totalQuizzes': totalQuizzes,
'activeQuizzes': activeQuizzes,
'totalContent': totalContent,
'weeklyStats': weeklyStats.map((w) => w.toFirestore()).toList(),
'studentsNeedingSupport': studentsNeedingSupport.map((s) => s.toFirestore()).toList(),
};
}
}
/// Estatísticas semanais da turma
class WeeklyStats {
final DateTime weekStart;
final int activeStudents;
final double averageScore;
final int totalStudyTime;
const WeeklyStats({
required this.weekStart,
required this.activeStudents,
required this.averageScore,
required this.totalStudyTime,
});
factory WeeklyStats.fromFirestore(Map<String, dynamic> data) {
return WeeklyStats(
weekStart: (data['weekStart'] as Timestamp?)?.toDate() ?? DateTime.now(),
activeStudents: data['activeStudents'] ?? 0,
averageScore: (data['averageScore'] ?? 0).toDouble(),
totalStudyTime: data['totalStudyTime'] ?? 0,
);
}
Map<String, dynamic> toFirestore() {
return {
'weekStart': Timestamp.fromDate(weekStart),
'activeStudents': activeStudents,
'averageScore': averageScore,
'totalStudyTime': totalStudyTime,
};
}
}
/// Aluno que precisa de apoio
class StudentNeedingSupport {
final String studentId;
final String studentName;
final String reason; // 'low_scores', 'inactivity', 'struggling_concept'
final DateTime lastActivity;
final double averageScore;
const StudentNeedingSupport({
required this.studentId,
required this.studentName,
required this.reason,
required this.lastActivity,
required this.averageScore,
});
factory StudentNeedingSupport.fromFirestore(Map<String, dynamic> data) {
return StudentNeedingSupport(
studentId: data['studentId'] ?? '',
studentName: data['studentName'] ?? '',
reason: data['reason'] ?? '',
lastActivity: (data['lastActivity'] as Timestamp?)?.toDate() ?? DateTime.now(),
averageScore: (data['averageScore'] ?? 0).toDouble(),
);
}
Map<String, dynamic> toFirestore() {
return {
'studentId': studentId,
'studentName': studentName,
'reason': reason,
'lastActivity': Timestamp.fromDate(lastActivity),
'averageScore': averageScore,
};
}
}
/// Ranking de alunos em uma turma
class StudentRanking {
final String studentId;
final String studentName;
final String studentEmail;
final double overallScore;
final int completedQuizzes;
final int totalQuizzes;
final double quizCompletionRate;
final int studyTimeMinutes;
final int currentStreak;
final DateTime lastActivity;
const StudentRanking({
required this.studentId,
required this.studentName,
required this.studentEmail,
required this.overallScore,
required this.completedQuizzes,
required this.totalQuizzes,
required this.quizCompletionRate,
required this.studyTimeMinutes,
required this.currentStreak,
required this.lastActivity,
});
double get quizCompletionPercentage => quizCompletionRate * 100;
factory StudentRanking.fromFirestore(Map<String, dynamic> data, String studentId) {
return StudentRanking(
studentId: studentId,
studentName: data['studentName'] ?? '',
studentEmail: data['studentEmail'] ?? '',
overallScore: (data['overallScore'] ?? 0).toDouble(),
completedQuizzes: data['completedQuizzes'] ?? 0,
totalQuizzes: data['totalQuizzes'] ?? 0,
quizCompletionRate: (data['quizCompletionRate'] ?? 0).toDouble(),
studyTimeMinutes: data['studyTimeMinutes'] ?? 0,
currentStreak: data['currentStreak'] ?? 0,
lastActivity: (data['lastActivity'] as Timestamp?)?.toDate() ?? DateTime.now(),
);
}
Map<String, dynamic> toFirestore() {
return {
'studentName': studentName,
'studentEmail': studentEmail,
'overallScore': overallScore,
'completedQuizzes': completedQuizzes,
'totalQuizzes': totalQuizzes,
'quizCompletionRate': quizCompletionRate,
'studyTimeMinutes': studyTimeMinutes,
'currentStreak': currentStreak,
'lastActivity': Timestamp.fromDate(lastActivity),
};
}
}

View File

@@ -0,0 +1,151 @@
import 'package:cloud_firestore/cloud_firestore.dart';
/// Model para estatísticas do usuário (aluno)
class UserStats {
final String userId;
final int currentStreak;
final int longestStreak;
final int totalStudyTime; // em minutos
final DateTime? lastActivityDate;
final int weeklyStudyTime; // minutos esta semana
final int monthlyStudyTime; // minutos este mês
final int completedQuizzes; // total de quizzes completos
final List<MasteredConcept> masteredConcepts;
final List<UnlockedAchievement> unlockedAchievements;
const UserStats({
required this.userId,
required this.currentStreak,
required this.longestStreak,
required this.totalStudyTime,
required this.weeklyStudyTime,
required this.monthlyStudyTime,
required this.completedQuizzes,
required this.masteredConcepts,
required this.unlockedAchievements,
this.lastActivityDate,
});
factory UserStats.fromFirestore(Map<String, dynamic> data, String userId) {
return UserStats(
userId: userId,
currentStreak: data['currentStreak'] ?? 0,
longestStreak: data['longestStreak'] ?? 0,
totalStudyTime: data['totalStudyTime'] ?? 0,
lastActivityDate: (data['lastActivityDate'] as Timestamp?)?.toDate(),
weeklyStudyTime: data['weeklyStudyTime'] ?? 0,
monthlyStudyTime: data['monthlyStudyTime'] ?? 0,
completedQuizzes: data['completedQuizzes'] ?? 0,
masteredConcepts: (data['masteredConcepts'] as List<dynamic>?)
?.map((c) => MasteredConcept.fromFirestore(c))
.toList() ??
[],
unlockedAchievements: (data['unlockedAchievements'] as List<dynamic>?)
?.map((a) => UnlockedAchievement.fromFirestore(a))
.toList() ??
[],
);
}
Map<String, dynamic> toFirestore() {
final data = {
'currentStreak': currentStreak,
'longestStreak': longestStreak,
'totalStudyTime': totalStudyTime,
'weeklyStudyTime': weeklyStudyTime,
'monthlyStudyTime': monthlyStudyTime,
'completedQuizzes': completedQuizzes,
'masteredConcepts': masteredConcepts.map((c) => c.toFirestore()).toList(),
'unlockedAchievements': unlockedAchievements.map((a) => a.toFirestore()).toList(),
};
if (lastActivityDate != null) {
data['lastActivityDate'] = Timestamp.fromDate(lastActivityDate!);
}
return data;
}
UserStats copyWith({
int? currentStreak,
int? longestStreak,
int? totalStudyTime,
DateTime? lastActivityDate,
int? weeklyStudyTime,
int? monthlyStudyTime,
int? completedQuizzes,
List<MasteredConcept>? masteredConcepts,
List<UnlockedAchievement>? unlockedAchievements,
}) {
return UserStats(
userId: userId,
currentStreak: currentStreak ?? this.currentStreak,
longestStreak: longestStreak ?? this.longestStreak,
totalStudyTime: totalStudyTime ?? this.totalStudyTime,
lastActivityDate: lastActivityDate ?? this.lastActivityDate,
weeklyStudyTime: weeklyStudyTime ?? this.weeklyStudyTime,
monthlyStudyTime: monthlyStudyTime ?? this.monthlyStudyTime,
completedQuizzes: completedQuizzes ?? this.completedQuizzes,
masteredConcepts: masteredConcepts ?? this.masteredConcepts,
unlockedAchievements: unlockedAchievements ?? this.unlockedAchievements,
);
}
}
/// Conceito dominado pelo aluno
class MasteredConcept {
final String conceptName;
final DateTime masteredAt;
final int masteryLevel; // 0-100
const MasteredConcept({
required this.conceptName,
required this.masteredAt,
required this.masteryLevel,
});
factory MasteredConcept.fromFirestore(Map<String, dynamic> data) {
return MasteredConcept(
conceptName: data['conceptName'] ?? '',
masteredAt: (data['masteredAt'] as Timestamp?)?.toDate() ?? DateTime.now(),
masteryLevel: data['masteryLevel'] ?? 0,
);
}
Map<String, dynamic> toFirestore() {
return {
'conceptName': conceptName,
'masteredAt': Timestamp.fromDate(masteredAt),
'masteryLevel': masteryLevel,
};
}
}
/// Conquista desbloqueada pelo aluno
class UnlockedAchievement {
final String achievementId;
final DateTime unlockedAt;
final Map<String, dynamic> metadata;
const UnlockedAchievement({
required this.achievementId,
required this.unlockedAt,
required this.metadata,
});
factory UnlockedAchievement.fromFirestore(Map<String, dynamic> data) {
return UnlockedAchievement(
achievementId: data['achievementId'] ?? '',
unlockedAt: (data['unlockedAt'] as Timestamp?)?.toDate() ?? DateTime.now(),
metadata: data['metadata'] ?? {},
);
}
Map<String, dynamic> toFirestore() {
return {
'achievementId': achievementId,
'unlockedAt': Timestamp.fromDate(unlockedAt),
'metadata': metadata,
};
}
}