tane/apps/app_seeds/lib/state/quick_add_cubit.dart
vjrj d3711e39b0 feat(quick-add): save & add another for rapid manual entry
The quick-add sheet no longer closes on every save: a new 'Save & add
another' action saves the seed, clears the form (keeping the chosen lot
type), refocuses the name field and shows an 'N added' running count, so
a whole shelf can be entered without reopening the sheet each time.
Plain Save still closes it. Adds a dedicated label field with its own
controller, submitAndAddAnother() on the cubit, and cubit + widget tests.
2026-07-09 14:45:48 +02:00

135 lines
3.9 KiB
Dart

import 'dart:typed_data';
import 'package:commons_core/commons_core.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../data/variety_repository.dart';
import '../db/enums.dart';
const Object _unset = Object();
/// Form state for the quick-add sheet. Only [label] is required; everything
/// else is progressive disclosure behind [expanded].
class QuickAddState extends Equatable {
const QuickAddState({
this.label = '',
this.quantity,
this.lotType = LotType.seed,
this.photoBytes,
this.expanded = false,
this.submitting = false,
this.submitted = false,
this.showLabelError = false,
this.addedCount = 0,
});
final String label;
final Quantity? quantity;
final LotType lotType;
final Uint8List? photoBytes;
final bool expanded;
final bool submitting;
final bool submitted;
final bool showLabelError;
/// How many seeds this sheet has saved via "save & add another" (it stays
/// open); drives the "N added" feedback and resets of the label field.
final int addedCount;
bool get hasValidLabel => label.trim().isNotEmpty;
QuickAddState copyWith({
String? label,
Object? quantity = _unset,
LotType? lotType,
Uint8List? photoBytes,
bool? expanded,
bool? submitting,
bool? submitted,
bool? showLabelError,
int? addedCount,
}) {
return QuickAddState(
label: label ?? this.label,
quantity: identical(quantity, _unset)
? this.quantity
: quantity as Quantity?,
lotType: lotType ?? this.lotType,
photoBytes: photoBytes ?? this.photoBytes,
expanded: expanded ?? this.expanded,
submitting: submitting ?? this.submitting,
submitted: submitted ?? this.submitted,
showLabelError: showLabelError ?? this.showLabelError,
addedCount: addedCount ?? this.addedCount,
);
}
@override
List<Object?> get props => [
label,
quantity,
lotType,
photoBytes,
expanded,
submitting,
submitted,
showLabelError,
addedCount,
];
}
/// Drives the 20-second quick-add flow and persists via [VarietyRepository].
class QuickAddCubit extends Cubit<QuickAddState> {
QuickAddCubit(this._repo) : super(const QuickAddState());
final VarietyRepository _repo;
void labelChanged(String value) =>
emit(state.copyWith(label: value, showLabelError: false));
void setQuantity(Quantity? quantity) =>
emit(state.copyWith(quantity: quantity));
void setLotType(LotType type) => emit(state.copyWith(lotType: type));
void photoPicked(Uint8List bytes) => emit(state.copyWith(photoBytes: bytes));
void toggleExpanded() => emit(state.copyWith(expanded: !state.expanded));
/// Validates and saves. No-op (sets [showLabelError]) if the label is empty.
Future<void> submit() async {
if (!state.hasValidLabel) {
emit(state.copyWith(showLabelError: true));
return;
}
if (state.submitting) return;
emit(state.copyWith(submitting: true));
await _repo.addQuickVariety(
label: state.label.trim(),
lotType: state.lotType,
quantity: state.quantity,
photoBytes: state.photoBytes,
);
emit(state.copyWith(submitting: false, submitted: true));
}
/// Saves and keeps the sheet open for the next seed: resets the form to a
/// clean state (keeping the chosen [lotType]) and bumps [addedCount]. Lets a
/// user rip through a whole shelf without reopening the sheet each time.
Future<void> submitAndAddAnother() async {
if (!state.hasValidLabel) {
emit(state.copyWith(showLabelError: true));
return;
}
if (state.submitting) return;
emit(state.copyWith(submitting: true));
await _repo.addQuickVariety(
label: state.label.trim(),
lotType: state.lotType,
quantity: state.quantity,
photoBytes: state.photoBytes,
);
emit(QuickAddState(lotType: state.lotType, addedCount: state.addedCount + 1));
}
}