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/api/api_paths.dart'; import '../../../core/api/api_providers.dart'; import '../../../core/l10n/l10n.dart'; import '../../../core/ui/async_state_views.dart'; import '../../auth/data/auth_providers.dart'; import '../data/inventory_providers.dart'; import 'swipeable_inventory_tile.dart'; class InventoryScreen extends ConsumerWidget { const InventoryScreen({super.key}); static const _locationOptions = ['', 'Kyl', 'Frys', 'Skafferi']; List<({String value, String label})> _sortOptions(BuildContext context) => [ (value: '', label: context.l10n.inventorySortLatest), (value: 'nameAsc', label: context.l10n.inventorySortNameAsc), (value: 'bestBeforeAsc', label: context.l10n.inventorySortBestBeforeAsc), (value: 'bestBeforeDesc', label: context.l10n.inventorySortBestBeforeDesc), ]; Future _createManualProduct(BuildContext context, WidgetRef ref) async { final nameCtrl = TextEditingController(); final created = await showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('Ny produkt'), content: TextField( controller: nameCtrl, autofocus: true, decoration: const InputDecoration( labelText: 'Produktnamn', border: OutlineInputBorder(), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(dialogContext), child: Text(context.l10n.cancelAction), ), FilledButton( onPressed: () { final value = nameCtrl.text.trim(); if (value.isEmpty) return; Navigator.pop(dialogContext, value); }, child: const Text('Skapa'), ), ], ), ); nameCtrl.dispose(); if (created == null || created.trim().isEmpty || !context.mounted) return; try { final token = ref.read(authStateProvider).maybeWhen( data: (t) => t, orElse: () => null, ) ?? await ref.read(authStateProvider.future); final api = ref.read(apiClientProvider); await api.postJson( ProductApiPaths.createPrivate, body: {'name': created.trim()}, token: token, ); if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Produkt skapad. Lägg nu till den i inventariet.')), ); context.push('/inventory/create'); } catch (e) { if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( buildCopyableErrorSnackBar(context, mapErrorToUserMessage(e, context)), ); } } @override Widget build(BuildContext context, WidgetRef ref) { final location = ref.watch(inventoryLocationFilterProvider); final sort = ref.watch(inventorySortFilterProvider); final inventoryAsync = ref.watch(inventoryProvider); return inventoryAsync.when( loading: () => LoadingStateView(label: context.l10n.inventoryLoading), error: (e, _) => ErrorStateView( message: mapErrorToUserMessage(e, context), onRetry: () => ref.invalidate(inventoryProvider), ), data: (items) { final filterSection = Padding( padding: const EdgeInsets.fromLTRB(12, 12, 12, 4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.l10n.inventoryFilterAndSort, style: const TextStyle(fontWeight: FontWeight.w600), ), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: _locationOptions .map( (option) => ChoiceChip( label: Text(option.isEmpty ? context.l10n.inventoryAllFilter : option), selected: location == option, onSelected: (_) => ref .read(inventoryLocationFilterProvider.notifier) .setValue(option), ), ) .toList(), ), const SizedBox(height: 8), DropdownButtonFormField( initialValue: sort, isExpanded: true, decoration: InputDecoration( labelText: context.l10n.inventorySortLabel, border: const OutlineInputBorder(), ), items: _sortOptions(context) .map( (option) => DropdownMenuItem( value: option.value, child: Text(option.label), ), ) .toList(), onChanged: (value) { ref .read(inventorySortFilterProvider.notifier) .setValue(value ?? ''); }, ), ], ), ); if (items.isEmpty) { return Stack( children: [ ListView( padding: const EdgeInsets.only(bottom: 88), children: [ filterSection, EmptyStateView(title: context.l10n.inventoryEmpty), ], ), Positioned( right: 16, bottom: 16, child: Column( mainAxisSize: MainAxisSize.min, children: [ FloatingActionButton.extended( onPressed: () => context.push('/inventory/create'), icon: const Icon(Icons.add), label: Text(context.l10n.addAction), ), const SizedBox(height: 8), FloatingActionButton.extended( onPressed: () => _createManualProduct(context, ref), icon: const Icon(Icons.add_box_outlined), label: const Text('Ny produkt'), ), ], ), ), ], ); } return Stack( children: [ ListView.separated( padding: const EdgeInsets.only(bottom: 88), itemCount: items.length + 1, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (context, index) { if (index == 0) return filterSection; final item = items[index - 1]; return SwipeableInventoryTile(item: item); }, ), Positioned( right: 16, bottom: 16, child: Column( mainAxisSize: MainAxisSize.min, children: [ FloatingActionButton.extended( onPressed: () => context.push('/inventory/create'), icon: const Icon(Icons.add), label: Text(context.l10n.addAction), ), const SizedBox(height: 8), FloatingActionButton.extended( onPressed: () => _createManualProduct(context, ref), icon: const Icon(Icons.add_box_outlined), label: const Text('Ny produkt'), ), const SizedBox(height: 8), FloatingActionButton.extended( onPressed: () => context.go('/recipes'), icon: const Icon(Icons.restaurant_menu), label: Text(context.l10n.inventoryRecipesAction), ), ], ), ), ], ); }, ); } }