diff --git a/trainerbox/lib/screens/calendar_tab.dart b/trainerbox/lib/screens/calendar_tab.dart index b47103b..6ea45bb 100644 --- a/trainerbox/lib/screens/calendar_tab.dart +++ b/trainerbox/lib/screens/calendar_tab.dart @@ -499,103 +499,36 @@ class _CalendarTabState extends State { Future _deleteTraining(Map event) async { if (_userRole != 'trainer' || !event['isCurrentUser']) return; - - print('=== DEBUG: Starting _deleteTraining ==='); - print('DEBUG: Event to delete: $event'); - print('DEBUG: Training ID: ${event['id']}'); - print('DEBUG: Date: ${event['date']}'); - print('DEBUG: Is weekly training: ${event['id']?.toString().startsWith('weekly_')}'); - try { final userDoc = await FirebaseFirestore.instance.collection('User').doc(_currentUserId).get(); - if (!userDoc.exists) { - print('DEBUG: User document does not exist'); - return; - } - + 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('DEBUG: Current trainings in DB: $trainings'); - print('DEBUG: Current cancelledTrainings in DB: $cancelledTrainings'); - // Wenn es sich um ein regelmäßiges Training handelt (ID beginnt mit 'weekly_') if (trainingId != null && trainingId.startsWith('weekly_')) { - print('DEBUG: Processing weekly training deletion'); - - // Für regelmäßige Trainings: Lösche alle Trainings dieser Serie - 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; - - print('DEBUG: Weekday to delete: $weekday'); - - // Lösche alle Trainings an diesem Wochentag - trainings.forEach((dateStr, trainingsList) { - final trainingDate = DateTime.tryParse(dateStr); - if (trainingDate != null && trainingDate.weekday == weekday) { - print('DEBUG: Checking date $dateStr (weekday: ${trainingDate.weekday})'); - final list = List>.from(trainingsList); - print('DEBUG: Trainings on this date before deletion: $list'); - - final beforeCount = list.length; - list.removeWhere((t) => t['id'] == trainingId); - final afterCount = list.length; - - print('DEBUG: Removed ${beforeCount - afterCount} trainings with ID $trainingId'); - - if (list.isEmpty) { - trainings.remove(dateStr); - print('DEBUG: Removed empty date $dateStr from trainings'); - } else { - trainings[dateStr] = list; - print('DEBUG: Updated trainings for date $dateStr: $list'); - } - } - }); - - // Entferne alle cancelledTrainings für diesen Wochentag - final beforeCancelledCount = cancelledTrainings.length; - cancelledTrainings.removeWhere((cancelled) => - cancelled.containsKey('date') && - DateTime.parse(cancelled['date'] as String).weekday == weekday - ); - final afterCancelledCount = cancelledTrainings.length; - - print('DEBUG: Removed ${beforeCancelledCount - afterCancelledCount} cancelled trainings for weekday $weekday'); - + // 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 { - print('DEBUG: Processing specific training deletion'); - - // Für spezifische Trainings: Entferne nur das Training an diesem Tag + // Für spezifische Trainings: Entferne das Training aus der Liste if (trainings.containsKey(dateString)) { final trainingsList = List>.from(trainings[dateString]); - print('DEBUG: Trainings on date $dateString before deletion: $trainingsList'); - - final beforeCount = trainingsList.length; trainingsList.removeWhere((t) => t['id'] == trainingId); - final afterCount = trainingsList.length; - - print('DEBUG: Removed ${beforeCount - afterCount} trainings with ID $trainingId'); if (trainingsList.isEmpty) { trainings.remove(dateString); - print('DEBUG: Removed empty date $dateString from trainings'); } else { trainings[dateString] = trainingsList; - print('DEBUG: Updated trainings for date $dateString: $trainingsList'); } } @@ -616,107 +549,45 @@ class _CalendarTabState extends State { final weekdayName = weekdays.entries.firstWhere((entry) => entry.value == weekday).key; final trainingTimes = data['trainingTimes'] as Map? ?? {}; - print('DEBUG: Weekday name: $weekdayName'); - print('DEBUG: Training times: $trainingTimes'); - print('DEBUG: Has regular training on this weekday: ${trainingTimes.containsKey(weekdayName)}'); - // Wenn an diesem Tag kein regelmäßiges Training stattfindet, entferne den Eintrag aus cancelledTrainings if (!trainingTimes.containsKey(weekdayName)) { - final beforeCancelledCount = cancelledTrainings.length; cancelledTrainings.removeWhere((cancelled) => + //cancelled is Map && cancelled.containsKey('date') && cancelled['date'] == dateString ); - final afterCancelledCount = cancelledTrainings.length; - - print('DEBUG: Removed ${beforeCancelledCount - afterCancelledCount} cancelled trainings for date $dateString'); } } - print('DEBUG: Final trainings after deletion: $trainings'); - print('DEBUG: Final cancelledTrainings after deletion: $cancelledTrainings'); - // Aktualisiere die Datenbank final updates = {}; // Aktualisiere trainings nur, wenn es nicht leer ist if (trainings.isNotEmpty) { updates['trainings'] = trainings; - print('DEBUG: Will update trainings in DB'); } else { updates['trainings'] = null; - print('DEBUG: Will set trainings to null in DB'); } // Aktualisiere cancelledTrainings nur, wenn es nicht leer ist if (cancelledTrainings.isNotEmpty) { updates['cancelledTrainings'] = cancelledTrainings; - print('DEBUG: Will update cancelledTrainings in DB'); } else { updates['cancelledTrainings'] = null; - print('DEBUG: Will set cancelledTrainings to null in DB'); } - print('DEBUG: Final updates to DB: $updates'); - // Führe die Aktualisierung durch await FirebaseFirestore.instance.collection('User').doc(_currentUserId).update(updates); - print('DEBUG: Database update completed successfully'); // Aktualisiere die UI sofort setState(() { - print('DEBUG: Updating UI...'); - - // Für regelmäßige Trainings: Entferne alle Events an diesem Wochentag - if (trainingId != null && trainingId.startsWith('weekly_')) { - final date = DateTime.parse(dateString); - final weekday = date.weekday; - - print('DEBUG: Removing events from UI for weekday $weekday'); - - _events.forEach((eventDate, eventList) { - if (eventDate.weekday == weekday) { - print('DEBUG: Checking event date $eventDate (weekday: ${eventDate.weekday})'); - print('DEBUG: Events before removal: $eventList'); - - final beforeCount = eventList.length; - eventList.removeWhere((e) => e['id'] == trainingId); - final afterCount = eventList.length; - - print('DEBUG: Removed ${beforeCount - afterCount} events with ID $trainingId'); - print('DEBUG: Events after removal: $eventList'); - } - }); - - // Entferne leere Event-Listen - final beforeEmptyCount = _events.length; - _events.removeWhere((date, events) => events.isEmpty); - final afterEmptyCount = _events.length; - - print('DEBUG: Removed ${beforeEmptyCount - afterEmptyCount} empty event lists'); - - } else { - // Für spezifische Trainings: Entferne nur das Event an diesem Tag - final normalizedDate = DateTime.parse(dateString); - if (_events.containsKey(normalizedDate)) { - print('DEBUG: Removing specific event from UI for date $normalizedDate'); - print('DEBUG: Events before removal: ${_events[normalizedDate]}'); - - final beforeCount = _events[normalizedDate]!.length; - _events[normalizedDate]!.removeWhere((e) => e['id'] == trainingId); - final afterCount = _events[normalizedDate]!.length; - - print('DEBUG: Removed ${beforeCount - afterCount} events with ID $trainingId'); - print('DEBUG: Events after removal: ${_events[normalizedDate]}'); - - if (_events[normalizedDate]!.isEmpty) { - _events.remove(normalizedDate); - print('DEBUG: Removed empty date $normalizedDate from events'); - } + final normalizedDate = DateTime.parse(dateString); + if (_events.containsKey(normalizedDate)) { + _events[normalizedDate]!.removeWhere((e) => e['id'] == trainingId); + if (_events[normalizedDate]!.isEmpty) { + _events.remove(normalizedDate); } } - - print('DEBUG: UI update completed'); }); if (mounted) { @@ -724,11 +595,7 @@ class _CalendarTabState extends State { const SnackBar(content: Text('Training wurde gelöscht')), ); } - - print('=== DEBUG: _deleteTraining completed successfully ==='); - } catch (e) { - print('DEBUG: Error in _deleteTraining: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fehler beim Löschen des Trainings')), diff --git a/trainerbox/lib/screens/favorites_tab.dart b/trainerbox/lib/screens/favorites_tab.dart index 271b938..8be1b9d 100644 --- a/trainerbox/lib/screens/favorites_tab.dart +++ b/trainerbox/lib/screens/favorites_tab.dart @@ -48,6 +48,38 @@ class _FavoritesTabState extends State { _selectedCategory = widget.categoryFilter; } + /// Loads the user's favorite exercises from Firestore. + Future> _loadFavorites() async { + final user = FirebaseAuth.instance.currentUser; + if (user == null) return {}; + + final doc = await FirebaseFirestore.instance.collection('User').doc(user.uid).get(); + final data = doc.data(); + if (data != null && data['favorites'] != null) { + return Set.from(data['favorites']); + } + return {}; + } + + /// Toggles the favorite status of an exercise for the current user. + Future _toggleFavorite(String trainingId, bool isFavorite) async { + final user = FirebaseAuth.instance.currentUser; + if (user == null) return; + + if (isFavorite) { + await FirebaseFirestore.instance.collection('User').doc(user.uid).update({ + 'favorites': FieldValue.arrayRemove([trainingId]), + }); + } else { + await FirebaseFirestore.instance.collection('User').doc(user.uid).update({ + 'favorites': FieldValue.arrayUnion([trainingId]), + }); + } + + // Refresh the favorites list + setState(() {}); + } + /// Opens the filter modal for advanced filtering (duration, level, rating). void _openFilterModal() async { _tempMinDuration = _minDuration; @@ -196,7 +228,7 @@ class _FavoritesTabState extends State { children: [ const Text( 'Kategorien', - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), // Filter button opens the modal with advanced filters ElevatedButton.icon( @@ -210,50 +242,64 @@ class _FavoritesTabState extends State { ), ], ), - const SizedBox(height: 16), - // Category filter chips - Wrap( - alignment: WrapAlignment.center, - spacing: 8, - runSpacing: 8, - children: [ - FilterChip( - label: const Text('Alle'), - selected: _selectedCategory == null, - onSelected: (selected) { - setState(() => _selectedCategory = null); - }, - ), - ..._categories.map((cat) => FilterChip( - label: Text(cat), - selected: _selectedCategory == cat, - onSelected: (selected) { - setState(() => _selectedCategory = selected ? cat : null); - }, - )), - ], + const SizedBox(height: 12), + // Horizontal scrollable category chips + SizedBox( + height: 40, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + // "Alle" chip + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: FilterChip( + label: const Text('Alle'), + selected: _selectedCategory == null, + onSelected: (selected) { + setState(() { + _selectedCategory = null; + }); + }, + ), + ), + // Category chips + ..._categories.map((category) => Padding( + padding: const EdgeInsets.only(right: 8.0), + child: FilterChip( + label: Text(category), + selected: _selectedCategory == category, + onSelected: (selected) { + setState(() { + _selectedCategory = selected ? category : null; + }); + }, + ), + )).toList(), + ], + ), ), ], ), ), + // Favorites list Expanded( - child: StreamBuilder( - stream: FirebaseFirestore.instance.collection('User').doc(user.uid).snapshots(), + child: FutureBuilder>( + future: _loadFavorites(), 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')); + + if (snapshot.hasError) { + return Center(child: Text('Fehler: ${snapshot.error}')); } - final data = snapshot.data!.data() as Map; - final allFavorites = List.from(data['favorites'] ?? []); + + final allFavorites = snapshot.data ?? {}; if (allFavorites.isEmpty) { return const Center(child: Text('Keine Favoriten gefunden')); } - // Load all favorite exercise documents at once return FutureBuilder>( future: Future.wait(allFavorites.map((id) => FirebaseFirestore.instance.collection('Training').doc(id).get() @@ -296,7 +342,7 @@ class _FavoritesTabState extends State { itemBuilder: (context, index) { final doc = filteredFavorites[index]; final trainingData = doc.data() as Map; - + return Card( child: Stack( children: [ diff --git a/trainerbox/lib/screens/search_tab.dart b/trainerbox/lib/screens/search_tab.dart index 88f16b1..a404ed7 100644 --- a/trainerbox/lib/screens/search_tab.dart +++ b/trainerbox/lib/screens/search_tab.dart @@ -249,14 +249,9 @@ class _SearchTabState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Übungen'), + title: const Text('Suche'), actions: [ - if (widget.selectMode) - IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.pop(context), - ), - if (_isTrainer && !widget.selectMode) + if (_isTrainer && _trainerChecked) IconButton( icon: const Icon(Icons.add), onPressed: () => _showCreateTrainingDialog(context), @@ -265,34 +260,21 @@ class _SearchTabState extends State { ), body: Column( children: [ - // Search input field. + // Search bar Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(16.0), child: TextField( controller: _searchController, - decoration: InputDecoration( - hintText: 'Suche nach Übungen...', - prefixIcon: const Icon(Icons.search), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - ), + decoration: const InputDecoration( + labelText: 'Suche nach Übungen...', + prefixIcon: Icon(Icons.search), + border: OutlineInputBorder(), ), ), ), - if (widget.selectMode && widget.remainingTime != null) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - 'Verbleibende Zeit: ${widget.remainingTime} Minuten', - style: const TextStyle( - color: Colors.grey, - fontWeight: FontWeight.bold, - ), - ), - ), - // Category filter chips and filter button only (no direct filter UI here) + // Category filter chips Padding( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -301,8 +283,9 @@ class _SearchTabState extends State { children: [ const Text( 'Kategorien', - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), + // Filter button opens the modal with advanced filters ElevatedButton.icon( onPressed: _openFilterModal, icon: const Icon(Icons.filter_list), @@ -314,26 +297,47 @@ class _SearchTabState extends State { ), ], ), - const SizedBox(height: 16), - Wrap( - spacing: 8, - runSpacing: 8, - children: _categories.map((category) { - return FilterChip( - label: Text(category), - selected: _selectedCategory == category, - onSelected: (bool selected) { - setState(() { - _selectedCategory = selected ? category : null; - }); - }, - ); - }).toList(), + const SizedBox(height: 12), + // Horizontal scrollable category chips + SizedBox( + height: 40, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + // "Alle" chip + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: FilterChip( + label: const Text('Alle'), + selected: _selectedCategory == null, + onSelected: (selected) { + setState(() { + _selectedCategory = null; + }); + }, + ), + ), + // Category chips + ..._categories.map((category) => Padding( + padding: const EdgeInsets.only(right: 8.0), + child: FilterChip( + label: Text(category), + selected: _selectedCategory == category, + onSelected: (selected) { + setState(() { + _selectedCategory = selected ? category : null; + }); + }, + ), + )).toList(), + ], + ), ), ], ), ), - // Exercise grid view. + const SizedBox(height: 16), + // Exercise list Expanded( child: FutureBuilder( future: FirebaseFirestore.instance.collection('Training').get(),