Files
kooltab/lib/services/default_product_seeder.dart
T
2026-07-30 17:08:00 +02:00

149 lines
4.3 KiB
Dart

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'),
('Witte Wijn', 380, 50, 'Wijn'),
('Rode Wijn', 400, 45, 'Wijn'),
('Rosé', 350, 40, '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'),
('Koffie', 280, 200, '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');
}
}