Reformulação de materias uploaded
This commit is contained in:
@@ -27,8 +27,38 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
);
|
||||
final ImagePicker _imagePicker = ImagePicker();
|
||||
bool _isUploading = false;
|
||||
List<Map<String, dynamic>> _classes = [];
|
||||
List<Map<String, dynamic>> _filteredClasses = [];
|
||||
String _searchQuery = '';
|
||||
|
||||
Stream<QuerySnapshot> _getMaterialsStream() {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadClasses();
|
||||
}
|
||||
|
||||
Future<void> _loadClasses() async {
|
||||
final currentUser = AuthService.currentUser;
|
||||
if (currentUser == null) return;
|
||||
|
||||
final snapshot = await _firestore
|
||||
.collection('classes')
|
||||
.where('teacherId', isEqualTo: currentUser.uid)
|
||||
.orderBy('createdAt', descending: true)
|
||||
.get();
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_classes = snapshot.docs.map((doc) {
|
||||
final data = doc.data();
|
||||
return {'id': doc.id, 'name': (data['name'] as String? ?? doc.id)};
|
||||
}).toList();
|
||||
_filteredClasses = List.from(_classes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Stream<QuerySnapshot> _getMaterialsStream(String classId) {
|
||||
final currentUser = AuthService.currentUser;
|
||||
if (currentUser == null) {
|
||||
return const Stream.empty();
|
||||
@@ -37,64 +67,142 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
return _firestore
|
||||
.collection('materials')
|
||||
.where('teacherId', isEqualTo: currentUser.uid)
|
||||
.orderBy('createdAt', descending: true)
|
||||
.where('classId', isEqualTo: classId)
|
||||
.snapshots();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'Materiais da Disciplina',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
if (_classes.isEmpty) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'Materiais da Disciplina',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
floatingActionButton: _isUploading
|
||||
? FloatingActionButton.extended(
|
||||
onPressed: null,
|
||||
backgroundColor: const Color(0xFFF68D2D).withOpacity(0.6),
|
||||
icon: const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
label: const Text(
|
||||
'A enviar...',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
)
|
||||
: FloatingActionButton.extended(
|
||||
onPressed: _showUploadOptions,
|
||||
backgroundColor: const Color(0xFFF68D2D),
|
||||
icon: const Icon(Icons.add, color: Colors.white),
|
||||
label: const Text(
|
||||
'Adicionar',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
body: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: Theme.of(context).brightness == Brightness.dark
|
||||
? [
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.3),
|
||||
Theme.of(context).colorScheme.background,
|
||||
]
|
||||
: [const Color(0xFF82C9BD), const Color(0xFFF8F9FA)],
|
||||
stops: const [0.0, 0.4],
|
||||
),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.folder_open, size: 64, color: Colors.grey),
|
||||
SizedBox(height: 16),
|
||||
Text('Ainda não tens turmas criadas.'),
|
||||
],
|
||||
),
|
||||
|
||||
body: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: Theme.of(context).brightness == Brightness.dark
|
||||
? [
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.3),
|
||||
Theme.of(context).colorScheme.background,
|
||||
]
|
||||
: [const Color(0xFF82C9BD), const Color(0xFFF8F9FA)],
|
||||
stops: const [0.0, 0.4],
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: StreamBuilder<QuerySnapshot>(
|
||||
stream: _getMaterialsStream(),
|
||||
);
|
||||
}
|
||||
|
||||
return DefaultTabController(
|
||||
length: _filteredClasses.length,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'Materiais da Disciplina',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
elevation: 0,
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(kToolbarHeight + 60),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Procurar turma...',
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_searchQuery = value.toLowerCase();
|
||||
_filteredClasses = _classes.where((c) {
|
||||
final name = (c['name'] as String).toLowerCase();
|
||||
return name.contains(_searchQuery);
|
||||
}).toList();
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
if (_filteredClasses.isNotEmpty)
|
||||
TabBar(
|
||||
isScrollable: _filteredClasses.length > 3,
|
||||
indicatorColor: const Color(0xFFF68D2D),
|
||||
labelColor: const Color(0xFFF68D2D),
|
||||
unselectedLabelColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurface,
|
||||
tabAlignment: TabAlignment.start,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
tabs: _filteredClasses
|
||||
.map((c) => Tab(text: c['name'] as String))
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: Theme.of(context).brightness == Brightness.dark
|
||||
? [
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.3),
|
||||
Theme.of(context).colorScheme.background,
|
||||
]
|
||||
: [const Color(0xFF82C9BD), const Color(0xFFF8F9FA)],
|
||||
stops: const [0.0, 0.4],
|
||||
),
|
||||
),
|
||||
child: TabBarView(
|
||||
children: _filteredClasses.map((classData) {
|
||||
final classId = classData['id'] as String;
|
||||
return _buildClassTab(classId: classId);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildClassTab({required String classId}) {
|
||||
return SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
StreamBuilder<QuerySnapshot>(
|
||||
stream: _getMaterialsStream(classId),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(
|
||||
@@ -128,6 +236,18 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
|
||||
final materials = snapshot.data?.docs ?? [];
|
||||
|
||||
// Sort by createdAt descending on client side
|
||||
materials.sort((a, b) {
|
||||
final aData = a.data() as Map<String, dynamic>?;
|
||||
final bData = b.data() as Map<String, dynamic>?;
|
||||
final aTime = aData?['createdAt'] as Timestamp?;
|
||||
final bTime = bData?['createdAt'] as Timestamp?;
|
||||
if (aTime == null && bTime == null) return 0;
|
||||
if (aTime == null) return 1;
|
||||
if (bTime == null) return -1;
|
||||
return bTime.compareTo(aTime);
|
||||
});
|
||||
|
||||
if (materials.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
@@ -167,7 +287,6 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
materials[index].data() as Map<String, dynamic>;
|
||||
final fileName = material['fileName'] ?? 'Ficheiro sem nome';
|
||||
final createdAt = material['createdAt'] as Timestamp?;
|
||||
// Inferir tipo pela extensão do filename
|
||||
final extension = path.extension(fileName).toLowerCase();
|
||||
final fileType = extension == '.pdf'
|
||||
? 'pdf'
|
||||
@@ -179,7 +298,6 @@ 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,
|
||||
@@ -193,12 +311,42 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
child: _isUploading
|
||||
? FloatingActionButton.extended(
|
||||
onPressed: null,
|
||||
backgroundColor: const Color(0xFFF68D2D).withOpacity(0.6),
|
||||
icon: const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
label: const Text(
|
||||
'A enviar...',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
)
|
||||
: FloatingActionButton.extended(
|
||||
onPressed: () => _showUploadOptions(classId),
|
||||
backgroundColor: const Color(0xFFF68D2D),
|
||||
icon: const Icon(Icons.add, color: Colors.white),
|
||||
label: const Text(
|
||||
'Adicionar',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showUploadOptions() {
|
||||
void _showUploadOptions(String classId) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
@@ -242,7 +390,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
subtitle: 'Selecionar ficheiro PDF',
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_selectPDF();
|
||||
_selectPDF(classId);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -253,7 +401,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
subtitle: 'Escolher foto existente',
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_selectImageFromGallery();
|
||||
_selectImageFromGallery(classId);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -264,7 +412,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
subtitle: 'Tirar foto nova',
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_takePhotoWithCamera();
|
||||
_takePhotoWithCamera(classId);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
@@ -332,7 +480,105 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _selectPDF() async {
|
||||
Future<String?> _showRenameDialog(String originalName) async {
|
||||
if (!mounted) return null;
|
||||
|
||||
final wantRename = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
title: const Text(
|
||||
'Renomear Ficheiro',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: const Text('Deseja renomear o ficheiro?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: const Text('Não'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFF68D2D),
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
child: const Text('Sim'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (wantRename != true || !mounted) return originalName;
|
||||
|
||||
final newName = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setDialogState) {
|
||||
String textValue = originalName;
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
title: const Text(
|
||||
'Novo Nome',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: originalName,
|
||||
filled: true,
|
||||
fillColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainerHighest,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
autofocus: true,
|
||||
onChanged: (value) => textValue = value,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(null),
|
||||
child: const Text('Cancelar'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
final name = textValue.trim();
|
||||
if (name.isNotEmpty) {
|
||||
Navigator.of(dialogContext).pop(name);
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFF68D2D),
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
child: const Text('Confirmar'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return newName?.isNotEmpty == true ? newName : originalName;
|
||||
}
|
||||
|
||||
Future<void> _selectPDF(String classId) async {
|
||||
try {
|
||||
const XTypeGroup pdfTypeGroup = XTypeGroup(
|
||||
label: 'PDFs',
|
||||
@@ -343,10 +589,15 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
final XFile? file = await openFile(acceptedTypeGroups: [pdfTypeGroup]);
|
||||
if (file == null) return;
|
||||
|
||||
await _pickClassAndUpload(
|
||||
final originalName = path.basename(file.path);
|
||||
final finalName = await _showRenameDialog(originalName);
|
||||
if (finalName == null) return;
|
||||
|
||||
await _uploadFile(
|
||||
filePath: file.path,
|
||||
fileName: path.basename(file.path),
|
||||
fileName: finalName,
|
||||
fileType: 'pdf',
|
||||
classId: classId,
|
||||
);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
@@ -355,7 +606,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _selectImageFromGallery() async {
|
||||
Future<void> _selectImageFromGallery(String classId) async {
|
||||
try {
|
||||
final XFile? image = await _imagePicker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
@@ -365,10 +616,15 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
);
|
||||
if (image == null) return;
|
||||
|
||||
await _pickClassAndUpload(
|
||||
final originalName = path.basename(image.path);
|
||||
final finalName = await _showRenameDialog(originalName);
|
||||
if (finalName == null) return;
|
||||
|
||||
await _uploadFile(
|
||||
filePath: image.path,
|
||||
fileName: path.basename(image.path),
|
||||
fileName: finalName,
|
||||
fileType: 'image',
|
||||
classId: classId,
|
||||
);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
@@ -377,7 +633,7 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _takePhotoWithCamera() async {
|
||||
Future<void> _takePhotoWithCamera(String classId) async {
|
||||
try {
|
||||
final XFile? photo = await _imagePicker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
@@ -387,10 +643,15 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
);
|
||||
if (photo == null) return;
|
||||
|
||||
await _pickClassAndUpload(
|
||||
final originalName = path.basename(photo.path);
|
||||
final finalName = await _showRenameDialog(originalName);
|
||||
if (finalName == null) return;
|
||||
|
||||
await _uploadFile(
|
||||
filePath: photo.path,
|
||||
fileName: path.basename(photo.path),
|
||||
fileName: finalName,
|
||||
fileType: 'image',
|
||||
classId: classId,
|
||||
);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
@@ -399,142 +660,6 @@ 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 disciplinas 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 disciplinas, fazer upload sem associar disciplina
|
||||
if (teacherClasses.isEmpty) {
|
||||
await _uploadFile(
|
||||
filePath: filePath,
|
||||
fileName: fileName,
|
||||
fileType: fileType,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mostrar diálogo de seleção de disciplina
|
||||
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 Disciplina',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Seleciona a disciplina 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 disciplina'),
|
||||
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,
|
||||
@@ -700,18 +825,6 @@ 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,
|
||||
@@ -773,58 +886,13 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
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: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
if (className != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.school_outlined,
|
||||
size: 12,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Flexible(
|
||||
child: Text(
|
||||
className,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
: Text(
|
||||
formattedDate,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
formattedDate,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete_outline, color: Colors.red),
|
||||
tooltip: 'Eliminar',
|
||||
|
||||
Reference in New Issue
Block a user