Warnung bei Wiederholenden Übungen

main
joschy2002 2025-06-20 22:23:16 +02:00
parent fb3b9027c5
commit 0923b85a20
3 changed files with 209 additions and 230 deletions

View File

@ -127,12 +127,6 @@ class _CalendarTabState extends State<CalendarTab> {
final trainingTimes = trainerData['trainingTimes'] as Map<String, dynamic>? ?? {};
final trainingDurations = trainerData['trainingDurations'] as Map<String, dynamic>? ?? {};
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<CalendarTab> {
.map((cancelled) => DateTime.parse((cancelled as Map<String, dynamic>)['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<CalendarTab> {
_isLoading = false;
});
} catch (e) {
print('Error loading events: $e');
setState(() => _isLoading = false);
}
}
@ -273,39 +264,84 @@ class _CalendarTabState extends State<CalendarTab> {
// Wenn eine Übung ausgewählt wurde
if (result != null && result is Map<String, dynamic>) {
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<String, dynamic>;
final trainings = Map<String, dynamic>.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<Map<String, dynamic>>.from(trainingsList);
for (final training in list) {
final exercises = List<Map<String, dynamic>>.from(training['exercises'] ?? []);
for (final exercise in exercises) {
if (exercise['id'] == result['id']) {
foundInLast4Weeks = true;
}
}
}
}
});
if (foundInLast4Weeks) {
shouldAddExercise = await showDialog<bool>(
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<String, dynamic>;
print('Übungsdaten: $exerciseData');
// Versuche die Bewertungen aus verschiedenen möglichen Feldern zu lesen
List<Map<String, dynamic>> ratings = [];
if (exerciseData.containsKey('ratings')) {
ratings = List<Map<String, dynamic>>.from(exerciseData['ratings'] ?? []);
print('Bewertungen aus Feld "ratings" gefunden: $ratings');
} else if (exerciseData.containsKey('rating')) {
ratings = List<Map<String, dynamic>>.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<CalendarTab> {
}
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<bool>(
showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
@ -348,21 +381,12 @@ class _CalendarTabState extends State<CalendarTab> {
],
),
) ?? 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<CalendarTab> {
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<Map<String, dynamic>>.from(trainings[dateString]);
final trainingIndex = trainingsList.indexWhere((t) => t['id'] == trainingId);
if (trainingIndex != -1) {
final exercises = List<Map<String, dynamic>>.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<Map<String, dynamic>>.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<Map<String, dynamic>>.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 = <String, dynamic>{'trainings': trainings};
// Wenn das Original-Event wiederkehrend war, unterdrücke es für diesen Tag
if (trainingId.startsWith('weekly_')) {
final cancelledTrainings = List<Map<String, dynamic>>.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<CalendarTab> {
if (_events.containsKey(normalizedDate)) {
final eventIndex = _events[normalizedDate]!.indexWhere((e) => e['id'] == trainingId);
if (eventIndex != -1) {
final exercises = List<Map<String, dynamic>>.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<int>(0, (sum, exercise) => sum + (exercise['duration'] as int));
final oldEvent = _events[normalizedDate]![eventIndex];
final exercises = List<Map<String, dynamic>>.from(oldEvent['exercises'] ?? []);
exercises.add(newExerciseData);
oldEvent['exercises'] = exercises;
oldEvent['remainingTime'] = (oldEvent['duration'] as int) - exercises.fold<int>(0, (sum, exercise) => sum + (exercise['duration'] as int));
if (trainingId.startsWith('weekly_')) {
oldEvent['id'] = finalTrainingId;
}
}
}
});
@ -439,7 +477,6 @@ class _CalendarTabState extends State<CalendarTab> {
);
}
} 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<CalendarTab> {
final trainingId = event['id'] as String?;
final cancelledTrainings = List<Map<String, dynamic>>.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<CalendarTab> {
}
}
print('Nachher - trainings: $trainings');
print('Nachher - cancelledTrainings: $cancelledTrainings');
// Aktualisiere die Datenbank
final updates = <String, dynamic>{};
@ -557,7 +585,6 @@ class _CalendarTabState extends State<CalendarTab> {
);
}
} 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<CalendarTab> {
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<Map<String, dynamic>>.from(trainings[dateString]);
@ -600,8 +622,6 @@ class _CalendarTabState extends State<CalendarTab> {
}
}
print('Nachher - trainings: $trainings');
// Aktualisiere die Datenbank
await FirebaseFirestore.instance
.collection('User')
@ -632,7 +652,6 @@ class _CalendarTabState extends State<CalendarTab> {
);
}
} 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<CalendarTab> {
final cancelledTrainings = List<Map<String, dynamic>>.from(data['cancelledTrainings'] ?? []);
final trainingTimes = data['trainingTimes'] as Map<String, dynamic>? ?? {};
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<Map<String, dynamic>>.from(trainings[dateString] ?? []);
@ -1133,9 +1146,6 @@ class _CalendarTabState extends State<CalendarTab> {
);
}
print('Nachher - trainings: $trainings');
print('Nachher - cancelledTrainings: $cancelledTrainings');
// Aktualisiere die Datenbank
final updates = <String, dynamic>{};

View File

@ -68,137 +68,121 @@ class _FavoritesTabState extends State<FavoritesTab> {
),
Expanded(
child: StreamBuilder<DocumentSnapshot>(
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<String, dynamic>;
final allFavorites = List<String>.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<String, dynamic>;
final favorites = List<String>.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<List<Map<String, dynamic>>> getFilteredFavorites() async {
List<Map<String, dynamic>> 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<String, dynamic>;
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<DocumentSnapshot>(
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<List<Map<String, dynamic>>>(
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<String, dynamic>;
final favId = fav['id'] as String;
return Card(
child: Stack(
final trainingData = trainingSnapshot.data!.data() as Map<String, dynamic>;
// 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]]),
});
},
),
),
],
),
);
},
);
},
);
},
),
),
],

View File

@ -23,49 +23,38 @@ class _LoginScreenState extends State<LoginScreen> {
Future<void> _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<LoginScreen> {
'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<LoginScreen> {
}
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; });