b31af6181c
- Updated next_steps_flutter.md to reflect completed tasks for user-scoped PantryItem and MealPlanEntry, including API contract publication and migration application. - Enhanced the prioritization plan with clear completion dates and added localization tasks. - Expanded teknisk_beskrivning_flutter.md with details on inventory filtering, sorting, and user-scoped backend changes, including migration notes and localization setup. - Improved error handling documentation and localization usage guidelines.
187 lines
10 KiB
Markdown
187 lines
10 KiB
Markdown
# Teknisk Beskrivning - Flutter Frontend
|
|
|
|
Detta dokument beskriver vad som ar byggt, arkitekturval och teknisk kontext for Flutter-frontenden.
|
|
Malgrupp: utvecklare och AI-assistent i denna chat.
|
|
|
|
Relaterade dokument:
|
|
- [next_steps_flutter.md](next_steps_flutter.md)
|
|
- [flutter/README.md](flutter/README.md)
|
|
- [teknisk_beskrivning.md](teknisk_beskrivning.md) (for backend-kontekst)
|
|
|
|
## Syfte
|
|
- Isolerad Flutter-baserad frontend i separat Docker-service.
|
|
- Web forst, men med arkitektur som kan ateranvandas for Android/iOS.
|
|
- Stegvis migrering av funktioner fran befintlig Next.js-frontend.
|
|
|
|
## Vad som ar gjort
|
|
- Ny compose override: [compose.flutter.yml](compose.flutter.yml).
|
|
- Ny Flutter-service: `recipe-flutter`.
|
|
- Service ansluten till natverk:
|
|
- `recipe-internal` (backend access)
|
|
- `proxy` (external Caddy reachability)
|
|
- Flutter-container serverar web build via intern Caddy.
|
|
- Exponering via extern Caddy med site `test.gynther.se` -> `recipe-flutter:5000`.
|
|
- API anrop gar same-origin via `/api` och proxas internt till `recipe-api:8080`.
|
|
|
|
### Inventarie (2026-04-22)
|
|
- Filtrering per plats (alla/kyl/frys/skafferi) via `inventoryLocationFilterProvider`.
|
|
- Sortering (namn A-O, bast fore stigande/fallande) via `inventorySortFilterProvider`.
|
|
- Riverpod-query (`InventoryQuery`) skickar `location` och `sort` som queryparametrar till backenden.
|
|
- Alla felmeddelanden gar via `mapErrorToUserMessage(error, context)`.
|
|
|
|
### Baslager/Pantry (2026-04-22)
|
|
- Pantry-produkter grupperas nu pa kategori utifrån backend-relationen `categoryRef` (rekursiv `parent`-kedja -> `categoryPath`), med fallback till legacy `product.category` och sist `Ovrigt`.
|
|
- `PantryProduct` har `categoryId` och `categoryPath` som parsas fran API-svaret.
|
|
|
|
### Backend: user-scope for pantry och matplan (2026-04-22)
|
|
- `PantryItem` och `MealPlanEntry` ar nu user-scopade i Prisma-schemat.
|
|
- `pantry`-controller/service filtrerar alltid per `userId` fran JWT.
|
|
- `meal-plan`-controller/service filtrerar alltid per `userId`; `inventoryCompare` anvander inloggad anvandares pantry.
|
|
- Migration: `20260422130000_user_scope_pantry_meal_plan` applicerad pa server.
|
|
- Next.js-frontenden kravde inga funktionella andringar (forfrågningar gar redan via auth-proxy).
|
|
|
|
## Arkitektur
|
|
### Lager
|
|
- Presentation: skarmar och widgets i `flutter/lib/features/*/presentation`.
|
|
- State/Application: Riverpod providers/notifiers i `flutter/lib/features/*/data`.
|
|
- Data/API: `ApiClient` i `flutter/lib/core/api`.
|
|
- Platform abstraction: token storage interface i `flutter/lib/core/platform`.
|
|
|
|
### Routing
|
|
- GoRouter i [flutter/lib/core/router/app_router.dart](flutter/lib/core/router/app_router.dart).
|
|
- Nuvarande routes:
|
|
- `/login` — loginskarm
|
|
- `/recipes` — receptlista (ShellRoute med AppShell)
|
|
- `/recipes/create` — nytt recept, utanfor ShellRoute
|
|
- `/recipes/:id` — receptdetalj, utanfor ShellRoute
|
|
- `/recipes/:id/edit` — redigera recept, utanfor ShellRoute
|
|
- `/profile` — profil (ShellRoute med AppShell)
|
|
- `/recipes/create` maste vara listad fore `/recipes/:id` i routelistan for att undvika konflikt.
|
|
- Detaljsidor (detalj, skapa, redigera) ligger utanfor ShellRoute for att fa full-screen med automatisk back-knapp.
|
|
|
|
### Auth
|
|
- Login endpoint: `POST /api/auth/login`.
|
|
- Backend kontrakt anvander `username` + `password`.
|
|
- Token-falt i svar: `accessToken`.
|
|
- Token lagras via `ITokenStorage` (web implementation med SharedPreferences).
|
|
- Auth-gate i router: utloggad anvandare redirectas till `/login`, inloggad redirectas fran `/login` till `/recipes`.
|
|
- Splash-skarm (`/`) visas medan auth-state lases fran storage vid app-start.
|
|
- `guardedApiCall()` i `flutter/lib/core/api/guarded_api_call.dart` hanterar automatisk logout vid 401.
|
|
|
|
### API-lager
|
|
- `ApiClient` i `flutter/lib/core/api/api_client.dart` exponerar: `getJson`, `postJson`, `patchJson`, `putJson`, `deleteJson`.
|
|
- Centralicerad HTTP-felklassning: 401 -> `ApiErrorType.unauthorized`, 403 -> `forbidden`, 5xx -> `server`, natverksfel -> `network`.
|
|
- `ApiException` i `flutter/lib/core/api/api_exception.dart` ar den enda feltypen som propageras fran repositories.
|
|
- `mapErrorToUserMessage(error, context)` i `flutter/lib/core/api/api_error_mapper.dart` oversatter fel till lokaliserade anvandarmedddelanden. Tar numera `BuildContext` som andra argument for att hämta korrekt sprak fran `AppLocalizations`.
|
|
|
|
### Lokalisering (2026-04-22)
|
|
- Flutter `flutter_localizations` + `intl` tillagda i `pubspec.yaml` med `generate: true`.
|
|
- Konfigurationsfil: [flutter/l10n.yaml](flutter/l10n.yaml) med `synthetic-package: false`.
|
|
- Kallstrangar i [flutter/lib/l10n/app_sv.arb](flutter/lib/l10n/app_sv.arb) och [flutter/lib/l10n/app_en.arb](flutter/lib/l10n/app_en.arb).
|
|
- Generade filer hamnar i `flutter/lib/l10n/generated/` och checkas inte in.
|
|
- Hjalpare `context.l10n` i [flutter/lib/core/l10n/l10n.dart](flutter/lib/core/l10n/l10n.dart).
|
|
- `MaterialApp.router` konfigurerad med `localizationsDelegates`, `supportedLocales` och `locale: const Locale('sv')`.
|
|
- Dockerfilen kor `flutter gen-l10n` innan `flutter build web` for att generera Dart-koden i containerbygget.
|
|
- Regressionstest i [flutter/test/core/swedish_strings_regression_test.dart](flutter/test/core/swedish_strings_regression_test.dart) failar om vanliga ASCII-varianter (Valj, Lagg, Forsok, etc.) dyker upp igen i `lib/`.
|
|
|
|
#### Anvandning av lokalisering
|
|
For att lagga till en ny lokaliserad strang:
|
|
1. Lagg till nyckeln i `app_sv.arb` (och `app_en.arb`).
|
|
2. Kör `flutter gen-l10n` lokalt (eller lat Docker-bygget gora det).
|
|
3. Anvand `context.l10n.dinNyckel` i widgetkoden.
|
|
|
|
For felstrangar fran API: anvand alltid `mapErrorToUserMessage(error, context)` — lagg inte in hardkodade strängar.
|
|
|
|
### Recipes
|
|
- `GET /api/recipes` — receptlista.
|
|
- `GET /api/recipes/:id` — receptdetalj inkl. ingredienser.
|
|
- `POST /api/recipes` — skapa recept.
|
|
- `PATCH /api/recipes/:id` — uppdatera recept (OBS: PATCH, inte PUT).
|
|
- `DELETE /api/recipes/:id` — ta bort recept (returnerar 204, ingen body).
|
|
- `POST /api/recipes/parse-markdown` — tolka Markdown, returnerar ParsedRecipe med ingrediensforslagslistor.
|
|
- Flutter Recipe-model: `name`-fallback till `title`, null-safe parsing av Decimal-strang till double.
|
|
|
|
### Gemensamma UI-komponenter
|
|
- `LoadingStateView`, `EmptyStateView`, `ErrorStateView` i `flutter/lib/core/ui/async_state_views.dart`.
|
|
- `AppShell` i `flutter/lib/core/ui/app_shell.dart`: responsiv NavigationRail (bred) / NavigationBar (smal), delad AppBar med logout.
|
|
|
|
## Kanda API-fallgropar (viktigt!)
|
|
|
|
> **Regel: Kontrollera alltid HTTP-metod i [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) innan en ny repository-metod implementeras.**
|
|
|
|
| Operation | Korrekt metod | Fel som gjorts |
|
|
|-----------|--------------|----------------|
|
|
| Uppdatera recept | `PATCH /api/recipes/:id` | `PUT` — ger 404 |
|
|
| Uppdatera inventariepost | `PATCH /api/inventory/:id` | Anvand PATCH |
|
|
| Uppdatera matplan | `POST /api/meal-plan` (upsert) | Ingen PUT/PATCH |
|
|
|
|
Generell regel: NestJS-backenden anvander `PATCH` for partiella uppdateringar, inte `PUT`. `PUT` exponeras inte pa nagra resurser i detta projekt.
|
|
|
|
## Drift och deploy
|
|
### Build/run
|
|
- Build argument i compose: `API_BASE_URL=/api`.
|
|
- Build command:
|
|
- `docker compose -f compose.yml -f compose.flutter.yml build recipe-flutter`
|
|
- Run command:
|
|
- `docker compose -f compose.yml -f compose.flutter.yml up -d --no-deps recipe-flutter`
|
|
|
|
### Rekommenderat kommandomonster
|
|
|
|
For att minska risken for fel startordning eller missforstand mellan huvudappen och Flutter-sparet:
|
|
|
|
**Nar huvudappen ska vara uppe:**
|
|
- `docker compose up -d recipe-db recipe-api recipe-frontend`
|
|
|
|
**Nar Flutter-klienten ska vara uppe:**
|
|
- `docker compose -f compose.yml -f compose.flutter.yml up -d --no-deps recipe-flutter`
|
|
|
|
**Nar bade huvudappen och Flutter testas parallellt:**
|
|
1. Starta huvudappen med `compose.yml`.
|
|
2. Starta sedan Flutter med override-filen `compose.flutter.yml`.
|
|
|
|
Viktigt:
|
|
- `docker compose build ...` bygger bara image.
|
|
- `docker compose up -d ...` kravs alltid for att containern faktiskt ska starta.
|
|
|
|
### Viktiga verifieringar
|
|
- Compose merge valid:
|
|
- `docker compose -f compose.yml -f compose.flutter.yml config`
|
|
- Container reachable from external Caddy:
|
|
- `docker exec caddy wget -S -O- http://recipe-flutter:5000`
|
|
- Public endpoint:
|
|
- `curl -I https://test.gynther.se`
|
|
|
|
## Viktiga beslut
|
|
- `compose.yml` lamnas orord; Flutter ligger i separat override-fil.
|
|
- Flutter-web anvander same-origin API (`/api`) for att undvika mixed-content.
|
|
- Intern Caddy i Flutter-container hanterar static hosting + `/api` proxy.
|
|
- Feature migration sker stegvis enligt [next_steps_flutter.md](next_steps_flutter.md).
|
|
|
|
## Kanda fallgropar
|
|
- Om `recipe-flutter` inte ar i `proxy` natverket blir det 502 fran extern Caddy.
|
|
- Om browser visar gammal JS kan gamla API-URL:er leva kvar i cache/service worker.
|
|
- Login med email fungerar inte om backend forvantar username.
|
|
- `recipe-flutter` kan stoppas av `docker compose down --remove-orphans` om kommandot kors utan override-filen och Flutter-sparat tidigare varit uppe.
|
|
- En orphan-varning for `recipe-flutter` ar normalt forvantad nar man kor huvudappen med bara `compose.yml`; det betyder inte att backend eller Prisma ar trasiga.
|
|
|
|
### Orphan-varning i praktiken
|
|
|
|
Om du ser en varning om orphan-containers under arbete med huvudappen betyder det oftast att `recipe-flutter` tidigare startats via:
|
|
|
|
- `docker compose -f compose.yml -f compose.flutter.yml up -d recipe-flutter`
|
|
|
|
och att du nu kor ett kommando som bara anvander `compose.yml`.
|
|
|
|
Detta ar normalt och ofarligt sa lange du vet vilken stack du avser att kora.
|
|
|
|
Om `test.gynther.se` slutar svara efter städning med `--remove-orphans`, starta om Flutter-sparet med:
|
|
|
|
- `docker compose -f compose.yml -f compose.flutter.yml up -d --no-deps recipe-flutter`
|
|
|
|
## Nasta tekniska steg
|
|
Fortsatt migrering enligt prioritering i [next_steps_flutter.md](next_steps_flutter.md):
|
|
1. Fortatt lokalisering av inventarie/pantry/recept-strangar till ARB.
|
|
2. Matplan parity mot nu user-scopat API.
|
|
3. Import parity.
|
|
4. Profil/admin parity.
|