import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'training_detail_screen.dart'; import 'package:uuid/uuid.dart'; const Map categoryColors = { 'Aufwärmen & Mobilisation': Colors.deepOrange, 'Wurf- & Torabschluss': Colors.orange, 'Torwarttraining': Colors.green, 'Athletik': Colors.blue, 'Pass': Colors.purple, 'Koordination': Colors.teal, }; class CalendarTab extends StatefulWidget { final DateTime? initialDate; const CalendarTab({super.key, this.initialDate}); @override State createState() => _CalendarTabState(); } class _CalendarTabState extends State { CalendarFormat _calendarFormat = CalendarFormat.week; late DateTime _focusedDay; DateTime? _selectedDay; Map>> _events = {}; bool _isLoading = false; String? _currentUserId; String? _userRole; final _exerciseController = TextEditingController(); final _durationController = TextEditingController(); @override void dispose() { _exerciseController.dispose(); _durationController.dispose(); super.dispose(); } @override void initState() { super.initState(); _focusedDay = widget.initialDate ?? DateTime.now(); _selectedDay = _focusedDay; _currentUserId = FirebaseAuth.instance.currentUser?.uid; _initializeData(); } @override void didUpdateWidget(covariant CalendarTab oldWidget) { super.didUpdateWidget(oldWidget); if (widget.initialDate != null && widget.initialDate != oldWidget.initialDate) { setState(() { _focusedDay = widget.initialDate!; _selectedDay = widget.initialDate!; }); } } Future _initializeData() async { if (_currentUserId != null) { final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (userDoc.exists) { setState(() { _userRole = userDoc.data()?['role'] as String?; }); await _loadEvents(); } } } Future _loadEvents() async { if (_userRole == null) return; setState(() => _isLoading = true); try { QuerySnapshot trainersSnapshot; if (_userRole == 'trainer') { trainersSnapshot = await FirebaseFirestore.instance .collection('User') .where('role', isEqualTo: 'trainer') .where(FieldPath.documentId, isEqualTo: _currentUserId) .get(); } else { final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (!userDoc.exists) { setState(() => _isLoading = false); return; } final userData = userDoc.data() as Map; final userClub = userData['club'] as String?; if (userClub == null || userClub.isEmpty) { setState(() { _events = {}; _isLoading = false; }); return; } trainersSnapshot = await FirebaseFirestore.instance .collection('User') .where('role', isEqualTo: 'trainer') .where('club', isEqualTo: userClub) .get(); } final events = >>{}; for (var trainerDoc in trainersSnapshot.docs) { final trainerData = trainerDoc.data() as Map; final trainings = trainerData['trainings'] as Map? ?? {}; final cancelledTrainings = trainerData['cancelledTrainings'] as List? ?? []; final trainingTimes = trainerData['trainingTimes'] as Map? ?? {}; final trainingDurations = trainerData['trainingDurations'] as Map? ?? {}; print('Lade Events:'); print('trainings: $trainings'); print('cancelledTrainings: $cancelledTrainings'); print('trainingTimes: $trainingTimes'); print('trainingDurations: $trainingDurations'); // Lade alle Trainings für das Datum trainings.forEach((dateString, trainingsList) { final date = DateTime.tryParse(dateString); if (date == null) return; final normalizedDate = DateTime(date.year, date.month, date.day); final list = List>.from(trainingsList); for (final training in list) { final timeStr = training['time'] as String? ?? '00:00'; final duration = training['duration'] as int? ?? 60; final exercises = training['exercises'] as List? ?? []; final totalExerciseDuration = exercises.fold(0, (sum, exercise) { if (exercise is Map) { return sum + (exercise['duration'] as int? ?? 0); } return sum; }); final event = { 'trainerName': trainerData['name'] ?? 'Unbekannter Trainer', 'time': timeStr, 'duration': duration, 'trainerId': trainerDoc.id, 'isCurrentUser': trainerDoc.id == _currentUserId, 'day': dateString, 'date': dateString, 'exercises': exercises, 'remainingTime': duration - totalExerciseDuration, 'club': trainerData['club'] ?? 'Kein Verein', 'id': training['id'], }; if (events.containsKey(normalizedDate)) { events[normalizedDate]!.add(event); } else { events[normalizedDate] = [event]; } } }); // Füge regelmäßige Trainings hinzu final now = DateTime.now(); final yearStart = DateTime(now.year, 1, 1); final yearEnd = DateTime(now.year + 1, 1, 1); final weekdays = { 'Montag': 1, 'Dienstag': 2, 'Mittwoch': 3, 'Donnerstag': 4, 'Freitag': 5, 'Samstag': 6, 'Sonntag': 7, }; // Erstelle eine Set von gecancelten Daten für schnelleren Zugriff final cancelledDates = cancelledTrainings .where((cancelled) => cancelled is Map) .map((cancelled) => DateTime.parse((cancelled as Map)['date'] as String)) .toSet(); print('Gecancelte Daten: $cancelledDates'); trainingTimes.forEach((day, timeStr) { if (timeStr == null) return; final timeParts = (timeStr as String).split(':'); if (timeParts.length != 2) return; final hour = int.tryParse(timeParts[0]) ?? 0; final minute = int.tryParse(timeParts[1]) ?? 0; final duration = trainingDurations[day] ?? 60; final targetWeekday = weekdays[day] ?? 1; // Finde das erste gewünschte Wochentags-Datum ab Jahresanfang DateTime firstDate = yearStart; while (firstDate.weekday != targetWeekday) { firstDate = firstDate.add(const Duration(days: 1)); } // Generiere Trainings für das gesamte Jahr while (firstDate.isBefore(yearEnd)) { final normalizedDate = DateTime(firstDate.year, firstDate.month, firstDate.day); final dateString = normalizedDate.toIso8601String(); // Prüfe, ob das Training für dieses Datum gecancelt ist final isCancelled = cancelledDates.any((cancelledDate) => isSameDay(cancelledDate, normalizedDate) ); // Prüfe, ob bereits ein spezifisches Training für dieses Datum existiert final hasSpecificTraining = trainings.containsKey(dateString); if (!isCancelled && !hasSpecificTraining) { final event = { 'trainerName': trainerData['name'] ?? 'Unbekannter Trainer', 'time': timeStr, 'duration': duration, 'trainerId': trainerDoc.id, 'isCurrentUser': trainerDoc.id == _currentUserId, 'day': day, 'date': dateString, 'exercises': [], 'remainingTime': duration, 'club': trainerData['club'] ?? 'Kein Verein', 'id': 'weekly_${day}_${dateString}', }; if (events.containsKey(normalizedDate)) { events[normalizedDate]!.add(event); } else { events[normalizedDate] = [event]; } } firstDate = firstDate.add(const Duration(days: 7)); } }); } setState(() { _events = events; _isLoading = false; }); } catch (e) { print('Error loading events: $e'); setState(() => _isLoading = false); } } Future _addExercise(Map event) async { if (_userRole != 'trainer' || !event['isCurrentUser']) return; // Navigiere zum Suchbildschirm und warte auf das Ergebnis final result = await Navigator.pushNamed( context, '/search', arguments: { 'selectMode': true, 'remainingTime': event['remainingTime'], }, ); // Wenn eine Übung ausgewählt wurde if (result != null && result is Map) { try { print('Übung ausgewählt:'); print('ID: ${result['id']}'); print('Titel: ${result['title']}'); print('Vollständige Übungsdaten: $result'); bool shouldAddExercise = true; // Prüfe die Bewertungen der Übung in der Training-Collection print('Suche Übung in der Datenbank...'); final exerciseDoc = await FirebaseFirestore.instance .collection('Training') .doc(result['id']) .get(); print('Übungsdokument gefunden: ${exerciseDoc.exists}'); print('Collection-Pfad: Training/${result['id']}'); if (exerciseDoc.exists) { final exerciseData = exerciseDoc.data() as Map; print('Übungsdaten: $exerciseData'); // Versuche die Bewertungen aus verschiedenen möglichen Feldern zu lesen List> ratings = []; if (exerciseData.containsKey('ratings')) { ratings = List>.from(exerciseData['ratings'] ?? []); print('Bewertungen aus Feld "ratings" gefunden: $ratings'); } else if (exerciseData.containsKey('rating')) { ratings = List>.from(exerciseData['rating'] ?? []); print('Bewertungen aus Feld "rating" gefunden: $ratings'); } print('Gefundene Bewertungen: $ratings'); if (ratings.isNotEmpty) { // Berechne den Durchschnitt der Bewertungen double totalRating = 0; for (var rating in ratings) { if (rating.containsKey('rating')) { totalRating += (rating['rating'] as num).toDouble(); } } final averageRating = totalRating / ratings.length; print('Durchschnittliche Bewertung: $averageRating'); // Wenn die durchschnittliche Bewertung unter 3 liegt, zeige eine Warnung if (averageRating < 3) { print('Zeige Warnung für schlechte Bewertung'); if (mounted) { shouldAddExercise = await showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: const Text('Hinweis zur Übungsbewertung'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Die Übung "${result['title']}" wurde von anderen Trainern mit ${averageRating.toStringAsFixed(1)} von 5 Sternen bewertet.'), const SizedBox(height: 16), const Text('Möchten Sie diese Übung trotzdem zu Ihrem Training hinzufügen?'), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Abbrechen'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('Trotzdem hinzufügen'), ), ], ), ) ?? false; print('Benutzer hat entschieden: ${shouldAddExercise ? "Hinzufügen" : "Abbrechen"}'); } } else { print('Bewertung ist gut genug, keine Warnung nötig'); } } else { print('Keine Bewertungen vorhanden'); } } else { print('Warnung: Übungsdokument nicht gefunden'); } if (!shouldAddExercise) { print('Übung wird nicht hinzugefügt (Benutzer hat abgebrochen)'); return; } // Füge die Übung zum Training hinzu final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (!userDoc.exists) return; final data = userDoc.data() as Map; final trainings = Map.from(data['trainings'] ?? {}); final dateString = event['date'] as String; final trainingId = event['id'] as String; print('Füge Übung zum Training hinzu:'); print('dateString: $dateString'); print('trainingId: $trainingId'); print('Vorher - trainings: $trainings'); // Finde das Training und füge die Übung hinzu if (trainings.containsKey(dateString)) { final trainingsList = List>.from(trainings[dateString]); final trainingIndex = trainingsList.indexWhere((t) => t['id'] == trainingId); if (trainingIndex != -1) { final exercises = List>.from(trainingsList[trainingIndex]['exercises'] ?? []); exercises.add({ 'id': result['id'], 'name': result['title'], 'description': result['description'], 'duration': result['duration'], }); trainingsList[trainingIndex]['exercises'] = exercises; trainings[dateString] = trainingsList; } } print('Nachher - trainings: $trainings'); // Aktualisiere die Datenbank await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .update({ 'trainings': trainings, }); // Aktualisiere die UI sofort setState(() { final normalizedDate = DateTime.parse(dateString); if (_events.containsKey(normalizedDate)) { final eventIndex = _events[normalizedDate]!.indexWhere((e) => e['id'] == trainingId); if (eventIndex != -1) { final exercises = List>.from(_events[normalizedDate]![eventIndex]['exercises'] ?? []); exercises.add({ 'id': result['id'], 'name': result['title'], 'description': result['description'], 'duration': result['duration'], }); _events[normalizedDate]![eventIndex]['exercises'] = exercises; _events[normalizedDate]![eventIndex]['remainingTime'] = (_events[normalizedDate]![eventIndex]['duration'] as int) - exercises.fold(0, (sum, exercise) => sum + (exercise['duration'] as int)); } } }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Übung wurde hinzugefügt')), ); } } catch (e) { print('Error adding exercise: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Hinzufügen der Übung')), ); } } } } Future _deleteTraining(Map event) async { if (_userRole != 'trainer' || !event['isCurrentUser']) return; try { final userDoc = await FirebaseFirestore.instance.collection('User').doc(_currentUserId).get(); if (!userDoc.exists) return; final data = userDoc.data() as Map; final trainings = Map.from(data['trainings'] ?? {}); final dateString = event['date'] as String; final trainingId = event['id'] as String?; final cancelledTrainings = List>.from(data['cancelledTrainings'] ?? []); print('Lösche Training:'); print('dateString: $dateString'); print('trainingId: $trainingId'); print('Vorher - trainings: $trainings'); print('Vorher - cancelledTrainings: $cancelledTrainings'); // Wenn es sich um ein regelmäßiges Training handelt (ID beginnt mit 'weekly_') if (trainingId != null && trainingId.startsWith('weekly_')) { // Füge das Datum zu cancelledTrainings hinzu, wenn es noch nicht existiert if (!cancelledTrainings.any((cancelled) => cancelled is Map && cancelled['date'] == dateString )) { cancelledTrainings.add({ 'date': dateString, }); } } else { // Für spezifische Trainings: Entferne das Training aus der Liste if (trainings.containsKey(dateString)) { final trainingsList = List>.from(trainings[dateString]); trainingsList.removeWhere((t) => t['id'] == trainingId); if (trainingsList.isEmpty) { trainings.remove(dateString); } else { trainings[dateString] = trainingsList; } } // Entferne den Eintrag aus cancelledTrainings nur, wenn es sich um ein spezifisches Training handelt // und kein regelmäßiges Training an diesem Tag stattfindet final weekdays = { 'Montag': 1, 'Dienstag': 2, 'Mittwoch': 3, 'Donnerstag': 4, 'Freitag': 5, 'Samstag': 6, 'Sonntag': 7, }; final date = DateTime.parse(dateString); final weekday = date.weekday; final weekdayName = weekdays.entries.firstWhere((entry) => entry.value == weekday).key; final trainingTimes = data['trainingTimes'] as Map? ?? {}; // Wenn an diesem Tag kein regelmäßiges Training stattfindet, entferne den Eintrag aus cancelledTrainings if (!trainingTimes.containsKey(weekdayName)) { cancelledTrainings.removeWhere((cancelled) => cancelled is Map && cancelled.containsKey('date') && cancelled['date'] == dateString ); } } print('Nachher - trainings: $trainings'); print('Nachher - cancelledTrainings: $cancelledTrainings'); // Aktualisiere die Datenbank final updates = {}; // Aktualisiere trainings nur, wenn es nicht leer ist if (trainings.isNotEmpty) { updates['trainings'] = trainings; } else { updates['trainings'] = null; } // Aktualisiere cancelledTrainings nur, wenn es nicht leer ist if (cancelledTrainings.isNotEmpty) { updates['cancelledTrainings'] = cancelledTrainings; } else { updates['cancelledTrainings'] = null; } // Führe die Aktualisierung durch await FirebaseFirestore.instance.collection('User').doc(_currentUserId).update(updates); // Aktualisiere die UI sofort setState(() { final normalizedDate = DateTime.parse(dateString); if (_events.containsKey(normalizedDate)) { _events[normalizedDate]!.removeWhere((e) => e['id'] == trainingId); if (_events[normalizedDate]!.isEmpty) { _events.remove(normalizedDate); } } }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Training wurde gelöscht')), ); } } catch (e) { print('Error deleting training: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Löschen des Trainings')), ); } } } Future _removeExercise(Map event, Map exercise) async { if (_userRole != 'trainer' || !event['isCurrentUser']) return; try { final userDoc = await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .get(); if (!userDoc.exists) return; final data = userDoc.data() as Map; final trainings = Map.from(data['trainings'] ?? {}); final dateString = event['date'] as String; final trainingId = event['id'] as String; print('Entferne Übung:'); print('dateString: $dateString'); print('trainingId: $trainingId'); print('Vorher - trainings: $trainings'); // Finde das Training und entferne die Übung if (trainings.containsKey(dateString)) { final trainingsList = List>.from(trainings[dateString]); final trainingIndex = trainingsList.indexWhere((t) => t['id'] == trainingId); if (trainingIndex != -1) { final exercises = List>.from(trainingsList[trainingIndex]['exercises'] ?? []); exercises.removeWhere((e) => e['id'] == exercise['id']); trainingsList[trainingIndex]['exercises'] = exercises; trainings[dateString] = trainingsList; } } print('Nachher - trainings: $trainings'); // Aktualisiere die Datenbank await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) .update({ 'trainings': trainings, }); // Aktualisiere die UI sofort setState(() { final normalizedDate = DateTime.parse(dateString); if (_events.containsKey(normalizedDate)) { final eventIndex = _events[normalizedDate]!.indexWhere((e) => e['id'] == trainingId); if (eventIndex != -1) { final exercises = List>.from(_events[normalizedDate]![eventIndex]['exercises'] ?? []); exercises.removeWhere((e) => e['id'] == exercise['id']); _events[normalizedDate]![eventIndex]['exercises'] = exercises; _events[normalizedDate]![eventIndex]['remainingTime'] = (_events[normalizedDate]![eventIndex]['duration'] as int) - exercises.fold(0, (sum, exercise) => sum + (exercise['duration'] as int)); } } }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Übung wurde entfernt')), ); } } catch (e) { print('Error removing exercise: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Entfernen der Übung')), ); } } } int _getDaysUntilNext(String day, int currentWeekday) { final weekdays = { 'Montag': 1, 'Dienstag': 2, 'Mittwoch': 3, 'Donnerstag': 4, 'Freitag': 5, 'Samstag': 6, 'Sonntag': 7, }; final targetWeekday = weekdays[day] ?? 1; var daysUntilNext = targetWeekday - currentWeekday; if (daysUntilNext <= 0) { daysUntilNext += 7; } return daysUntilNext; } List> _getEventsForDay(DateTime day) { final normalizedDate = DateTime(day.year, day.month, day.day); return _events[normalizedDate] ?? []; } @override Widget build(BuildContext context) { final isTrainer = _userRole == 'trainer'; return Scaffold( appBar: AppBar( title: const Text('Kalender'), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: _isLoading ? null : _loadEvents, ), ], ), body: SafeArea( child: Column( children: [ Container( padding: const EdgeInsets.all(8.0), child: TableCalendar( firstDay: DateTime.utc(2024, 1, 1), lastDay: DateTime.utc(2025, 12, 31), focusedDay: _focusedDay, calendarFormat: _calendarFormat, locale: 'de_DE', selectedDayPredicate: (day) { return isSameDay(_selectedDay, day); }, onDaySelected: (selectedDay, focusedDay) { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; }); }, onFormatChanged: (format) { setState(() { _calendarFormat = format; }); }, onPageChanged: (focusedDay) { setState(() { _focusedDay = focusedDay; }); }, eventLoader: _getEventsForDay, calendarStyle: const CalendarStyle( markersMaxCount: 1, markerDecoration: BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ), headerStyle: const HeaderStyle( formatButtonVisible: true, titleCentered: true, ), calendarBuilders: CalendarBuilders( markerBuilder: (context, date, events) { if (events.isEmpty) return null; return Positioned( bottom: 1, child: Container( width: 8, height: 8, decoration: const BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ), ); }, ), startingDayOfWeek: StartingDayOfWeek.monday, daysOfWeekStyle: const DaysOfWeekStyle( weekdayStyle: TextStyle(fontWeight: FontWeight.bold), weekendStyle: TextStyle(color: Colors.red), ), availableCalendarFormats: const { CalendarFormat.week: 'Woche', CalendarFormat.month: 'Monat', }, ), ), if (isTrainer && _selectedDay != null) ...[ Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: SizedBox( width: double.infinity, child: ElevatedButton.icon( icon: const Icon(Icons.add), label: const Text('Training an diesem Tag hinzufügen'), onPressed: () async { final result = await showDialog>( context: context, builder: (context) => _TrainingEditDialog( date: _selectedDay!, ), ); if (result != null) { await _addOrEditTraining(_selectedDay!, result['time'], result['duration'], isException: false); await _loadEvents(); } }, ), ), ), ], const Divider(), Expanded( child: _isLoading ? const Center(child: CircularProgressIndicator()) : _selectedDay == null ? const Center(child: Text('Bitte wähle einen Tag aus')) : (() { final events = _getEventsForDay(_selectedDay!); // Sortiere nach Uhrzeit (Format: 'HH:mm') events.sort((a, b) { final aTime = a['time'] as String? ?? '00:00'; final bTime = b['time'] as String? ?? '00:00'; final aParts = aTime.split(':').map(int.parse).toList(); final bParts = bTime.split(':').map(int.parse).toList(); final aMinutes = aParts[0] * 60 + aParts[1]; final bMinutes = bParts[0] * 60 + bParts[1]; return aMinutes.compareTo(bMinutes); }); return ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: events.length, itemBuilder: (context, index) { final event = events[index]; final isCurrentUser = event['isCurrentUser'] as bool; final exercises = event['exercises'] as List; final remainingTime = event['remainingTime'] as int; return Card( margin: const EdgeInsets.only(bottom: 8), color: isCurrentUser ? Colors.blue.withOpacity(0.1) : null, child: Column( children: [ ListTile( leading: Icon( Icons.sports, color: categoryColors[event['day']] ?? (isCurrentUser ? Colors.blue : Colors.grey), ), title: Text( isCurrentUser ? 'Training' : event['trainerName'], style: TextStyle( fontWeight: isCurrentUser ? FontWeight.bold : null, ), ), subtitle: Text( '${event['time']} - ${event['duration']} Minuten\nVerbleibende Zeit: $remainingTime Minuten', ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (isCurrentUser) IconButton( icon: const Icon(Icons.edit), onPressed: () async { final result = await showDialog>( context: context, builder: (context) => _TrainingEditDialog( date: _selectedDay!, initialTime: event['time'], initialDuration: event['duration'], trainingId: event['id'], ), ); if (result != null) { await _addOrEditTraining( _selectedDay!, result['time'], result['duration'], isException: true, trainingId: result['trainingId'], ); await _loadEvents(); } }, ), if (isCurrentUser) IconButton( icon: const Icon(Icons.add), onPressed: () => _addExercise(event), ), IconButton( icon: const Icon(Icons.info), onPressed: () { showDialog( context: context, builder: (context) => AlertDialog( title: Text(isCurrentUser ? 'Training' : event['trainerName']), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Zeit: ${event['time']}'), const SizedBox(height: 8), Text('Dauer: ${event['duration']} Minuten'), const SizedBox(height: 8), Text('Verbleibende Zeit: $remainingTime Minuten'), if (exercises.isNotEmpty) ...[ const SizedBox(height: 16), const Text('Übungen:', style: TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 8), ...exercises.map((exercise) { if (exercise is Map) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: Row( children: [ const Icon(Icons.fitness_center, size: 16), const SizedBox(width: 8), Expanded( child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => TrainingDetailScreen( trainingId: exercise['id'], ), ), ); }, child: Text( '${exercise['name']} - ${exercise['duration']} Minuten', style: const TextStyle( decoration: TextDecoration.underline, color: Colors.blue, ), ), ), ), if (isCurrentUser) IconButton( icon: const Icon(Icons.delete, color: Colors.red, size: 20), onPressed: () { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Übung entfernen'), content: Text('Möchten Sie die Übung "${exercise['name']}" wirklich entfernen?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Abbrechen'), ), TextButton( onPressed: () { Navigator.pop(context); _removeExercise(event, exercise); }, child: const Text('Entfernen', style: TextStyle(color: Colors.red)), ), ], ), ); }, ), ], ), ); } return const SizedBox.shrink(); }), ], ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Schließen'), ), ], ), ); }, ), if (isCurrentUser) IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Training löschen'), content: const Text('Möchten Sie dieses Training wirklich löschen?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Abbrechen'), ), TextButton( onPressed: () { Navigator.pop(context); _deleteTraining(event); }, child: const Text('Löschen', style: TextStyle(color: Colors.red)), ), ], ), ); }, ), ], ), ), if (exercises.isNotEmpty) Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Divider(), const Text('Übungen:', style: TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 8), ...exercises.map((exercise) { if (exercise is Map) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: Row( children: [ const Icon(Icons.fitness_center, size: 16), const SizedBox(width: 8), Expanded( child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => TrainingDetailScreen( trainingId: exercise['id'], ), ), ); }, child: Text( '${exercise['name']} - ${exercise['duration']} Minuten', style: const TextStyle( decoration: TextDecoration.underline, color: Colors.blue, ), ), ), ), if (isCurrentUser) IconButton( icon: const Icon(Icons.delete, color: Colors.red, size: 20), onPressed: () { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Übung entfernen'), content: Text('Möchten Sie die Übung "${exercise['name']}" wirklich entfernen?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Abbrechen'), ), TextButton( onPressed: () { Navigator.pop(context); _removeExercise(event, exercise); }, child: const Text('Entfernen', style: TextStyle(color: Colors.red)), ), ], ), ); }, ), ], ), ); } return const SizedBox.shrink(); }), ], ), ), ], ), ); }, ); })() ), ], ), ), ); } Future _addOrEditTraining(DateTime date, String time, int duration, {bool isException = false, String? trainingId}) async { final dateString = DateTime(date.year, date.month, date.day).toIso8601String(); final userDoc = await FirebaseFirestore.instance.collection('User').doc(_currentUserId).get(); final data = userDoc.data() ?? {}; final trainings = Map.from(data['trainings'] ?? {}); final uuid = const Uuid(); final cancelledTrainings = List>.from(data['cancelledTrainings'] ?? []); final trainingTimes = data['trainingTimes'] as Map? ?? {}; print('Füge/Bearbeite Training:'); print('dateString: $dateString'); print('trainingId: $trainingId'); print('Vorher - trainings: $trainings'); print('Vorher - cancelledTrainings: $cancelledTrainings'); // Trainingsliste für das Datum holen oder neu anlegen final trainingsList = List>.from(trainings[dateString] ?? []); if (trainingId != null) { // Bearbeiten: Training mit ID ersetzen final idx = trainingsList.indexWhere((t) => t['id'] == trainingId); if (idx != -1) { trainingsList[idx] = { 'id': trainingId, 'time': time, 'duration': duration, 'exercises': trainingsList[idx]['exercises'] ?? [], }; } } else { // Hinzufügen: neues Training-Objekt anfügen trainingsList.add({ 'id': uuid.v4(), 'time': time, 'duration': duration, 'exercises': [], }); } trainings[dateString] = trainingsList; // Prüfe, ob an diesem Tag ein regelmäßiges Training stattfindet final weekdays = { 'Montag': 1, 'Dienstag': 2, 'Mittwoch': 3, 'Donnerstag': 4, 'Freitag': 5, 'Samstag': 6, 'Sonntag': 7, }; final weekday = date.weekday; final weekdayName = weekdays.entries.firstWhere((entry) => entry.value == weekday).key; // Wenn an diesem Tag ein regelmäßiges Training stattfindet, füge es zu cancelledTrainings hinzu if (trainingTimes.containsKey(weekdayName)) { if (!cancelledTrainings.any((cancelled) => cancelled is Map && cancelled['date'] == dateString )) { cancelledTrainings.add({ 'date': dateString, }); } } else { // Wenn kein regelmäßiges Training stattfindet, entferne den Eintrag aus cancelledTrainings cancelledTrainings.removeWhere((cancelled) => cancelled is Map && cancelled.containsKey('date') && cancelled['date'] == dateString ); } print('Nachher - trainings: $trainings'); print('Nachher - cancelledTrainings: $cancelledTrainings'); // Aktualisiere die Datenbank final updates = {}; // Aktualisiere trainings nur, wenn es nicht leer ist if (trainings.isNotEmpty) { updates['trainings'] = trainings; } else { updates['trainings'] = null; } // Aktualisiere cancelledTrainings nur, wenn es nicht leer ist if (cancelledTrainings.isNotEmpty) { updates['cancelledTrainings'] = cancelledTrainings; } else { updates['cancelledTrainings'] = null; } // Führe die Aktualisierung durch await FirebaseFirestore.instance.collection('User').doc(_currentUserId).update(updates); // Aktualisiere die UI sofort setState(() { final normalizedDate = DateTime(date.year, date.month, date.day); final event = { 'trainerName': data['name'] ?? 'Unbekannter Trainer', 'time': time, 'duration': duration, 'trainerId': _currentUserId, 'isCurrentUser': true, 'day': dateString, 'date': dateString, 'exercises': [], 'remainingTime': duration, 'club': data['club'] ?? 'Kein Verein', 'id': trainingId ?? uuid.v4(), }; if (_events.containsKey(normalizedDate)) { _events[normalizedDate]!.add(event); } else { _events[normalizedDate] = [event]; } }); } } class _TrainingEditDialog extends StatefulWidget { final DateTime date; final String? initialTime; final int? initialDuration; final String? trainingId; const _TrainingEditDialog({ required this.date, this.initialTime, this.initialDuration, this.trainingId, }); @override State<_TrainingEditDialog> createState() => _TrainingEditDialogState(); } class _TrainingEditDialogState extends State<_TrainingEditDialog> { TimeOfDay? _selectedTime; int _duration = 60; @override void initState() { super.initState(); if (widget.initialTime != null) { final parts = widget.initialTime!.split(':'); if (parts.length == 2) { _selectedTime = TimeOfDay(hour: int.parse(parts[0]), minute: int.parse(parts[1])); } } if (widget.initialDuration != null) { _duration = widget.initialDuration!; } } @override Widget build(BuildContext context) { return AlertDialog( title: Text('Training bearbeiten (${widget.date.day}.${widget.date.month}.${widget.date.year})'), content: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.access_time), title: const Text('Uhrzeit wählen'), subtitle: Text(_selectedTime != null ? _selectedTime!.format(context) : 'Keine Uhrzeit gewählt'), onTap: () async { final picked = await showTimePicker( context: context, initialTime: _selectedTime ?? TimeOfDay.now(), ); if (picked != null) { setState(() => _selectedTime = picked); } }, ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.remove), onPressed: () { if (_duration > 15) setState(() => _duration -= 15); }, ), Text('$_duration Minuten', style: const TextStyle(fontSize: 18)), IconButton( icon: const Icon(Icons.add), onPressed: _duration < 300 ? () => setState(() => _duration += 15) : null, ), ], ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Abbrechen'), ), ElevatedButton( onPressed: _selectedTime != null ? () { final timeString = _selectedTime!.hour.toString().padLeft(2, '0') + ':' + _selectedTime!.minute.toString().padLeft(2, '0'); Navigator.pop(context, { 'time': timeString, 'duration': _duration, 'trainingId': widget.trainingId, }); } : null, child: const Text('Speichern'), ), ], ); } }