feat: implement update functionality for receipt aliases and add corresponding tests
Test Suite / backend-pr-quick (24.15.0) (push) Has been skipped
Test Suite / quick-import-pr-quick (24.15.0) (push) Has been skipped
Test Suite / backend-full (24.15.0) (push) Failing after 22s
Test Suite / flutter-quality (push) Failing after 4s

This commit is contained in:
Nils-Johan Gynther
2026-05-12 21:25:48 +02:00
parent fb6b371fb7
commit 46b9be4791
10 changed files with 403 additions and 21 deletions
@@ -498,6 +498,74 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
setState(() {});
}
Future<void> _editAliasForItem(int index) async {
final items = _items;
if (items == null || index < 0 || index >= items.length) return;
final item = items[index];
final edit = _edits[index];
final productId = edit?.productId;
final initialAlias = item.rawName.trim();
if (productId == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Välj produkt för raden innan du redigerar alias.')),
);
return;
}
final controller = TextEditingController(text: initialAlias);
final aliasName = await showDialog<String>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Spara alias för raden'),
content: TextField(
controller: controller,
autofocus: true,
decoration: const InputDecoration(
labelText: 'Aliasnamn',
border: OutlineInputBorder(),
),
onSubmitted: (value) => Navigator.pop(ctx, value.trim()),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('Avbryt'),
),
FilledButton(
onPressed: () => Navigator.pop(ctx, controller.text.trim()),
child: const Text('Spara'),
),
],
),
);
controller.dispose();
if (!mounted || aliasName == null) return;
if (aliasName.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Aliasnamn kan inte vara tomt.')),
);
return;
}
try {
await ref.read(adminRepositoryProvider).upsertReceiptAlias(
receiptName: aliasName,
productId: productId,
isGlobal: false,
);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Alias sparat.')),
);
} catch (e) {
if (!mounted) return;
showGlobalErrorDialog(context, 'Kunde inte uppdatera alias: $e');
}
}
Future<void> _addSelected() async {
final items = _items;
if (items == null) return;
@@ -844,6 +912,7 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
i,
initialEntryMode: ImportProductEntryMode.create,
),
onAliasEditRequested: () => _editAliasForItem(i),
onDeleteRequested: () => _deleteItem(i),
matchedViaBadgeBuilder: _buildMatchedViaBadge,
);
@@ -877,6 +946,7 @@ class _ReceiptImportResultRow extends ConsumerWidget {
final VoidCallback onEditRequested;
final VoidCallback onSelectExistingRequested;
final VoidCallback onCreateRequested;
final VoidCallback onAliasEditRequested;
final VoidCallback onDeleteRequested;
final Widget Function(ParsedReceiptItem item, ThemeData theme)
matchedViaBadgeBuilder;
@@ -891,6 +961,7 @@ class _ReceiptImportResultRow extends ConsumerWidget {
required this.onEditRequested,
required this.onSelectExistingRequested,
required this.onCreateRequested,
required this.onAliasEditRequested,
required this.onDeleteRequested,
required this.matchedViaBadgeBuilder,
});
@@ -1088,7 +1159,7 @@ class _ReceiptImportResultRow extends ConsumerWidget {
],
),
trailing: SizedBox(
width: 80,
width: 120,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
@@ -1101,6 +1172,14 @@ class _ReceiptImportResultRow extends ConsumerWidget {
: (isSuggested ? Colors.orange : theme.colorScheme.tertiary),
size: 20,
),
if (hasProduct)
IconButton(
icon: const Icon(Icons.drive_file_rename_outline, size: 18),
onPressed: onAliasEditRequested,
tooltip: 'Spara alias',
constraints: const BoxConstraints(minWidth: 40, minHeight: 40),
padding: const EdgeInsets.all(4),
),
IconButton(
icon: Icon(Icons.delete_outline, size: 18, color: theme.colorScheme.error),
onPressed: onDeleteRequested,