refactor: remove ReceiptImportTab and its state management for cleaner code structure
This commit is contained in:
@@ -590,131 +590,3 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ReceiptImportTab extends ConsumerStatefulWidget {
|
|
||||||
const ReceiptImportTab({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState<ReceiptImportTab> createState() => _ReceiptImportTabState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
|
|
||||||
bool _isLoading = false;
|
|
||||||
String? _error;
|
|
||||||
PlatformFile? _pickedFile;
|
|
||||||
List<ParsedReceiptItem>? _items;
|
|
||||||
|
|
||||||
Future<void> _pickFile() async {
|
|
||||||
final result = await FilePicker.pickFiles(
|
|
||||||
type: FileType.custom,
|
|
||||||
allowedExtensions: ['pdf', 'png', 'jpg', 'jpeg', 'webp', 'bmp'],
|
|
||||||
withData: true,
|
|
||||||
);
|
|
||||||
if (result == null || result.files.isEmpty) return;
|
|
||||||
setState(() {
|
|
||||||
_pickedFile = result.files.first;
|
|
||||||
_error = null;
|
|
||||||
_items = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _submit() async {
|
|
||||||
if (_pickedFile == null) {
|
|
||||||
setState(() => _error = 'Vänligen välj en fil först');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
_error = null;
|
|
||||||
_items = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
final token = await ref.read(authStateProvider.future);
|
|
||||||
final repo = ref.read(importRepositoryProvider);
|
|
||||||
final items = await repo.importReceiptFile(
|
|
||||||
bytes: _pickedFile!.bytes!,
|
|
||||||
filename: _pickedFile!.name,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
if (!mounted) return;
|
|
||||||
setState(() => _items = items);
|
|
||||||
} catch (e) {
|
|
||||||
showGlobalErrorDialog(context, 'Ett fel uppstod vid import: $e');
|
|
||||||
} finally {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _canSubmit => !_isLoading && _pickedFile?.bytes != null;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
return SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('Ladda upp ett kvitto (PDF eller bild) — raderna tolkas och kan läggas till i ditt inventarie.',
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.onSurfaceVariant)),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: _isLoading ? null : _pickFile,
|
|
||||||
icon: const Icon(Icons.attach_file),
|
|
||||||
label: Text(_pickedFile == null ? 'Välj kvittofil' : _pickedFile!.name),
|
|
||||||
),
|
|
||||||
if (_pickedFile != null) ...[
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text('${(_pickedFile!.size / 1024).round()} KB', style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.outline)),
|
|
||||||
],
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
if (_isLoading) ...[
|
|
||||||
const LinearProgressIndicator(),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text('Tolkar kvittot — detta kan ta upp till en minut...', style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onSurfaceVariant)),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
],
|
|
||||||
if (_error != null) ...[
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: theme.colorScheme.errorContainer,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.error_outline, color: theme.colorScheme.onErrorContainer, size: 18),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(child: Text(_error!, style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onErrorContainer))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
],
|
|
||||||
FilledButton.icon(
|
|
||||||
onPressed: _canSubmit ? _submit : null,
|
|
||||||
icon: const Icon(Icons.receipt_long_outlined),
|
|
||||||
label: const Text('Importera kvitto'),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
if (_items != null) ...[
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Text('Granska rader:', style: theme.textTheme.titleMedium),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
..._items!.map((item) => ListTile(
|
|
||||||
leading: const Icon(Icons.shopping_cart_outlined),
|
|
||||||
title: Text(item.rawName),
|
|
||||||
subtitle: Text('${item.quantity ?? ''} ${item.unit ?? ''}'),
|
|
||||||
trailing: Text(item.suggestedProductName ?? '', style: theme.textTheme.bodySmall),
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user