import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/auth/auth_provider.dart'; import '../../shared/models/diary_entry.dart'; import '../../shared/models/menu.dart'; import '../../shared/models/shopping_item.dart'; import 'menu_service.dart'; // ── Service provider ────────────────────────────────────────── final menuServiceProvider = Provider((ref) { return MenuService(ref.read(apiClientProvider)); }); // ── Current week (state) ────────────────────────────────────── /// The ISO week string for the currently displayed week, e.g. "2026-W08". final currentWeekProvider = StateProvider((ref) { final now = DateTime.now().toUtc(); final (y, w) = _isoWeek(now); return '$y-W${w.toString().padLeft(2, '0')}'; }); (int year, int week) _isoWeek(DateTime dt) { // Shift to Thursday to get ISO week year. final thu = dt.add(Duration(days: 4 - (dt.weekday == 7 ? 0 : dt.weekday))); final jan1 = DateTime.utc(thu.year, 1, 1); final week = ((thu.difference(jan1).inDays) / 7).ceil(); return (thu.year, week); } /// Returns the ISO 8601 week string for [date], e.g. "2026-W12". String isoWeekString(DateTime date) { final (year, week) = _isoWeek(date.toUtc()); return '$year-W${week.toString().padLeft(2, '0')}'; } // ── Menu notifier ───────────────────────────────────────────── class MenuNotifier extends StateNotifier> { final MenuService _service; final String _week; MenuNotifier(this._service, this._week) : super(const AsyncValue.loading()) { load(); } Future load() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() => _service.getMenu(week: _week)); } Future generate() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() => _service.generateMenu(week: _week)); } Future updateItem(String itemId, String recipeId) async { await _service.updateMenuItem(itemId, recipeId); await load(); } Future deleteItem(String itemId) async { await _service.deleteMenuItem(itemId); await load(); } } final menuProvider = StateNotifierProvider.family, String>( (ref, week) => MenuNotifier(ref.read(menuServiceProvider), week), ); // ── Shopping list notifier ──────────────────────────────────── class ShoppingListNotifier extends StateNotifier>> { final MenuService _service; final String _week; ShoppingListNotifier(this._service, this._week) : super(const AsyncValue.loading()) { load(); } Future load() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() => _service.getShoppingList(week: _week)); } Future regenerate() async { state = const AsyncValue.loading(); state = await AsyncValue.guard( () => _service.generateShoppingList(week: _week)); } Future toggle(int index, bool checked) async { // Optimistic update. state = state.whenData((items) { final list = List.from(items); if (index < list.length) { list[index] = list[index].copyWith(checked: checked); } return list; }); try { await _service.toggleShoppingItem(index, checked, week: _week); } catch (_) { // Revert on failure. await load(); } } } final shoppingListProvider = StateNotifierProvider.family< ShoppingListNotifier, AsyncValue>, String>( (ref, week) => ShoppingListNotifier(ref.read(menuServiceProvider), week), ); // ── Diary notifier ──────────────────────────────────────────── class DiaryNotifier extends StateNotifier>> { final MenuService _service; final String _date; DiaryNotifier(this._service, this._date) : super(const AsyncValue.loading()) { load(); } Future load() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() => _service.getDiary(_date)); } Future add(Map body) async { await _service.createDiaryEntry(body); await load(); } Future remove(String id) async { final prev = state; state = state.whenData((l) => l.where((e) => e.id != id).toList()); try { await _service.deleteDiaryEntry(id); } catch (_) { state = prev; } } } final diaryProvider = StateNotifierProvider.family>, String>( (ref, date) => DiaryNotifier(ref.read(menuServiceProvider), date), );