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:
Nils-Johan Gynther
2026-04-22 08:12:37 +02:00
parent af1a3cd6eb
commit 967121113e
12 changed files with 1301 additions and 0 deletions
@@ -0,0 +1,40 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/api/api_providers.dart';
import '../../../core/api/guarded_api_call.dart';
import '../../auth/data/auth_providers.dart';
import '../domain/inventory_item.dart';
import '../domain/inventory_consumption.dart';
import 'inventory_repository.dart';
final inventoryRepositoryProvider = Provider<InventoryRepository>((ref) {
return InventoryRepository(ref.watch(apiClientProvider));
});
final inventoryProvider = FutureProvider<List<InventoryItem>>((ref) async {
final token = await ref.watch(authStateProvider.future);
return guardedApiCall(
ref,
() => ref.read(inventoryRepositoryProvider).fetchInventory(token: token),
);
});
final inventoryDetailProvider =
FutureProvider.family<InventoryItem, int>((ref, id) async {
final token = await ref.watch(authStateProvider.future);
return guardedApiCall(
ref,
() => ref.read(inventoryRepositoryProvider).fetchInventoryItem(id, token: token),
);
});
final consumptionHistoryProvider =
FutureProvider.family<List<InventoryConsumption>, int>((ref, id) async {
final token = await ref.watch(authStateProvider.future);
return guardedApiCall(
ref,
() => ref
.read(inventoryRepositoryProvider)
.fetchConsumptionHistory(id, token: token),
);
});
@@ -0,0 +1,76 @@
import '../../../core/api/api_client.dart';
import '../domain/inventory_item.dart';
import '../domain/inventory_consumption.dart';
class InventoryRepository {
final ApiClient _api;
const InventoryRepository(this._api);
Future<List<InventoryItem>> fetchInventory({
String? location,
String? sort,
String? token,
}) async {
final params = <String, String>{};
if (location != null && location.isNotEmpty) params['location'] = location;
if (sort != null && sort.isNotEmpty) params['sort'] = sort;
final query = params.isEmpty
? ''
: '?${params.entries.map((e) => '${e.key}=${Uri.encodeComponent(e.value)}').join('&')}';
final data = await _api.getJson('/inventory$query', token: token);
final list = data as List<dynamic>;
return list.map((e) => InventoryItem.fromJson(e as Map<String, dynamic>)).toList();
}
Future<InventoryItem> fetchInventoryItem(int id, {String? token}) async {
final data = await _api.getJson('/inventory/$id', token: token);
return InventoryItem.fromJson(data as Map<String, dynamic>);
}
Future<InventoryItem> createInventoryItem(
Map<String, dynamic> body, {
String? token,
}) async {
final data = await _api.postJson('/inventory', body: body, token: token);
return InventoryItem.fromJson(data as Map<String, dynamic>);
}
Future<InventoryItem> updateInventoryItem(
int id,
Map<String, dynamic> body, {
String? token,
}) async {
final data = await _api.patchJson('/inventory/$id', body: body, token: token);
return InventoryItem.fromJson(data as Map<String, dynamic>);
}
Future<void> deleteInventoryItem(int id, {String? token}) async {
await _api.deleteJson('/inventory/$id', token: token);
}
Future<InventoryItem> consumeInventoryItem(
int id, {
required double amountUsed,
String? comment,
String? token,
}) async {
final body = <String, dynamic>{'amountUsed': amountUsed};
if (comment != null && comment.isNotEmpty) body['comment'] = comment;
final data = await _api.postJson('/inventory/$id/consume', body: body, token: token);
return InventoryItem.fromJson(data as Map<String, dynamic>);
}
Future<List<InventoryConsumption>> fetchConsumptionHistory(
int id, {
String? token,
}) async {
final data = await _api.getJson('/inventory/$id/consumption-history', token: token);
final list = data as List<dynamic>;
return list
.map((e) => InventoryConsumption.fromJson(e as Map<String, dynamic>))
.toList();
}
}