feat: update README and technical documentation with onboarding improvements, user experience enhancements, and architectural details
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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<String>(
|
||||
value: 'logout',
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.logout),
|
||||
title: Text('Logga ut'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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 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`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user