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:
2025-10-04 13:54:21 -04:00
commit 7be7b270e6
155 changed files with 13133 additions and 0 deletions

View File

@@ -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);
}

View File

@@ -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();
}
}
}