Initial commit: Sage Kitchen Management App v1.0.0
✨ Features implemented: - Smart inventory tracking with Hive database - Barcode scanning with auto-populated product info - Multiple API fallbacks (Open Food Facts, UPCItemDB) - Smart expiration date predictions by category - Discord webhook notifications (persisted) - Custom sage leaf vector icon - Material Design 3 UI with sage green theme - Privacy Policy & Terms of Service - Local-first, privacy-focused architecture 🎨 UI/UX: - Home dashboard with inventory stats - Add Item screen with barcode integration - Inventory list with expiration indicators - Settings with persistent preferences - About section with legal docs 🔧 Technical: - Flutter 3.35.5 with Riverpod state management - Hive 2.2.3 for local database - Mobile scanner for barcode detection - Feature-first architecture 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import '../models/food_item.dart';
|
||||
|
||||
/// Repository interface for inventory operations
|
||||
/// This defines the contract for inventory data access
|
||||
abstract class InventoryRepository {
|
||||
/// Get all items in inventory
|
||||
Future<List<FoodItem>> getAllItems();
|
||||
|
||||
/// Get a single item by ID
|
||||
Future<FoodItem?> getItemById(int id);
|
||||
|
||||
/// Add a new item to inventory
|
||||
Future<void> addItem(FoodItem item);
|
||||
|
||||
/// Update an existing item
|
||||
Future<void> updateItem(FoodItem item);
|
||||
|
||||
/// Delete an item
|
||||
Future<void> deleteItem(int id);
|
||||
|
||||
/// Get items by location
|
||||
Future<List<FoodItem>> getItemsByLocation(Location location);
|
||||
|
||||
/// Get items expiring within X days
|
||||
Future<List<FoodItem>> getItemsExpiringWithinDays(int days);
|
||||
|
||||
/// Get all expired items
|
||||
Future<List<FoodItem>> getExpiredItems();
|
||||
|
||||
/// Search items by name
|
||||
Future<List<FoodItem>> searchItemsByName(String query);
|
||||
|
||||
/// Get count of all items
|
||||
Future<int> getItemCount();
|
||||
|
||||
/// Watch all items (stream for real-time updates)
|
||||
Stream<List<FoodItem>> watchAllItems();
|
||||
|
||||
/// Watch items expiring soon
|
||||
Stream<List<FoodItem>> watchExpiringItems(int days);
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
import 'package:hive/hive.dart';
|
||||
import '../../../data/local/hive_database.dart';
|
||||
import '../models/food_item.dart';
|
||||
import 'inventory_repository.dart';
|
||||
|
||||
/// Hive implementation of InventoryRepository
|
||||
class InventoryRepositoryImpl implements InventoryRepository {
|
||||
Future<Box<FoodItem>> get _box async => await HiveDatabase.getFoodBox();
|
||||
|
||||
@override
|
||||
Future<List<FoodItem>> getAllItems() async {
|
||||
final box = await _box;
|
||||
return box.values.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<FoodItem?> getItemById(int id) async {
|
||||
final box = await _box;
|
||||
return box.get(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addItem(FoodItem item) async {
|
||||
final box = await _box;
|
||||
item.lastModified = DateTime.now();
|
||||
await box.add(item);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateItem(FoodItem item) async {
|
||||
item.lastModified = DateTime.now();
|
||||
await item.save();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteItem(int id) async {
|
||||
final box = await _box;
|
||||
await box.delete(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<FoodItem>> getItemsByLocation(Location location) async {
|
||||
final box = await _box;
|
||||
return box.values
|
||||
.where((item) => item.location == location)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<FoodItem>> getItemsExpiringWithinDays(int days) async {
|
||||
final box = await _box;
|
||||
final targetDate = DateTime.now().add(Duration(days: days));
|
||||
return box.values
|
||||
.where((item) =>
|
||||
item.expirationDate.isBefore(targetDate) &&
|
||||
item.expirationDate.isAfter(DateTime.now()))
|
||||
.toList()
|
||||
..sort((a, b) => a.expirationDate.compareTo(b.expirationDate));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<FoodItem>> getExpiredItems() async {
|
||||
final box = await _box;
|
||||
return box.values
|
||||
.where((item) => item.expirationDate.isBefore(DateTime.now()))
|
||||
.toList()
|
||||
..sort((a, b) => a.expirationDate.compareTo(b.expirationDate));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<FoodItem>> searchItemsByName(String query) async {
|
||||
final box = await _box;
|
||||
final lowerQuery = query.toLowerCase();
|
||||
return box.values
|
||||
.where((item) => item.name.toLowerCase().contains(lowerQuery))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getItemCount() async {
|
||||
final box = await _box;
|
||||
return box.length;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<FoodItem>> watchAllItems() async* {
|
||||
final box = await _box;
|
||||
yield box.values.toList();
|
||||
|
||||
await for (final _ in box.watch()) {
|
||||
yield box.values.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<FoodItem>> watchExpiringItems(int days) async* {
|
||||
final box = await _box;
|
||||
final targetDate = DateTime.now().add(Duration(days: days));
|
||||
|
||||
yield box.values
|
||||
.where((item) =>
|
||||
item.expirationDate.isBefore(targetDate) &&
|
||||
item.expirationDate.isAfter(DateTime.now()))
|
||||
.toList();
|
||||
|
||||
await for (final _ in box.watch()) {
|
||||
yield box.values
|
||||
.where((item) =>
|
||||
item.expirationDate.isBefore(targetDate) &&
|
||||
item.expirationDate.isAfter(DateTime.now()))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user