import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import '../../../../core/services/auth_service.dart'; /// Página para o aluno entrar numa turma usando o código class JoinClassPage extends StatefulWidget { const JoinClassPage({super.key}); @override State createState() => _JoinClassPageState(); } class _JoinClassPageState extends State { final _codeController = TextEditingController(); bool _isLoading = false; @override void dispose() { _codeController.dispose(); super.dispose(); } Future _joinClass() async { final code = _codeController.text.trim().toUpperCase(); if (code.isEmpty) { _showError('Insere o código da turma'); return; } setState(() => _isLoading = true); try { final currentUser = AuthService.currentUser; if (currentUser == null) { setState(() => _isLoading = false); _showError('Erro: Utilizador não autenticado'); return; } // Verificar role — apenas alunos podem entrar por código final userRole = await AuthService.getUserRole(currentUser.uid); if (userRole != 'student') { setState(() => _isLoading = false); _showError('Apenas alunos podem entrar em turmas por código.'); return; } // Ler classId autorizado do aluno (definido no registo) final authorizedClassId = await AuthService.getStudentClassId( currentUser.uid, ); // Procurar turma pelo código final classQuery = await FirebaseFirestore.instance .collection('classes') .where('code', isEqualTo: code) .limit(1) .get(); if (classQuery.docs.isEmpty) { setState(() => _isLoading = false); _showError('Código de turma inválido'); return; } final classDoc = classQuery.docs.first; final classId = classDoc.id; // Verificar se o aluno está autorizado a entrar nesta turma if (authorizedClassId == null || authorizedClassId != classId) { setState(() => _isLoading = false); _showError( 'Não tens permissão para entrar nesta turma.\n' 'O teu professor ainda não te adicionou a esta disciplina.', ); return; } // Verificar se já está inscrito nesta turma final existingEnrollment = await FirebaseFirestore.instance .collection('enrollments') .where('classId', isEqualTo: classId) .where('studentId', isEqualTo: currentUser.uid) .limit(1) .get(); if (existingEnrollment.docs.isNotEmpty) { setState(() => _isLoading = false); _showError('Já estás inscrito nesta turma'); return; } // Criar documento de inscrição await FirebaseFirestore.instance.collection('enrollments').add({ 'classId': classId, 'studentId': currentUser.uid, 'studentName': currentUser.displayName ?? currentUser.email?.split('@')[0] ?? 'Aluno', 'joinedAt': FieldValue.serverTimestamp(), }); setState(() => _isLoading = false); // Mostrar sucesso if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Entraste na turma com sucesso!'), backgroundColor: Color(0xFF10B981), duration: Duration(seconds: 2), ), ); // Voltar para a home Navigator.of(context).pop(); } } catch (e) { setState(() => _isLoading = false); _showError('Erro ao entrar na turma: $e'); } } void _showError(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: const Color(0xFFEF4444), duration: const Duration(seconds: 3), ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF8F9FA), appBar: AppBar( backgroundColor: const Color(0xFF82C9BD), elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () => Navigator.of(context).pop(), ), title: const Text( 'Entrar numa Turma', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), ), body: Padding( padding: const EdgeInsets.all(24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Ícone e descrição Center( child: Container( width: 80, height: 80, decoration: BoxDecoration( color: const Color(0xFF82C9BD).withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: const Icon( Icons.group_add, color: Color(0xFF82C9BD), size: 40, ), ), ), const SizedBox(height: 24), const Center( child: Text( 'Insere o código da turma', style: TextStyle( color: Color(0xFF2D3748), fontSize: 18, fontWeight: FontWeight.bold, ), ), ), const SizedBox(height: 8), Center( child: Text( 'O professor partilhou contigo um código de 6 caracteres.', style: TextStyle(color: Colors.grey[600], fontSize: 14), textAlign: TextAlign.center, ), ), const SizedBox(height: 32), // Campo de código Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFE2E8F0), width: 1), ), child: TextField( controller: _codeController, textCapitalization: TextCapitalization.characters, maxLength: 6, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, letterSpacing: 4, color: Color(0xFF2D3748), ), textAlign: TextAlign.center, decoration: InputDecoration( hintText: 'XXXXXX', hintStyle: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, letterSpacing: 4, color: Colors.grey[400], ), border: InputBorder.none, contentPadding: const EdgeInsets.all(20), counterText: '', ), ), ), const SizedBox(height: 24), // Botão de entrar SizedBox( width: double.infinity, height: 56, child: ElevatedButton( onPressed: _isLoading ? null : _joinClass, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF82C9BD), foregroundColor: Colors.white, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), disabledBackgroundColor: const Color( 0xFF82C9BD, ).withOpacity(0.5), ), child: _isLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Text( 'Entrar na Turma', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ); } }