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_ai_panel.dart'; import 'admin_aliases_panel.dart'; import 'admin_inventory_panel.dart'; import 'admin_pantry_panel.dart'; import 'admin_private_products_panel.dart'; import 'admin_pending_products_panel.dart'; import 'admin_products_panel.dart'; import '../../profile/data/profile_repository.dart'; enum _DatabaseTab { inventory, pantry, products, privateProducts, pending, aliases, ai } class AdminDatabasePanel extends ConsumerStatefulWidget { final bool embedded; const AdminDatabasePanel({super.key, this.embedded = false}); @override ConsumerState createState() => _AdminDatabasePanelState(); } class _AdminDatabasePanelState extends ConsumerState { _DatabaseTab _activeTab = _DatabaseTab.inventory; bool _isRefreshingCategories = false; 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( buildCopyableErrorSnackBar(context, mapErrorToUserMessage(e, context)), ); } finally { if (mounted) setState(() => _isRefreshingCategories = false); } } 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 _panelShell({ required String title, required String description, required Widget child, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ 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), child, ], ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); String tabLabel(_DatabaseTab tab) { return switch (tab) { _DatabaseTab.inventory => context.l10n.profileInventoryTab, _DatabaseTab.pantry => context.l10n.profilePantryTab, _DatabaseTab.products => context.l10n.profileProductsTab, _DatabaseTab.privateProducts => 'Privata produkter', _DatabaseTab.pending => context.l10n.profilePendingTab, _DatabaseTab.aliases => 'Alias', _DatabaseTab.ai => 'AI', }; } Widget activeSection; switch (_activeTab) { case _DatabaseTab.inventory: activeSection = _panelShell( title: context.l10n.profileInventoryTab, description: 'Granska, filtrera och redigera inventory-poster. Välj användare för att arbeta på en specifik ägares data.', child: const AdminInventoryPanel(embedded: true), ); case _DatabaseTab.pantry: activeSection = _panelShell( title: context.l10n.profilePantryTab, description: 'Granska och redigera användarnas baslager. Flytta poster till inventarie eller ta bort dem vid behov.', child: const AdminPantryPanel(embedded: true), ); case _DatabaseTab.products: activeSection = _panelShell( title: context.l10n.profileProductsTab, description: 'Hantera globala produkter: kategorisering, restaurering, merge och AI-stöd.', child: const AdminProductsPanel(embedded: true), ); case _DatabaseTab.privateProducts: activeSection = _panelShell( title: 'Privata produkter', description: 'Promotera privata produkter till den globala produkt-tabellen.', child: const AdminPrivateProductsPanel(embedded: true), ); case _DatabaseTab.pending: activeSection = _panelShell( title: context.l10n.profilePendingTab, description: 'Godkänn eller avslå nya produkter som föreslagits av användare.', child: const AdminPendingProductsPanel(embedded: true), ); case _DatabaseTab.aliases: activeSection = _panelShell( title: 'Alias', description: 'Hantera globala alias som används i receipt-importens första matchningssteg.', child: const AdminAliasesPanel(embedded: true), ); case _DatabaseTab.ai: activeSection = _panelShell( title: 'AI', description: 'Se vilka AI-modeller som används och hur de är exponerade i systemet.', child: const AdminAiPanel(embedded: true), ); } final header = SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Databas', style: theme.textTheme.titleMedium), const SizedBox(height: 8), Text( 'Arbetsyta för data med tydlig scope: inventory och baslager är användarspecifika, produkter är globala eller privata och alias styr importmatchning.', style: theme.textTheme.bodyMedium, ), const SizedBox(height: 8), const Wrap( spacing: 8, runSpacing: 8, children: [ Chip(label: Text('User-scope')), Chip(label: Text('Global scope')), Chip(label: Text('Private products')), Chip(label: Text('Alias')), ], ), ], ), ), ), const SizedBox(height: 12), Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Adminverktyg', style: theme.textTheme.titleMedium), const SizedBox(height: 8), Text('Uppdatera kategorier manuellt i backend-cachen.', style: theme.textTheme.bodyMedium), const SizedBox(height: 12), 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'), ), ), ], ), ), ), const SizedBox(height: 12), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: _DatabaseTab.values .map( (tab) => Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(tabLabel(tab)), selected: _activeTab == tab, onSelected: (_) => setState(() => _activeTab = tab), ), ), ) .toList(), ), ), ], ), ); return Padding( padding: widget.embedded ? EdgeInsets.zero : const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded(flex: 2, child: header), const SizedBox(height: 16), Expanded(flex: 5, child: activeSection), ], ), ); } }