feat: Add inventory management feature with CRUD operations
- Implemented inventory screen to display items with details. - Added create, edit, and consume inventory screens for managing items. - Introduced consumption history screen to track item usage. - Created inventory repository and providers for API interactions. - Enhanced routing to include inventory-related paths. - Added necessary models for inventory items and consumption history. - Integrated error handling and loading states for better user experience.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../core/ui/async_state_views.dart';
|
||||
import '../data/inventory_providers.dart';
|
||||
import '../domain/inventory_item.dart';
|
||||
|
||||
class InventoryScreen extends ConsumerWidget {
|
||||
const InventoryScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final inventoryAsync = ref.watch(inventoryProvider);
|
||||
|
||||
return inventoryAsync.when(
|
||||
loading: () => const LoadingStateView(label: 'Laddar inventarie...'),
|
||||
error: (e, _) => ErrorStateView(
|
||||
message: e.toString(),
|
||||
onRetry: () => ref.invalidate(inventoryProvider),
|
||||
),
|
||||
data: (items) {
|
||||
if (items.isEmpty) {
|
||||
return EmptyStateView(
|
||||
message: 'Inventariet är tomt.',
|
||||
action: FloatingActionButton.extended(
|
||||
onPressed: () => context.push('/inventory/create'),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Lägg till'),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Stack(
|
||||
children: [
|
||||
ListView.separated(
|
||||
padding: const EdgeInsets.only(bottom: 88),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
itemBuilder: (context, index) {
|
||||
final item = items[index];
|
||||
return _InventoryTile(item: item);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
child: FloatingActionButton.extended(
|
||||
onPressed: () => context.push('/inventory/create'),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Lägg till'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InventoryTile extends StatelessWidget {
|
||||
final InventoryItem item;
|
||||
|
||||
const _InventoryTile({required this.item});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final subtitle = [
|
||||
'${item.quantity} ${item.unit}',
|
||||
if (item.location != null && item.location!.isNotEmpty) item.location!,
|
||||
if (item.bestBeforeDate != null) 'Bäst före: ${_formatDate(item.bestBeforeDate!)}',
|
||||
].join(' · ');
|
||||
|
||||
return ListTile(
|
||||
title: Text(item.productName),
|
||||
subtitle: Text(subtitle),
|
||||
trailing: item.opened
|
||||
? const Chip(
|
||||
label: Text('Öppnad'),
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
)
|
||||
: null,
|
||||
onTap: () => context.push('/inventory/${item.id}'),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDate(String iso) {
|
||||
try {
|
||||
final dt = DateTime.parse(iso);
|
||||
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')}';
|
||||
} catch (_) {
|
||||
return iso;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user