import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/api/api_error_mapper.dart'; import '../../admin/presentation/admin_ai_panel.dart'; import '../../admin/presentation/admin_pending_products_panel.dart'; import '../../admin/presentation/admin_users_panel.dart'; import '../../auth/data/auth_providers.dart'; import '../data/profile_repository.dart'; import '../domain/user_profile.dart'; enum _ProfileTab { profile, database, users, suggestions, ai } enum _DatabaseTab { inventory, pantry, products } class ProfileScreen extends ConsumerStatefulWidget { const ProfileScreen({super.key}); @override ConsumerState createState() => _ProfileScreenState(); } class _ProfileScreenState extends ConsumerState { final _formKey = GlobalKey(); bool _isLoading = true; bool _isSaving = false; String? _error; UserProfile? _profile; _ProfileTab _activeTab = _ProfileTab.profile; _DatabaseTab _activeDatabaseTab = _DatabaseTab.inventory; late final TextEditingController _emailCtrl; late final TextEditingController _firstNameCtrl; late final TextEditingController _lastNameCtrl; @override void initState() { super.initState(); _emailCtrl = TextEditingController(); _firstNameCtrl = TextEditingController(); _lastNameCtrl = TextEditingController(); _loadProfile(); } @override void dispose() { _emailCtrl.dispose(); _firstNameCtrl.dispose(); _lastNameCtrl.dispose(); super.dispose(); } Future _loadProfile() async { setState(() { _isLoading = true; _error = null; }); try { final profile = await ref.read(profileRepositoryProvider).getMe(); if (!mounted) return; setState(() { _profile = profile; _emailCtrl.text = profile.email; _firstNameCtrl.text = profile.firstName ?? ''; _lastNameCtrl.text = profile.lastName ?? ''; }); } catch (e) { if (!mounted) return; setState(() => _error = mapErrorToUserMessage(e, context)); } finally { if (mounted) setState(() => _isLoading = false); } } Future _save() async { if (!_formKey.currentState!.validate()) return; setState(() => _isSaving = true); try { final updated = await ref.read(profileRepositoryProvider).updateMe( email: _emailCtrl.text.trim(), firstName: _firstNameCtrl.text.trim().isEmpty ? null : _firstNameCtrl.text.trim(), lastName: _lastNameCtrl.text.trim().isEmpty ? null : _lastNameCtrl.text.trim(), ); if (!mounted) return; setState(() => _profile = updated); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Profil sparad!')), ); } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(mapErrorToUserMessage(e, context))), ); } finally { if (mounted) setState(() => _isSaving = false); } } Future _logout() async { await ref.read(authStateProvider.notifier).logout(); if (!mounted) return; context.go('/login'); } List<_ProfileTab> _visibleTabs(bool isAdmin) { return [ _ProfileTab.profile, _ProfileTab.database, if (isAdmin) ...[ _ProfileTab.users, _ProfileTab.suggestions, _ProfileTab.ai, ], ]; } String _tabLabel(_ProfileTab tab) { switch (tab) { case _ProfileTab.profile: return 'Min profil'; case _ProfileTab.database: return 'Databas'; case _ProfileTab.users: return 'Användare'; case _ProfileTab.suggestions: return 'Förslag'; case _ProfileTab.ai: return 'AI'; } } Widget _buildTabBar(BuildContext context, List<_ProfileTab> tabs) { return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: tabs .map( (tab) => Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(_tabLabel(tab)), selected: _activeTab == tab, onSelected: (_) => setState(() => _activeTab = tab), ), ), ) .toList(), ), ); } Widget _buildProfileForm(BuildContext context, ThemeData theme) { return Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Användarnamn', style: theme.textTheme.labelMedium?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Text(_profile?.username ?? '', style: theme.textTheme.bodyLarge), const Divider(height: 32), TextFormField( controller: _emailCtrl, decoration: const InputDecoration( labelText: 'E-post', border: OutlineInputBorder(), ), keyboardType: TextInputType.emailAddress, validator: (v) { if (v == null || v.isEmpty) return 'Ange en e-postadress'; if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(v)) { return 'Ogiltig e-postadress'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _firstNameCtrl, decoration: const InputDecoration( labelText: 'Förnamn', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextFormField( controller: _lastNameCtrl, decoration: const InputDecoration( labelText: 'Efternamn', border: OutlineInputBorder(), ), ), const SizedBox(height: 24), SizedBox( width: double.infinity, child: FilledButton( onPressed: _isSaving ? null : _save, child: _isSaving ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Spara ändringar'), ), ), ], ), ); } Widget _buildDatabaseTab(BuildContext context) { final isAdmin = _profile?.isAdmin == true; final visibleTabs = [ _DatabaseTab.inventory, _DatabaseTab.pantry, if (isAdmin) _DatabaseTab.products, ]; if (!visibleTabs.contains(_activeDatabaseTab)) { _activeDatabaseTab = _DatabaseTab.inventory; } String tabLabel(_DatabaseTab tab) { switch (tab) { case _DatabaseTab.inventory: return 'Inventarie'; case _DatabaseTab.pantry: return 'Baslager'; case _DatabaseTab.products: return 'Produkter'; } } Widget sectionCard({ required IconData icon, required String title, required String description, required VoidCallback onPressed, required String buttonLabel, }) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon), const SizedBox(width: 8), Expanded( child: Text( title, style: Theme.of(context).textTheme.titleMedium, ), ), ], ), const SizedBox(height: 8), Text(description), const SizedBox(height: 16), FilledButton.icon( onPressed: onPressed, icon: Icon(icon), label: Text(buttonLabel), ), ], ), ), ); } Widget activeSection; switch (_activeDatabaseTab) { case _DatabaseTab.inventory: activeSection = sectionCard( icon: Icons.inventory_2_outlined, title: 'Inventarie', description: 'Lägg till, uppdatera och konsumera varor i ditt inventarie. Detta motsvarar inventarievyn under Databas i recipe-frontend.', onPressed: () => context.go('/inventory'), buttonLabel: 'Öppna inventarie', ); case _DatabaseTab.pantry: activeSection = sectionCard( icon: Icons.storefront_outlined, title: 'Baslager', description: 'Hantera varor du alltid räknar med att ha hemma. Detta motsvarar baslagervyn under Databas i recipe-frontend.', onPressed: () => context.go('/baslager'), buttonLabel: 'Öppna baslager', ); case _DatabaseTab.products: activeSection = sectionCard( icon: Icons.category_outlined, title: 'Produkter', description: 'Adminhantering av produktkatalogen, inklusive standardisering och vidare produktadministration.', onPressed: () => context.go('/admin'), buttonLabel: 'Öppna Admin', ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Databasfliken samlar samma huvudområden som i recipe-frontend.', style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: 12), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: visibleTabs .map( (tab) => Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(tabLabel(tab)), selected: _activeDatabaseTab == tab, onSelected: (_) => setState(() => _activeDatabaseTab = tab), ), ), ) .toList(), ), ), const SizedBox(height: 16), activeSection, ], ); } Widget _buildAdminPlaceholder( BuildContext context, { required String title, required String description, }) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), Text(description), const SizedBox(height: 12), FilledButton.icon( onPressed: () => context.go('/admin'), icon: const Icon(Icons.admin_panel_settings_outlined), label: const Text('Öppna Admin'), ), ], ), ), ); } Widget _buildActiveTabContent(BuildContext context, ThemeData theme) { switch (_activeTab) { case _ProfileTab.profile: return _buildProfileForm(context, theme); case _ProfileTab.database: return _buildDatabaseTab(context); case _ProfileTab.users: return const AdminUsersPanel(embedded: true); case _ProfileTab.suggestions: return const AdminPendingProductsPanel(embedded: true); case _ProfileTab.ai: return const AdminAiPanel(embedded: true); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final tabs = _visibleTabs(_profile?.isAdmin == true); if (!tabs.contains(_activeTab)) { _activeTab = _ProfileTab.profile; } if (_isLoading) { return const Center(child: CircularProgressIndicator()); } if (_error != null) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(_error!, style: TextStyle(color: theme.colorScheme.error)), const SizedBox(height: 16), FilledButton(onPressed: _loadProfile, child: const Text('Försök igen')), ], ), ); } return ListView( padding: const EdgeInsets.all(16), children: [ Row( children: [ CircleAvatar( radius: 28, child: Text( (_profile?.username.isNotEmpty == true ? _profile!.username[0] : '?') .toUpperCase(), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _profile?.username ?? '', style: theme.textTheme.titleLarge, ), if ((_profile?.email ?? '').isNotEmpty) Text( _profile!.email, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), ], ), ), if (_profile?.isAdmin == true) Chip( label: const Text('Admin'), avatar: const Icon(Icons.shield_outlined, size: 16), backgroundColor: theme.colorScheme.primaryContainer, labelStyle: TextStyle(color: theme.colorScheme.onPrimaryContainer), ), ], ), const SizedBox(height: 16), _buildTabBar(context, tabs), const SizedBox(height: 16), _buildActiveTabContent(context, theme), const SizedBox(height: 24), SizedBox( width: double.infinity, child: OutlinedButton.icon( onPressed: _logout, icon: const Icon(Icons.logout), label: const Text('Logga ut'), ), ), ], ); } }