diff --git a/trainerbox/lib/screens/favorites_tab.dart b/trainerbox/lib/screens/favorites_tab.dart index f4ab0a9..d3f86d5 100644 --- a/trainerbox/lib/screens/favorites_tab.dart +++ b/trainerbox/lib/screens/favorites_tab.dart @@ -3,8 +3,30 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'training_detail_screen.dart'; -class FavoritesTab extends StatelessWidget { - const FavoritesTab({super.key}); +class FavoritesTab extends StatefulWidget { + final String? categoryFilter; + const FavoritesTab({super.key, this.categoryFilter}); + + @override + State createState() => _FavoritesTabState(); +} + +class _FavoritesTabState extends State { + static const List _categories = [ + 'Aufwärmen & Mobilisation', + 'Wurf- & Torabschluss', + 'Torwarttraining', + 'Athletik', + 'Pass', + 'Koordination', + ]; + String? _selectedCategory; + + @override + void initState() { + super.initState(); + _selectedCategory = widget.categoryFilter; + } @override Widget build(BuildContext context) { @@ -17,118 +39,169 @@ class FavoritesTab extends StatelessWidget { appBar: AppBar( title: const Text('Favoriten'), ), - body: 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 favorites = List.from(data['favorites'] ?? []); - - if (favorites.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, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Filter-Chip-Leiste + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Wrap( + 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); + }, + )), + ], ), - 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(); + ), + 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')); + } + + // 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}); + } } - final trainingData = trainingSnapshot.data!.data() as Map; - return Card( - child: Stack( - children: [ - InkWell( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => TrainingDetailScreen(trainingId: favorites[index]), - ), - ); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + 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( 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), + InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => TrainingDetailScreen(trainingId: favId), + ), + ); + }, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, 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), + Expanded( + child: Container( + color: Colors.grey[200], + child: const Center( + child: Icon(Icons.fitness_center, size: 50), ), - ], + ), ), - const SizedBox(height: 4), - Text( - 'Dauer: ${trainingData['duration'] ?? '-'} Minuten', - style: const TextStyle(fontSize: 12, color: Colors.grey), + 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([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/home_screen.dart b/trainerbox/lib/screens/home_screen.dart index f72f7b6..37f4bb5 100644 --- a/trainerbox/lib/screens/home_screen.dart +++ b/trainerbox/lib/screens/home_screen.dart @@ -22,6 +22,7 @@ class _HomeScreenState extends State { Map? _nextTraining; bool _isLoading = true; DateTime? _calendarInitialDate; + String? _favoriteCategoryFilter; @override void initState() { @@ -165,7 +166,7 @@ class _HomeScreenState extends State { List get _screens => [ _buildHomeTab(), const SearchTab(), - const FavoritesTab(), + FavoritesTab(categoryFilter: _favoriteCategoryFilter), CalendarTab(initialDate: _calendarInitialDate), ProfileTab(onLogoutSuccess: widget.onLogoutSuccess), ]; @@ -349,25 +350,33 @@ class _HomeScreenState extends State { Widget _buildFavoriteCircle(String label, IconData icon, Color color) { return Padding( padding: const EdgeInsets.only(right: 16.0), - child: Column( - children: [ - CircleAvatar( - radius: 24, - backgroundColor: color.withOpacity(0.2), - child: Icon(icon, color: color, size: 26), - ), - const SizedBox(height: 4), - SizedBox( - width: 60, - child: Text( - label, - style: const TextStyle(fontSize: 12), - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, + child: GestureDetector( + onTap: () { + setState(() { + _favoriteCategoryFilter = label; + _selectedIndex = 2; + }); + }, + child: Column( + children: [ + CircleAvatar( + radius: 24, + backgroundColor: color.withOpacity(0.2), + child: Icon(icon, color: color, size: 26), ), - ), - ], + const SizedBox(height: 4), + SizedBox( + width: 60, + child: Text( + label, + style: const TextStyle(fontSize: 12), + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), ), ); } diff --git a/trainerbox/pubspec.lock b/trainerbox/pubspec.lock index 97b22c3..9249a94 100644 --- a/trainerbox/pubspec.lock +++ b/trainerbox/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: dda4fd7909a732a014239009aa52537b136f8ce568de23c212587097887e2307 + sha256: "37a42d06068e2fe3deddb2da079a8c4d105f241225ba27b7122b37e9865fd8f7" url: "https://pub.dev" source: hosted - version: "1.3.56" + version: "1.3.35" async: dependency: transitive description: @@ -45,26 +45,26 @@ packages: dependency: "direct main" description: name: cloud_firestore - sha256: "80cafe016ea12e4b76737487784dc659720065705445be547d2f47aaaa3f2874" + sha256: a0f161b92610e078b4962d7e6ebeb66dc9cce0ada3514aeee442f68165d78185 url: "https://pub.dev" source: hosted - version: "5.6.9" + version: "4.17.5" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface - sha256: "9c5adf444b6e4eca15ecbc497678fba6d9944041fbf22810b756269ddefa75c7" + sha256: "6a55b319f8d33c307396b9104512e8130a61904528ab7bd8b5402678fca54b81" url: "https://pub.dev" source: hosted - version: "6.6.9" + version: "6.2.5" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - sha256: "884c153ac66cdef469ca5e55bf71d979c3b53dc20a4c1662fae5c07bf603d878" + sha256: "89dfa1304d3da48b3039abbb2865e3d30896ef858e569a16804a99f4362283a9" url: "https://pub.dev" source: hosted - version: "4.4.9" + version: "3.12.5" collection: dependency: transitive description: @@ -141,34 +141,34 @@ packages: dependency: "direct main" description: name: firebase_auth - sha256: "10ddd766bd3d3baf1cc8fed544ef83a2c0e6027514a020e56114212ffe1036f2" + sha256: cfc2d970829202eca09e2896f0a5aa7c87302817ecc0bdfa954f026046bf10ba url: "https://pub.dev" source: hosted - version: "5.6.0" + version: "4.20.0" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: fde52352bfd553f5e38239b868e1e6fe6c1a6af542f5cc547554c61fef30b99b + sha256: a0270e1db3b2098a14cb2a2342b3cd2e7e458e0c391b1f64f6f78b14296ec093 url: "https://pub.dev" source: hosted - version: "7.7.0" + version: "7.3.0" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "33a0e6521a1c8c476949cc4f2f7b7d9ed8f99514b08dd905fdb9546d2391e5d9" + sha256: "64e067e763c6378b7e774e872f0f59f6812885e43020e25cde08f42e9459837b" url: "https://pub.dev" source: hosted - version: "5.15.0" + version: "5.12.0" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "420d9111dcf095341f1ea8fdce926eef750cf7b9745d21f38000de780c94f608" + sha256: "26de145bb9688a90962faec6f838247377b0b0d32cc0abecd9a4e43525fc856c" url: "https://pub.dev" source: hosted - version: "3.14.0" + version: "2.32.0" firebase_core_platform_interface: dependency: transitive description: @@ -181,34 +181,34 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: ddd72baa6f727e5b23f32d9af23d7d453d67946f380bd9c21daf474ee0f7326e + sha256: "362e52457ed2b7b180964769c1e04d1e0ea0259fdf7025fdfedd019d4ae2bd88" url: "https://pub.dev" source: hosted - version: "2.23.0" + version: "2.17.5" firebase_storage: dependency: "direct main" description: name: firebase_storage - sha256: a14390edb4b7a119297ddd09773421233ffddcfa1f8dcb670d9ce71672fdb624 + sha256: "2ae478ceec9f458c1bcbf0ee3e0100e4e909708979e83f16d5d9fba35a5b42c1" url: "https://pub.dev" source: hosted - version: "12.4.7" + version: "11.7.7" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface - sha256: d6c7a32c714cd7d59b61a5c610137695547986b8a5abe71aaf71197d6d255006 + sha256: "4e18662e6a66e2e0e181c06f94707de06d5097d70cfe2b5141bf64660c5b5da9" url: "https://pub.dev" source: hosted - version: "5.2.7" + version: "5.1.22" firebase_storage_web: dependency: transitive description: name: firebase_storage_web - sha256: "45ad71124862ef6d3a68770edc0aaefb854c0b4781aa96e9b0ec10400af03861" + sha256: "3a44aacd38a372efb159f6fe36bb4a7d79823949383816457fd43d3d47602a53" url: "https://pub.dev" source: hosted - version: "3.10.14" + version: "3.9.7" fixnum: dependency: transitive description: @@ -226,10 +226,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "5.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -332,10 +332,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.20.2" leak_tracker: dependency: transitive description: @@ -364,10 +364,10 @@ packages: dependency: transitive description: name: lints - sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "5.1.1" matcher: dependency: transitive description: @@ -489,10 +489,10 @@ packages: dependency: "direct main" description: name: table_calendar - sha256: "1e3521a3e6d3fc7f645a58b135ab663d458ab12504f1ea7f9b4b81d47086c478" + sha256: "0c0c6219878b363a2d5f40c7afb159d845f253d061dc3c822aa0d5fe0f721982" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.2.0" term_glyph: dependency: transitive description: @@ -545,10 +545,10 @@ packages: dependency: transitive description: name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "0.5.1" sdks: - dart: ">=3.8.0 <4.0.0" + dart: ">=3.7.0 <4.0.0" flutter: ">=3.27.0" diff --git a/trainerbox/pubspec.yaml b/trainerbox/pubspec.yaml index 5a95327..582d58b 100644 --- a/trainerbox/pubspec.yaml +++ b/trainerbox/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=3.2.3 <4.0.0' + sdk: '>=3.7.0 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -33,16 +33,16 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - firebase_core: ^3.14.0 - table_calendar: ^3.0.9 - cloud_firestore: ^5.6.9 - firebase_auth: ^5.6.0 - firebase_storage: ^12.4.7 - image_picker: ^1.0.7 - provider: ^6.1.1 - uuid: ^4.2.1 - intl: ^0.18.0 + cupertino_icons: ^1.0.8 + firebase_core: ^2.30.1 + table_calendar: ^3.1.1 + cloud_firestore: ^4.15.8 + firebase_auth: ^4.17.8 + firebase_storage: ^11.6.6 + image_picker: ^1.1.1 + provider: ^6.1.2 + uuid: ^4.3.3 + intl: ^0.20.2 dev_dependencies: flutter_test: @@ -53,7 +53,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^6.0.0 + flutter_lints: ^5.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec