feat: add profile screen and update routing; enhance login validation and logout functionality

This commit is contained in:
Nils-Johan Gynther
2026-04-21 22:30:35 +02:00
parent fa06ba0915
commit 056d5a8a1b
7 changed files with 240 additions and 1 deletions
+37
View File
@@ -0,0 +1,37 @@
# Flutter Frontend - User Guide
This README describes how to use the Flutter frontend for Recipe App from a user and operator perspective.
Related documents:
- [next_steps_flutter.md](../next_steps_flutter.md)
- [teknisk_beskrivning_flutter.md](../teknisk_beskrivning_flutter.md)
## What this app is
This is a Flutter Web frontend for Recipe App, served in Docker and exposed through Caddy.
It is intended to behave like the existing web frontend, but built in Flutter to support future Android and iOS clients.
## Current user flows
- Login with username and password.
- Recipe list view after login.
- Profile page (base version).
- Logout from recipe/profile pages.
## Where to access it
- Test environment: `https://test.gynther.se`
## Login details
- Login expects username, not email.
- Example seeded admin user in backend bootstrap: `Nadmin`.
- Password is controlled by server environment variable (`ADMIN_NADMIN_PASSWORD`).
## Known current scope
This is an active migration track. Not all pages from the existing frontend are moved yet.
Planned migration sequence is documented in [next_steps_flutter.md](../next_steps_flutter.md).
## Troubleshooting (user level)
1. If page shows old behavior after deploy: hard refresh or open in incognito.
2. If login fails: verify username/password (not email).
3. If recipes do not load: report browser console/network errors to the dev team.
## Release expectation
This frontend is available for iterative testing. Feature parity with the current production frontend is delivered step by step.
+5
View File
@@ -2,6 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../features/recipes/presentation/recipes_screen.dart';
import '../../features/auth/presentation/login_screen.dart';
import '../../features/profile/presentation/profile_screen.dart';
final appRouterProvider = Provider<GoRouter>((ref) {
return GoRouter(
@@ -15,6 +16,10 @@ final appRouterProvider = Provider<GoRouter>((ref) {
path: '/recipes',
builder: (context, state) => const RecipesScreen(),
),
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileScreen(),
),
],
);
});
@@ -22,6 +22,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
}
Future<void> _submit() async {
if (_usernameCtrl.text.trim().isEmpty || _passwordCtrl.text.isEmpty) {
return;
}
await ref.read(authStateProvider.notifier).login(
_usernameCtrl.text.trim(),
_passwordCtrl.text,
@@ -47,12 +50,15 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
TextField(
controller: _usernameCtrl,
decoration: const InputDecoration(labelText: 'Anvandarnamn'),
textInputAction: TextInputAction.next,
),
const SizedBox(height: 12),
TextField(
controller: _passwordCtrl,
decoration: const InputDecoration(labelText: 'Lösenord'),
obscureText: true,
textInputAction: TextInputAction.done,
onSubmitted: (_) => _submit(),
),
const SizedBox(height: 24),
if (authState is AsyncLoading)
@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../auth/data/auth_providers.dart';
class ProfileScreen extends ConsumerWidget {
const ProfileScreen({super.key});
Future<void> _logout(BuildContext context, WidgetRef ref) async {
await ref.read(authStateProvider.notifier).logout();
if (context.mounted) context.go('/login');
}
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text('Profil'),
actions: [
IconButton(
tooltip: 'Recept',
icon: const Icon(Icons.restaurant_menu),
onPressed: () => context.go('/recipes'),
),
IconButton(
tooltip: 'Logga ut',
icon: const Icon(Icons.logout),
onPressed: () => _logout(context, ref),
),
],
),
body: const Center(
child: Text('Profilsida (grundversion)'),
),
);
}
}
@@ -1,15 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../auth/data/auth_providers.dart';
import '../data/recipe_providers.dart';
class RecipesScreen extends ConsumerWidget {
const RecipesScreen({super.key});
Future<void> _logout(BuildContext context, WidgetRef ref) async {
await ref.read(authStateProvider.notifier).logout();
if (context.mounted) context.go('/login');
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final recipesAsync = ref.watch(recipesProvider);
return Scaffold(
appBar: AppBar(title: const Text('Recept')),
appBar: AppBar(
title: const Text('Recept'),
actions: [
IconButton(
tooltip: 'Profil',
icon: const Icon(Icons.person),
onPressed: () => context.go('/profile'),
),
IconButton(
tooltip: 'Logga ut',
icon: const Icon(Icons.logout),
onPressed: () => _logout(context, ref),
),
],
),
body: recipesAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(child: Text('Fel: $e')),
+52
View File
@@ -0,0 +1,52 @@
# Next Steps: Flutter-migrering (Alternativ 3)
Relaterade dokument:
- [flutter/README.md](flutter/README.md)
- [teknisk_beskrivning_flutter.md](teknisk_beskrivning_flutter.md)
## 1. Definiera malbild och scope forst
- Bestam vilka floden som maste vara parity i v1: login, receptlista, receptdetalj, inventarie, matsedel, profil.
- Satt tydlig definition of done per feature: UI, navigation, API, felhantering, loading states, auth-skydd.
## 2. Bygg gemensam app-shell innan fler sidor
- Stabil routingstruktur.
- Gemensam navigation (top/bottom/nav drawer).
- Auth-gate och logout-flode.
- Standardkomponenter for tomma lagen, felmeddelanden och laddning.
Det gor att varje ny sida gar snabbare och mer konsekvent.
## 3. Migrera i denna ordning (hogst affarsvarde forst)
- Auth: login, session, logout.
- Recept: lista -> detalj -> skapa/andra.
- Inventarie: lista -> skapa -> uppdatera -> forbrukning.
- Matsedel.
- Profil/admin.
Ordningen minimerar blockerare eftersom recept + auth ofta anvands av allt annat.
## 4. Kor API-contract first per feature
- Verifiera exakt request/response mot backend innan UI putsas.
- Mappa datamodeller robust (null, typskillnader, fallback-falt).
- Lagg in central felhantering for 401/403/500 tidigt.
## 5. Satt enhetliga kvalitetsgrindar per migrerad feature
- Manuell testlista for kritiska scenarier.
- En liten smoke-test efter varje deploy.
- Kontroll att web + mobilanpassning fungerar (utan web-specifika genvagar).
## 6. Leverera i korta iterationer
- 1 feature at gangen till testmiljo.
- Demo + snabb feedback.
- Justera innan nasta feature.
Det minskar risken att du bygger fel saker for langt.
## 7. Avveckla gamla frontend stegvis
- Kor dubbel drift under en period.
- Peka en testdoman mot Flutter tills parity ar bekräftad.
- Flytta trafik gradvis nar karnfloden ar stabila.
## Tumregel
- Sikta pa funktionell parity forst, pixel-perfect parity senare.
- Det ger snabbare nytta och farre regressionsproblem.
+81
View File
@@ -0,0 +1,81 @@
# 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)
## 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`.
## 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`
- `/recipes`
- `/profile`
### 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).
### Recipes
- Recipes endpoint: `GET /api/recipes`.
- Flutter model ar hardad for backend-falt (`name` fallback till `title`) och null-safe parsing.
## 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`
### 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.
## Nasta tekniska steg
Fortsatt migrering enligt prioritering i [next_steps_flutter.md](next_steps_flutter.md):
1. Auth parity (session behavior refinements).
2. Recipes parity (list -> detail -> create/edit).
3. Inventory and meal plan pages.
4. Profile/admin parity.