feat(inventory): edit and delete existing lots

Lots could only be created; now they can be edited and removed:
- Repository: updateLot (LWW) + softDeleteLot (tombstone).
- Cubit: updateLot / deleteLot.
- The add-lot sheet is now a shared add/edit sheet — tapping a lot opens it
  prefilled (type, year, quantity), with a trash action to delete. The reactive
  detail view refreshes on save/delete.

Tests: repo updateLot/softDeleteLot + widget edit-lot and delete-lot. 42 green.
This commit is contained in:
vjrj 2026-07-08 11:10:43 +02:00
parent 7e92410728
commit f8d4d9a778
10 changed files with 192 additions and 16 deletions

View file

@ -1,4 +1,3 @@
import 'package:commons_core/commons_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -100,7 +99,7 @@ class _DetailView extends StatelessWidget {
_SectionTitle(t.detail.lots),
TextButton.icon(
key: const Key('detail.addLot'),
onPressed: () => _showAddLotSheet(context, cubit),
onPressed: () => _showLotSheet(context, cubit),
icon: const Icon(Icons.add),
label: Text(t.detail.addLot),
),
@ -111,7 +110,9 @@ class _DetailView extends StatelessWidget {
else
for (final lot in detail.lots)
ListTile(
key: Key('lot.${lot.id}'),
contentPadding: EdgeInsets.zero,
onTap: () => _showLotSheet(context, cubit, existing: lot),
leading: lot.quantity == null
? const Icon(Icons.inventory_2_outlined)
: QuantityKindIcon(lot.quantity!.kind, size: 28),
@ -475,15 +476,20 @@ class _EditVarietySheetState extends State<_EditVarietySheet> {
}
}
Future<void> _showAddLotSheet(BuildContext context, VarietyDetailCubit cubit) {
Future<void> _showLotSheet(
BuildContext context,
VarietyDetailCubit cubit, {
VarietyLot? existing,
}) {
final t = context.t;
final currentYear = DateTime.now().year;
// Harvest years: current year down through the last four decades. Newest
// first because a freshly harvested lot is the common case.
final years = [for (var y = currentYear; y >= currentYear - 40; y--) y];
var selectedYear = currentYear;
var selectedType = LotType.seed;
Quantity? selectedQuantity;
var selectedYear = existing?.harvestYear ?? currentYear;
var selectedType = existing?.type ?? LotType.seed;
var selectedQuantity = existing?.quantity;
final editing = existing != null;
return showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
@ -499,9 +505,25 @@ Future<void> _showAddLotSheet(BuildContext context, VarietyDetailCubit cubit) {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
t.addLot.title,
style: Theme.of(sheetContext).textTheme.titleLarge,
Row(
children: [
Expanded(
child: Text(
editing ? t.detail.editLot : t.addLot.title,
style: Theme.of(sheetContext).textTheme.titleLarge,
),
),
if (editing)
IconButton(
key: const Key('lot.delete'),
icon: const Icon(Icons.delete_outline),
tooltip: t.common.delete,
onPressed: () {
cubit.deleteLot(existing.id);
Navigator.of(sheetContext).pop();
},
),
],
),
const SizedBox(height: 12),
LotTypeSelector(
@ -549,11 +571,20 @@ Future<void> _showAddLotSheet(BuildContext context, VarietyDetailCubit cubit) {
FilledButton(
key: const Key('addLot.save'),
onPressed: () {
cubit.addLot(
type: selectedType,
year: selectedYear,
quantity: selectedQuantity,
);
if (editing) {
cubit.updateLot(
lotId: existing.id,
type: selectedType,
year: selectedYear,
quantity: selectedQuantity,
);
} else {
cubit.addLot(
type: selectedType,
year: selectedYear,
quantity: selectedQuantity,
);
}
Navigator.of(sheetContext).pop();
},
child: Text(t.common.save),