import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/api/api_error_mapper.dart'; import '../../../core/api/api_exception.dart'; import '../../../core/ui/async_state_views.dart'; import '../../auth/data/auth_providers.dart'; import '../data/recipe_providers.dart'; import '../domain/recipe.dart'; class RecipeDetailScreen extends ConsumerWidget { final int recipeId; const RecipeDetailScreen({super.key, required this.recipeId}); @override Widget build(BuildContext context, WidgetRef ref) { final recipeAsync = ref.watch(recipeDetailProvider(recipeId)); return Scaffold( appBar: AppBar( title: Text(recipeAsync.valueOrNull?.title ?? 'Recept'), actions: recipeAsync.valueOrNull == null ? [] : [ IconButton( tooltip: 'Redigera', icon: const Icon(Icons.edit_outlined), onPressed: () => context.push('/recipes/$recipeId/edit'), ), _DeleteButton(recipe: recipeAsync.value!), ], ), body: recipeAsync.when( loading: () => const LoadingStateView(label: 'Laddar recept...'), error: (error, _) => ErrorStateView( message: mapErrorToUserMessage(error, context), onRetry: () => ref.invalidate(recipeDetailProvider(recipeId)), ), data: (recipe) => _RecipeBody(recipe: recipe), ), ); } } class _DeleteButton extends ConsumerWidget { final Recipe recipe; const _DeleteButton({required this.recipe}); @override Widget build(BuildContext context, WidgetRef ref) { return IconButton( tooltip: 'Ta bort', icon: const Icon(Icons.delete_outline), onPressed: () => _confirmDelete(context, ref), ); } Future _confirmDelete(BuildContext context, WidgetRef ref) async { final confirmed = await showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Ta bort recept?'), content: Text( 'Vill du ta bort "${recipe.title}"? Åtgärden kan inte ångras.'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Avbryt'), ), FilledButton( style: FilledButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.error), onPressed: () => Navigator.pop(context, true), child: const Text('Ta bort'), ), ], ), ); if (confirmed != true || !context.mounted) return; try { final token = await ref.read(authStateProvider.future); await ref.read(recipeRepositoryProvider).deleteRecipe(recipe.id, token: token); ref.invalidate(recipesProvider); if (context.mounted) context.go('/recipes'); } on ApiException catch (e) { if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(mapErrorToUserMessage(e, context))), ); } } } class _RecipeBody extends StatelessWidget { final Recipe recipe; const _RecipeBody({required this.recipe}); String _formatQty(double qty) { if (qty == 0) return ''; return qty == qty.truncateToDouble() ? qty.toInt().toString() : qty.toString(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (recipe.imageUrl != null) ClipRRect( borderRadius: BorderRadius.circular(12), child: AspectRatio( aspectRatio: 16 / 9, child: Image.network( recipe.imageUrl!, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const SizedBox.shrink(), ), ), ), if (recipe.imageUrl != null) const SizedBox(height: 20), Text(recipe.title, style: theme.textTheme.headlineSmall), if (recipe.description != null) ...[ const SizedBox(height: 8), Text(recipe.description!, style: theme.textTheme.bodyMedium ?.copyWith(color: theme.colorScheme.onSurfaceVariant)), ], if (recipe.servings != null) ...[ const SizedBox(height: 8), Row( children: [ const Icon(Icons.people_outline, size: 16), const SizedBox(width: 4), Text('${recipe.servings} portioner', style: theme.textTheme.bodySmall), ], ), ], if (recipe.ingredients.isNotEmpty) ...[ const SizedBox(height: 24), Text('Ingredienser', style: theme.textTheme.titleMedium), const SizedBox(height: 8), ...recipe.ingredients.map((ing) { final qtyStr = _formatQty(ing.quantity); final parts = [ if (qtyStr.isNotEmpty) qtyStr, if (ing.unit.isNotEmpty) ing.unit, ing.productName, if (ing.note != null) '(${ing.note})', ]; return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('• '), Expanded(child: Text(parts.join(' '))), ], ), ); }), ], if (recipe.instructions != null && recipe.instructions!.isNotEmpty) ...[ const SizedBox(height: 24), Text('Tillvägagångssätt', style: theme.textTheme.titleMedium), const SizedBox(height: 8), Text(recipe.instructions!, style: theme.textTheme.bodyMedium ?.copyWith(height: 1.6)), ], const SizedBox(height: 40), ], ), ); } }