- migration 013: create languages table (code PK, native_name, english_name,
is_active, sort_order) with all 12 existing languages seeded
- locale: add Language struct, Languages []Language, LoadFromDB() — queries
languages table at startup and populates both Supported map and Languages
slice; existing Parse/FromContext/FromRequest unchanged
- main.go: call locale.LoadFromDB after pool is ready
- gemini/recipe.go: remove hardcoded langNames map, use locale.Languages
linear lookup for English name in prompt
- language/handler.go: new package with GET /languages handler returning
active languages list (no auth required)
- server.go: register GET /languages as public route
- Flutter: add LanguageRepository + languageRepositoryProvider that fetches
/languages from backend
- language_provider.dart: replace const supportedLanguages map with
supportedLanguagesProvider (FutureProvider) backed by LanguageRepository
- profile_provider.dart: remove supportedLanguages.containsKey validation —
backend is source of truth; sync any non-empty language from preferences
- profile_screen.dart: use supportedLanguagesProvider for display name and
dropdown (async with loading/error states)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Store date_of_birth (DATE) instead of a static age integer so that age
is always computed dynamically from the stored date of birth.
- Migration 011: adds date_of_birth, backfills from age, drops age
- AgeFromDOB helper computes current age from YYYY-MM-DD string
- User model, repository SQL, and service validation updated
- Flutter: User.age becomes a computed getter; profile edit screen
uses a date picker bounded to [today-120y, today-10y]
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Passing the outer widget context to Navigator.pop() inside a dialog or
bottom sheet builder caused GoRouter to pop a page route instead of the
modal, triggering the "no pages left to show" assertion.
Affects _showChangeDialog and _confirmGenerate in menu_screen.dart,
and _showAddMenu bottom sheet in products_screen.dart.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add languageProvider (StateProvider<String>, default 'ru') with
supportedLanguages map matching backend locale.Supported
- Wire Accept-Language header into AuthInterceptor via languageGetter
callback; all API requests now carry the current language
- Sync language from user profile preferences into languageProvider
on every ProfileNotifier load/update
- Add language field to UpdateProfileRequest, serialized as
preferences.language in PUT /profile
- Profile screen: НАСТРОЙКИ section displays current language;
edit sheet adds DropdownButtonFormField for language selection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
routerProvider was watching authProvider and returning a new GoRouter
on every status transition (unknown → authenticated). This caused
MaterialApp.router to rebuild the entire navigation tree, which
triggered all data providers to start loading before auth was confirmed.
Switch to refreshListenable pattern: GoRouter is created once,
_RouterNotifier fires notifyListeners() when auth changes, and GoRouter
re-runs redirect using ref.read(authProvider). Add /loading splash route
shown during AuthStatus.unknown so no authenticated screen (and no API
call) is initiated until the stored-token check completes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Navigator.pop(context) with the outer tile context caused go_router to
pop the main navigation stack instead of closing the dialog. Use the
builder's ctx parameter instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add ProfileService (GET/PUT /profile), ProfileNotifier provider,
and full ProfileScreen with body-params, goal/activity, daily-calories
sections and logout confirmation. EditProfileSheet lets user update
name, height, weight, age, gender, goal and activity; backend
auto-recalculates daily_calories via Mifflin-St Jeor.
HomeScreen greeting now shows the user's real name from profileProvider.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch AppColors to iOS system palette (007AFF blue, F2F2F7 grouped
background, separator, label hierarchy) and rewrite AppTheme with
iOS-inspired Material 3 tokens (no elevation, negative letter-spacing,
50px buttons, 12px radii). Replace removed primaryLight/accent references
in recipe screens with primary.withValues(alpha:0.15) and primary.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- api_client.dart: connectTimeout 10s → 60s — Flutter was cancelling
requests to /recommendations after 10s while the server waited for
OpenAI, causing 'context canceled' on the server side
- product/repository.go ListForPrompt: expires_at is computed via
(added_at + storage_days * INTERVAL '1 day'), not a stored column;
wrap in CTE to reference it by alias in SELECT/ORDER BY
- home/handler.go getExpiringSoon: same fix — use CTE to compute
expires_at before filtering/ordering by it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- internal/home: GET /home/summary endpoint
- Today's meal plan from menu_plans/menu_items
- Logged calories sum from meal_diary
- Daily goal from user profile (default 2000)
- Expiring products within 3 days
- Last 3 saved recommendations (no AI call on home load)
- Wire homeHandler in server.go and main.go
Flutter:
- shared/models/home_summary.dart: HomeSummary, TodaySummary,
TodayMealPlan, ExpiringSoon, HomeRecipe
- features/home/home_service.dart + home_provider.dart
- features/home/home_screen.dart: greeting, calorie progress bar,
today's meals card, expiring banner, quick actions row,
recommendations horizontal list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace decommissioned llama-3.2-11b-vision-preview with
meta-llama/llama-4-scout-17b-16e-instruct (Groq deprecation)
- Use XFile.readAsBytes() instead of File(path).readAsBytes() so
Android content URIs (from gallery picks) are read correctly
- Add maxWidth/maxHeight constraints to image picker calls to reduce
payload size
- Increase receiveTimeout from 30s to 120s to accommodate slow vision AI
- Log recognition errors via debugPrint instead of swallowing them
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend (Go):
- Project structure with chi router, pgxpool, goose migrations
- JWT auth (access/refresh tokens) with Firebase token verification
- NoopTokenVerifier for local dev without Firebase credentials
- PostgreSQL user repository with atomic profile updates (transactions)
- Mifflin-St Jeor calorie calculation based on profile data
- REST API: POST /auth/login, /auth/refresh, /auth/logout, GET/PUT /profile, GET /health
- Middleware: auth, CORS (localhost wildcard), logging, recovery, request_id
- Unit tests (51 passing) and integration tests (testcontainers)
- Docker Compose setup with postgres healthcheck and graceful shutdown
Flutter client:
- Riverpod state management with GoRouter navigation
- Firebase Auth (email/password + Google sign-in with web popup support)
- Platform-aware API URLs (web/Android/iOS)
- Dio HTTP client with JWT auth interceptor and concurrent refresh handling
- Secure token storage
- Screens: Login, Register, Home (tabs: Menu, Recipes, Products, Profile)
- Unit tests (17 passing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>