diff --git a/flutter/README.md b/flutter/README.md index f99ebe02..4ffc0bef 100644 --- a/flutter/README.md +++ b/flutter/README.md @@ -54,6 +54,24 @@ Detta är en Flutter Web frontend för Recipe App, som körs i Docker och expone Denna frontend är tillgänglig för iterativ testning. Funktionell parity med den nuvarande produktionsfrontenden levereras stegvis. +## Konverteringsvägen och Onboarding +- **CTA (Call-to-Action):** Tydliga och lockande CTAs som "Se vad du kan laga med det du har hemma." +- **Förenkla onboarding:** Gästinloggning eller demo-läge, sociala inloggningar, välkomstguide, och pop-up-tips. +- **Snabb start med exempeldata:** Förifyllt exempeldata och "Snabbstart"-knapp. +- **Fokusera på "Aha!"-momentet:** Omedelbara receptförslag och automatiska inköpslistor. +- **Tydliga instruktioner och feedback:** Instruktioner för varje steg och omedelbar feedback. +- **Användarvänlig design:** Fokus på kärnfunktioner och enkelhet. +- **Möjlighet att hoppa över onboarding:** Knapp för att hoppa över onboarding. + +## Testa och iterera +- Gå igenom appen som en ny användare för att identifiera eventuella problem. +- A/B-testa olika CTAs och onboarding-flöden. +- Implementera analytik för att spåra var användare hoppar av och varför. + +## Anpassa till målgruppen +- Anpassa marknadsföring och CTAs till målgruppen (t.ex. familjer, matintresserade, meal preppers). +- Var tydlig med appens värdeproposition, t.ex. "Slipp slösa mat – vi visar vad du kan laga med det du har hemma." + --- ## Nyheter och förbättringar (2026-04-24) diff --git a/flutter/lib/core/ui/app_shell.dart b/flutter/lib/core/ui/app_shell.dart index d762855d..397e2482 100644 --- a/flutter/lib/core/ui/app_shell.dart +++ b/flutter/lib/core/ui/app_shell.dart @@ -178,8 +178,6 @@ class AppShell extends ConsumerWidget { if (location != '/profile' && context.mounted) { context.go('/profile'); } - case 'logout': - logout(); } }, itemBuilder: (context) => const [ @@ -191,14 +189,6 @@ class AppShell extends ConsumerWidget { contentPadding: EdgeInsets.zero, ), ), - PopupMenuItem( - value: 'logout', - child: ListTile( - leading: Icon(Icons.logout), - title: Text('Logga ut'), - contentPadding: EdgeInsets.zero, - ), - ), ], ), ], diff --git a/flutter/teknisk_beskrivning_flutter.md b/flutter/teknisk_beskrivning_flutter.md index a8ac4582..4d9822aa 100644 --- a/flutter/teknisk_beskrivning_flutter.md +++ b/flutter/teknisk_beskrivning_flutter.md @@ -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`