Files
recipe-app/flutter/IMPLEMENTATION_PLAN_RECEIPT_PREVIEW.md
T

8.9 KiB
Raw Blame History

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

// 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:

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:

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:

// 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

2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.

2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.

2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.

2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.

2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.

2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.

2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.

2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.