diff --git a/trainerbox/lib/screens/calendar_tab.dart b/trainerbox/lib/screens/calendar_tab.dart index fdb9110..c04c911 100644 --- a/trainerbox/lib/screens/calendar_tab.dart +++ b/trainerbox/lib/screens/calendar_tab.dart @@ -127,12 +127,6 @@ class _CalendarTabState extends State { 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); @@ -191,8 +185,6 @@ class _CalendarTabState extends State { .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(':'); @@ -252,7 +244,6 @@ class _CalendarTabState extends State { _isLoading = false; }); } catch (e) { - print('Error loading events: $e'); setState(() => _isLoading = false); } } @@ -273,39 +264,84 @@ class _CalendarTabState extends State { // 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']}'); + // NEU: Prüfe, ob die Übung in den letzten 4 Wochen schon einmal durchgeführt wurde + final userDocForCheck = await FirebaseFirestore.instance + .collection('User') + .doc(_currentUserId) + .get(); + if (userDocForCheck.exists) { + final userData = userDocForCheck.data() as Map; + final trainings = Map.from(userData['trainings'] ?? {}); + final trainingDate = DateTime.tryParse(event['date'] as String) ?? DateTime.now(); + final fourWeeksAgo = trainingDate.subtract(const Duration(days: 28)); + bool foundInLast4Weeks = false; + trainings.forEach((dateString, trainingsList) { + final date = DateTime.tryParse(dateString); + // Prüfe: im Zeitraum [fourWeeksAgo, trainingDate) (also vor dem aktuellen Training) + if (date != null && date.isAfter(fourWeeksAgo) && date.isBefore(trainingDate)) { + final list = List>.from(trainingsList); + for (final training in list) { + final exercises = List>.from(training['exercises'] ?? []); + for (final exercise in exercises) { + if (exercise['id'] == result['id']) { + foundInLast4Weeks = true; + } + } + } + } + }); + if (foundInLast4Weeks) { + shouldAddExercise = await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: const Text('Übung kürzlich verwendet'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Die Übung "${result['title']}" wurde in den 4 Wochen vor diesem Training bereits durchgeführt.'), + 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; + if (!shouldAddExercise) { + return; + } + } + } 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; @@ -316,13 +352,10 @@ class _CalendarTabState extends State { } 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( + showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( @@ -348,21 +381,12 @@ class _CalendarTabState extends State { ], ), ) ?? 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; } @@ -379,38 +403,53 @@ class _CalendarTabState extends State { 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'); + final newExerciseData = { + 'id': result['id'], + 'name': result['title'], + 'description': result['description'], + 'duration': result['duration'], + }; - // 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; + final trainingsForDay = List>.from(trainings[dateString] ?? []); + final trainingIndex = trainingsForDay.indexWhere((t) => t['id'] == trainingId); + + String finalTrainingId = trainingId; + + if (trainingIndex != -1) { + // Fall 1: Training ist bereits spezifisch, füge die Übung hinzu + final exercises = List>.from(trainingsForDay[trainingIndex]['exercises'] ?? []); + exercises.add(newExerciseData); + trainingsForDay[trainingIndex]['exercises'] = exercises; + } else { + // Fall 2: Training ist wiederkehrend, erstelle eine neue spezifische Instanz + final newId = const Uuid().v4(); + finalTrainingId = newId; + trainingsForDay.add({ + 'id': newId, + 'time': event['time'], + 'duration': event['duration'], + 'exercises': [newExerciseData], + }); + } + + trainings[dateString] = trainingsForDay; + + final updates = {'trainings': trainings}; + + // Wenn das Original-Event wiederkehrend war, unterdrücke es für diesen Tag + if (trainingId.startsWith('weekly_')) { + final cancelledTrainings = List>.from(data['cancelledTrainings'] ?? []); + if (!cancelledTrainings.any((c) => c['date'] == dateString)) { + cancelledTrainings.add({'date': dateString}); + updates['cancelledTrainings'] = cancelledTrainings; } } - - print('Nachher - trainings: $trainings'); - + // Aktualisiere die Datenbank await FirebaseFirestore.instance .collection('User') .doc(_currentUserId) - .update({ - 'trainings': trainings, - }); + .update(updates); // Aktualisiere die UI sofort setState(() { @@ -418,17 +457,16 @@ class _CalendarTabState extends State { 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)); + final oldEvent = _events[normalizedDate]![eventIndex]; + final exercises = List>.from(oldEvent['exercises'] ?? []); + exercises.add(newExerciseData); + + oldEvent['exercises'] = exercises; + oldEvent['remainingTime'] = (oldEvent['duration'] as int) - exercises.fold(0, (sum, exercise) => sum + (exercise['duration'] as int)); + + if (trainingId.startsWith('weekly_')) { + oldEvent['id'] = finalTrainingId; + } } } }); @@ -439,7 +477,6 @@ class _CalendarTabState extends State { ); } } catch (e) { - print('Error adding exercise: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Hinzufügen der Übung')), @@ -460,12 +497,6 @@ class _CalendarTabState extends State { 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 @@ -517,9 +548,6 @@ class _CalendarTabState extends State { } } - print('Nachher - trainings: $trainings'); - print('Nachher - cancelledTrainings: $cancelledTrainings'); - // Aktualisiere die Datenbank final updates = {}; @@ -557,7 +585,6 @@ class _CalendarTabState extends State { ); } } catch (e) { - print('Error deleting training: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Löschen des Trainings')), @@ -582,11 +609,6 @@ class _CalendarTabState extends State { 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]); @@ -600,8 +622,6 @@ class _CalendarTabState extends State { } } - print('Nachher - trainings: $trainings'); - // Aktualisiere die Datenbank await FirebaseFirestore.instance .collection('User') @@ -632,7 +652,6 @@ class _CalendarTabState extends State { ); } } catch (e) { - print('Error removing exercise: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Entfernen der Übung')), @@ -1068,12 +1087,6 @@ class _CalendarTabState extends State { 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] ?? []); @@ -1133,9 +1146,6 @@ class _CalendarTabState extends State { ); } - print('Nachher - trainings: $trainings'); - print('Nachher - cancelledTrainings: $cancelledTrainings'); - // Aktualisiere die Datenbank final updates = {}; diff --git a/trainerbox/lib/screens/favorites_tab.dart b/trainerbox/lib/screens/favorites_tab.dart index d3f86d5..1a07078 100644 --- a/trainerbox/lib/screens/favorites_tab.dart +++ b/trainerbox/lib/screens/favorites_tab.dart @@ -68,137 +68,121 @@ class _FavoritesTabState extends State { ), Expanded( child: StreamBuilder( - stream: FirebaseFirestore.instance.collection('User').doc(user.uid).snapshots(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } - if (!snapshot.hasData || !snapshot.data!.exists) { - return const Center(child: Text('Keine Favoriten gefunden')); - } - final data = snapshot.data!.data() as Map; - final allFavorites = List.from(data['favorites'] ?? []); + stream: FirebaseFirestore.instance.collection('User').doc(user.uid).snapshots(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + if (!snapshot.hasData || !snapshot.data!.exists) { + return const Center(child: Text('Keine Favoriten gefunden')); + } + final data = snapshot.data!.data() as Map; + final favorites = List.from(data['favorites'] ?? []); - if (allFavorites.isEmpty) { - return const Center(child: Text('Keine Favoriten gefunden')); - } + if (favorites.isEmpty) { + return const Center(child: Text('Keine Favoriten gefunden')); + } - // Filtere Favoriten nach Kategorie - Future>> getFilteredFavorites() async { - List> filtered = []; - for (final favId in allFavorites) { - final doc = await FirebaseFirestore.instance.collection('Training').doc(favId).get(); - if (!doc.exists) continue; - final trainingData = doc.data() as Map; - if (_selectedCategory == null || trainingData['category'] == _selectedCategory) { - filtered.add({'id': favId, 'data': trainingData}); - } + return GridView.builder( + padding: const EdgeInsets.all(8), + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 300, + childAspectRatio: 0.75, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + ), + itemCount: favorites.length, + itemBuilder: (context, index) { + return FutureBuilder( + future: FirebaseFirestore.instance.collection('Training').doc(favorites[index]).get(), + builder: (context, trainingSnapshot) { + if (!trainingSnapshot.hasData || !trainingSnapshot.data!.exists) { + return const SizedBox.shrink(); } - return filtered; - } - - return FutureBuilder>>( - future: getFilteredFavorites(), - builder: (context, favSnapshot) { - if (!favSnapshot.hasData) { - return const Center(child: CircularProgressIndicator()); - } - final filteredFavorites = favSnapshot.data!; - if (filteredFavorites.isEmpty) { - return const Center(child: Text('Keine Favoriten gefunden')); - } - return GridView.builder( - padding: const EdgeInsets.all(8), - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 300, - childAspectRatio: 0.75, - crossAxisSpacing: 10, - mainAxisSpacing: 10, - ), - itemCount: filteredFavorites.length, - itemBuilder: (context, index) { - final fav = filteredFavorites[index]; - final trainingData = fav['data'] as Map; - final favId = fav['id'] as String; - return Card( - child: Stack( + final trainingData = trainingSnapshot.data!.data() as Map; + // Filter nach Kategorie, falls gesetzt + if (_selectedCategory != null && trainingData['category'] != _selectedCategory) { + return const SizedBox.shrink(); + } + return Card( + child: Stack( + children: [ + InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => TrainingDetailScreen(trainingId: favorites[index]), + ), + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - InkWell( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => TrainingDetailScreen(trainingId: favId), - ), - ); - }, + Expanded( + child: Container( + color: Colors.grey[200], + child: const Center( + child: Icon(Icons.fitness_center, size: 50), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: Container( - color: Colors.grey[200], - child: const Center( - child: Icon(Icons.fitness_center, size: 50), - ), + Text( + trainingData['title'] ?? 'Unbekannt', + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, ), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - trainingData['title'] ?? 'Unbekannt', - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 4), - Row( - children: [ - const Icon(Icons.star, color: Colors.amber, size: 16), - const SizedBox(width: 4), - Text( - (trainingData['rating overall'] ?? 0.0).toStringAsFixed(1), - style: const TextStyle(fontSize: 12), - ), - ], - ), - const SizedBox(height: 4), - Text( + const SizedBox(height: 4), + Row( + children: [ + const Icon(Icons.star, color: Colors.amber, size: 16), + const SizedBox(width: 4), + Text( + (trainingData['rating overall'] ?? 0.0).toStringAsFixed(1), + style: const TextStyle(fontSize: 12), + ), + ], + ), + const SizedBox(height: 4), + Text( 'Dauer: \t${trainingData['duration'] ?? '-'} Minuten', - style: const TextStyle(fontSize: 12, color: Colors.grey), - ), - ], - ), + style: const TextStyle(fontSize: 12, color: Colors.grey), ), ], ), ), - Positioned( - top: 4, - right: 4, - child: IconButton( - icon: const Icon(Icons.favorite, color: Colors.red), - onPressed: () async { - await FirebaseFirestore.instance.collection('User').doc(user.uid).update({ - 'favorites': FieldValue.arrayRemove([favId]), - }); - }, - ), - ), ], ), - ); - }, - ); - }, - ); - }, + ), + Positioned( + top: 4, + right: 4, + child: IconButton( + icon: const Icon(Icons.favorite, color: Colors.red), + onPressed: () async { + await FirebaseFirestore.instance.collection('User').doc(user.uid).update({ + 'favorites': FieldValue.arrayRemove([favorites[index]]), + }); + }, + ), + ), + ], + ), + ); + }, + ); + }, + ); + }, ), ), ], diff --git a/trainerbox/lib/screens/login_screen.dart b/trainerbox/lib/screens/login_screen.dart index 9adf5cb..228b137 100644 --- a/trainerbox/lib/screens/login_screen.dart +++ b/trainerbox/lib/screens/login_screen.dart @@ -23,49 +23,38 @@ class _LoginScreenState extends State { Future _submit() async { setState(() { _loading = true; _error = null; }); try { - print('Login-Versuch gestartet...'); // Debug if (_isLogin) { - print('Login-Modus: E-Mail: ${_emailController.text.trim()}'); // Debug // Login try { UserCredential cred = await FirebaseAuth.instance.signInWithEmailAndPassword( email: _emailController.text.trim(), password: _passwordController.text.trim(), ); - print('Firebase Auth erfolgreich: ${cred.user?.uid}'); // Debug // Firestore-Check final uid = cred.user!.uid; - print('Firestore-Check für UID: $uid'); // Debug final userDoc = await FirebaseFirestore.instance.collection('User').doc(uid).get(); - print('Firestore-Dokument existiert: ${userDoc.exists}'); // Debug if (userDoc.exists) { - print('Login erfolgreich, Benutzer gefunden'); // Debug widget.onLoginSuccess(); } else { - print('Kein Benutzerprofil in Firestore gefunden'); // Debug setState(() { _error = 'Kein Benutzerprofil in der Datenbank gefunden!'; }); await FirebaseAuth.instance.signOut(); } } catch (e) { - print('Fehler beim Firebase Auth: $e'); // Debug rethrow; } } else { - print('Registrierungs-Modus: E-Mail: ${_emailController.text.trim()}'); // Debug // Registrierung try { UserCredential cred = await FirebaseAuth.instance.createUserWithEmailAndPassword( email: _emailController.text.trim(), password: _passwordController.text.trim(), ); - print('Firebase Auth Registrierung erfolgreich: ${cred.user?.uid}'); // Debug // User-Datensatz in Firestore anlegen final uid = cred.user!.uid; - print('Erstelle Firestore-Dokument für UID: $uid'); // Debug await FirebaseFirestore.instance.collection('User').doc(uid).set({ 'email': _emailController.text.trim(), @@ -73,16 +62,13 @@ class _LoginScreenState extends State { 'role': _isTrainer ? 'trainer' : 'player', 'createdAt': FieldValue.serverTimestamp(), }); - print('Firestore-Dokument erstellt'); // Debug widget.onLoginSuccess(); } catch (e) { - print('Fehler bei der Registrierung: $e'); // Debug rethrow; } } } on FirebaseAuthException catch (e) { - print('FirebaseAuthException: ${e.code} - ${e.message}'); // Debug String errorMessage; switch (e.code) { case 'user-not-found': @@ -111,7 +97,6 @@ class _LoginScreenState extends State { } setState(() { _error = errorMessage; }); } catch (e) { - print('Unerwarteter Fehler: $e'); // Debug setState(() { _error = 'Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.'; }); } finally { setState(() { _loading = false; });