feat: enhance category handling with new category chip logic and user-specific product categorization
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -4,6 +4,26 @@ String? normalizedOptionalText(String? value) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
String l1CategoryChipLabel(String prefix, String l1Category) => '$prefix$l1Category';
|
||||
({String label, String tooltip}) categoryChipText({
|
||||
required String? categoryPath,
|
||||
required String fallbackL1,
|
||||
}) {
|
||||
final path = categoryPath?.trim();
|
||||
if (path == null || path.isEmpty) {
|
||||
return (label: fallbackL1, tooltip: fallbackL1);
|
||||
}
|
||||
|
||||
final parts = path
|
||||
.split('>')
|
||||
.map((part) => part.trim())
|
||||
.where((part) => part.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
if (parts.isEmpty) {
|
||||
return (label: fallbackL1, tooltip: fallbackL1);
|
||||
}
|
||||
|
||||
return (label: parts.last, tooltip: parts.join(' > '));
|
||||
}
|
||||
|
||||
String locationLabel(String prefix, String location) => '$prefix$location';
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../../../core/api/api_error_mapper.dart';
|
||||
import '../../../core/l10n/l10n.dart';
|
||||
import '../../../core/ui/searchable_category_field.dart';
|
||||
import '../../inventory/data/inventory_providers.dart';
|
||||
import '../../pantry/data/pantry_providers.dart';
|
||||
import '../data/admin_repository.dart';
|
||||
import '../domain/admin_ai_categorize_result.dart';
|
||||
import '../domain/admin_category_node.dart';
|
||||
@@ -138,6 +140,8 @@ class _AdminProductsPanelState extends ConsumerState<AdminProductsPanel> {
|
||||
_selectedIds.clear();
|
||||
_bulkCategoryValue = null;
|
||||
});
|
||||
ref.invalidate(inventoryProvider);
|
||||
ref.invalidate(pantryProvider);
|
||||
await _load();
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -192,6 +196,8 @@ class _AdminProductsPanelState extends ConsumerState<AdminProductsPanel> {
|
||||
}
|
||||
if (!mounted) return;
|
||||
setState(() => _selectedIds.clear());
|
||||
ref.invalidate(inventoryProvider);
|
||||
ref.invalidate(pantryProvider);
|
||||
await _load();
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -504,6 +510,8 @@ class _AdminProductsPanelState extends ConsumerState<AdminProductsPanel> {
|
||||
categoryId: categoryId,
|
||||
);
|
||||
if (!mounted) return;
|
||||
ref.invalidate(inventoryProvider);
|
||||
ref.invalidate(pantryProvider);
|
||||
await _load();
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
|
||||
@@ -288,6 +288,10 @@ class _ForegroundTile extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = Theme.of(context);
|
||||
final location = normalizedOptionalText(item.location);
|
||||
final categoryChip = categoryChipText(
|
||||
categoryPath: item.categoryPath,
|
||||
fallbackL1: item.l1Category,
|
||||
);
|
||||
|
||||
final subtitleText = [
|
||||
'${_fmtQty(item.quantity)} ${item.unit}',
|
||||
@@ -333,19 +337,22 @@ class _ForegroundTile extends ConsumerWidget {
|
||||
const SizedBox(height: 6),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Chip(
|
||||
label: Text(
|
||||
l1CategoryChipLabel('L1: ', item.l1Category),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: BorderSide(color: theme.colorScheme.outlineVariant),
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
labelStyle: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
child: Tooltip(
|
||||
message: categoryChip.tooltip,
|
||||
child: Chip(
|
||||
label: Text(
|
||||
categoryChip.label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: BorderSide(color: theme.colorScheme.outlineVariant),
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
labelStyle: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -374,6 +374,10 @@ class _PantryScreenState extends ConsumerState<PantryScreen> {
|
||||
if (index == 1) return headerSection;
|
||||
final item = filteredItems[index - 2];
|
||||
final l1Category = _resolveL1Category(item);
|
||||
final categoryChip = categoryChipText(
|
||||
categoryPath: item.categoryPath,
|
||||
fallbackL1: l1Category,
|
||||
);
|
||||
final location = normalizedOptionalText(item.location);
|
||||
|
||||
return ListTile(
|
||||
@@ -390,19 +394,22 @@ class _PantryScreenState extends ConsumerState<PantryScreen> {
|
||||
const SizedBox(height: 6),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Chip(
|
||||
label: Text(
|
||||
l1CategoryChipLabel('L1: ', l1Category),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: BorderSide(color: colorScheme.outlineVariant),
|
||||
backgroundColor: colorScheme.surface,
|
||||
labelStyle: textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
child: Tooltip(
|
||||
message: categoryChip.tooltip,
|
||||
child: Chip(
|
||||
label: Text(
|
||||
categoryChip.label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: BorderSide(color: colorScheme.outlineVariant),
|
||||
backgroundColor: colorScheme.surface,
|
||||
labelStyle: textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user