fix(receipt-import): surface create-product errors and harden response parsing

This commit is contained in:
Nils-Johan Gynther
2026-05-02 19:53:11 +02:00
parent 64bc9997ad
commit 0103a22558
@@ -1,6 +1,7 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/api/api_exception.dart';
import '../../../core/api/api_paths.dart';
import '../../../core/api/api_providers.dart';
import '../../../core/ui/category_then_product_picker.dart';
@@ -370,7 +371,16 @@ class _EditDialogState extends State<_EditDialog> {
_newProductNameCtrl.text.trim(),
_newCategoryId!,
);
if (newProduct == null || !mounted) return;
if (newProduct == null || !mounted) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Kunde inte skapa produkten. Försök igen.'),
),
);
}
return;
}
if (!_localProducts.any((p) => p.id == newProduct.id)) {
_localProducts = [..._localProducts, newProduct];
}
@@ -379,6 +389,28 @@ class _EditDialogState extends State<_EditDialog> {
_productCategoryId = _newCategoryId;
_productCategoryPath = _newCategoryPath;
_productCategorySource = _newCategorySource ?? CategorySelectionSource.manual;
} on ApiException catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
e.message.trim().isEmpty
? 'Kunde inte skapa produkten. Försök igen.'
: e.message,
),
),
);
}
return;
} catch (_) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Kunde inte skapa produkten. Försök igen.'),
),
);
}
return;
} finally {
if (mounted) setState(() => _isCreatingProduct = false);
}
@@ -997,27 +1029,44 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
categoryTree: _categoryTree,
initialEntryMode: initialEntryMode,
onCreate: (name, categoryId) async {
try {
final token = await ref.read(authStateProvider.future);
final api = ref.read(apiClientProvider);
final data = await api.postJson(
final raw = await api.postJson(
ProductApiPaths.createPrivate,
body: {
'name': name.trim(),
'categoryId': categoryId,
},
token: token,
) as Map<String, dynamic>;
final newProduct = (
id: data['id'] as int,
name: (data['canonicalName'] ?? data['name']) as String,
categoryId: categoryId,
);
if (raw is! Map<String, dynamic>) {
throw Exception('Ogiltigt API-svar vid produktskapande.');
}
final idRaw = raw['id'];
if (idRaw is! num) {
throw Exception('API-svar saknar giltigt produkt-id.');
}
final displayNameRaw = raw['canonicalName'] ?? raw['name'];
final displayName = displayNameRaw?.toString().trim();
if (displayName == null || displayName.isEmpty) {
throw Exception('API-svar saknar produktnamn.');
}
final returnedCategoryId = raw['categoryId'] is num
? (raw['categoryId'] as num).toInt()
: categoryId;
final newProduct = (
id: idRaw.toInt(),
name: displayName,
categoryId: returnedCategoryId,
);
if (mounted) setState(() => _products = [..._products, newProduct]);
return newProduct;
} catch (_) {
return null;
}
},
),
);