Files
recipe-app/flutter/IMPLEMENTATION_PLAN_RECEIPT_PREVIEW.md
T
Nils-Johan Gynther bd78b1de81
Test Suite / test (24.15.0) (push) Has been cancelled
feat: add "See receipt" button and preview modal in receipt import flow
2026-05-08 16:56:03 +02:00

221 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Implementeringsplan: "Se kvitto"-Modal för Kvittoimporten
**Mål**: MVP-vägen för split-view UX lägg till modal som visar OCR-text från parsade kvittoraderna.
**Scope**: 2-3 timmar
**Status**: Planering
---
## 1. Ändringar i `receipt_import_tab.dart`
### 1.1 Lägg till knapp "Se kvitto" i header-raden (rad ~745-752)
**Plats**: Höger om "Välj alla/Avmarkera alla"-knappen
```dart
// Innan: Row med bara "Välj alla"-knapp
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${items.length} rader — tryck för att redigera', style: theme.textTheme.titleSmall),
TextButton(...), // "Välj alla/Avmarkera alla"
],
)
// Efter: Lägg till "Se kvitto"-knapp
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${items.length} rader — tryck för att redigera', style: theme.textTheme.titleSmall),
Row(
children: [
TextButton.icon(
onPressed: items.isEmpty ? null : () => _showReceiptPreview(context, items),
icon: const Icon(Icons.description_outlined),
label: const Text('Se kvitto'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () => setState(...), // Befintlig "Välj alla"
child: Text(...),
),
],
),
],
)
```
### 1.2 Implementera `_showReceiptPreview`-metod
Lägg till denna metod i `_ReceiptImportTabState`:
```dart
Future<void> _showReceiptPreview(BuildContext context, List<ParsedReceiptItem> items) async {
if (!context.mounted) return;
await showDialog(
context: context,
builder: (ctx) => _ReceiptPreviewDialog(items: items),
);
}
```
---
## 2. Ny widget: `_ReceiptPreviewDialog`
Lägg till denna widget **i samma fil** (`receipt_import_tab.dart`), efter `_ReceiptImportResultRow`-klassen:
```dart
class _ReceiptPreviewDialog extends StatelessWidget {
final List<ParsedReceiptItem> items;
const _ReceiptPreviewDialog({required this.items});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return AlertDialog(
title: const Text('Kvittotexten i sin helhet'),
content: SizedBox(
width: 600, // Responsiv bredd på desktop
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Här visas all OCR-parsad text från kvittot. En rad per artikel:',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 12),
Container(
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerLowest,
border: Border.all(color: theme.colorScheme.outlineVariant),
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(12),
child: SelectableText.rich(
TextSpan(
children: items.isEmpty
? [TextSpan(text: '(Inga rader)', style: theme.textTheme.bodySmall)]
: items
.asMap()
.entries
.map((entry) {
final item = entry.value;
final lineNumber = entry.key + 1;
final lineText = _formatReceiptLine(item);
return TextSpan(
children: [
TextSpan(
text: '$lineNumber. ',
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.outlineVariant,
),
),
TextSpan(
text: lineText,
style: theme.textTheme.bodySmall?.copyWith(
fontFamily: 'monospace',
),
),
const TextSpan(text: '\n'),
],
);
})
.toList(),
),
style: theme.textTheme.bodySmall,
),
),
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Stäng'),
),
],
);
}
String _formatReceiptLine(ParsedReceiptItem item) {
final parts = <String>[];
if (item.quantity != null) {
parts.add('${item.quantity}');
}
if (item.unit != null) {
parts.add(item.unit!);
}
parts.add(item.rawName);
if (item.price != null) {
parts.add('${item.price} kr');
}
return parts.join(' ');
}
}
```
---
## 3. Implementeringssteg (steg-för-steg)
1. **Läs receipt_import_tab.dart** och identifiera raden med "Välj alla/Avmarkera alla"-knappen
2. **Refaktorera Row**: Lägg "Se kvitto"-knapp bredvid befintliga knapp
3. **Lägg till `_showReceiptPreview()`-metod** i `_ReceiptImportTabState`
4. **Implementera `_ReceiptPreviewDialog`-widget** på slutet av filen
5. **Testa**:
- Ladda ett kvitto
- Klicka "Se kvitto"-knappen
- Verifiera att texten är lesbar och formaterad
- Testa responsive bredd (dialog behöver minska på mobil)
---
## 4. Responsiv förbättring (optional)
Om dialogen behöver anpassas för mobil:
```dart
// I _ReceiptPreviewDialog.build():
final isWide = MediaQuery.of(context).size.width > 600;
return Dialog(
insetPadding: const EdgeInsets.all(16),
child: SizedBox(
width: isWide ? 600 : double.maxFinite, // Full bredd på mobil
// ...
),
);
```
---
## 5. Långsiktiga förbättringar (Phase 2)
Se `next_steps_flutter.md` för split-view roadmap:
- Horisontell split-view på desktop
- Scroll-synkronisering
- Tab-fallback på mobil
- AI-guiding labels
---
## Ärendemal
**Titel**: "Se kvitto"-modal för kvittoimporten
**Branch**: `feat/receipt-preview-modal`
**Labels**: `enhancement`, `import-ux`, `phase-1-mvp`
**Estimate**: 2-3h