feat: update README and technical documentation with onboarding improvements, user experience enhancements, and architectural details

This commit is contained in:
Nils-Johan Gynther
2026-04-25 16:26:34 +02:00
parent 5103cac112
commit 63d225d7f7
3 changed files with 129 additions and 65 deletions
+111 -55
View File
@@ -1,4 +1,8 @@
# Senaste ändringar (2026-04-24)
# Teknisk Beskrivning - Flutter Frontend
Viktigt att komma ihåg vid implementering av nya funktioner och kodning är att inte använda Windows-sökvägar. Använd inte `c:/dev/recipe-app/...` eftersom bygg- och testmiljön är på en remote Ubuntu-server. Utveckling sker lokalt och test samt drift sker på remote server. Säkerställ att inga absoluta Windows-sökvägar används i koden, för att stödja bygg och drift på Linux/Ubuntu.
## Senaste ändringar (2026-04-25)
**Arkitektur- och UX-förbättringar:**
- Grid-vy för recept: Kolumnval (2/4/6/8) via ikon i AppShell, med Riverpod-provider och SharedPreferences.
@@ -6,51 +10,103 @@
- AppShell visar grid-ikon endast på /recipes.
- Buggfix: Produktväljaren i pantry/inventarie (ProductPickerField) — bottenark implementeras.
- Kodkvalitet: Inga absoluta Windows-sökvägar.
- Dokumentation och next_steps uppdaterade.
# Teknisk Beskrivning - Flutter Frontend
Viktigt att komma ihåg vid implementering av nya funktioner och kodning är att inte använda windows sökvägar. Att inte använda c:/dev/recpie-app.... Detta eftersom bygg- och testmiljön är på en remote ubuntu-server. Utveckling sker lokalt och test samt drift
sker på remote server. Säkerställ att inga absoluta Windows-sökvägar används i koden, för att stödja bygg och drift på Linux/Ubuntu
**Tekniska förbättringar:**
- Förbättrad bildhantering med normalisering av URL:er, fallback och diagnostikloggning.
- Robustare importflöde för recept med stöd för PDF, bild och ICA-länkar.
- User-scope för pantry och matplan: Alla baslager- och matplansdata är nu per användare.
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
## Syfte och mål
- 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.
- Web först, men med arkitektur som kan återanvändas för Android/iOS.
- Stegvis migrering av funktioner från befintlig Next.js-frontend.
## Vad som ar gjort
## Relaterade dokument
- [next_steps_flutter.md](next_steps_flutter.md)
- [README.md](README.md)
- [teknisk_beskrivning.md](../TEKNISK_BESKRIVNING.md) (för backend-kontext)
## Arkitektur
### Lager
- **Presentation:** Skärmar 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` — loginskärm
- `/recipes` — receptlista (ShellRoute med AppShell)
- `/recipes/create` — nytt recept, utanför ShellRoute
- `/recipes/:id` — receptdetalj, utanför ShellRoute
- `/recipes/:id/edit` — redigera recept, utanför ShellRoute
- `/profile` — profil (ShellRoute med AppShell)
- `/recipes/create` måste vara listad före `/recipes/:id` i routelistan för att undvika konflikt.
- Detaljsidor (detalj, skapa, redigera) ligger utanför ShellRoute för att få full-screen med automatisk back-knapp.
### Auth
- Login endpoint: `POST /api/auth/login`.
- Backend kontrakt använder `username` + `password`.
- Token-fält i svar: `accessToken`.
- Token lagras via `ITokenStorage` (web implementation med SharedPreferences).
- Auth-gate i router: utloggad användare redirectas till `/login`, inloggad redirectas från `/login` till `/recipes`.
- Splash-skärm (`/`) visas medan auth-state läses från 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`.
- Centraliserad HTTP-felklassning: 401 -> `ApiErrorType.unauthorized`, 403 -> `forbidden`, 5xx -> `server`, nätverksfel -> `network`.
- `ApiException` i `flutter/lib/core/api/api_exception.dart` är den enda feltypen som propageras från repositories.
- `mapErrorToUserMessage(error, context)` i `flutter/lib/core/api/api_error_mapper.dart` översätter fel till lokaliserade användarmeddelanden. Tar numera `BuildContext` som andra argument för att hämta korrekt språk från `AppLocalizations`.
### Lokalisering
- Flutter `flutter_localizations` + `intl` tillagda i `pubspec.yaml` med `generate: true`.
- Konfigurationsfil: [flutter/l10n.yaml](flutter/l10n.yaml) med `synthetic-package: false`.
- Källsträngar 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).
- Genererade filer hamnar i `flutter/lib/l10n/generated/` och checkas inte in.
- Hjälpare `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 kör `flutter gen-l10n` innan `flutter build web` för att generera Dart-koden i containerbygget.
- Regressionstest i [flutter/test/core/swedish_strings_regression_test.dart](flutter/test/core/swedish_strings_regression_test.dart) misslyckas om vanliga ASCII-varianter (Välj, Lägg, Försök, etc.) dyker upp igen i `lib/`.
#### Användning av lokalisering
För att lägga till en ny lokaliserad sträng:
1. Lägg till nyckeln i `app_sv.arb` (och `app_en.arb`).
2. Kör `flutter gen-l10n` lokalt (eller låt Docker-bygget göra det).
3. Använd `context.l10n.dinNyckel` i widgetkoden.
För felsträngar från API: använd alltid `mapErrorToUserMessage(error, context)` — lägg inte in hårdkodade strängar.
## Vad som är gjort
### Infrastruktur
- Ny compose override: [compose.flutter.yml](compose.flutter.yml).
- Ny Flutter-service: `recipe-flutter`.
- Service ansluten till natverk:
- Service ansluten till nätverk:
- `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`.
- API-anrop går 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`.
- Filtrering per plats (alla/kyl/frys/skåfferi) via `inventoryLocationFilterProvider`.
- Sortering (namn A-Ö, bäst före stigande/fallande) via `inventorySortFilterProvider`.
- Riverpod-query (`InventoryQuery`) skickar `location` och `sort` som queryparametrar till backenden.
- Alla felmeddelanden gar via `mapErrorToUserMessage(error, context)`.
- Alla felmeddelanden går 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.
- Pantry-produkter grupperas nu på kategori utifrån backend-relationen `categoryRef` (rekursiv `parent`-kedja -> `categoryPath`), med fallback till legacy `product.category` och sist `Övrigt`.
- `PantryProduct` har `categoryId` och `categoryPath` som parsas från 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).
### Backend: User-scope för pantry och matplan (2026-04-22)
- `PantryItem` och `MealPlanEntry` är nu user-scopade i Prisma-schemat.
- `pantry`-controller/service filtrerar alltid per `userId` från JWT.
- `meal-plan`-controller/service filtrerar alltid per `userId`; `inventoryCompare` använder inloggad användares pantry.
- Migration: `20260422130000_user_scope_pantry_meal_plan` applicerad på server.
- Next.js-frontenden krävde inga funktionella ändringar (förfrågningar går redan via auth-proxy).
## Arkitektur
### Lager
@@ -117,17 +173,17 @@ For felstrangar fran API: anvand alltid `mapErrorToUserMessage(error, context)`
- `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!)
## Kända API-fallgropar (viktigt!)
> **Regel: Kontrollera alltid HTTP-metod i [TEKNISK_BESKRIVNING.md i /recipe-app] innan en ny repository-metod implementeras.**
> **Regel:** Kontrollera alltid HTTP-metod i [TEKNISK_BESKRIVNING.md i /recipe-app] 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 |
| Operation | Korrekt metod | Fel som gjorts |
|-------------------------|-----------------------------|------------------------------|
| Uppdatera recept | `PATCH /api/recipes/:id` | `PUT` — ger 404 |
| Uppdatera inventariepost| `PATCH /api/inventory/:id` | Använd 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.
Generell regel: NestJS-backenden använder `PATCH` för partiella uppdateringar, inte `PUT`. `PUT` exponeras inte på några resurser i detta projekt.
## Drift och deploy
### Build/run
@@ -137,23 +193,23 @@ Generell regel: NestJS-backenden anvander `PATCH` for partiella uppdateringar, i
- Run command:
- `docker compose -f compose.yml -f compose.flutter.yml up -d --no-deps recipe-flutter`
### Rekommenderat kommandomonster
### Rekommenderat kommandomönster
For att minska risken for fel startordning eller missforstand mellan huvudappen och Flutter-sparet:
För att minska risken för fel startordning eller missförstånd mellan huvudappen och Flutter-spåret:
**Nar huvudappen ska vara uppe:**
**När huvudappen ska vara uppe:**
- `docker compose up -d recipe-db recipe-api recipe-frontend`
**Nar Flutter-klienten ska vara uppe:**
**När 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:**
**När både 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.
- `docker compose up -d ...` krävs alltid för att containern faktiskt ska starta.
### Viktiga verifieringar
- Compose merge valid:
@@ -164,17 +220,17 @@ Viktigt:
- `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.
- `compose.yml` lämnas orörd; Flutter ligger i separat override-fil.
- Flutter-web använder same-origin API (`/api`) för 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.
## Kända fallgropar
- Om `recipe-flutter` inte är i `proxy` nätverket blir det 502 från extern Caddy.
- Om webbläsaren visar gammal JS kan gamla API-URL:er leva kvar i cache/service worker.
- Login med e-post fungerar inte om backend förväntar sig användarnamn.
- `recipe-flutter` kan stoppas av `docker compose down --remove-orphans` om kommandot körs utan override-filen och Flutter-spåret tidigare varit uppe.
- En orphan-varning för `recipe-flutter` är normalt förväntad när man kör huvudappen med bara `compose.yml`; det betyder inte att backend eller Prisma är trasiga.
### Orphan-varning i praktiken
@@ -182,11 +238,11 @@ Om du ser en varning om orphan-containers under arbete med huvudappen betyder de
- `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`.
och att du nu kör ett kommando som bara använder `compose.yml`.
Detta ar normalt och ofarligt sa lange du vet vilken stack du avser att kora.
Detta är normalt och ofarligt så länge du vet vilken stack du avser att köra.
Om `test.gynther.se` slutar svara efter städning med `--remove-orphans`, starta om Flutter-sparet med:
Om `test.gynther.se` slutar svara efter städning med `--remove-orphans`, starta om Flutter-spåret med:
- `docker compose -f compose.yml -f compose.flutter.yml up -d --no-deps recipe-flutter`