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 '../../../core/l10n/l10n.dart'; import '../../admin/presentation/admin_ai_panel.dart'; import '../../admin/presentation/admin_pending_products_panel.dart'; import '../../admin/presentation/admin_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; bool _isRefreshingCategories = 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( SnackBar(content: Text(context.l10n.profileSaved)), ); } 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'); } Future _refreshCategories() async { setState(() => _isRefreshingCategories = true); try { await ref.read(profileRepositoryProvider).refreshCategories(); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Kategorier har uppdaterats.')), ); } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(mapErrorToUserMessage(e, context))), ); } finally { if (mounted) setState(() => _isRefreshingCategories = false); } } 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 context.l10n.profileMyProfileTab; case _ProfileTab.database: return context.l10n.profileDatabaseTab; case _ProfileTab.users: return context.l10n.profileUsersTab; case _ProfileTab.suggestions: return context.l10n.profilePendingTab; case _ProfileTab.ai: return context.l10n.profileAiTab; } } 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( context.l10n.profileUsernameLabel, 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: InputDecoration( labelText: context.l10n.profileEmailLabel, border: const OutlineInputBorder(), ), keyboardType: TextInputType.emailAddress, validator: (v) { if (v == null || v.isEmpty) return context.l10n.profileEmailHint; if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(v)) { return context.l10n.profileEmailInvalid; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _firstNameCtrl, decoration: InputDecoration( labelText: context.l10n.profileFirstNameLabel, border: const OutlineInputBorder(), ), ), const SizedBox(height: 16), TextFormField( controller: _lastNameCtrl, decoration: InputDecoration( labelText: context.l10n.profileLastNameLabel, border: const 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), ) : Text(context.l10n.profileSaveAction), ), ), const SizedBox(height: 16), const Divider(), const SizedBox(height: 8), SizedBox( width: double.infinity, child: OutlinedButton.icon( onPressed: _isRefreshingCategories ? null : _refreshCategories, icon: _isRefreshingCategories ? const SizedBox( height: 16, width: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.refresh), label: const Text('Uppdatera kategorier'), ), ), ], ), ); } 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 context.l10n.profileInventoryTab; case _DatabaseTab.pantry: return context.l10n.profilePantryTab; case _DatabaseTab.products: return context.l10n.profileProductsTab; } } 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: context.l10n.profileInventoryTab, description: context.l10n.profileInventoryDescription, onPressed: () => context.go('/inventory'), buttonLabel: context.l10n.profileOpenInventory, ); case _DatabaseTab.pantry: activeSection = sectionCard( icon: Icons.storefront_outlined, title: context.l10n.profilePantryTab, description: context.l10n.profilePantryDescription, onPressed: () => context.go('/baslager'), buttonLabel: context.l10n.profileOpenPantry, ); case _DatabaseTab.products: activeSection = const AdminProductsPanel(embedded: true); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.l10n.profileDatabaseDescription, 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 _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: Text(context.l10n.retryAction)), ], ), ); } 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: Text(context.l10n.logoutAction), ), ), ], ); } }