// favorites_tab.dart // This file contains the FavoritesTab widget, which displays the user's favorite exercises and allows filtering by category. import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'training_detail_screen.dart'; import '../models/categories.dart'; /// The FavoritesTab displays the user's favorite exercises and allows filtering by category. class FavoritesTab extends StatefulWidget { final String? categoryFilter; const FavoritesTab({super.key, this.categoryFilter}); @override State createState() => _FavoritesTabState(); } /// State for the FavoritesTab, manages category selection and favorite loading. class _FavoritesTabState extends State { List get _categories => kTrainingCategories.map((c) => c.name).toList(); String? _selectedCategory; // Level categories for filter final List _levelCategories = [ 'Bambini', 'F-Jugend', 'E-Jugend', 'D-Jugend', 'C-Jugend', 'B-Jugend', 'A-Jugend', 'Herren', ]; String? _selectedLevel; // Filter state variables for duration and rating double _minDuration = 0; // Minimum duration filter double _maxDuration = 120; // Maximum duration filter double _minRating = 0.0; // Minimum rating filter // Temporary filter state for modal double _tempMinDuration = 0; double _tempMaxDuration = 120; String? _tempSelectedLevel; double _tempMinRating = 0.0; @override void initState() { super.initState(); _selectedCategory = widget.categoryFilter; } /// Opens the filter modal for advanced filtering (duration, level, rating). void _openFilterModal() async { _tempMinDuration = _minDuration; _tempMaxDuration = _maxDuration; _tempSelectedLevel = _selectedLevel; _tempMinRating = _minRating; await showModalBottomSheet( context: context, isScrollControlled: true, builder: (context) { return Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, left: 16, right: 16, top: 24, ), child: StatefulBuilder( builder: (context, setModalState) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Filter', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), const SizedBox(height: 16), // Duration filter (RangeSlider) Row( children: [ const Text('Dauer:'), Expanded( child: RangeSlider( values: RangeValues(_tempMinDuration, _tempMaxDuration), min: 0, max: 300, divisions: 30, labels: RangeLabels('${_tempMinDuration.toInt()} min', '${_tempMaxDuration.toInt()} min'), onChanged: (values) { setModalState(() { _tempMinDuration = values.start; _tempMaxDuration = values.end; }); }, ), ), ], ), // Level filter (Dropdown) Row( children: [ const Text('Level:'), const SizedBox(width: 8), Expanded( child: DropdownButton( value: _tempSelectedLevel, hint: const Text('Alle'), items: [ const DropdownMenuItem(value: null, child: Text('Alle')), ..._levelCategories.map((y) => DropdownMenuItem(value: y, child: Text(y))), ], onChanged: (value) { setModalState(() { _tempSelectedLevel = value; }); }, ), ), ], ), // Minimum rating filter (Slider) Row( children: [ const Text('Mindestbewertung:'), Expanded( child: Slider( value: _tempMinRating, min: 0, max: 5, divisions: 10, label: _tempMinRating.toStringAsFixed(1), onChanged: (value) { setModalState(() { _tempMinRating = value; }); }, ), ), Text(_tempMinRating.toStringAsFixed(1)), ], ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () { Navigator.pop(context); }, child: const Text('Abbrechen'), ), ElevatedButton( onPressed: () { setState(() { _minDuration = _tempMinDuration; _maxDuration = _tempMaxDuration; _selectedLevel = _tempSelectedLevel; _minRating = _tempMinRating; }); Navigator.pop(context); }, child: const Text('Anwenden'), ), ], ), const SizedBox(height: 8), ], ); }, ), ); }, ); } @override Widget build(BuildContext context) { final user = FirebaseAuth.instance.currentUser; if (user == null) { return const Center(child: Text('Nicht eingeloggt')); } return Scaffold( appBar: AppBar( title: const Text('Favoriten'), ), body: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Category filter chips and filter button Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Kategorien', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), // Filter button opens the modal with advanced filters ElevatedButton.icon( onPressed: _openFilterModal, icon: const Icon(Icons.filter_list), label: const Text('Filter'), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), textStyle: const TextStyle(fontSize: 14), ), ), ], ), 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); }, )), ], ), ], ), ), 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'] ?? []); 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() )), builder: (context, multiSnapshot) { if (multiSnapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (!multiSnapshot.hasData) { return const Center(child: Text('Favoriten konnten nicht geladen werden')); } // Filter favorites by selected category and filter options final filteredFavorites = multiSnapshot.data!.where((doc) { if (!doc.exists) return false; final trainingData = doc.data() as Map; final duration = (trainingData['duration'] as num?)?.toInt() ?? 0; final rating = (trainingData['rating overall'] as num?)?.toDouble() ?? 0.0; final matchesCategory = _selectedCategory == null || trainingData['category'] == _selectedCategory; final matchesLevel = _selectedLevel == null || trainingData['year'] == _selectedLevel; final matchesDuration = duration >= _minDuration && duration <= _maxDuration; final matchesRating = rating >= _minRating; // Only show favorites matching all selected filters return matchesCategory && matchesLevel && matchesDuration && matchesRating; }).toList(); if (filteredFavorites.isEmpty) { return const Center(child: Text('Keine Favoriten in dieser Kategorie 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 doc = filteredFavorites[index]; final trainingData = doc.data() as Map; return Card( child: Stack( children: [ InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => TrainingDetailScreen(trainingId: doc.id), ), ); }, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ 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.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( 'Dauer: \t${trainingData['duration'] ?? '-'} Minuten', 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([doc.id]), }); }, ), ), ], ), ); }, ); }, ); }, ), ), ], ), ); } }