feat: meal tracking, dish recognition UX improvements, English AI prompts

Backend:
- Translate all recognition prompts (receipt, products, dish) from Russian to English
- Add lang parameter to Recognizer interface and pass locale.FromContext in handlers
- DishResult type uses candidates array for multi-candidate responses

Client:
- Add meal tracking: diary provider, date selector, meal type model
- DishResult parser: backward-compatible with legacy flat format and new candidates format
- DishResultScreen: sticky bottom button, full-width portion/meal-type inputs,
  КБЖУ disclaimer moved under nutrition card, add date field to diary POST body
- Recognition prompts now return dish/product names in user's preferred language
- Onboarding, profile, home screen visual updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-17 14:29:36 +02:00
parent 2a95bcd53c
commit 87ef2097fc
16 changed files with 1269 additions and 350 deletions

View File

@@ -69,10 +69,15 @@ final routerProvider = Provider<GoRouter>((ref) {
}
final profileUser = profileState.valueOrNull;
// If profile failed to load, don't block navigation.
if (profileUser == null) return isAuthRoute ? '/home' : null;
if (profileUser == null) {
if (isAuthRoute || state.matchedLocation == '/loading') return '/home';
return null;
}
final needsOnboarding = !profileUser.hasCompletedOnboarding;
if (isAuthRoute) return needsOnboarding ? '/onboarding' : '/home';
if (isAuthRoute || state.matchedLocation == '/loading') {
return needsOnboarding ? '/onboarding' : '/home';
}
if (needsOnboarding && !isOnboarding) return '/onboarding';
if (!needsOnboarding && isOnboarding) return '/home';
}
@@ -148,9 +153,11 @@ final routerProvider = Provider<GoRouter>((ref) {
GoRoute(
path: '/scan/dish',
builder: (context, state) {
final dish = state.extra as DishResult?;
final extra = state.extra as Map<String, dynamic>?;
final dish = extra?['dish'] as DishResult?;
final mealType = extra?['meal_type'] as String?;
if (dish == null) return const _InvalidRoute();
return DishResultScreen(dish: dish);
return DishResultScreen(dish: dish, preselectedMealType: mealType);
},
),
ShellRoute(