import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../main.dart'; import '../services/collection_service.dart'; import '../services/main_collection_sync.dart'; import '../theme/app_colors.dart'; import 'garage_screen.dart'; import 'manage_collection_screen.dart'; /// Lists all collections the current user is a member of. class CollectionsScreen extends StatefulWidget { const CollectionsScreen({super.key}); @override State createState() => CollectionsScreenState(); } class CollectionsScreenState extends State with WidgetsBindingObserver { static const _activeCollectionPrefKey = 'active_collection_id'; List _collections = []; bool _isLoading = true; String? _error; String? _activeCollectionId; DateTime _lastLoadedAt = DateTime.fromMillisecondsSinceEpoch(0); @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _load(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { refreshIfStale(maxAge: const Duration(seconds: 3)); } } /// Public so other tabs can trigger a refresh. void refresh() => _load(); void refreshIfStale({Duration maxAge = const Duration(seconds: 10)}) { if (DateTime.now().difference(_lastLoadedAt) > maxAge) { _load(); } } Future _load() async { setState(() { _isLoading = true; _error = null; }); try { final list = await CollectionService.getMyCollections(); final prefs = await SharedPreferences.getInstance(); final persisted = prefs.getString(_activeCollectionPrefKey); String? activeId = persisted; if (activeId == null && list.isNotEmpty) { activeId = list.first.id; } if (activeId != null && !list.any((c) => c.id == activeId)) { activeId = list.isNotEmpty ? list.first.id : null; } final shouldNotifySync = activeId != null && activeId != persisted; if (!mounted) return; setState(() { _collections = list; _activeCollectionId = activeId; _isLoading = false; _lastLoadedAt = DateTime.now(); }); if (activeId != null) { await prefs.setString(_activeCollectionPrefKey, activeId); if (shouldNotifySync) { MainCollectionSync.notifyChanged(); } } } catch (e) { if (!mounted) return; setState(() { _error = e.toString(); _isLoading = false; }); } } Future _createCollection() async { final formKey = GlobalKey(); final nameCtrl = TextEditingController(); final descCtrl = TextEditingController(); final result = await showDialog( context: context, builder: (_) => AlertDialog( icon: Container( padding: const EdgeInsets.all(12), decoration: const BoxDecoration( gradient: AppColors.brandGradient, shape: BoxShape.circle, ), child: const Icon(Icons.add, color: Colors.white, size: 28), ), title: const Text('New Collection'), content: Form( key: formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextFormField( controller: nameCtrl, autofocus: true, maxLength: 50, decoration: const InputDecoration( labelText: 'Name', hintText: 'e.g. Hot Wheels, Matchbox…', ), validator: (value) { final trimmed = value?.trim() ?? ''; if (trimmed.isEmpty) return 'Name is required'; if (trimmed.length < 2) return 'Name must be at least 2 characters'; if (trimmed.length > 50) return 'Name must be 50 characters or fewer'; return null; }, ), const SizedBox(height: 12), TextField( controller: descCtrl, decoration: const InputDecoration( labelText: 'Description (optional)', hintText: 'What is this collection for?', ), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), ElevatedButton( onPressed: () { if (formKey.currentState!.validate()) { Navigator.pop(context, true); } }, child: const Text('Create'), ), ], ), ); if (result != true) return; try { await CollectionService.create( name: nameCtrl.text.trim(), description: descCtrl.text.trim(), ); showGlobalSnackBar('Collection created!'); _load(); } catch (e) { showGlobalSnackBar('Failed: $e', isError: true); } } void _openCollection(Collection c) { navigatorKey.currentState!.push( MaterialPageRoute( builder: (_) => GarageScreen( collectionId: c.id, collectionName: c.name, isOwner: c.isOwner, ), ), ).then((_) => _load()); // refresh counts when coming back } void _manageCollection(Collection c) { navigatorKey.currentState!.push( MaterialPageRoute( builder: (_) => ManageCollectionScreen(collection: c), ), ).then((_) => _load()); } Future _setActiveCollection(String collectionId) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString(_activeCollectionPrefKey, collectionId); if (!mounted) return; setState(() => _activeCollectionId = collectionId); MainCollectionSync.notifyChanged(); showGlobalSnackBar('Main collection set for scanning.'); } @override Widget build(BuildContext context) { return Scaffold( body: RefreshIndicator( onRefresh: _load, child: CustomScrollView( slivers: [ // ── Header ── SliverAppBar( expandedHeight: 140, pinned: true, flexibleSpace: FlexibleSpaceBar( titlePadding: const EdgeInsets.only(left: 20, bottom: 16), title: Text( 'Collections', style: TextStyle( fontFamily: 'Poppins', fontWeight: FontWeight.w700, fontSize: 22, color: Colors.white, shadows: [ Shadow( color: Colors.black.withValues(alpha: 0.3), blurRadius: 4, ), ], ), ), background: Container( decoration: BoxDecoration( image: const DecorationImage( image: AssetImage('assets/img/login_bg.jpg'), fit: BoxFit.cover, ), color: Colors.black.withValues(alpha: 0.15), ), child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black.withValues(alpha: 0.3), Colors.black.withValues(alpha: 0.55), ], ), ), child: Align( alignment: Alignment.centerRight, child: Padding( padding: const EdgeInsets.only(right: 24), child: Icon( Icons.collections_bookmark, size: 72, color: Colors.white.withValues(alpha: 0.18), ), ), ), ), ), ), ), // ── Content ── if (_isLoading) const SliverFillRemaining( child: Center(child: CircularProgressIndicator()), ) else if (_error != null) SliverFillRemaining( child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, size: 56, color: AppColors.error), const SizedBox(height: 16), Text(_error!, textAlign: TextAlign.center, style: const TextStyle(color: AppColors.textSecondary)), const SizedBox(height: 16), ElevatedButton.icon( onPressed: _load, icon: const Icon(Icons.refresh), label: const Text('Retry'), ), ], ), ), ) else if (_collections.isEmpty) SliverFillRemaining( child: Center( child: Padding( padding: const EdgeInsets.all(40), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.collections_bookmark_outlined, size: 64, color: AppColors.textHint), const SizedBox(height: 16), const Text( 'No collections yet', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: AppColors.textSecondary, ), ), const SizedBox(height: 8), const Text( 'Create your first collection to start tracking!', textAlign: TextAlign.center, style: TextStyle(color: AppColors.textHint), ), const SizedBox(height: 24), ElevatedButton.icon( onPressed: _createCollection, icon: const Icon(Icons.add), label: const Text('Create Collection'), ), ], ), ), ), ) else SliverPadding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 100), sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, index) { final c = _collections[index]; return _CollectionCard( collection: c, isActive: _activeCollectionId == c.id, onSetActive: () => _setActiveCollection(c.id), onTap: () => _openCollection(c), onManage: () => _manageCollection(c), ); }, childCount: _collections.length, ), ), ), ], ), ), floatingActionButton: _collections.isNotEmpty ? FloatingActionButton( onPressed: _createCollection, child: const Icon(Icons.add), ) : null, ); } } // ── Collection Card ────────────────────────────────────────────────── class _CollectionCard extends StatelessWidget { final Collection collection; final bool isActive; final VoidCallback onSetActive; final VoidCallback onTap; final VoidCallback onManage; const _CollectionCard({ required this.collection, required this.isActive, required this.onSetActive, required this.onTap, required this.onManage, }); @override Widget build(BuildContext context) { final c = collection; return Card( margin: const EdgeInsets.only(bottom: 12), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(16), child: Row( children: [ // Icon Container( width: 56, height: 56, decoration: BoxDecoration( gradient: c.isOwner ? AppColors.brandGradient : const LinearGradient( colors: [AppColors.navy, AppColors.navyLight], ), borderRadius: BorderRadius.circular(14), ), child: Icon( c.isOwner ? Icons.collections_bookmark : Icons.group, color: Colors.white, size: 28, ), ), const SizedBox(width: 16), // Info Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( c.name, style: const TextStyle( fontSize: 17, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 4), Row( children: [ _MiniStat( icon: Icons.directions_car, value: '${c.itemCount}'), const SizedBox(width: 16), _MiniStat( icon: Icons.people, value: '${c.memberCount}'), const SizedBox(width: 16), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2), decoration: BoxDecoration( color: c.isOwner ? AppColors.orange.withValues(alpha: 0.15) : AppColors.navy.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(6), ), child: Text( c.isOwner ? 'Owner' : 'Member', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: c.isOwner ? AppColors.orange : AppColors.navy, ), ), ), const SizedBox(width: 8), if (isActive) Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2), decoration: BoxDecoration( color: AppColors.success.withValues(alpha: 0.14), borderRadius: BorderRadius.circular(6), ), child: const Text( 'Main', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: AppColors.success, ), ), ), ], ), ], ), ), Column( children: [ IconButton( tooltip: isActive ? 'Already main collection' : 'Make main collection', icon: Icon( isActive ? Icons.my_location : Icons.location_searching, color: isActive ? AppColors.success : AppColors.textHint, ), onPressed: isActive ? null : onSetActive, ), IconButton( icon: const Icon(Icons.settings_outlined, color: AppColors.textHint), onPressed: onManage, ), ], ), ], ), ), ), ); } } class _MiniStat extends StatelessWidget { final IconData icon; final String value; const _MiniStat({required this.icon, required this.value}); @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 14, color: AppColors.textHint), const SizedBox(width: 4), Text( value, style: const TextStyle( fontSize: 13, color: AppColors.textSecondary, ), ), ], ); } }