feat: add location field to PantryItem model and update related functionality
Test Suite / test (24.15.0) (push) Has been cancelled

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Nils-Johan Gynther
2026-05-06 11:54:56 +02:00
parent 63d249b0a8
commit e7251fd94c
8 changed files with 114 additions and 37 deletions
@@ -10,7 +10,9 @@ import '../../../core/forms/form_options.dart';
import '../../../core/l10n/l10n.dart';
import '../../../core/ui/product_picker_field.dart';
import '../../auth/data/auth_providers.dart';
import '../../pantry/data/pantry_providers.dart';
import '../data/inventory_providers.dart';
import '../../import/data/receipt_import_session.dart' show ImportDestination;
class CreateInventoryScreen extends ConsumerStatefulWidget {
const CreateInventoryScreen({super.key});
@@ -36,6 +38,7 @@ class _CreateInventoryScreenState
DateTime? _bestBeforeDate;
bool _opened = false;
bool _saving = false;
ImportDestination _destination = ImportDestination.inventory;
@override
void initState() {
@@ -101,13 +104,40 @@ class _CreateInventoryScreenState
}
Future<void> _save() async {
if (!_formKey.currentState!.validate()) return;
if (_selectedProductId == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.inventorySelectProduct)),
);
return;
}
if (_destination == ImportDestination.pantry) {
setState(() => _saving = true);
try {
final token = await ref.read(authStateProvider.future);
await ref
.read(pantryRepositoryProvider)
.createPantryItem(
_selectedProductId!,
token: token,
location: _locationController.text.trim().isEmpty
? null
: _locationController.text.trim(),
);
ref.invalidate(pantryProvider);
if (mounted) context.pop();
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
buildCopyableErrorSnackBar(context, mapErrorToUserMessage(e, context)));
}
} finally {
if (mounted) setState(() => _saving = false);
}
return;
}
if (!_formKey.currentState!.validate()) return;
setState(() => _saving = true);
try {
final token = await ref.read(authStateProvider.future);
@@ -163,6 +193,8 @@ class _CreateInventoryScreenState
)
.toList();
final isInventory = _destination == ImportDestination.inventory;
return Scaffold(
appBar: AppBar(title: Text(context.l10n.inventoryCreateTitle)),
body: Form(
@@ -170,6 +202,25 @@ class _CreateInventoryScreenState
child: ListView(
padding: const EdgeInsets.all(16),
children: [
SegmentedButton<ImportDestination>(
segments: const [
ButtonSegment(
value: ImportDestination.inventory,
label: Text('Inventarie'),
icon: Icon(Icons.inventory_2_outlined),
),
ButtonSegment(
value: ImportDestination.pantry,
label: Text('Skafferi'),
icon: Icon(Icons.kitchen_outlined),
),
],
selected: {_destination},
onSelectionChanged: _saving
? null
: (selected) => setState(() => _destination = selected.first),
),
const SizedBox(height: 16),
ProductPickerField(
products: productOptions,
value: _selectedProductId,
@@ -179,7 +230,31 @@ class _CreateInventoryScreenState
onChanged: (value) => setState(() => _selectedProductId = value),
),
const SizedBox(height: 12),
Row(
DropdownButtonFormField<String>(
initialValue: _locationController.text.trim().isEmpty
? null
: _locationController.text.trim(),
isExpanded: true,
decoration: InputDecoration(
labelText: context.l10n.locationOptionalLabel,
border: const OutlineInputBorder(),
),
items: inventoryLocationOptions
.map(
(location) => DropdownMenuItem<String>(
value: location,
child: Text(location),
),
)
.toList(),
onChanged: _saving
? null
: (value) => setState(() {
_locationController.text = value ?? '';
}),
),
if (isInventory) const SizedBox(height: 12),
if (isInventory) Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
@@ -232,32 +307,8 @@ class _CreateInventoryScreenState
),
],
),
const SizedBox(height: 12),
DropdownButtonFormField<String>(
initialValue: _locationController.text.trim().isEmpty
? null
: _locationController.text.trim(),
isExpanded: true,
decoration: InputDecoration(
labelText: context.l10n.locationOptionalLabel,
border: const OutlineInputBorder(),
),
items: inventoryLocationOptions
.map(
(location) => DropdownMenuItem<String>(
value: location,
child: Text(location),
),
)
.toList(),
onChanged: _saving
? null
: (value) => setState(() {
_locationController.text = value ?? '';
}),
),
const SizedBox(height: 12),
TextFormField(
if (isInventory) const SizedBox(height: 12),
if (isInventory) TextFormField(
controller: _brandController,
decoration: InputDecoration(
labelText: context.l10n.brandOptionalLabel,
@@ -265,8 +316,8 @@ class _CreateInventoryScreenState
),
enabled: !_saving,
),
const SizedBox(height: 12),
Row(
if (isInventory) const SizedBox(height: 12),
if (isInventory) Row(
children: [
Expanded(
child: OutlinedButton.icon(
@@ -291,7 +342,7 @@ class _CreateInventoryScreenState
),
],
),
CheckboxListTile(
if (isInventory) CheckboxListTile(
title: Text(context.l10n.openedLabel),
value: _opened,
onChanged:
@@ -299,7 +350,7 @@ class _CreateInventoryScreenState
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
),
TextFormField(
if (isInventory) TextFormField(
controller: _commentController,
decoration: InputDecoration(
labelText: context.l10n.commentOptionalLabel,