import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/api/api_error_mapper.dart'; import '../../admin/data/admin_repository.dart'; import '../../admin/domain/receipt_alias.dart'; class UserAliasesScreen extends ConsumerStatefulWidget { const UserAliasesScreen({super.key}); @override ConsumerState createState() => _UserAliasesScreenState(); } class _UserAliasesScreenState extends ConsumerState { List _aliases = []; bool _isLoading = true; String? _error; @override void initState() { super.initState(); _load(); } Future _load() async { setState(() { _isLoading = true; _error = null; }); try { final aliases = await ref.read(adminRepositoryProvider).listReceiptAliases(); if (!mounted) return; setState(() { _aliases = [...aliases] ..sort((a, b) { if (a.isGlobal != b.isGlobal) return a.isGlobal ? 1 : -1; return a.receiptName.compareTo(b.receiptName); }); }); } catch (e) { if (!mounted) return; setState(() => _error = mapErrorToUserMessage(e, context)); } finally { if (mounted) setState(() => _isLoading = false); } } Future _delete(ReceiptAlias alias) async { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Ta bort alias'), content: Text('Ta bort alias "${alias.receiptName}" → ${alias.displayProductName}?'), actions: [ TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('Avbryt')), FilledButton( onPressed: () => Navigator.pop(ctx, true), child: const Text('Ta bort'), ), ], ), ); if (confirmed != true || !mounted) return; try { await ref.read(adminRepositoryProvider).removeReceiptAlias(alias.id); await _load(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Alias borttaget.')), ); } } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Kunde inte ta bort alias: $e')), ); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: AppBar( title: const Text('Mina kvittoalias'), actions: [ IconButton( onPressed: _load, icon: const Icon(Icons.refresh), tooltip: 'Uppdatera', ), ], ), body: Builder(builder: (_) { if (_isLoading) return const Center(child: CircularProgressIndicator()); if (_error != null) { return buildCopyableErrorPanel( context: context, message: _error!, onRetry: _load, title: 'Kunde inte läsa alias', ); } if (_aliases.isEmpty) { return ListView( padding: const EdgeInsets.all(16), children: [ Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Aliasöversikt', style: theme.textTheme.titleMedium), const SizedBox(height: 8), Text( 'Privata alias gäller bara dig. Globala alias används som fallback i receipt-importen och skapas av admin.', style: theme.textTheme.bodyMedium, ), const SizedBox(height: 8), const Wrap( spacing: 8, runSpacing: 8, children: [ Chip(label: Text('Privat alias')), Chip(label: Text('Global fallback')), Chip(label: Text('Receipt-import')), ], ), ], ), ), ), const SizedBox(height: 16), Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.link_off_outlined, size: 48, color: theme.colorScheme.outlineVariant), const SizedBox(height: 16), Text( 'Inga alias sparade ännu.', style: theme.textTheme.bodyLarge, textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( 'Alias skapas när du väljer att lära in dem under kvittoimporten.', style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), ], ), ), ), ], ); } return ListView( padding: const EdgeInsets.all(16), children: [ Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Aliasöversikt', style: theme.textTheme.titleMedium), const SizedBox(height: 8), Text( 'Privata alias gäller bara dig. Globala alias används som fallback i receipt-importen och skapas av admin.', style: theme.textTheme.bodyMedium, ), const SizedBox(height: 8), const Wrap( spacing: 8, runSpacing: 8, children: [ Chip(label: Text('Privat alias')), Chip(label: Text('Global fallback')), Chip(label: Text('Receipt-import')), ], ), ], ), ), ), const SizedBox(height: 16), ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: _aliases.length, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (ctx, i) { final alias = _aliases[i]; return ListTile( leading: Icon( alias.isGlobal ? Icons.public_outlined : Icons.link_outlined, color: theme.colorScheme.primary, ), title: Text( alias.receiptName, style: theme.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( '→ ${alias.displayProductName}', style: theme.textTheme.bodySmall, ), const SizedBox(height: 4), Wrap( spacing: 8, children: [ Chip( visualDensity: VisualDensity.compact, label: Text(alias.isGlobal ? 'Global fallback' : 'Privat alias'), ), ], ), ], ), trailing: alias.isPrivate ? IconButton( icon: const Icon(Icons.delete_outline), tooltip: 'Ta bort alias', color: theme.colorScheme.error, onPressed: () => _delete(alias), ) : null, ); }, ), ], ); }), ); } }