This commit is contained in:
Phuoc Nguyen
2025-10-10 16:38:07 +07:00
parent e5b247d622
commit b94c158004
177 changed files with 25080 additions and 152 deletions

View File

@@ -0,0 +1,101 @@
import 'package:retail/core/database/hive_database.dart';
import 'package:retail/core/database/seed_data.dart';
/// Database initialization and seeding utility
class DatabaseInitializer {
final HiveDatabase _database;
DatabaseInitializer(this._database);
/// Initialize database and seed with sample data if empty
Future<void> initialize({bool seedIfEmpty = true}) async {
// Initialize Hive
await _database.init();
// Seed data if boxes are empty and seeding is enabled
if (seedIfEmpty) {
await _seedIfEmpty();
}
}
/// Seed database with sample data if empty
Future<void> _seedIfEmpty() async {
final productsBox = _database.productsBox;
final categoriesBox = _database.categoriesBox;
// Check if database is empty
if (productsBox.isEmpty && categoriesBox.isEmpty) {
await seedDatabase(forceReseed: false);
}
}
/// Seed database with sample data
Future<void> seedDatabase({bool forceReseed = false}) async {
final productsBox = _database.productsBox;
final categoriesBox = _database.categoriesBox;
// Clear existing data if force reseed
if (forceReseed) {
await productsBox.clear();
await categoriesBox.clear();
}
// Only seed if boxes are empty
if (productsBox.isEmpty && categoriesBox.isEmpty) {
// Generate and save categories
final categories = SeedData.generateCategories();
final categoriesMap = {
for (var category in categories) category.id: category
};
await categoriesBox.putAll(categoriesMap);
// Generate and save products
final products = SeedData.generateProducts();
final productsMap = {
for (var product in products) product.id: product
};
await productsBox.putAll(productsMap);
// Update category product counts
await _updateCategoryProductCounts();
}
}
/// Update product counts for all categories
Future<void> _updateCategoryProductCounts() async {
final productsBox = _database.productsBox;
final categoriesBox = _database.categoriesBox;
// Count products per category
final productCounts = <String, int>{};
for (var product in productsBox.values) {
productCounts[product.categoryId] =
(productCounts[product.categoryId] ?? 0) + 1;
}
// Update category product counts
for (var category in categoriesBox.values) {
final count = productCounts[category.id] ?? 0;
if (category.productCount != count) {
final updated = category.copyWith(productCount: count);
await categoriesBox.put(category.id, updated);
}
}
}
/// Reset database (clear all data and reseed)
Future<void> resetDatabase() async {
await _database.clearAllData();
await seedDatabase(forceReseed: true);
}
/// Get database statistics
Map<String, dynamic> getDatabaseStats() {
return _database.getStatistics();
}
/// Compact database (optimize storage)
Future<void> compactDatabase() async {
await _database.compactAll();
}
}

View File

