Mudanças na aba de quiz
This commit is contained in:
@@ -67,6 +67,9 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
|
||||
bool _loadingHistory = true;
|
||||
String? _generatingForId;
|
||||
|
||||
// Disciplina seleccionada no histórico (null = vista de disciplinas)
|
||||
String? _selectedHistoryDisciplineId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -256,7 +259,13 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () => context.go('/teacher-dashboard'),
|
||||
onPressed: () {
|
||||
if (_selectedHistoryDisciplineId != null) {
|
||||
setState(() => _selectedHistoryDisciplineId = null);
|
||||
} else {
|
||||
context.go('/teacher-dashboard');
|
||||
}
|
||||
},
|
||||
),
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
@@ -330,6 +339,82 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, String> get _classNamesMap {
|
||||
return {for (final c in _teacherClasses) c['id']!: c['name'] ?? c['id']!};
|
||||
}
|
||||
|
||||
Map<String, List<Map<String, dynamic>>> _groupHistoryByDiscipline() {
|
||||
final classNames = _classNamesMap;
|
||||
final Map<String, List<Map<String, dynamic>>> groups = {};
|
||||
for (final quiz in _history) {
|
||||
final quizClassIds = (quiz['classIds'] as List?)?.cast<String>() ?? [];
|
||||
String? groupId = quizClassIds.cast<String?>().firstWhere(
|
||||
(cid) => cid != null && classNames.containsKey(cid),
|
||||
orElse: () => null,
|
||||
);
|
||||
groupId ??= quizClassIds.isNotEmpty ? quizClassIds.first : '__geral__';
|
||||
groups.putIfAbsent(groupId, () => []).add(quiz);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
Widget _buildHistoryQuizTile(Map<String, dynamic> item, ColorScheme cs) {
|
||||
final name = (item['materialName'] as String? ?? 'Material')
|
||||
.replaceAll('.pdf', '')
|
||||
.replaceAll('_', ' ');
|
||||
final ts = item['createdAt'];
|
||||
String dateStr = '';
|
||||
if (ts is Timestamp) {
|
||||
final dt = ts.toDate();
|
||||
dateStr =
|
||||
'${dt.day.toString().padLeft(2, '0')}/${dt.month.toString().padLeft(2, '0')}/${dt.year} ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: cs.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: cs.outline.withValues(alpha: 0.15)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: cs.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
leading: Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: cs.primary.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(Icons.quiz, color: cs.primary, size: 22),
|
||||
),
|
||||
title: Text(
|
||||
name,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
color: cs.onSurface,
|
||||
),
|
||||
),
|
||||
subtitle: dateStr.isNotEmpty
|
||||
? Text(
|
||||
dateStr,
|
||||
style: TextStyle(fontSize: 12, color: cs.onSurfaceVariant),
|
||||
)
|
||||
: null,
|
||||
trailing: Icon(Icons.bar_chart, color: cs.onSurfaceVariant),
|
||||
onTap: () => _showResultsPopup(item),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHistoryTab(ColorScheme cs) {
|
||||
if (_loadingHistory)
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
@@ -355,67 +440,131 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final groups = _groupHistoryByDiscipline();
|
||||
final classNames = _classNamesMap;
|
||||
|
||||
// Vista de quizzes de uma disciplina
|
||||
if (_selectedHistoryDisciplineId != null) {
|
||||
final quizzes = groups[_selectedHistoryDisciplineId] ?? [];
|
||||
final disciplineName =
|
||||
classNames[_selectedHistoryDisciplineId] ??
|
||||
(_selectedHistoryDisciplineId == '__geral__'
|
||||
? 'Geral'
|
||||
: _selectedHistoryDisciplineId!);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 8, 16, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: cs.onSurface),
|
||||
onPressed: () =>
|
||||
setState(() => _selectedHistoryDisciplineId = null),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
disciplineName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: cs.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${quizzes.length} quiz${quizzes.length != 1 ? 'zes' : ''}',
|
||||
style: TextStyle(fontSize: 13, color: cs.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: quizzes.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 12),
|
||||
itemBuilder: (context, i) =>
|
||||
_buildHistoryQuizTile(quizzes[i], cs),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Vista de disciplinas
|
||||
final disciplineIds = groups.keys.toList();
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _history.length,
|
||||
itemCount: disciplineIds.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 12),
|
||||
itemBuilder: (context, i) {
|
||||
final item = _history[i];
|
||||
final name = (item['materialName'] as String? ?? 'Material')
|
||||
.replaceAll('.pdf', '')
|
||||
.replaceAll('_', ' ');
|
||||
final ts = item['createdAt'];
|
||||
String dateStr = '';
|
||||
if (ts is Timestamp) {
|
||||
final dt = ts.toDate();
|
||||
dateStr =
|
||||
'${dt.day.toString().padLeft(2, '0')}/${dt.month.toString().padLeft(2, '0')}/${dt.year} ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: cs.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: cs.outline.withValues(alpha: 0.15)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: cs.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
final dId = disciplineIds[i];
|
||||
final dName = classNames[dId] ?? (dId == '__geral__' ? 'Geral' : dId);
|
||||
final count = groups[dId]!.length;
|
||||
return InkWell(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
onTap: () => setState(() => _selectedHistoryDisciplineId = dId),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: cs.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: cs.outline.withValues(alpha: 0.15)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: cs.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
leading: Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: cs.primary.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(Icons.quiz, color: cs.primary, size: 22),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: cs.primary.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(Icons.school, color: cs.primary, size: 26),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
dName,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: cs.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'$count quiz${count != 1 ? 'zes' : ''} criado${count != 1 ? 's' : ''}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: cs.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(Icons.chevron_right, color: cs.onSurfaceVariant),
|
||||
],
|
||||
),
|
||||
title: Text(
|
||||
name,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
color: cs.onSurface,
|
||||
),
|
||||
),
|
||||
subtitle: dateStr.isNotEmpty
|
||||
? Text(
|
||||
dateStr,
|
||||
style: TextStyle(fontSize: 12, color: cs.onSurfaceVariant),
|
||||
)
|
||||
: null,
|
||||
trailing: Icon(Icons.bar_chart, color: cs.onSurfaceVariant),
|
||||
onTap: () => _showResultsPopup(item),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user