fix: unify date limits, fix ISO week calculation, refactor home screen plan button

- Fix _isoWeek: correct Sunday shift (-3 instead of +4), use floor+1 formula,
  match jan1 timezone to input — planned meals now appear correctly for UTC+ users
- Add kPlanningHorizonDays=28 / kMenuPastWeeks=8 constants; apply to home date
  strip, plan picker (strip + calendar), and menu screen prev/next navigation
- Menu screen week nav: disable arrows at min/max limits using compareTo
- Home screen: replace _GenerateActionCard/_WeekPlannedChip conditional with
  always-visible _FutureDayPlanButton(dateString); show _DayPlannedChip only
  when the specific day has planned meals; remove standalone _PlanMenuButton
- _FutureDayPlanButton uses selected date as defaultStart instead of lastPlanned+1
- Rename weekPlannedLabel -> dayPlannedLabel across all 12 locales

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-22 23:25:46 +02:00
parent edf587e798
commit 9a6b7800a3
30 changed files with 106 additions and 167 deletions

View File

@@ -12,6 +12,7 @@ import '../../core/storage/local_preferences_provider.dart';
import '../../core/theme/app_colors.dart'; import '../../core/theme/app_colors.dart';
import '../../shared/models/diary_entry.dart'; import '../../shared/models/diary_entry.dart';
import '../../shared/models/home_summary.dart'; import '../../shared/models/home_summary.dart';
import '../../shared/constants/date_limits.dart';
import '../../shared/models/meal_type.dart'; import '../../shared/models/meal_type.dart';
import '../../shared/models/menu.dart'; import '../../shared/models/menu.dart';
import '../diary/food_search_sheet.dart'; import '../diary/food_search_sheet.dart';
@@ -128,8 +129,6 @@ class HomeScreen extends ConsumerWidget {
], ],
const SizedBox(height: 16), const SizedBox(height: 16),
_QuickActionsRow(), _QuickActionsRow(),
const SizedBox(height: 8),
_PlanMenuButton(),
if (!isFutureDate && recommendations.isNotEmpty) ...[ if (!isFutureDate && recommendations.isNotEmpty) ...[
const SizedBox(height: 20), const SizedBox(height: 20),
_SectionTitle(l10n.recommendCook), _SectionTitle(l10n.recommendCook),
@@ -191,10 +190,10 @@ class _DateSelector extends StatefulWidget {
} }
class _DateSelectorState extends State<_DateSelector> { class _DateSelectorState extends State<_DateSelector> {
// Strip covers 7 future days + today + 364 past days = 372 items total. // Strip covers kPlanningHorizonDays future days + today + 364 past days.
// With reverse: true, index 0 is rendered at the RIGHT edge (newest). // With reverse: true, index 0 is rendered at the RIGHT edge (newest).
// index 0 = today + 7, index 7 = today, index 371 = today - 364. // index 0 = today + kPlanningHorizonDays, index kPlanningHorizonDays = today.
static const _futureDays = 7; static const _futureDays = kPlanningHorizonDays;
static const _pastDays = 364; static const _pastDays = 364;
static const _totalDays = _futureDays + 1 + _pastDays; // 372 static const _totalDays = _futureDays + 1 + _pastDays; // 372
static const _pillWidth = 48.0; static const _pillWidth = 48.0;
@@ -1156,125 +1155,26 @@ class _FutureDayHeader extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
final date = DateTime.parse(dateString); final plannedMeals = ref.watch(plannedMealsProvider(dateString));
final weekString = isoWeekString(date);
final menuState = ref.watch(menuProvider(weekString));
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
_PlanningBanner(dateString: dateString), _PlanningBanner(dateString: dateString),
const SizedBox(height: 8), const SizedBox(height: 8),
menuState.when( _FutureDayPlanButton(dateString: dateString),
loading: () => _GenerateLoadingCard(l10n: l10n), if (plannedMeals.isNotEmpty) ...[
error: (_, __) => _GenerateActionCard( const SizedBox(height: 8),
l10n: l10n, _DayPlannedChip(l10n: l10n),
onGenerate: () => ],
ref.read(menuProvider(weekString).notifier).generate(),
),
data: (plan) => plan == null
? _GenerateActionCard(
l10n: l10n,
onGenerate: () =>
ref.read(menuProvider(weekString).notifier).generate(),
)
: _WeekPlannedChip(l10n: l10n),
),
], ],
); );
} }
} }
class _GenerateActionCard extends StatelessWidget { class _DayPlannedChip extends StatelessWidget {
final AppLocalizations l10n; final AppLocalizations l10n;
final VoidCallback onGenerate; const _DayPlannedChip({required this.l10n});
const _GenerateActionCard({required this.l10n, required this.onGenerate});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.auto_awesome,
color: theme.colorScheme.primary, size: 20),
const SizedBox(width: 8),
Expanded(
child: Text(
l10n.generateWeekLabel,
style: theme.textTheme.titleSmall?.copyWith(
color: theme.colorScheme.onSurface,
fontWeight: FontWeight.w600,
),
),
),
],
),
const SizedBox(height: 4),
Text(
l10n.generateWeekSubtitle,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 12),
FilledButton(
onPressed: onGenerate,
child: Text(l10n.generateWeekLabel),
),
],
),
);
}
}
class _GenerateLoadingCard extends StatelessWidget {
final AppLocalizations l10n;
const _GenerateLoadingCard({required this.l10n});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator.adaptive(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation(theme.colorScheme.primary),
),
),
const SizedBox(width: 12),
Text(
l10n.generatingMenu,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
);
}
}
class _WeekPlannedChip extends StatelessWidget {
final AppLocalizations l10n;
const _WeekPlannedChip({required this.l10n});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -1292,7 +1192,7 @@ class _WeekPlannedChip extends StatelessWidget {
color: theme.colorScheme.onSecondaryContainer, size: 18), color: theme.colorScheme.onSecondaryContainer, size: 18),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
l10n.weekPlannedLabel, l10n.dayPlannedLabel,
style: theme.textTheme.bodySmall?.copyWith( style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSecondaryContainer, color: theme.colorScheme.onSecondaryContainer,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@@ -1647,8 +1547,9 @@ class _ActionButton extends StatelessWidget {
// ── Plan menu button ────────────────────────────────────────── // ── Plan menu button ──────────────────────────────────────────
class _PlanMenuButton extends ConsumerWidget { class _FutureDayPlanButton extends ConsumerWidget {
const _PlanMenuButton(); final String dateString;
const _FutureDayPlanButton({required this.dateString});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -1682,16 +1583,13 @@ class _PlanMenuButton extends ConsumerWidget {
} }
void _openPlanSheet(BuildContext context, WidgetRef ref) { void _openPlanSheet(BuildContext context, WidgetRef ref) {
final defaultStart = DateTime.parse(dateString);
showModalBottomSheet<void>( showModalBottomSheet<void>(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
useSafeArea: true, useSafeArea: true,
builder: (_) => PlanMenuSheet( builder: (_) => PlanMenuSheet(
onModeSelected: (mode) { onModeSelected: (mode) {
final lastPlanned = ref.read(lastPlannedDateProvider);
final defaultStart = lastPlanned != null
? lastPlanned.add(const Duration(days: 1))
: DateTime.now().add(const Duration(days: 1));
showModalBottomSheet<void>( showModalBottomSheet<void>(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,

View File

@@ -16,22 +16,27 @@ final menuServiceProvider = Provider<MenuService>((ref) {
/// The ISO week string for the currently displayed week, e.g. "2026-W08". /// The ISO week string for the currently displayed week, e.g. "2026-W08".
final currentWeekProvider = StateProvider<String>((ref) { final currentWeekProvider = StateProvider<String>((ref) {
final now = DateTime.now().toUtc(); final now = DateTime.now();
final (y, w) = _isoWeek(now); final (y, w) = _isoWeek(now);
return '$y-W${w.toString().padLeft(2, '0')}'; return '$y-W${w.toString().padLeft(2, '0')}';
}); });
(int year, int week) _isoWeek(DateTime dt) { (int year, int week) _isoWeek(DateTime dt) {
// Shift to Thursday to get ISO week year. // Shift to Thursday of the same ISO week.
final thu = dt.add(Duration(days: 4 - (dt.weekday == 7 ? 0 : dt.weekday))); // Monday=1…Saturday=6 → add (4 - weekday) days; Sunday=7 → subtract 3 days.
final jan1 = DateTime.utc(thu.year, 1, 1); final int shift = dt.weekday == 7 ? -3 : 4 - dt.weekday;
final week = ((thu.difference(jan1).inDays) / 7).ceil(); final thu = dt.add(Duration(days: shift));
// Use the same timezone as the input to avoid offset drift on the difference.
final jan1 = dt.isUtc
? DateTime.utc(thu.year, 1, 1)
: DateTime(thu.year, 1, 1);
final week = (thu.difference(jan1).inDays ~/ 7) + 1;
return (thu.year, week); return (thu.year, week);
} }
/// Returns the ISO 8601 week string for [date], e.g. "2026-W12". /// Returns the ISO 8601 week string for [date], e.g. "2026-W12".
String isoWeekString(DateTime date) { String isoWeekString(DateTime date) {
final (year, week) = _isoWeek(date.toUtc()); final (year, week) = _isoWeek(date);
return '$year-W${week.toString().padLeft(2, '0')}'; return '$year-W${week.toString().padLeft(2, '0')}';
} }

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../shared/constants/date_limits.dart';
import '../../shared/models/menu.dart'; import '../../shared/models/menu.dart';
import 'menu_provider.dart'; import 'menu_provider.dart';
@@ -95,31 +96,48 @@ class _WeekNavBar extends StatelessWidget implements PreferredSizeWidget {
} }
(int, int) _isoWeekOf(DateTime dt) { (int, int) _isoWeekOf(DateTime dt) {
final thu = dt.add(Duration(days: 4 - (dt.weekday == 7 ? 0 : dt.weekday))); final int shift = dt.weekday == 7 ? -3 : 4 - dt.weekday;
final jan1 = DateTime.utc(thu.year, 1, 1); final thu = dt.add(Duration(days: shift));
final w = ((thu.difference(jan1).inDays) / 7).ceil(); final jan1 = dt.isUtc
? DateTime.utc(thu.year, 1, 1)
: DateTime(thu.year, 1, 1);
final w = (thu.difference(jan1).inDays ~/ 7) + 1;
return (thu.year, w); return (thu.year, w);
} }
String _currentWeekString(DateTime date) {
final (y, w) = _isoWeekOf(date);
return '$y-W${w.toString().padLeft(2, '0')}';
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final now = DateTime.now();
final maxWeek = _currentWeekString(
now.add(const Duration(days: kPlanningHorizonDays)));
final minWeek = _currentWeekString(
now.subtract(Duration(days: kMenuPastWeeks * 7)));
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
IconButton( IconButton(
icon: const Icon(Icons.chevron_left), icon: const Icon(Icons.chevron_left),
onPressed: () { onPressed: week.compareTo(minWeek) <= 0
ref.read(currentWeekProvider.notifier).state = ? null
_offsetWeek(week, -1); : () {
}, ref.read(currentWeekProvider.notifier).state =
_offsetWeek(week, -1);
},
), ),
Text(_weekLabel(week), style: Theme.of(context).textTheme.bodyMedium), Text(_weekLabel(week), style: Theme.of(context).textTheme.bodyMedium),
IconButton( IconButton(
icon: const Icon(Icons.chevron_right), icon: const Icon(Icons.chevron_right),
onPressed: () { onPressed: week.compareTo(maxWeek) >= 0
ref.read(currentWeekProvider.notifier).state = ? null
_offsetWeek(week, 1); : () {
}, ref.read(currentWeekProvider.notifier).state =
_offsetWeek(week, 1);
},
), ),
], ],
); );

View File

@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:food_ai/l10n/app_localizations.dart'; import 'package:food_ai/l10n/app_localizations.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../shared/constants/date_limits.dart';
import '../../shared/models/meal_type.dart'; import '../../shared/models/meal_type.dart';
import '../profile/profile_provider.dart'; import '../profile/profile_provider.dart';
import 'menu_provider.dart'; import 'menu_provider.dart';
@@ -279,8 +280,8 @@ class _DateStripSelector extends StatefulWidget {
class _DateStripSelectorState extends State<_DateStripSelector> { class _DateStripSelectorState extends State<_DateStripSelector> {
late final ScrollController _scrollController; late final ScrollController _scrollController;
// Show 30 upcoming days (today excluded, starts tomorrow). // Show upcoming days up to the planning horizon (today excluded, starts tomorrow).
static const _futureDays = 30; static const _futureDays = kPlanningHorizonDays;
static const _itemWidth = 64.0; static const _itemWidth = 64.0;
DateTime get _tomorrow => DateTime get _tomorrow =>
@@ -407,12 +408,22 @@ class _CalendarRangePickerState extends State<_CalendarRangePicker> {
} }
void _nextMonth() { void _nextMonth() {
final horizon = DateTime.now().add(const Duration(days: kPlanningHorizonDays));
final limitMonth = DateTime(horizon.year, horizon.month);
if (!DateTime(_displayMonth.year, _displayMonth.month).isBefore(limitMonth)) return;
setState(() { setState(() {
_displayMonth = _displayMonth =
DateTime(_displayMonth.year, _displayMonth.month + 1); DateTime(_displayMonth.year, _displayMonth.month + 1);
}); });
} }
bool _isBeyondHorizon(DateTime date) {
final today = DateTime.now();
final horizon = DateTime(today.year, today.month, today.day)
.add(const Duration(days: kPlanningHorizonDays));
return date.isAfter(horizon);
}
bool _isInRange(DateTime date) { bool _isInRange(DateTime date) {
final dayOnly = DateTime(date.year, date.month, date.day); final dayOnly = DateTime(date.year, date.month, date.day);
final start = final start =
@@ -510,11 +521,13 @@ class _CalendarRangePickerState extends State<_CalendarRangePicker> {
final isStart = _isRangeStart(date); final isStart = _isRangeStart(date);
final isEnd = _isRangeEnd(date); final isEnd = _isRangeEnd(date);
final isPast = _isPast(date); final isPast = _isPast(date);
final isBeyond = _isBeyondHorizon(date);
final isDisabled = isPast || isBeyond;
Color bgColor = Colors.transparent; Color bgColor = Colors.transparent;
Color textColor = theme.colorScheme.onSurface; Color textColor = theme.colorScheme.onSurface;
if (isPast) { if (isDisabled) {
// ignore: deprecated_member_use // ignore: deprecated_member_use
textColor = theme.colorScheme.onSurface.withOpacity(0.3); textColor = theme.colorScheme.onSurface.withOpacity(0.3);
} else if (isStart || isEnd) { } else if (isStart || isEnd) {
@@ -526,7 +539,7 @@ class _CalendarRangePickerState extends State<_CalendarRangePicker> {
} }
return GestureDetector( return GestureDetector(
onTap: isPast ? null : () => widget.onDayTapped(date), onTap: isDisabled ? null : () => widget.onDayTapped(date),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: bgColor, color: bgColor,

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "تخطيط الأسبوع", "generateWeekLabel": "تخطيط الأسبوع",
"generateWeekSubtitle": "سيقوم الذكاء الاصطناعي بإنشاء قائمة طعام تشمل الإفطار والغداء والعشاء لكامل الأسبوع", "generateWeekSubtitle": "سيقوم الذكاء الاصطناعي بإنشاء قائمة طعام تشمل الإفطار والغداء والعشاء لكامل الأسبوع",
"generatingMenu": "جارٍ إنشاء القائمة...", "generatingMenu": "جارٍ إنشاء القائمة...",
"weekPlannedLabel": "تم تخطيط الأسبوع", "dayPlannedLabel": "تم تخطيط اليوم",
"planMenuButton": "تخطيط الوجبات", "planMenuButton": "تخطيط الوجبات",
"planMenuTitle": "ماذا تريد تخطيطه؟", "planMenuTitle": "ماذا تريد تخطيطه؟",

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "Woche planen", "generateWeekLabel": "Woche planen",
"generateWeekSubtitle": "KI erstellt einen Menüplan mit Frühstück, Mittagessen und Abendessen für die ganze Woche", "generateWeekSubtitle": "KI erstellt einen Menüplan mit Frühstück, Mittagessen und Abendessen für die ganze Woche",
"generatingMenu": "Menü wird erstellt...", "generatingMenu": "Menü wird erstellt...",
"weekPlannedLabel": "Woche geplant", "dayPlannedLabel": "Tag geplant",
"planMenuButton": "Mahlzeiten planen", "planMenuButton": "Mahlzeiten planen",
"planMenuTitle": "Was planen?", "planMenuTitle": "Was planen?",

View File

@@ -133,7 +133,7 @@
"generateWeekLabel": "Plan the week", "generateWeekLabel": "Plan the week",
"generateWeekSubtitle": "AI will create a menu with breakfast, lunch and dinner for the whole week", "generateWeekSubtitle": "AI will create a menu with breakfast, lunch and dinner for the whole week",
"generatingMenu": "Generating menu...", "generatingMenu": "Generating menu...",
"weekPlannedLabel": "Week planned", "dayPlannedLabel": "Day planned",
"planMenuButton": "Plan meals", "planMenuButton": "Plan meals",
"planMenuTitle": "What to plan?", "planMenuTitle": "What to plan?",

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "Planificar la semana", "generateWeekLabel": "Planificar la semana",
"generateWeekSubtitle": "La IA creará un menú con desayuno, comida y cena para toda la semana", "generateWeekSubtitle": "La IA creará un menú con desayuno, comida y cena para toda la semana",
"generatingMenu": "Generando menú...", "generatingMenu": "Generando menú...",
"weekPlannedLabel": "Semana planificada", "dayPlannedLabel": "a planificado",
"planMenuButton": "Planificar comidas", "planMenuButton": "Planificar comidas",
"planMenuTitle": "¿Qué planificar?", "planMenuTitle": "¿Qué planificar?",

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "Planifier la semaine", "generateWeekLabel": "Planifier la semaine",
"generateWeekSubtitle": "L'IA créera un menu avec petit-déjeuner, déjeuner et dîner pour toute 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...", "generatingMenu": "Génération du menu...",
"weekPlannedLabel": "Semaine planifiée", "dayPlannedLabel": "Jour planifié",
"planMenuButton": "Planifier les repas", "planMenuButton": "Planifier les repas",
"planMenuTitle": "Que planifier ?", "planMenuTitle": "Que planifier ?",

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "सप्ताह की योजना बनाएं", "generateWeekLabel": "सप्ताह की योजना बनाएं",
"generateWeekSubtitle": "AI पूरे सप्ताह के लिए नाश्ता, दोपहर का खाना और रात के खाने के साथ मेनू बनाएगा", "generateWeekSubtitle": "AI पूरे सप्ताह के लिए नाश्ता, दोपहर का खाना और रात के खाने के साथ मेनू बनाएगा",
"generatingMenu": "मेनू बना रहे हैं...", "generatingMenu": "मेनू बना रहे हैं...",
"weekPlannedLabel": "सप्ताह की योजना बनाई गई", "dayPlannedLabel": "दिन की योजना बनाई गई",
"planMenuButton": "भोजन की योजना बनाएं", "planMenuButton": "भोजन की योजना बनाएं",
"planMenuTitle": "क्या योजना बनानी है?", "planMenuTitle": "क्या योजना बनानी है?",

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "Pianifica la settimana", "generateWeekLabel": "Pianifica la settimana",
"generateWeekSubtitle": "L'AI creerà un menu con colazione, pranzo e cena per tutta la settimana", "generateWeekSubtitle": "L'AI creerà un menu con colazione, pranzo e cena per tutta la settimana",
"generatingMenu": "Generazione menu...", "generatingMenu": "Generazione menu...",
"weekPlannedLabel": "Settimana pianificata", "dayPlannedLabel": "Giorno pianificato",
"planMenuButton": "Pianifica i pasti", "planMenuButton": "Pianifica i pasti",
"planMenuTitle": "Cosa pianificare?", "planMenuTitle": "Cosa pianificare?",

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "週を計画する", "generateWeekLabel": "週を計画する",
"generateWeekSubtitle": "AIが一週間の朝食・昼食・夕食のメニューを作成します", "generateWeekSubtitle": "AIが一週間の朝食・昼食・夕食のメニューを作成します",
"generatingMenu": "メニューを生成中...", "generatingMenu": "メニューを生成中...",
"weekPlannedLabel": "の計画済み", "dayPlannedLabel": "の計画済み",
"planMenuButton": "食事を計画する", "planMenuButton": "食事を計画する",
"planMenuTitle": "何を計画する?", "planMenuTitle": "何を計画する?",

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "주간 계획하기", "generateWeekLabel": "주간 계획하기",
"generateWeekSubtitle": "AI가 한 주 동안 아침, 점심, 저녁 식사 메뉴를 만들어 드립니다", "generateWeekSubtitle": "AI가 한 주 동안 아침, 점심, 저녁 식사 메뉴를 만들어 드립니다",
"generatingMenu": "메뉴 생성 중...", "generatingMenu": "메뉴 생성 중...",
"weekPlannedLabel": "주간 계획 완료", "dayPlannedLabel": "일일 계획 완료",
"planMenuButton": "식사 계획하기", "planMenuButton": "식사 계획하기",
"planMenuTitle": "무엇을 계획하시겠어요?", "planMenuTitle": "무엇을 계획하시겠어요?",

View File

@@ -826,11 +826,11 @@ abstract class AppLocalizations {
/// **'Generating menu...'** /// **'Generating menu...'**
String get generatingMenu; String get generatingMenu;
/// No description provided for @weekPlannedLabel. /// No description provided for @dayPlannedLabel.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Week planned'** /// **'Day planned'**
String get weekPlannedLabel; String get dayPlannedLabel;
/// No description provided for @planMenuButton. /// No description provided for @planMenuButton.
/// ///

View File

@@ -371,7 +371,7 @@ class AppLocalizationsAr extends AppLocalizations {
String get generatingMenu => 'جارٍ إنشاء القائمة...'; String get generatingMenu => 'جارٍ إنشاء القائمة...';
@override @override
String get weekPlannedLabel => 'تم تخطيط الأسبوع'; String get dayPlannedLabel => 'تم تخطيط اليوم';
@override @override
String get planMenuButton => 'تخطيط الوجبات'; String get planMenuButton => 'تخطيط الوجبات';

View File

@@ -373,7 +373,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get generatingMenu => 'Menü wird erstellt...'; String get generatingMenu => 'Menü wird erstellt...';
@override @override
String get weekPlannedLabel => 'Woche geplant'; String get dayPlannedLabel => 'Tag geplant';
@override @override
String get planMenuButton => 'Mahlzeiten planen'; String get planMenuButton => 'Mahlzeiten planen';

View File

@@ -371,7 +371,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get generatingMenu => 'Generating menu...'; String get generatingMenu => 'Generating menu...';
@override @override
String get weekPlannedLabel => 'Week planned'; String get dayPlannedLabel => 'Day planned';
@override @override
String get planMenuButton => 'Plan meals'; String get planMenuButton => 'Plan meals';

View File

@@ -373,7 +373,7 @@ class AppLocalizationsEs extends AppLocalizations {
String get generatingMenu => 'Generando menú...'; String get generatingMenu => 'Generando menú...';
@override @override
String get weekPlannedLabel => 'Semana planificada'; String get dayPlannedLabel => 'a planificado';
@override @override
String get planMenuButton => 'Planificar comidas'; String get planMenuButton => 'Planificar comidas';

View File

@@ -374,7 +374,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get generatingMenu => 'Génération du menu...'; String get generatingMenu => 'Génération du menu...';
@override @override
String get weekPlannedLabel => 'Semaine planifiée'; String get dayPlannedLabel => 'Jour planifié';
@override @override
String get planMenuButton => 'Planifier les repas'; String get planMenuButton => 'Planifier les repas';

View File

@@ -372,7 +372,7 @@ class AppLocalizationsHi extends AppLocalizations {
String get generatingMenu => 'मेनू बना रहे हैं...'; String get generatingMenu => 'मेनू बना रहे हैं...';
@override @override
String get weekPlannedLabel => 'सप्ताह की योजना बनाई गई'; String get dayPlannedLabel => 'दिन की योजना बनाई गई';
@override @override
String get planMenuButton => 'भोजन की योजना बनाएं'; String get planMenuButton => 'भोजन की योजना बनाएं';

View File

@@ -373,7 +373,7 @@ class AppLocalizationsIt extends AppLocalizations {
String get generatingMenu => 'Generazione menu...'; String get generatingMenu => 'Generazione menu...';
@override @override
String get weekPlannedLabel => 'Settimana pianificata'; String get dayPlannedLabel => 'Giorno pianificato';
@override @override
String get planMenuButton => 'Pianifica i pasti'; String get planMenuButton => 'Pianifica i pasti';

View File

@@ -369,7 +369,7 @@ class AppLocalizationsJa extends AppLocalizations {
String get generatingMenu => 'メニューを生成中...'; String get generatingMenu => 'メニューを生成中...';
@override @override
String get weekPlannedLabel => 'の計画済み'; String get dayPlannedLabel => 'の計画済み';
@override @override
String get planMenuButton => '食事を計画する'; String get planMenuButton => '食事を計画する';

View File

@@ -369,7 +369,7 @@ class AppLocalizationsKo extends AppLocalizations {
String get generatingMenu => '메뉴 생성 중...'; String get generatingMenu => '메뉴 생성 중...';
@override @override
String get weekPlannedLabel => '주간 계획 완료'; String get dayPlannedLabel => '일일 계획 완료';
@override @override
String get planMenuButton => '식사 계획하기'; String get planMenuButton => '식사 계획하기';

View File

@@ -373,7 +373,7 @@ class AppLocalizationsPt extends AppLocalizations {
String get generatingMenu => 'Gerando menu...'; String get generatingMenu => 'Gerando menu...';
@override @override
String get weekPlannedLabel => 'Semana planejada'; String get dayPlannedLabel => 'Dia planejado';
@override @override
String get planMenuButton => 'Planejar refeições'; String get planMenuButton => 'Planejar refeições';

View File

@@ -371,7 +371,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get generatingMenu => 'Генерируем меню...'; String get generatingMenu => 'Генерируем меню...';
@override @override
String get weekPlannedLabel => 'Неделя запланирована'; String get dayPlannedLabel => 'День запланирован';
@override @override
String get planMenuButton => 'Спланировать меню'; String get planMenuButton => 'Спланировать меню';

View File

@@ -369,7 +369,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get generatingMenu => '正在生成菜单...'; String get generatingMenu => '正在生成菜单...';
@override @override
String get weekPlannedLabel => '本周已规划'; String get dayPlannedLabel => '今日已规划';
@override @override
String get planMenuButton => '规划餐食'; String get planMenuButton => '规划餐食';

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "Planejar a semana", "generateWeekLabel": "Planejar a semana",
"generateWeekSubtitle": "A IA criará um menu com café da manhã, almoço e jantar para a semana inteira", "generateWeekSubtitle": "A IA criará um menu com café da manhã, almoço e jantar para a semana inteira",
"generatingMenu": "Gerando menu...", "generatingMenu": "Gerando menu...",
"weekPlannedLabel": "Semana planejada", "dayPlannedLabel": "Dia planejado",
"planMenuButton": "Planejar refeições", "planMenuButton": "Planejar refeições",
"planMenuTitle": "O que planejar?", "planMenuTitle": "O que planejar?",

View File

@@ -133,7 +133,7 @@
"generateWeekLabel": "Запланировать неделю", "generateWeekLabel": "Запланировать неделю",
"generateWeekSubtitle": "AI составит меню с завтраком, обедом и ужином на всю неделю", "generateWeekSubtitle": "AI составит меню с завтраком, обедом и ужином на всю неделю",
"generatingMenu": "Генерируем меню...", "generatingMenu": "Генерируем меню...",
"weekPlannedLabel": "Неделя запланирована", "dayPlannedLabel": "День запланирован",
"planMenuButton": "Спланировать меню", "planMenuButton": "Спланировать меню",
"planMenuTitle": "Что запланировать?", "planMenuTitle": "Что запланировать?",

View File

@@ -135,7 +135,7 @@
"generateWeekLabel": "规划本周", "generateWeekLabel": "规划本周",
"generateWeekSubtitle": "AI将为整周创建含早餐、午餐和晚餐的菜单", "generateWeekSubtitle": "AI将为整周创建含早餐、午餐和晚餐的菜单",
"generatingMenu": "正在生成菜单...", "generatingMenu": "正在生成菜单...",
"weekPlannedLabel": "本周已规划", "dayPlannedLabel": "今日已规划",
"planMenuButton": "规划餐食", "planMenuButton": "规划餐食",
"planMenuTitle": "规划什么?", "planMenuTitle": "规划什么?",

View File

@@ -0,0 +1,5 @@
/// How many days into the future the app allows planning and browsing.
const int kPlanningHorizonDays = 28;
/// How many weeks into the past the menu screen allows backward navigation.
const int kMenuPastWeeks = 8;