@@ -0,0 +1,171 @@
import 'package:hive_ce/hive.dart';
import 'package:hive_ce_flutter/hive_flutter.dart';
import 'package:retail/core/constants/storage_constants.dart';
import 'package:retail/features/products/data/models/product_model.dart';
import 'package:retail/features/categories/data/models/category_model.dart';
import 'package:retail/features/home/data/models/cart_item_model.dart';
import 'package:retail/features/home/data/models/transaction_model.dart';
import 'package:retail/features/settings/data/models/app_settings_model.dart';
/// Hive database initialization and management
class HiveDatabase {
static HiveDatabase? _instance;
static HiveDatabase get instance => _instance ??= HiveDatabase._();
HiveDatabase._();
bool _isInitialized = false;
/// Initialize Hive database
Future<void> init() async {
if (_isInitialized) {
return;
}
try {
// Initialize Hive for Flutter
await Hive.initFlutter();
// Register all type adapters
_registerAdapters();
// Open all boxes
await _openBoxes();
// Initialize default settings if needed
await _initializeDefaults();
_isInitialized = true;
} catch (e) {
throw Exception('Failed to initialize Hive database: $e');
}
}
/// Register all Hive type adapters
void _registerAdapters() {
// Register only if not already registered
if (!Hive.isAdapterRegistered(StorageConstants.productTypeId)) {
Hive.registerAdapter(ProductModelAdapter());
}
if (!Hive.isAdapterRegistered(StorageConstants.categoryTypeId)) {
Hive.registerAdapter(CategoryModelAdapter());
}
if (!Hive.isAdapterRegistered(StorageConstants.cartItemTypeId)) {
Hive.registerAdapter(CartItemModelAdapter());
}
if (!Hive.isAdapterRegistered(StorageConstants.transactionTypeId)) {
Hive.registerAdapter(TransactionModelAdapter());
}
if (!Hive.isAdapterRegistered(StorageConstants.appSettingsTypeId)) {
Hive.registerAdapter(AppSettingsModelAdapter());
}
}
/// Open all required boxes
Future<void> _openBoxes() async {
await Future.wait([
Hive.openBox<ProductModel>(StorageConstants.productsBox),
Hive.openBox<CategoryModel>(StorageConstants.categoriesBox),
Hive.openBox<CartItemModel>(StorageConstants.cartBox),
Hive.openBox<TransactionModel>(StorageConstants.transactionsBox),
Hive.openBox<AppSettingsModel>(StorageConstants.settingsBox),
]);
}
/// Initialize default settings and seed data if first launch
Future<void> _initializeDefaults() async {
final settingsBox = Hive.box<AppSettingsModel>(StorageConstants.settingsBox);
// Initialize default settings if not exists
if (settingsBox.isEmpty) {
await settingsBox.put(
'app_settings',
AppSettingsModel.defaultSettings(),
);
}
}
/// Get a specific box by name
Box<T> getBox<T>(String boxName) {
return Hive.box<T>(boxName);
}
/// Get products box
Box<ProductModel> get productsBox =>
Hive.box<ProductModel>(StorageConstants.productsBox);
/// Get categories box
Box<CategoryModel> get categoriesBox =>
Hive.box<CategoryModel>(StorageConstants.categoriesBox);
/// Get cart box
Box<CartItemModel> get cartBox =>
Hive.box<CartItemModel>(StorageConstants.cartBox);
/// Get transactions box
Box<TransactionModel> get transactionsBox =>
Hive.box<TransactionModel>(StorageConstants.transactionsBox);
/// Get settings box
Box<AppSettingsModel> get settingsBox =>
Hive.box<AppSettingsModel>(StorageConstants.settingsBox);
/// Clear all data from all boxes (useful for logout or reset)
Future<void> clearAllData() async {
await Future.wait([
productsBox.clear(),
categoriesBox.clear(),
cartBox.clear(),
transactionsBox.clear(),
// Don't clear settings
]);
}
/// Clear cart only
Future<void> clearCart() async {
await cartBox.clear();
}
/// Compact all boxes (optimize storage)
Future<void> compactAll() async {
await Future.wait([
productsBox.compact(),
categoriesBox.compact(),
cartBox.compact(),
transactionsBox.compact(),
settingsBox.compact(),
]);
}
/// Close all boxes
Future<void> closeAll() async {
await Hive.close();
_isInitialized = false;
}
/// Delete all boxes (complete database reset)
Future<void> deleteAll() async {
await Future.wait([
Hive.deleteBoxFromDisk(StorageConstants.productsBox),
Hive.deleteBoxFromDisk(StorageConstants.categoriesBox),
Hive.deleteBoxFromDisk(StorageConstants.cartBox),
Hive.deleteBoxFromDisk(StorageConstants.transactionsBox),
Hive.deleteBoxFromDisk(StorageConstants.settingsBox),
]);
_isInitialized = false;
}
/// Get database statistics
Map<String, dynamic> getStatistics() {
return {
'products': productsBox.length,
'categories': categoriesBox.length,
'cartItems': cartBox.length,
'transactions': transactionsBox.length,
'isInitialized': _isInitialized,
};
}
/// Check if database is initialized
bool get isInitialized => _isInitialized;
}

View File

