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
66
apps/app_seeds/test/services/share_catalog_service_test.dart
Normal file
66
apps/app_seeds/test/services/share_catalog_service_test.dart
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tane/services/file_service.dart';
|
||||
import 'package:tane/services/share_catalog_service.dart';
|
||||
|
||||
/// Captures the bytes handed to the save dialog instead of touching disk.
|
||||
class _RecordingFileService implements FileService {
|
||||
Uint8List? saved;
|
||||
String? savedName;
|
||||
|
||||
@override
|
||||
Future<String?> saveFile({
|
||||
required String suggestedName,
|
||||
required Uint8List bytes,
|
||||
}) async {
|
||||
saved = bytes;
|
||||
savedName = suggestedName;
|
||||
return '/dev/null/$suggestedName';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List?> pickFileBytes({List<String>? allowedExtensions}) async =>
|
||||
null;
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
test('buildPdf renders a well-formed PDF with the given rows', () async {
|
||||
final service = ShareCatalogService(files: _RecordingFileService());
|
||||
final bytes = await service.buildPdf(
|
||||
title: 'What I share',
|
||||
date: 'July 9, 2026',
|
||||
rows: const [
|
||||
ShareCatalogRow(
|
||||
name: 'Rebordanes maize',
|
||||
scientificName: 'Zea mays',
|
||||
details: 'Seeds · Year 2024 · 2 cobs',
|
||||
mode: 'To give away',
|
||||
),
|
||||
ShareCatalogRow(name: 'Alubia de Tolosa', mode: 'To swap'),
|
||||
],
|
||||
);
|
||||
|
||||
// %PDF header + non-trivial body; text content is compressed, so the
|
||||
// human-readable row assertions live in the widget/data tests.
|
||||
expect(String.fromCharCodes(bytes.take(5)), '%PDF-');
|
||||
expect(bytes.length, greaterThan(1000));
|
||||
});
|
||||
|
||||
test('saveCatalog hands the PDF to the save dialog', () async {
|
||||
final files = _RecordingFileService();
|
||||
final service = ShareCatalogService(files: files);
|
||||
final saved = await service.saveCatalog(
|
||||
title: 'What I share',
|
||||
date: 'July 9, 2026',
|
||||
suggestedName: 'tanemaki-catalog-2026-07-09.pdf',
|
||||
rows: const [ShareCatalogRow(name: 'Maize', mode: 'To give away')],
|
||||
);
|
||||
|
||||
expect(saved, isTrue);
|
||||
expect(files.savedName, 'tanemaki-catalog-2026-07-09.pdf');
|
||||
expect(String.fromCharCodes(files.saved!.take(5)), '%PDF-');
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue