fix: fix menu generation errors and show planned meals on home screen

Backend fixes:
- migration 003: add 'menu' value to recipe_source enum (was causing SQLSTATE 22P02)
- migration 004: rename recipe_products→recipe_ingredients, product_id→ingredient_id (was causing SQLSTATE 42P01)
- dish/repository.go: fix INSERT INTO tags using $1/$1 for two columns → $1/$2 (was causing SQLSTATE 42P08)
- home/handler.go: replace non-existent saved_recipes table with correct joins (recipes→dishes→dish_translations, user_saved_recipes) so today's plan and recommendations load correctly
- reqlog: new slog.Handler wrapper that adds request_id and stack trace to ERROR-level logs
- all handlers: slog.Error→slog.ErrorContext so error logs include request context; writeError includes request_id in response body

Client:
- home_screen.dart: extend home screen to future dates, show planned meals as ghost entries
- l10n: add new localisation keys for home screen date navigation and planned meal UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-22 00:35:11 +02:00
parent 9306d59d36
commit 5096df2102
49 changed files with 824 additions and 299 deletions

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "وضع علامة كمأكول",
"plannedMealLabel": "مخطط",
"generateWeekLabel": "تخطيط الأسبوع",
"generateWeekSubtitle": "سيقوم الذكاء الاصطناعي بإنشاء قائمة طعام تشمل الإفطار والغداء والعشاء لكامل الأسبوع",
"generatingMenu": "جارٍ إنشاء القائمة...",
"weekPlannedLabel": "تم تخطيط الأسبوع"
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "Als gegessen markieren",
"plannedMealLabel": "Geplant",
"generateWeekLabel": "Woche planen",
"generateWeekSubtitle": "KI erstellt einen Menüplan mit Frühstück, Mittagessen und Abendessen für die ganze Woche",
"generatingMenu": "Menü wird erstellt...",
"weekPlannedLabel": "Woche geplant"
}

View File

@@ -129,5 +129,9 @@
}
},
"markAsEaten": "Mark as eaten",
"plannedMealLabel": "Planned"
"plannedMealLabel": "Planned",
"generateWeekLabel": "Plan the week",
"generateWeekSubtitle": "AI will create a menu with breakfast, lunch and dinner for the whole week",
"generatingMenu": "Generating menu...",
"weekPlannedLabel": "Week planned"
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "Marcar como comido",
"plannedMealLabel": "Planificado",
"generateWeekLabel": "Planificar la semana",
"generateWeekSubtitle": "La IA creará un menú con desayuno, comida y cena para toda la semana",
"generatingMenu": "Generando menú...",
"weekPlannedLabel": "Semana planificada"
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "Marquer comme mangé",
"plannedMealLabel": "Planifié",
"generateWeekLabel": "Planifier la semaine",
"generateWeekSubtitle": "L'IA créera un menu avec petit-déjeuner, déjeuner et dîner pour toute la semaine",
"generatingMenu": "Génération du menu...",
"weekPlannedLabel": "Semaine planifiée"
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "खाया हुआ चिह्नित करें",
"plannedMealLabel": "नियोजित",
"generateWeekLabel": "सप्ताह की योजना बनाएं",
"generateWeekSubtitle": "AI पूरे सप्ताह के लिए नाश्ता, दोपहर का खाना और रात के खाने के साथ मेनू बनाएगा",
"generatingMenu": "मेनू बना रहे हैं...",
"weekPlannedLabel": "सप्ताह की योजना बनाई गई"
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "Segna come mangiato",
"plannedMealLabel": "Pianificato",
"generateWeekLabel": "Pianifica la settimana",
"generateWeekSubtitle": "L'AI creerà un menu con colazione, pranzo e cena per tutta la settimana",
"generatingMenu": "Generazione menu...",
"weekPlannedLabel": "Settimana pianificata"
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "食べた印をつける",
"plannedMealLabel": "予定済み",
"generateWeekLabel": "週を計画する",
"generateWeekSubtitle": "AIが一週間の朝食・昼食・夕食のメニューを作成します",
"generatingMenu": "メニューを生成中...",
"weekPlannedLabel": "週の計画済み"
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "먹은 것으로 표시",
"plannedMealLabel": "계획됨",
"generateWeekLabel": "주간 계획하기",
"generateWeekSubtitle": "AI가 한 주 동안 아침, 점심, 저녁 식사 메뉴를 만들어 드립니다",
"generatingMenu": "메뉴 생성 중...",
"weekPlannedLabel": "주간 계획 완료"
}

View File

@@ -807,6 +807,30 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Planned'**
String get plannedMealLabel;
/// No description provided for @generateWeekLabel.
///
/// In en, this message translates to:
/// **'Plan the week'**
String get generateWeekLabel;
/// No description provided for @generateWeekSubtitle.
///
/// In en, this message translates to:
/// **'AI will create a menu with breakfast, lunch and dinner for the whole week'**
String get generateWeekSubtitle;
/// No description provided for @generatingMenu.
///
/// In en, this message translates to:
/// **'Generating menu...'**
String get generatingMenu;
/// No description provided for @weekPlannedLabel.
///
/// In en, this message translates to:
/// **'Week planned'**
String get weekPlannedLabel;
}
class _AppLocalizationsDelegate

View File

@@ -355,8 +355,21 @@ class AppLocalizationsAr extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => 'وضع علامة كمأكول';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => 'مخطط';
@override
String get generateWeekLabel => 'تخطيط الأسبوع';
@override
String get generateWeekSubtitle =>
'سيقوم الذكاء الاصطناعي بإنشاء قائمة طعام تشمل الإفطار والغداء والعشاء لكامل الأسبوع';
@override
String get generatingMenu => 'جارٍ إنشاء القائمة...';
@override
String get weekPlannedLabel => 'تم تخطيط الأسبوع';
}

View File

@@ -357,8 +357,21 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => 'Als gegessen markieren';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => 'Geplant';
@override
String get generateWeekLabel => 'Woche planen';
@override
String get generateWeekSubtitle =>
'KI erstellt einen Menüplan mit Frühstück, Mittagessen und Abendessen für die ganze Woche';
@override
String get generatingMenu => 'Menü wird erstellt...';
@override
String get weekPlannedLabel => 'Woche geplant';
}

View File

@@ -359,4 +359,17 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get plannedMealLabel => 'Planned';
@override
String get generateWeekLabel => 'Plan the week';
@override
String get generateWeekSubtitle =>
'AI will create a menu with breakfast, lunch and dinner for the whole week';
@override
String get generatingMenu => 'Generating menu...';
@override
String get weekPlannedLabel => 'Week planned';
}

View File

@@ -357,8 +357,21 @@ class AppLocalizationsEs extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => 'Marcar como comido';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => 'Planificado';
@override
String get generateWeekLabel => 'Planificar la semana';
@override
String get generateWeekSubtitle =>
'La IA creará un menú con desayuno, comida y cena para toda la semana';
@override
String get generatingMenu => 'Generando menú...';
@override
String get weekPlannedLabel => 'Semana planificada';
}

View File

@@ -358,8 +358,21 @@ class AppLocalizationsFr extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => 'Marquer comme mangé';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => 'Planifié';
@override
String get generateWeekLabel => 'Planifier la semaine';
@override
String get generateWeekSubtitle =>
'L\'IA créera un menu avec petit-déjeuner, déjeuner et dîner pour toute la semaine';
@override
String get generatingMenu => 'Génération du menu...';
@override
String get weekPlannedLabel => 'Semaine planifiée';
}

View File

@@ -356,8 +356,21 @@ class AppLocalizationsHi extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => 'खाया हुआ चिह्नित करें';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => 'नियोजित';
@override
String get generateWeekLabel => 'सप्ताह की योजना बनाएं';
@override
String get generateWeekSubtitle =>
'AI पूरे सप्ताह के लिए नाश्ता, दोपहर का खाना और रात के खाने के साथ मेनू बनाएगा';
@override
String get generatingMenu => 'मेनू बना रहे हैं...';
@override
String get weekPlannedLabel => 'सप्ताह की योजना बनाई गई';
}

