feat(inventory): local sharing — per-lot share terms, filter and printable catalog
The local half of the original Fase 2 (no network, no Offer entity yet): - Lot sheet gains a 'do you share it?' choice (gift/swap first, sale last, never the default); declaring plenty of abundance reveals it with a nudge. - Lot lines and list tiles badge shared batches; an 'I share' filter and a printable PDF catalog (paper bridge for fairs) round out the view. - Catalog PDF embeds DejaVu (Latin-ext/Cyrillic/Greek); fonts are a per-locale resource like the OCR packs.
This commit is contained in:
parent
e3ec855630
commit
ba87bf2719
20 changed files with 982 additions and 36 deletions
|
|
@ -18,6 +18,7 @@ class InventoryState extends Equatable {
|
|||
this.typeFilter = const {},
|
||||
this.organicOnly = false,
|
||||
this.needsReproductionOnly = false,
|
||||
this.sharingOnly = false,
|
||||
this.loading = true,
|
||||
});
|
||||
|
||||
|
|
@ -42,6 +43,10 @@ class InventoryState extends Equatable {
|
|||
/// When true, keep only varieties flagged "needs reproducing this season".
|
||||
final bool needsReproductionOnly;
|
||||
|
||||
/// When true, keep only varieties with some lot offered to others — the
|
||||
/// "what I share" view.
|
||||
final bool sharingOnly;
|
||||
|
||||
final bool loading;
|
||||
|
||||
/// Categories present across all items, in display order (deduped), so the
|
||||
|
|
@ -60,15 +65,16 @@ class InventoryState extends Equatable {
|
|||
final q = query.trim().toLowerCase();
|
||||
return items.where((i) {
|
||||
if (q.isNotEmpty && !i.label.toLowerCase().contains(q)) return false;
|
||||
if (categoryFilter.isNotEmpty &&
|
||||
!categoryFilter.contains(i.category)) {
|
||||
if (categoryFilter.isNotEmpty && !categoryFilter.contains(i.category)) {
|
||||
return false;
|
||||
}
|
||||
if (typeFilter.isNotEmpty && i.lotTypes.intersection(typeFilter).isEmpty) {
|
||||
if (typeFilter.isNotEmpty &&
|
||||
i.lotTypes.intersection(typeFilter).isEmpty) {
|
||||
return false;
|
||||
}
|
||||
if (organicOnly && !i.isOrganic) return false;
|
||||
if (needsReproductionOnly && !i.needsReproduction) return false;
|
||||
if (sharingOnly && !i.isShared) return false;
|
||||
return true;
|
||||
}).toList();
|
||||
}
|
||||
|
|
@ -81,6 +87,10 @@ class InventoryState extends Equatable {
|
|||
/// that filter chip when it would match something.
|
||||
bool get hasNeedsReproduction => items.any((i) => i.needsReproduction);
|
||||
|
||||
/// Whether any variety has a lot offered to others, so the UI only offers
|
||||
/// the "sharing" chip and the printable catalog when they'd show something.
|
||||
bool get hasShared => items.any((i) => i.isShared);
|
||||
|
||||
InventoryState copyWith({
|
||||
List<VarietyListItem>? items,
|
||||
List<VarietyListItem>? drafts,
|
||||
|
|
@ -89,6 +99,7 @@ class InventoryState extends Equatable {
|
|||
Set<LotType>? typeFilter,
|
||||
bool? organicOnly,
|
||||
bool? needsReproductionOnly,
|
||||
bool? sharingOnly,
|
||||
bool? loading,
|
||||
}) {
|
||||
return InventoryState(
|
||||
|
|
@ -100,6 +111,7 @@ class InventoryState extends Equatable {
|
|||
organicOnly: organicOnly ?? this.organicOnly,
|
||||
needsReproductionOnly:
|
||||
needsReproductionOnly ?? this.needsReproductionOnly,
|
||||
sharingOnly: sharingOnly ?? this.sharingOnly,
|
||||
loading: loading ?? this.loading,
|
||||
);
|
||||
}
|
||||
|
|
@ -113,6 +125,7 @@ class InventoryState extends Equatable {
|
|||
typeFilter,
|
||||
organicOnly,
|
||||
needsReproductionOnly,
|
||||
sharingOnly,
|
||||
loading,
|
||||
];
|
||||
}
|
||||
|
|
@ -157,9 +170,12 @@ class InventoryCubit extends Cubit<InventoryState> {
|
|||
emit(state.copyWith(organicOnly: !state.organicOnly));
|
||||
|
||||
/// Toggles the "needs reproducing only" filter.
|
||||
void toggleNeedsReproductionOnly() => emit(
|
||||
state.copyWith(needsReproductionOnly: !state.needsReproductionOnly),
|
||||
);
|
||||
void toggleNeedsReproductionOnly() =>
|
||||
emit(state.copyWith(needsReproductionOnly: !state.needsReproductionOnly));
|
||||
|
||||
/// Toggles the "what I share" filter.
|
||||
void toggleSharingOnly() =>
|
||||
emit(state.copyWith(sharingOnly: !state.sharingOnly));
|
||||
|
||||
/// Clears all filters (search is left untouched).
|
||||
void clearFilters() => emit(
|
||||
|
|
@ -168,6 +184,7 @@ class InventoryCubit extends Cubit<InventoryState> {
|
|||
typeFilter: const {},
|
||||
organicOnly: false,
|
||||
needsReproductionOnly: false,
|
||||
sharingOnly: false,
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class VarietyDetailCubit extends Cubit<VarietyDetailState> {
|
|||
String? originPlace,
|
||||
Abundance? abundance,
|
||||
PreservationFormat? preservationFormat,
|
||||
OfferStatus offerStatus = OfferStatus.private,
|
||||
}) => _repo.addLot(
|
||||
varietyId: varietyId,
|
||||
type: type,
|
||||
|
|
@ -91,6 +92,7 @@ class VarietyDetailCubit extends Cubit<VarietyDetailState> {
|
|||
originPlace: originPlace,
|
||||
abundance: abundance,
|
||||
preservationFormat: preservationFormat,
|
||||
offerStatus: offerStatus,
|
||||
);
|
||||
|
||||
Future<void> updateLot({
|
||||
|
|
@ -104,6 +106,7 @@ class VarietyDetailCubit extends Cubit<VarietyDetailState> {
|
|||
String? originPlace,
|
||||
Abundance? abundance,
|
||||
PreservationFormat? preservationFormat,
|
||||
OfferStatus offerStatus = OfferStatus.private,
|
||||
}) => _repo.updateLot(
|
||||
lotId: lotId,
|
||||
type: type,
|
||||
|
|
@ -115,6 +118,7 @@ class VarietyDetailCubit extends Cubit<VarietyDetailState> {
|
|||
originPlace: originPlace,
|
||||
abundance: abundance,
|
||||
preservationFormat: preservationFormat,
|
||||
offerStatus: offerStatus,
|
||||
);
|
||||
|
||||
Future<void> deleteLot(String lotId) => _repo.softDeleteLot(lotId);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue