feat: add default mock data

This commit is contained in:
2026-07-30 04:01:01 +02:00
parent ae67878a3c
commit 715aa529d4
9 changed files with 242 additions and 7 deletions
+156
View File
@@ -0,0 +1,156 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:drift/drift.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:uuid/uuid.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import '../database/app_database.dart';
class DefaultProductSeeder {
final AppDatabase database;
DefaultProductSeeder({required this.database});
static const _defaultProducts = [
('Jupiler', 280, 80, 'Bier'),
('Stella Artois', 300, 60, 'Bier'),
('Leffe Blonde', 380, 30, 'Bier'),
('Duvel', 420, 25, 'Bier'),
('Hoegaarden', 350, 35, 'Bier'),
('Chimay Blue', 500, 20, 'Bier'),
('Witte Wijn', 380, 50, 'Wijn'),
('Rode Wijn', 400, 45, 'Wijn'),
('Rosé', 350, 40, 'Wijn'),
('Cava', 650, 15, 'Wijn'),
('Cola', 250, 100, 'Frisdrank'),
('Cola Zero', 250, 80, 'Frisdrank'),
('Spa Bruis', 200, 70, 'Frisdrank'),
('Spa Plat', 200, 70, 'Frisdrank'),
('Ice Tea', 280, 60, 'Frisdrank'),
('Limonade', 250, 50, 'Frisdrank'),
('Chips', 220, 40, 'Snacks'),
('Nootjes', 280, 35, 'Snacks'),
('Kroketten', 350, 30, 'Snacks'),
('Bitterballen', 320, 30, 'Snacks'),
('Koffie', 280, 200, 'Coffee'),
('Espresso', 250, 200, 'Coffee'),
('Latte Macchiato', 350, 150, 'Coffee'),
('Warme Chocomelk', 320, 100, 'Coffee'),
('Thee', 250, 150, 'Coffee'),
];
Future<void> seedIfEmpty() async {
try {
final count = await _getProductCount();
if (count > 0) return;
final imagesDir = await _ensureImagesDir();
await database.transaction(() async {
final uuid = const Uuid();
for (final (name, priceInCents, stock, category) in _defaultProducts) {
String? imagePath;
if (imagesDir != null) {
imagePath = await _copyBundledImage(name, imagesDir);
}
await database.into(database.products).insert(
ProductsCompanion.insert(
id: uuid.v4(),
name: name,
category: category,
stockQuantity: stock,
lowStockThreshold: 10,
priceInCents: priceInCents,
imagePath: Value(imagePath),
),
);
}
});
} catch (e, stack) {
debugPrint('DefaultProductSeeder: seedIfEmpty error: $e');
Sentry.captureException(e, stackTrace: stack);
}
}
Future<int> _getProductCount() async {
final countExpr = database.products.id.count();
final query = database.selectOnly(database.products)
..addColumns([countExpr]);
final row = await query.getSingle();
return row.read(countExpr) ?? 0;
}
Future<Directory?> _ensureImagesDir() async {
try {
final appDir = await getApplicationSupportDirectory();
final imagesDir = Directory(path.join(appDir.path, 'product_images'));
if (!imagesDir.existsSync()) {
await imagesDir.create(recursive: true);
}
return imagesDir;
} catch (e) {
debugPrint('DefaultProductSeeder: could not create images dir: $e');
return null;
}
}
Future<String?> _copyBundledImage(String productName, Directory imagesDir) async {
final slug = _toSlug(productName);
for (final ext in ['png', 'jpg', 'webp']) {
final assetKey = 'assets/products/$slug.$ext';
try {
final bytes = await rootBundle.load(assetKey);
final fileName = '${DateTime.now().millisecondsSinceEpoch}_$slug.$ext';
final destPath = path.join(imagesDir.path, fileName);
final destFile = File(destPath);
await destFile.writeAsBytes(bytes.buffer.asUint8List(
bytes.offsetInBytes,
bytes.lengthInBytes,
));
return destPath;
} catch (_) {
// Asset not found for this extension, try next
}
}
debugPrint('DefaultProductSeeder: no bundled image found for "$productName" '
'(looked for assets/products/$slug.{png,jpg,webp})');
return null;
}
String _toSlug(String name) {
return name
.toLowerCase()
.replaceAll(' ', '_')
.replaceAll("'", '')
.replaceAll('é', 'e')
.replaceAll('è', 'e')
.replaceAll('ê', 'e')
.replaceAll('ë', 'e')
.replaceAll('â', 'a')
.replaceAll('à', 'a')
.replaceAll('ä', 'a')
.replaceAll('û', 'u')
.replaceAll('ù', 'u')
.replaceAll('ü', 'u')
.replaceAll('ô', 'o')
.replaceAll('ö', 'o')
.replaceAll('ï', 'i')
.replaceAll('î', 'i')
.replaceAll('ç', 'c')
.replaceAll('œ', 'oe');
}
}