Escolher permissao no upload de conteudo
This commit is contained in:
@@ -178,6 +178,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
|
||||
final docId = materials[index].id;
|
||||
final url = material['url'] as String?;
|
||||
final classId = material['classId'] as String?;
|
||||
|
||||
return _buildMaterialCard(
|
||||
docId: docId,
|
||||
@@ -185,6 +186,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
fileType: fileType,
|
||||
createdAt: createdAt,
|
||||
url: url,
|
||||
classId: classId,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -355,7 +357,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
final XFile? file = await openFile(acceptedTypeGroups: [pdfTypeGroup]);
|
||||
if (file == null) return;
|
||||
|
||||
await _uploadFile(
|
||||
await _pickClassAndUpload(
|
||||
filePath: file.path,
|
||||
fileName: path.basename(file.path),
|
||||
fileType: 'pdf',
|
||||
@@ -377,7 +379,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
);
|
||||
if (image == null) return;
|
||||
|
||||
await _uploadFile(
|
||||
await _pickClassAndUpload(
|
||||
filePath: image.path,
|
||||
fileName: path.basename(image.path),
|
||||
fileType: 'image',
|
||||
@@ -399,7 +401,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
);
|
||||
if (photo == null) return;
|
||||
|
||||
await _uploadFile(
|
||||
await _pickClassAndUpload(
|
||||
filePath: photo.path,
|
||||
fileName: path.basename(photo.path),
|
||||
fileType: 'image',
|
||||
@@ -411,10 +413,148 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickClassAndUpload({
|
||||
required String filePath,
|
||||
required String fileName,
|
||||
required String fileType,
|
||||
}) async {
|
||||
final currentUser = AuthService.currentUser;
|
||||
if (currentUser == null) {
|
||||
_showErrorSnackBar('Utilizador não autenticado');
|
||||
return;
|
||||
}
|
||||
|
||||
// Carregar as turmas do professor
|
||||
List<Map<String, String>> teacherClasses = [];
|
||||
try {
|
||||
final snapshot = await _firestore
|
||||
.collection('classes')
|
||||
.where('teacherId', isEqualTo: currentUser.uid)
|
||||
.orderBy('createdAt', descending: true)
|
||||
.get();
|
||||
teacherClasses = snapshot.docs.map((doc) {
|
||||
final data = doc.data();
|
||||
return {'id': doc.id, 'name': (data['name'] as String? ?? doc.id)};
|
||||
}).toList();
|
||||
} catch (_) {}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
// Se o professor não tem turmas, fazer upload sem associar turma
|
||||
if (teacherClasses.isEmpty) {
|
||||
await _uploadFile(
|
||||
filePath: filePath,
|
||||
fileName: fileName,
|
||||
fileType: fileType,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mostrar diálogo de seleção de turma
|
||||
String? selectedClassId = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
String? picked;
|
||||
return StatefulBuilder(
|
||||
builder: (context, setDialogState) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
title: const Text(
|
||||
'Escolher Turma',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Seleciona a turma que terá acesso a este material:',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
DropdownButtonFormField<String>(
|
||||
value: picked,
|
||||
isExpanded: true,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainerHighest,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 10,
|
||||
),
|
||||
),
|
||||
hint: const Text('Seleciona a turma'),
|
||||
items: teacherClasses
|
||||
.map(
|
||||
(c) => DropdownMenuItem<String>(
|
||||
value: c['id'],
|
||||
child: Text(c['name']!),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (value) =>
|
||||
setDialogState(() => picked = value),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(null),
|
||||
child: const Text('Cancelar'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: picked == null
|
||||
? null
|
||||
: () => Navigator.of(dialogContext).pop(picked),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFF68D2D),
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
child: const Text('Confirmar'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Se cancelou o diálogo, não fazer upload
|
||||
if (selectedClassId == null) return;
|
||||
|
||||
await _uploadFile(
|
||||
filePath: filePath,
|
||||
fileName: fileName,
|
||||
fileType: fileType,
|
||||
classId: selectedClassId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _uploadFile({
|
||||
required String filePath,
|
||||
required String fileName,
|
||||
required String fileType,
|
||||
String? classId,
|
||||
}) async {
|
||||
final currentUser = FirebaseAuth.instance.currentUser;
|
||||
if (currentUser == null) {
|
||||
@@ -443,12 +583,18 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
final downloadUrl = await ref.getDownloadURL();
|
||||
|
||||
// Criar documento no Firestore
|
||||
await FirebaseFirestore.instance.collection('materials').add({
|
||||
final materialData = <String, dynamic>{
|
||||
'teacherId': uid,
|
||||
'fileName': cleanFileName,
|
||||
'url': downloadUrl,
|
||||
'createdAt': FieldValue.serverTimestamp(),
|
||||
});
|
||||
};
|
||||
if (classId != null && classId.isNotEmpty) {
|
||||
materialData['classId'] = classId;
|
||||
}
|
||||
await FirebaseFirestore.instance
|
||||
.collection('materials')
|
||||
.add(materialData);
|
||||
|
||||
if (mounted) {
|
||||
_showSuccessSnackBar('Material enviado com sucesso!');
|
||||
@@ -569,12 +715,25 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> _getClassName(String classId) async {
|
||||
try {
|
||||
final doc = await _firestore.collection('classes').doc(classId).get();
|
||||
if (doc.exists) {
|
||||
return doc.data()?['name'] as String?;
|
||||
}
|
||||
return null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildMaterialCard({
|
||||
required String docId,
|
||||
required String fileName,
|
||||
required String fileType,
|
||||
required Timestamp? createdAt,
|
||||
String? url,
|
||||
String? classId,
|
||||
}) {
|
||||
IconData iconData;
|
||||
Color iconColor;
|
||||
@@ -635,7 +794,52 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: Text(
|
||||
subtitle: classId != null
|
||||
? FutureBuilder<String?>(
|
||||
future: _getClassName(classId),
|
||||
builder: (context, snap) {
|
||||
final className = snap.data;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
formattedDate,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF718096),
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
if (className != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.school_outlined,
|
||||
size: 12,
|
||||
color: Color(0xFF82C9BD),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Flexible(
|
||||
child: Text(
|
||||
className,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF82C9BD),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
: Text(
|
||||
formattedDate,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF718096),
|
||||
|
||||
Reference in New Issue
Block a user