@@ -0,0 +1,210 @@
import 'package:uuid/uuid.dart';
import 'package:retail/features/products/data/models/product_model.dart';
import 'package:retail/features/categories/data/models/category_model.dart';
/// Seed data generator for testing and initial app setup
class SeedData {
static const _uuid = Uuid();
/// Generate sample categories
static List<CategoryModel> generateCategories() {
final now = DateTime.now();
return [
CategoryModel(
id: 'cat_electronics',
name: 'Electronics',
description: 'Electronic devices and accessories',
iconPath: 'devices',
color: '#2196F3', // Blue
productCount: 0,
createdAt: now.subtract(const Duration(days: 60)),
),
CategoryModel(
id: 'cat_appliances',
name: 'Home Appliances',
description: 'Kitchen and home appliances',
iconPath: 'kitchen',
color: '#4CAF50', // Green
productCount: 0,
createdAt: now.subtract(const Duration(days: 55)),
),
CategoryModel(
id: 'cat_sports',
name: 'Sports & Fitness',
description: 'Sports equipment and fitness gear',
iconPath: 'fitness_center',
color: '#FF9800', // Orange
productCount: 0,
createdAt: now.subtract(const Duration(days: 50)),
),
CategoryModel(
id: 'cat_fashion',
name: 'Fashion',
description: 'Clothing, shoes, and accessories',
iconPath: 'checkroom',
color: '#E91E63', // Pink
productCount: 0,
createdAt: now.subtract(const Duration(days: 45)),
),
CategoryModel(
id: 'cat_books',
name: 'Books & Media',
description: 'Books, magazines, and media',
iconPath: 'book',
color: '#9C27B0', // Purple
productCount: 0,
createdAt: now.subtract(const Duration(days: 40)),
),
];
}
/// Generate sample products
static List<ProductModel> generateProducts() {
final now = DateTime.now();
return [
// Electronics (3 products)
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Wireless Headphones',
description: 'Premium noise-cancelling wireless headphones with 30-hour battery life',
price: 299.99,
imageUrl: 'https://picsum.photos/seed/headphones/400/400',
categoryId: 'cat_electronics',
stockQuantity: 25,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 30)),
updatedAt: now.subtract(const Duration(days: 1)),
),
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Smart Watch',
description: 'Fitness tracking smart watch with heart rate monitor and GPS',
price: 199.99,
imageUrl: 'https://picsum.photos/seed/smartwatch/400/400',
categoryId: 'cat_electronics',
stockQuantity: 15,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 25)),
updatedAt: now.subtract(const Duration(days: 2)),
),
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Laptop Stand',
description: 'Adjustable aluminum laptop stand with ergonomic design',
price: 39.99,
imageUrl: 'https://picsum.photos/seed/laptopstand/400/400',
categoryId: 'cat_electronics',
stockQuantity: 20,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 20)),
updatedAt: now.subtract(const Duration(days: 1)),
),
// Home Appliances (2 products)
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Coffee Maker',
description: 'Automatic drip coffee maker with programmable timer and thermal carafe',
price: 79.99,
imageUrl: 'https://picsum.photos/seed/coffeemaker/400/400',
categoryId: 'cat_appliances',
stockQuantity: 8,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 18)),
updatedAt: now.subtract(const Duration(days: 3)),
),
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Blender',
description: 'High-power 1000W blender perfect for smoothies and crushing ice',
price: 59.99,
imageUrl: 'https://picsum.photos/seed/blender/400/400',
categoryId: 'cat_appliances',
stockQuantity: 12,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 15)),
updatedAt: now.subtract(const Duration(days: 2)),
),
// Sports & Fitness (3 products)
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Yoga Mat',
description: 'Non-slip exercise yoga mat with carrying strap, 6mm thickness',
price: 29.99,
imageUrl: 'https://picsum.photos/seed/yogamat/400/400',
categoryId: 'cat_sports',
stockQuantity: 50,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 12)),
updatedAt: now.subtract(const Duration(days: 1)),
),
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Running Shoes',
description: 'Comfortable running shoes with responsive cushioning and breathable mesh',
price: 89.99,
imageUrl: 'https://picsum.photos/seed/runningshoes/400/400',
categoryId: 'cat_sports',
stockQuantity: 30,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 10)),
updatedAt: now.subtract(const Duration(days: 2)),
),
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Water Bottle',
description: 'Insulated stainless steel water bottle, 32oz capacity, keeps cold 24hrs',
price: 24.99,
imageUrl: 'https://picsum.photos/seed/waterbottle/400/400',
categoryId: 'cat_sports',
stockQuantity: 45,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 8)),
updatedAt: now,
),
// Fashion (2 products)
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Leather Backpack',
description: 'Premium leather backpack with laptop compartment and multiple pockets',
price: 129.99,
imageUrl: 'https://picsum.photos/seed/backpack/400/400',
categoryId: 'cat_fashion',
stockQuantity: 18,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 7)),
updatedAt: now.subtract(const Duration(days: 1)),
),
ProductModel(
id: 'prod_${_uuid.v4()}',
name: 'Sunglasses',
description: 'UV protection polarized sunglasses with stylish design',
price: 49.99,
imageUrl: 'https://picsum.photos/seed/sunglasses/400/400',
categoryId: 'cat_fashion',
stockQuantity: 35,
isAvailable: true,
createdAt: now.subtract(const Duration(days: 5)),
updatedAt: now.subtract(const Duration(days: 1)),
),
];
}
/// Seed database with sample data
static Future<void> seedDatabase({
required Future<void> Function(List<CategoryModel>) saveCategories,
required Future<void> Function(List<ProductModel>) saveProducts,
}) async {
// Generate and save categories
final categories = generateCategories();
await saveCategories(categories);
// Generate and save products
final products = generateProducts();
await saveProducts(products);
}
}