View File

@@ -357,8 +357,21 @@ class AppLocalizationsIt extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => 'Segna come mangiato';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => 'Pianificato';
@override
String get generateWeekLabel => 'Pianifica la settimana';
@override
String get generateWeekSubtitle =>
'L\'AI creerà un menu con colazione, pranzo e cena per tutta la settimana';
@override
String get generatingMenu => 'Generazione menu...';
@override
String get weekPlannedLabel => 'Settimana pianificata';
}

View File

@@ -354,8 +354,20 @@ class AppLocalizationsJa extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => '食べた印をつける';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => '予定済み';
@override
String get generateWeekLabel => '週を計画する';
@override
String get generateWeekSubtitle => 'AIが一週間の朝食・昼食・夕食のメニューを作成します';
@override
String get generatingMenu => 'メニューを生成中...';
@override
String get weekPlannedLabel => '週の計画済み';
}

View File

@@ -354,8 +354,20 @@ class AppLocalizationsKo extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => '먹은 것으로 표시';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => '계획됨';
@override
String get generateWeekLabel => '주간 계획하기';
@override
String get generateWeekSubtitle => 'AI가 한 주 동안 아침, 점심, 저녁 식사 메뉴를 만들어 드립니다';
@override
String get generatingMenu => '메뉴 생성 중...';
@override
String get weekPlannedLabel => '주간 계획 완료';
}

View File

@@ -357,8 +357,21 @@ class AppLocalizationsPt extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => 'Marcar como comido';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => 'Planejado';
@override
String get generateWeekLabel => 'Planejar a semana';
@override
String get generateWeekSubtitle =>
'A IA criará um menu com café da manhã, almoço e jantar para a semana inteira';
@override
String get generatingMenu => 'Gerando menu...';
@override
String get weekPlannedLabel => 'Semana planejada';
}

View File

@@ -359,4 +359,17 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get plannedMealLabel => 'Запланировано';
@override
String get generateWeekLabel => 'Запланировать неделю';
@override
String get generateWeekSubtitle =>
'AI составит меню с завтраком, обедом и ужином на всю неделю';
@override
String get generatingMenu => 'Генерируем меню...';
@override
String get weekPlannedLabel => 'Неделя запланирована';
}

View File

@@ -354,8 +354,20 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
String get markAsEaten => '';
String get markAsEaten => '标记为已吃';
@override
String get plannedMealLabel => '';
String get plannedMealLabel => '已计划';
@override
String get generateWeekLabel => '规划本周';
@override
String get generateWeekSubtitle => 'AI将为整周创建含早餐、午餐和晚餐的菜单';
@override
String get generatingMenu => '正在生成菜单...';
@override
String get weekPlannedLabel => '本周已规划';
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "Marcar como comido",
"plannedMealLabel": "Planejado",
"generateWeekLabel": "Planejar a semana",
"generateWeekSubtitle": "A IA criará um menu com café da manhã, almoço e jantar para a semana inteira",
"generatingMenu": "Gerando menu...",
"weekPlannedLabel": "Semana planejada"
}

View File

@@ -129,5 +129,9 @@
}
},
"markAsEaten": "Отметить как съеденное",
"plannedMealLabel": "Запланировано"
"plannedMealLabel": "Запланировано",
"generateWeekLabel": "Запланировать неделю",
"generateWeekSubtitle": "AI составит меню с завтраком, обедом и ужином на всю неделю",
"generatingMenu": "Генерируем меню...",
"weekPlannedLabel": "Неделя запланирована"
}

View File

@@ -130,6 +130,10 @@
"date": { "type": "String" }
}
},
"markAsEaten": "",
"plannedMealLabel": ""
"markAsEaten": "标记为已吃",
"plannedMealLabel": "已计划",
"generateWeekLabel": "规划本周",
"generateWeekSubtitle": "AI将为整周创建含早餐、午餐和晚餐的菜单",
"generatingMenu": "正在生成菜单...",
"weekPlannedLabel": "本周已规划"
}