Backend: - Migrations 007 (menu_plans, menu_items, shopping_lists) and 008 (meal_diary) - gemini/menu.go: GenerateMenu — 7-day × 3-meal plan via one Groq call - internal/menu: model, repository (GetByWeek, SaveMenuInTx, shopping list CRUD), handler (GET/PUT/DELETE /menu, POST /ai/generate-menu, shopping list endpoints) - internal/diary: model, repository, handler (GET/POST/DELETE /diary) - Increase server WriteTimeout to 120s for long AI calls - api_client.go: add patch() and postList() helpers Flutter: - shared/models: menu.dart, shopping_item.dart, diary_entry.dart - features/menu: menu_service.dart, menu_provider.dart (MenuNotifier, ShoppingListNotifier, DiaryNotifier with family) - MenuScreen: 7-day view, week nav, skeleton on generation, generate FAB with confirmation dialog - ShoppingListScreen: items by category, optimistic checkbox toggle - DiaryScreen: daily entries with swipe-to-delete, add-entry sheet - Router: /menu/shopping-list and /menu/diary routes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
57 lines
1.4 KiB
Dart
57 lines
1.4 KiB
Dart
class DiaryEntry {
|
|
final String id;
|
|
final String date;
|
|
final String mealType;
|
|
final String name;
|
|
final double portions;
|
|
final double? calories;
|
|
final double? proteinG;
|
|
final double? fatG;
|
|
final double? carbsG;
|
|
final String source;
|
|
final String? recipeId;
|
|
|
|
const DiaryEntry({
|
|
required this.id,
|
|
required this.date,
|
|
required this.mealType,
|
|
required this.name,
|
|
required this.portions,
|
|
this.calories,
|
|
this.proteinG,
|
|
this.fatG,
|
|
this.carbsG,
|
|
required this.source,
|
|
this.recipeId,
|
|
});
|
|
|
|
factory DiaryEntry.fromJson(Map<String, dynamic> json) {
|
|
return DiaryEntry(
|
|
id: json['id'] as String? ?? '',
|
|
date: json['date'] as String? ?? '',
|
|
mealType: json['meal_type'] as String? ?? '',
|
|
name: json['name'] as String? ?? '',
|
|
portions: (json['portions'] as num?)?.toDouble() ?? 1,
|
|
calories: (json['calories'] as num?)?.toDouble(),
|
|
proteinG: (json['protein_g'] as num?)?.toDouble(),
|
|
fatG: (json['fat_g'] as num?)?.toDouble(),
|
|
carbsG: (json['carbs_g'] as num?)?.toDouble(),
|
|
source: json['source'] as String? ?? 'manual',
|
|
recipeId: json['recipe_id'] as String?,
|
|
);
|
|
}
|
|
|
|
String get mealLabel {
|
|
switch (mealType) {
|
|
case 'breakfast':
|
|
return 'Завтрак';
|
|
case 'lunch':
|
|
return 'Обед';
|
|
case 'dinner':
|
|
return 'Ужин';
|
|
default:
|
|
return mealType;
|
|
}
|
|
}
|
|
}
|