feat: enhance inventory and pantry features with filtering, sorting, and error handling improvements
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../core/api/api_error_mapper.dart';
|
||||
import '../../../core/ui/async_state_views.dart';
|
||||
import '../data/inventory_providers.dart';
|
||||
import '../domain/inventory_consumption.dart';
|
||||
@@ -25,7 +26,7 @@ class ConsumptionHistoryScreen extends ConsumerWidget {
|
||||
body: historyAsync.when(
|
||||
loading: () => const LoadingStateView(label: 'Laddar historik...'),
|
||||
error: (e, _) => ErrorStateView(
|
||||
message: e.toString(),
|
||||
message: mapErrorToUserMessage(e),
|
||||
onRetry: () => ref.invalidate(consumptionHistoryProvider(itemId)),
|
||||
),
|
||||
data: (history) {
|
||||
|
||||
@@ -36,7 +36,7 @@ class InventoryDetailScreen extends ConsumerWidget {
|
||||
body: itemAsync.when(
|
||||
loading: () => const LoadingStateView(label: 'Laddar...'),
|
||||
error: (e, _) => ErrorStateView(
|
||||
message: e.toString(),
|
||||
message: mapErrorToUserMessage(e),
|
||||
onRetry: () => ref.invalidate(inventoryDetailProvider(itemId)),
|
||||
),
|
||||
data: (item) => ListView(
|
||||
|
||||
@@ -130,7 +130,7 @@ class _InventoryEditScreenState extends ConsumerState<InventoryEditScreen> {
|
||||
body: itemAsync.when(
|
||||
loading: () => const LoadingStateView(label: 'Laddar...'),
|
||||
error: (e, _) => ErrorStateView(
|
||||
message: e.toString(),
|
||||
message: mapErrorToUserMessage(e),
|
||||
onRetry: () => ref.invalidate(inventoryDetailProvider(widget.itemId)),
|
||||
),
|
||||
data: (item) {
|
||||
|
||||
@@ -11,21 +11,87 @@ import '../domain/inventory_item.dart';
|
||||
class InventoryScreen extends ConsumerWidget {
|
||||
const InventoryScreen({super.key});
|
||||
|
||||
static const _locationOptions = <String>['', 'Kyl', 'Frys', 'Skafferi'];
|
||||
static const _sortOptions = <({String value, String label})>[
|
||||
(value: '', label: 'Senast tillagda'),
|
||||
(value: 'nameAsc', label: 'Namn A-O'),
|
||||
(value: 'bestBeforeAsc', label: 'Bast fore stigande'),
|
||||
(value: 'bestBeforeDesc', label: 'Bast fore fallande'),
|
||||
];
|
||||
|
||||
@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: () => const LoadingStateView(label: 'Laddar inventarie...'),
|
||||
error: (e, _) => ErrorStateView(
|
||||
message: e.toString(),
|
||||
message: mapErrorToUserMessage(e),
|
||||
onRetry: () => ref.invalidate(inventoryProvider),
|
||||
),
|
||||
data: (items) {
|
||||
final filterSection = Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Filter och sortering',
|
||||
style: TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: _locationOptions
|
||||
.map(
|
||||
(option) => ChoiceChip(
|
||||
label: Text(option.isEmpty ? 'Alla' : option),
|
||||
selected: location == option,
|
||||
onSelected: (_) => ref
|
||||
.read(inventoryLocationFilterProvider.notifier)
|
||||
.state = option,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
DropdownButtonFormField<String>(
|
||||
value: sort,
|
||||
isExpanded: true,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Sortering',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
items: _sortOptions
|
||||
.map(
|
||||
(option) => DropdownMenuItem<String>(
|
||||
value: option.value,
|
||||
child: Text(option.label),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
ref.read(inventorySortFilterProvider.notifier).state =
|
||||
value ?? '';
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (items.isEmpty) {
|
||||
return Stack(
|
||||
children: [
|
||||
const EmptyStateView(title: 'Inventariet är tomt.'),
|
||||
ListView(
|
||||
padding: const EdgeInsets.only(bottom: 88),
|
||||
children: [
|
||||
filterSection,
|
||||
const EmptyStateView(title: 'Inventariet ar tomt.'),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
@@ -42,10 +108,11 @@ class InventoryScreen extends ConsumerWidget {
|
||||
children: [
|
||||
ListView.separated(
|
||||
padding: const EdgeInsets.only(bottom: 88),
|
||||
itemCount: items.length,
|
||||
itemCount: items.length + 1,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
itemBuilder: (context, index) {
|
||||
final item = items[index];
|
||||
if (index == 0) return filterSection;
|
||||
final item = items[index - 1];
|
||||
return _InventoryTile(item: item);
|
||||
},
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user