feat: enhance ProductPickerField and ParsedReceiptItem to support category filtering in receipt import
This commit is contained in:
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||
typedef ValueChanged<T> = void Function(T value);
|
||||
|
||||
/// A named record representing a selectable product option.
|
||||
typedef ProductOption = ({int id, String name});
|
||||
typedef ProductOption = ({int id, String name, int? categoryId});
|
||||
|
||||
/// A form field that opens a searchable bottom sheet for selecting a product.
|
||||
///
|
||||
@@ -110,19 +110,28 @@ class ProductPickerField extends StatelessWidget {
|
||||
|
||||
/// Öppnar produktväljarens bottenark utan att binda den till en specifik widget-instans.
|
||||
/// Returnerar valt produkt-id, null (ingen ändring), eller [_clearSelectionToken] (rensa).
|
||||
///
|
||||
/// [categoryFilter] — om satt visas bara produkter vars categoryId finns i listan.
|
||||
/// Används med AI-kategorisuggestion för att förifiltrera på rätt kategorigren.
|
||||
static Future<int?> showSheet(
|
||||
BuildContext context, {
|
||||
required List<ProductOption> products,
|
||||
int? value,
|
||||
String label = 'Produkt',
|
||||
String? initialQuery,
|
||||
Set<int>? categoryFilter,
|
||||
}) async {
|
||||
// Filtrera på kategori om angiven, men visa alla om filtret ger nollresultat
|
||||
final baseList = categoryFilter != null && categoryFilter.isNotEmpty
|
||||
? products.where((p) => p.categoryId != null && categoryFilter.contains(p.categoryId)).toList()
|
||||
: products;
|
||||
final useList = baseList.isNotEmpty ? baseList : products;
|
||||
|
||||
final result = await showModalBottomSheet<Object?>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (sheetContext) {
|
||||
// Skapa controller EN gång per öppnad ark — inte inuti StatefulBuilder
|
||||
final controller = TextEditingController(text: initialQuery ?? '');
|
||||
var query = initialQuery ?? '';
|
||||
|
||||
@@ -130,11 +139,13 @@ class ProductPickerField extends StatelessWidget {
|
||||
builder: (ctx, setModalState) {
|
||||
final normalizedQuery = query.trim().toLowerCase();
|
||||
final filtered = normalizedQuery.isEmpty
|
||||
? products
|
||||
: products
|
||||
? useList
|
||||
: useList
|
||||
.where((p) => p.name.toLowerCase().contains(normalizedQuery))
|
||||
.toList();
|
||||
|
||||
final isFiltered = useList.length < products.length;
|
||||
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(ctx).size.height * 0.85,
|
||||
child: Column(
|
||||
@@ -144,7 +155,17 @@ class ProductPickerField extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(label, style: Theme.of(ctx).textTheme.titleMedium),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: Theme.of(ctx).textTheme.titleMedium),
|
||||
if (isFiltered)
|
||||
Text(
|
||||
'Visar ${useList.length} produkter i föreslagen kategori',
|
||||
style: Theme.of(ctx).textTheme.labelSmall?.copyWith(color: Colors.green.shade700),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () => Navigator.pop(ctx, _clearSelectionToken),
|
||||
|
||||
Reference in New Issue
Block a user