diff --git a/lib/core/database/database_handler.dart b/lib/core/database/database_handler.dart index c6a7e21..543a9c1 100644 --- a/lib/core/database/database_handler.dart +++ b/lib/core/database/database_handler.dart @@ -23,7 +23,7 @@ class DatabaseHelper { return await openDatabase( path, - version: 2, // Updated version for printer table + version: 3, // Updated version for categories table onCreate: _onCreate, onUpgrade: _onUpgrade, ); @@ -66,7 +66,22 @@ class DatabaseHelper { ) '''); - // Printer table - NEW + // Categories table - NEW + await db.execute(''' + CREATE TABLE categories ( + id TEXT PRIMARY KEY, + organization_id TEXT, + name TEXT NOT NULL, + description TEXT, + business_type TEXT, + metadata TEXT, + is_active INTEGER DEFAULT 1, + created_at TEXT, + updated_at TEXT + ) + '''); + + // Printer table await db.execute(''' CREATE TABLE printers ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -85,6 +100,11 @@ class DatabaseHelper { 'CREATE INDEX idx_products_category_id ON products(category_id)'); await db.execute('CREATE INDEX idx_products_name ON products(name)'); await db.execute('CREATE INDEX idx_products_sku ON products(sku)'); + await db.execute('CREATE INDEX idx_categories_name ON categories(name)'); + await db.execute( + 'CREATE INDEX idx_categories_organization_id ON categories(organization_id)'); + await db.execute( + 'CREATE INDEX idx_categories_is_active ON categories(is_active)'); await db.execute('CREATE INDEX idx_printers_code ON printers(code)'); await db.execute('CREATE INDEX idx_printers_type ON printers(type)'); } @@ -105,10 +125,32 @@ class DatabaseHelper { ) '''); - // Add indexes for printer table await db.execute('CREATE INDEX idx_printers_code ON printers(code)'); await db.execute('CREATE INDEX idx_printers_type ON printers(type)'); } + + if (oldVersion < 3) { + // Add categories table in version 3 + await db.execute(''' + CREATE TABLE categories ( + id TEXT PRIMARY KEY, + organization_id TEXT, + name TEXT NOT NULL, + description TEXT, + business_type TEXT, + metadata TEXT, + is_active INTEGER DEFAULT 1, + created_at TEXT, + updated_at TEXT + ) + '''); + + await db.execute('CREATE INDEX idx_categories_name ON categories(name)'); + await db.execute( + 'CREATE INDEX idx_categories_organization_id ON categories(organization_id)'); + await db.execute( + 'CREATE INDEX idx_categories_is_active ON categories(is_active)'); + } } Future close() async { diff --git a/lib/data/datasources/category/category_local_datasource.dart b/lib/data/datasources/category/category_local_datasource.dart new file mode 100644 index 0000000..e9e8846 --- /dev/null +++ b/lib/data/datasources/category/category_local_datasource.dart @@ -0,0 +1,373 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'package:enaklo_pos/core/database/database_handler.dart'; +import 'package:enaklo_pos/data/models/response/category_response_model.dart'; +import 'package:sqflite/sqflite.dart'; + +class CategoryLocalDatasource { + static CategoryLocalDatasource? _instance; + + CategoryLocalDatasource._internal(); + + static CategoryLocalDatasource get instance { + _instance ??= CategoryLocalDatasource._internal(); + return _instance!; + } + + Future get _db async => await DatabaseHelper.instance.database; + + // ======================================== + // CACHING SYSTEM + // ======================================== + final Map> _queryCache = {}; + final Duration _cacheExpiry = + Duration(minutes: 10); // Lebih lama untuk categories + final Map _cacheTimestamps = {}; + + // ======================================== + // BATCH SAVE CATEGORIES + // ======================================== + Future saveCategoriesBatch(List categories, + {bool clearFirst = false}) async { + final db = await _db; + + try { + await db.transaction((txn) async { + if (clearFirst) { + log('๐Ÿ—‘๏ธ Clearing existing categories...'); + await txn.delete('categories'); + } + + log('๐Ÿ’พ Batch saving ${categories.length} categories...'); + + // Batch insert categories + final batch = txn.batch(); + for (final category in categories) { + batch.insert( + 'categories', + _categoryToMap(category), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + await batch.commit(noResult: true); + }); + + // Clear cache after update + clearCache(); + log('โœ… Successfully batch saved ${categories.length} categories'); + } catch (e) { + log('โŒ Error batch saving categories: $e'); + rethrow; + } + } + + // ======================================== + // CACHED QUERY + // ======================================== + Future> getCachedCategories({ + int page = 1, + int limit = 10, + bool isActive = true, + String? search, + }) async { + final cacheKey = _generateCacheKey(page, limit, isActive, search); + final now = DateTime.now(); + + // Check cache first + if (_queryCache.containsKey(cacheKey) && + _cacheTimestamps.containsKey(cacheKey)) { + final cacheTime = _cacheTimestamps[cacheKey]!; + if (now.difference(cacheTime) < _cacheExpiry) { + log('๐Ÿš€ Cache HIT: $cacheKey (${_queryCache[cacheKey]!.length} categories)'); + return _queryCache[cacheKey]!; + } + } + + log('๐Ÿ“€ Cache MISS: $cacheKey, querying database...'); + + // Cache miss, query database + final categories = await getCategories( + page: page, + limit: limit, + isActive: isActive, + search: search, + ); + + // Store in cache + _queryCache[cacheKey] = categories; + _cacheTimestamps[cacheKey] = now; + + log('๐Ÿ’พ Cached ${categories.length} categories for key: $cacheKey'); + return categories; + } + + // ======================================== + // REGULAR GET CATEGORIES + // ======================================== + Future> getCategories({ + int page = 1, + int limit = 10, + bool isActive = true, + String? search, + }) async { + final db = await _db; + + try { + String query = 'SELECT * FROM categories WHERE 1=1'; + List whereArgs = []; + + // Note: Assuming is_active will be added to database schema + if (isActive) { + query += ' AND is_active = ?'; + whereArgs.add(1); + } + + if (search != null && search.isNotEmpty) { + query += ' AND (name LIKE ? OR description LIKE ?)'; + whereArgs.add('%$search%'); + whereArgs.add('%$search%'); + } + + query += ' ORDER BY name ASC'; + + if (limit > 0) { + query += ' LIMIT ?'; + whereArgs.add(limit); + + if (page > 1) { + query += ' OFFSET ?'; + whereArgs.add((page - 1) * limit); + } + } + + final List> maps = + await db.rawQuery(query, whereArgs); + + List categories = []; + for (final map in maps) { + categories.add(_mapToCategory(map)); + } + + log('๐Ÿ“Š Retrieved ${categories.length} categories from database'); + return categories; + } catch (e) { + log('โŒ Error getting categories: $e'); + return []; + } + } + + // ======================================== + // GET ALL CATEGORIES (For dropdowns) + // ======================================== + Future> getAllCategories() async { + const cacheKey = 'all_categories'; + final now = DateTime.now(); + + // Check cache + if (_queryCache.containsKey(cacheKey) && + _cacheTimestamps.containsKey(cacheKey)) { + final cacheTime = _cacheTimestamps[cacheKey]!; + if (now.difference(cacheTime) < _cacheExpiry) { + return _queryCache[cacheKey]!; + } + } + + final db = await _db; + + try { + final List> maps = await db.query( + 'categories', + orderBy: 'name ASC', + ); + + final categories = maps.map((map) => _mapToCategory(map)).toList(); + + // Cache all categories + _queryCache[cacheKey] = categories; + _cacheTimestamps[cacheKey] = now; + + log('๐Ÿ“Š Retrieved ${categories.length} total categories'); + return categories; + } catch (e) { + log('โŒ Error getting all categories: $e'); + return []; + } + } + + // ======================================== + // GET CATEGORY BY ID + // ======================================== + Future getCategoryById(String id) async { + final db = await _db; + + try { + final List> maps = await db.query( + 'categories', + where: 'id = ?', + whereArgs: [id], + ); + + if (maps.isEmpty) { + log('โŒ Category not found: $id'); + return null; + } + + final category = _mapToCategory(maps.first); + log('โœ… Category found: ${category.name}'); + return category; + } catch (e) { + log('โŒ Error getting category by ID: $e'); + return null; + } + } + + // ======================================== + // GET TOTAL COUNT + // ======================================== + Future getTotalCount({bool isActive = true, String? search}) async { + final db = await _db; + + try { + String query = 'SELECT COUNT(*) FROM categories WHERE 1=1'; + List whereArgs = []; + + if (isActive) { + query += ' AND is_active = ?'; + whereArgs.add(1); + } + + if (search != null && search.isNotEmpty) { + query += ' AND (name LIKE ? OR description LIKE ?)'; + whereArgs.add('%$search%'); + whereArgs.add('%$search%'); + } + + final result = await db.rawQuery(query, whereArgs); + final count = Sqflite.firstIntValue(result) ?? 0; + log('๐Ÿ“Š Category total count: $count (isActive: $isActive, search: $search)'); + return count; + } catch (e) { + log('โŒ Error getting category total count: $e'); + return 0; + } + } + + // ======================================== + // HAS CATEGORIES + // ======================================== + Future hasCategories() async { + final count = await getTotalCount(); + final hasData = count > 0; + log('๐Ÿ” Has categories: $hasData ($count categories)'); + return hasData; + } + + // ======================================== + // CLEAR ALL CATEGORIES + // ======================================== + Future clearAllCategories() async { + final db = await _db; + + try { + await db.delete('categories'); + clearCache(); + log('๐Ÿ—‘๏ธ All categories cleared from local DB'); + } catch (e) { + log('โŒ Error clearing categories: $e'); + rethrow; + } + } + + // ======================================== + // CACHE MANAGEMENT + // ======================================== + String _generateCacheKey(int page, int limit, bool isActive, String? search) { + return 'categories_${page}_${limit}_${isActive}_${search ?? 'null'}'; + } + + void clearCache() { + final count = _queryCache.length; + _queryCache.clear(); + _cacheTimestamps.clear(); + log('๐Ÿงน Category cache cleared: $count entries removed'); + } + + void clearExpiredCache() { + final now = DateTime.now(); + final expiredKeys = []; + + _cacheTimestamps.forEach((key, timestamp) { + if (now.difference(timestamp) > _cacheExpiry) { + expiredKeys.add(key); + } + }); + + for (final key in expiredKeys) { + _queryCache.remove(key); + _cacheTimestamps.remove(key); + } + + if (expiredKeys.isNotEmpty) { + log('โฐ Expired category cache cleared: ${expiredKeys.length} entries'); + } + } + + // ======================================== + // DATABASE STATS + // ======================================== + Future> getDatabaseStats() async { + final db = await _db; + + try { + final categoryCount = Sqflite.firstIntValue( + await db.rawQuery('SELECT COUNT(*) FROM categories')) ?? + 0; + + final activeCount = Sqflite.firstIntValue(await db.rawQuery( + 'SELECT COUNT(*) FROM categories WHERE is_active = 1')) ?? + 0; + + final stats = { + 'total_categories': categoryCount, + 'active_categories': activeCount, + 'cache_entries': _queryCache.length, + }; + + log('๐Ÿ“Š Category Database Stats: $stats'); + return stats; + } catch (e) { + log('โŒ Error getting category database stats: $e'); + return {}; + } + } + + // ======================================== + // HELPER METHODS + // ======================================== + Map _categoryToMap(CategoryModel category) { + return { + 'id': category.id, + 'organization_id': category.organizationId, + 'name': category.name, + 'description': category.description, + 'business_type': category.businessType, + 'metadata': json.encode(category.metadata), + 'is_active': 1, // Assuming all synced categories are active + 'created_at': category.createdAt.toIso8601String(), + 'updated_at': category.updatedAt.toIso8601String(), + }; + } + + CategoryModel _mapToCategory(Map map) { + return CategoryModel( + id: map['id'], + organizationId: map['organization_id'], + name: map['name'], + description: map['description'], + businessType: map['business_type'], + metadata: map['metadata'] != null ? json.decode(map['metadata']) : {}, + createdAt: DateTime.parse(map['created_at']), + updatedAt: DateTime.parse(map['updated_at']), + ); + } +} diff --git a/lib/data/datasources/category_remote_datasource.dart b/lib/data/datasources/category/category_remote_datasource.dart similarity index 100% rename from lib/data/datasources/category_remote_datasource.dart rename to lib/data/datasources/category/category_remote_datasource.dart diff --git a/lib/data/repositories/category/category_repository.dart b/lib/data/repositories/category/category_repository.dart new file mode 100644 index 0000000..1083987 --- /dev/null +++ b/lib/data/repositories/category/category_repository.dart @@ -0,0 +1,285 @@ +import 'dart:developer'; +import 'package:dartz/dartz.dart'; +import 'package:enaklo_pos/data/datasources/category/category_local_datasource.dart'; +import 'package:enaklo_pos/data/datasources/category/category_remote_datasource.dart'; +import 'package:enaklo_pos/data/models/response/category_response_model.dart'; + +class CategoryRepository { + static CategoryRepository? _instance; + + final CategoryLocalDatasource _localDatasource; + final CategoryRemoteDatasource _remoteDatasource; + + CategoryRepository._internal() + : _localDatasource = CategoryLocalDatasource.instance, + _remoteDatasource = CategoryRemoteDatasource(); + + static CategoryRepository get instance { + _instance ??= CategoryRepository._internal(); + return _instance!; + } + + // ======================================== + // SYNC STRATEGY: REMOTE-FIRST WITH LOCAL FALLBACK + // ======================================== + Future> getCategories({ + int page = 1, + int limit = 10, + bool isActive = true, + String? search, + bool forceRemote = false, + }) async { + try { + log('๐Ÿ“ฑ Getting categories - page: $page, isActive: $isActive, search: $search, forceRemote: $forceRemote'); + + // Clean expired cache + _localDatasource.clearExpiredCache(); + + // Check if we should try remote first + if (forceRemote || !await _localDatasource.hasCategories()) { + log('๐ŸŒ Attempting remote fetch first...'); + + final remoteResult = await _getRemoteCategories( + page: page, + limit: limit, + isActive: isActive, + ); + + return await remoteResult.fold( + (failure) async { + log('โŒ Remote fetch failed: $failure'); + log('๐Ÿ“ฑ Falling back to local data...'); + return _getLocalCategories( + page: page, + limit: limit, + isActive: isActive, + search: search, + ); + }, + (response) async { + log('โœ… Remote fetch successful, syncing to local...'); + + // Sync remote data to local + if (response.data.categories.isNotEmpty) { + await _syncToLocal(response.data.categories, + clearFirst: page == 1); + } + + return Right(response); + }, + ); + } else { + log('๐Ÿ“ฑ Using local data (cache available)...'); + return _getLocalCategories( + page: page, + limit: limit, + isActive: isActive, + search: search, + ); + } + } catch (e) { + log('โŒ Error in getCategories: $e'); + return Left('Gagal memuat kategori: $e'); + } + } + + // ======================================== + // PURE LOCAL OPERATIONS + // ======================================== + Future> _getLocalCategories({ + int page = 1, + int limit = 10, + bool isActive = true, + String? search, + }) async { + try { + final cachedCategories = await _localDatasource.getCachedCategories( + page: page, + limit: limit, + isActive: isActive, + search: search, + ); + + final totalCount = await _localDatasource.getTotalCount( + isActive: isActive, + search: search, + ); + + final categoryData = CategoryData( + categories: cachedCategories, + totalCount: totalCount, + page: page, + limit: limit, + totalPages: totalCount > 0 ? (totalCount / limit).ceil() : 0, + ); + + final response = CategoryResponseModel( + success: true, + data: categoryData, + ); + + log('โœ… Returned ${cachedCategories.length} local categories (${totalCount} total)'); + return Right(response); + } catch (e) { + log('โŒ Error getting local categories: $e'); + return Left('Gagal memuat kategori dari database lokal: $e'); + } + } + + // ======================================== + // REMOTE FETCH + // ======================================== + Future> _getRemoteCategories({ + int page = 1, + int limit = 10, + bool isActive = true, + }) async { + try { + log('๐ŸŒ Fetching categories from remote...'); + return await _remoteDatasource.getCategories( + page: page, + limit: limit, + isActive: isActive, + ); + } catch (e) { + log('โŒ Remote fetch error: $e'); + return Left('Gagal mengambil data dari server: $e'); + } + } + + // ======================================== + // SYNC TO LOCAL + // ======================================== + Future _syncToLocal(List categories, + {bool clearFirst = false}) async { + try { + log('๐Ÿ’พ Syncing ${categories.length} categories to local database...'); + await _localDatasource.saveCategoriesBatch(categories, + clearFirst: clearFirst); + log('โœ… Categories synced to local successfully'); + } catch (e) { + log('โŒ Error syncing categories to local: $e'); + rethrow; + } + } + + // ======================================== + // MANUAL SYNC OPERATIONS + // ======================================== + Future> syncAllCategories() async { + try { + log('๐Ÿ”„ Starting manual sync of all categories...'); + + int page = 1; + const limit = 50; // Higher limit for bulk sync + bool hasMore = true; + int totalSynced = 0; + + // Clear local data first for fresh sync + await _localDatasource.clearAllCategories(); + + while (hasMore) { + log('๐Ÿ“„ Syncing page $page...'); + + final result = await _remoteDatasource.getCategories( + page: page, + limit: limit, + isActive: true, + ); + + await result.fold( + (failure) async { + log('โŒ Sync failed at page $page: $failure'); + throw Exception(failure); + }, + (response) async { + final categories = response.data.categories; + + if (categories.isNotEmpty) { + await _localDatasource.saveCategoriesBatch( + categories, + clearFirst: false, // Don't clear on subsequent pages + ); + totalSynced += categories.length; + + // Check if we have more pages + hasMore = page < response.data.totalPages; + page++; + + log('๐Ÿ“ฆ Page $page synced: ${categories.length} categories'); + } else { + hasMore = false; + } + }, + ); + } + + final message = 'Berhasil sinkronisasi $totalSynced kategori'; + log('โœ… $message'); + return Right(message); + } catch (e) { + final error = 'Gagal sinkronisasi kategori: $e'; + log('โŒ $error'); + return Left(error); + } + } + + // ======================================== + // UTILITY METHODS + // ======================================== + Future> refreshCategories({ + bool isActive = true, + String? search, + }) async { + log('๐Ÿ”„ Refreshing categories...'); + clearCache(); + + return await getCategories( + page: 1, + limit: 10, + isActive: isActive, + search: search, + forceRemote: true, // Force remote refresh + ); + } + + Future getCategoryById(String id) async { + log('๐Ÿ” Getting category by ID: $id'); + return await _localDatasource.getCategoryById(id); + } + + Future> getAllCategories() async { + log('๐Ÿ“‹ Getting all categories for dropdown...'); + return await _localDatasource.getAllCategories(); + } + + Future hasLocalCategories() async { + final hasCategories = await _localDatasource.hasCategories(); + log('๐Ÿ“Š Has local categories: $hasCategories'); + return hasCategories; + } + + Future> getDatabaseStats() async { + final stats = await _localDatasource.getDatabaseStats(); + log('๐Ÿ“Š Category database stats: $stats'); + return stats; + } + + void clearCache() { + log('๐Ÿงน Clearing category cache'); + _localDatasource.clearCache(); + } + + Future isLocalDatabaseReady() async { + try { + final stats = await getDatabaseStats(); + final categoryCount = stats['total_categories'] ?? 0; + final isReady = categoryCount > 0; + log('๐Ÿ” Category database ready: $isReady ($categoryCount categories)'); + return isReady; + } catch (e) { + log('โŒ Error checking category database readiness: $e'); + return false; + } + } +} diff --git a/lib/main.dart b/lib/main.dart index 1fee13d..a231080 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,7 +34,7 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/material.dart'; import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/auth_remote_datasource.dart'; -import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart'; +import 'package:enaklo_pos/data/datasources/category/category_remote_datasource.dart'; import 'package:enaklo_pos/data/datasources/discount_remote_datasource.dart'; import 'package:enaklo_pos/data/datasources/midtrans_remote_datasource.dart'; import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart'; @@ -275,7 +275,7 @@ class _MyAppState extends State { create: (context) => UploadFileBloc(FileRemoteDataSource()), ), BlocProvider( - create: (context) => CategoryLoaderBloc(CategoryRemoteDatasource()), + create: (context) => CategoryLoaderBloc(), ), BlocProvider( create: (context) => GetPrinterTicketBloc(), diff --git a/lib/presentation/home/bloc/category_loader/category_loader_bloc.dart b/lib/presentation/home/bloc/category_loader/category_loader_bloc.dart index d1653d1..ce56006 100644 --- a/lib/presentation/home/bloc/category_loader/category_loader_bloc.dart +++ b/lib/presentation/home/bloc/category_loader/category_loader_bloc.dart @@ -1,6 +1,8 @@ -import 'package:bloc/bloc.dart'; -import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart'; +import 'dart:async'; +import 'dart:developer'; import 'package:enaklo_pos/data/models/response/category_response_model.dart'; +import 'package:enaklo_pos/data/repositories/category/category_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'category_loader_event.dart'; @@ -9,35 +11,310 @@ part 'category_loader_bloc.freezed.dart'; class CategoryLoaderBloc extends Bloc { - final CategoryRemoteDatasource _datasource; - CategoryLoaderBloc(this._datasource) : super(CategoryLoaderState.initial()) { - on<_Get>((event, emit) async { - emit(const _Loading()); - final result = await _datasource.getCategories(limit: 50); - result.fold( - (l) => emit(_Error(l)), - (r) async { - List categories = r.data.categories; - categories.insert( - 0, - CategoryModel( - id: "", - name: 'Semua', - organizationId: '', - businessType: '', - metadata: {}, - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - ), - ); - emit(_Loaded(categories, null)); + final CategoryRepository _categoryRepository = CategoryRepository.instance; + + Timer? _searchDebounce; + bool _isLoadingMore = false; + + CategoryLoaderBloc() : super(const CategoryLoaderState.initial()) { + on<_GetCategories>(_onGetCategories); + on<_LoadMore>(_onLoadMore); + on<_Refresh>(_onRefresh); + on<_Search>(_onSearch); + on<_SyncAll>(_onSyncAll); + on<_GetAllCategories>(_onGetAllCategories); + on<_ClearCache>(_onClearCache); + on<_GetDatabaseStats>(_onGetDatabaseStats); + } + + @override + Future close() { + _searchDebounce?.cancel(); + return super.close(); + } + + // ======================================== + // GET CATEGORIES (Remote-first with local fallback) + // ======================================== + Future _onGetCategories( + _GetCategories event, + Emitter emit, + ) async { + emit(const CategoryLoaderState.loading()); + _isLoadingMore = false; + + log('๐Ÿ“ฑ Loading categories - isActive: ${event.isActive}, forceRemote: ${event.forceRemote}'); + + final result = await _categoryRepository.getCategories( + page: 1, + limit: 10, + isActive: event.isActive, + search: event.search, + forceRemote: event.forceRemote, + ); + + await result.fold( + (failure) async { + log('โŒ Error loading categories: $failure'); + emit(CategoryLoaderState.error(failure)); + }, + (response) async { + final categories = response.data.categories; + final totalPages = response.data.totalPages; + final hasReachedMax = categories.length < 10 || 1 >= totalPages; + + log('โœ… Categories loaded: ${categories.length}, hasReachedMax: $hasReachedMax'); + + emit(CategoryLoaderState.loaded( + categories: categories, + hasReachedMax: hasReachedMax, + currentPage: 1, + isLoadingMore: false, + isActive: event.isActive, + searchQuery: event.search, + )); + }, + ); + } + + // ======================================== + // LOAD MORE CATEGORIES + // ======================================== + Future _onLoadMore( + _LoadMore event, + Emitter emit, + ) async { + final currentState = state; + + if (currentState is! _Loaded || + currentState.hasReachedMax || + _isLoadingMore || + currentState.isLoadingMore) { + log('โน๏ธ Load more blocked - state: ${currentState.runtimeType}, isLoadingMore: $_isLoadingMore'); + return; + } + + _isLoadingMore = true; + emit(currentState.copyWith(isLoadingMore: true)); + + final nextPage = currentState.currentPage + 1; + log('๐Ÿ“„ Loading more categories - page: $nextPage'); + + try { + final result = await _categoryRepository.getCategories( + page: nextPage, + limit: 10, + isActive: currentState.isActive, + search: currentState.searchQuery, + ); + + await result.fold( + (failure) async { + log('โŒ Error loading more categories: $failure'); + emit(currentState.copyWith(isLoadingMore: false)); + }, + (response) async { + final newCategories = response.data.categories; + final totalPages = response.data.totalPages; + + // Prevent duplicate categories + final currentCategoryIds = + currentState.categories.map((c) => c.id).toSet(); + final filteredNewCategories = newCategories + .where((category) => !currentCategoryIds.contains(category.id)) + .toList(); + + final allCategories = + List.from(currentState.categories) + ..addAll(filteredNewCategories); + + final hasReachedMax = + newCategories.length < 10 || nextPage >= totalPages; + + log('โœ… More categories loaded: ${filteredNewCategories.length} new, total: ${allCategories.length}'); + + emit(CategoryLoaderState.loaded( + categories: allCategories, + hasReachedMax: hasReachedMax, + currentPage: nextPage, + isLoadingMore: false, + isActive: currentState.isActive, + searchQuery: currentState.searchQuery, + )); + }, + ); + } catch (e) { + log('โŒ Exception loading more categories: $e'); + emit(currentState.copyWith(isLoadingMore: false)); + } finally { + _isLoadingMore = false; + } + } + + // ======================================== + // REFRESH CATEGORIES + // ======================================== + Future _onRefresh( + _Refresh event, + Emitter emit, + ) async { + final currentState = state; + bool isActive = true; + String? searchQuery; + + if (currentState is _Loaded) { + isActive = currentState.isActive; + searchQuery = currentState.searchQuery; + } + + _isLoadingMore = false; + _searchDebounce?.cancel(); + + log('๐Ÿ”„ Refreshing categories'); + + // Clear local cache + _categoryRepository.clearCache(); + + add(CategoryLoaderEvent.getCategories( + isActive: isActive, + search: searchQuery, + forceRemote: true, // Force remote refresh + )); + } + + // ======================================== + // SEARCH CATEGORIES + // ======================================== + Future _onSearch( + _Search event, + Emitter emit, + ) async { + // Cancel previous search + _searchDebounce?.cancel(); + + // Debounce search for better UX + _searchDebounce = Timer(Duration(milliseconds: 300), () async { + emit(const CategoryLoaderState.loading()); + _isLoadingMore = false; + + log('๐Ÿ” Searching categories: "${event.query}"'); + + final result = await _categoryRepository.getCategories( + page: 1, + limit: 20, // More results for search + isActive: event.isActive, + search: event.query, + ); + + await result.fold( + (failure) async { + log('โŒ Search error: $failure'); + emit(CategoryLoaderState.error(failure)); + }, + (response) async { + final categories = response.data.categories; + final totalPages = response.data.totalPages; + final hasReachedMax = categories.length < 20 || 1 >= totalPages; + + log('โœ… Search results: ${categories.length} categories found'); + + emit(CategoryLoaderState.loaded( + categories: categories, + hasReachedMax: hasReachedMax, + currentPage: 1, + isLoadingMore: false, + isActive: event.isActive, + searchQuery: event.query, + )); }, ); }); - on<_SetCategoryId>((event, emit) async { - var currentState = state as _Loaded; + } - emit(_Loaded(currentState.categories, event.categoryId)); - }); + // ======================================== + // SYNC ALL CATEGORIES + // ======================================== + Future _onSyncAll( + _SyncAll event, + Emitter emit, + ) async { + emit(const CategoryLoaderState.syncing()); + + log('๐Ÿ”„ Starting full category sync...'); + + final result = await _categoryRepository.syncAllCategories(); + + await result.fold( + (failure) async { + log('โŒ Sync failed: $failure'); + emit(CategoryLoaderState.syncError(failure)); + + // After sync error, try to load local data + Timer(Duration(seconds: 2), () { + add(const CategoryLoaderEvent.getCategories()); + }); + }, + (successMessage) async { + log('โœ… Sync completed: $successMessage'); + emit(CategoryLoaderState.syncSuccess(successMessage)); + + // After successful sync, load the updated data + Timer(Duration(seconds: 1), () { + add(const CategoryLoaderEvent.getCategories()); + }); + }, + ); + } + + // ======================================== + // GET ALL CATEGORIES (For Dropdown) + // ======================================== + Future _onGetAllCategories( + _GetAllCategories event, + Emitter emit, + ) async { + try { + log('๐Ÿ“‹ Loading all categories for dropdown...'); + + final categories = await _categoryRepository.getAllCategories(); + + emit(CategoryLoaderState.allCategoriesLoaded(categories)); + log('โœ… All categories loaded: ${categories.length}'); + } catch (e) { + log('โŒ Error loading all categories: $e'); + emit(CategoryLoaderState.error('Gagal memuat semua kategori: $e')); + } + } + + // ======================================== + // GET DATABASE STATS + // ======================================== + Future _onGetDatabaseStats( + _GetDatabaseStats event, + Emitter emit, + ) async { + try { + final stats = await _categoryRepository.getDatabaseStats(); + log('๐Ÿ“Š Category database stats retrieved: $stats'); + + // You can emit a special state here if needed for UI updates + // For now, just log the stats + } catch (e) { + log('โŒ Error getting category database stats: $e'); + } + } + + // ======================================== + // CLEAR CACHE + // ======================================== + Future _onClearCache( + _ClearCache event, + Emitter emit, + ) async { + log('๐Ÿงน Manually clearing category cache'); + _categoryRepository.clearCache(); + + // Refresh current data after cache clear + add(const CategoryLoaderEvent.refresh()); } } diff --git a/lib/presentation/home/bloc/category_loader/category_loader_bloc.freezed.dart b/lib/presentation/home/bloc/category_loader/category_loader_bloc.freezed.dart index 0bd9d16..0c629a1 100644 --- a/lib/presentation/home/bloc/category_loader/category_loader_bloc.freezed.dart +++ b/lib/presentation/home/bloc/category_loader/category_loader_bloc.freezed.dart @@ -18,39 +18,78 @@ final _privateConstructorUsedError = UnsupportedError( mixin _$CategoryLoaderEvent { @optionalTypeArgs TResult when({ - required TResult Function() get, - required TResult Function(String categoryId) setCategoryId, + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ - TResult? Function()? get, - TResult? Function(String categoryId)? setCategoryId, + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ - TResult Function()? get, - TResult Function(String categoryId)? setCategoryId, + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, required TResult orElse(), }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult map({ - required TResult Function(_Get value) get, - required TResult Function(_SetCategoryId value) setCategoryId, + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? mapOrNull({ - TResult? Function(_Get value)? get, - TResult? Function(_SetCategoryId value)? setCategoryId, + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeMap({ - TResult Function(_Get value)? get, - TResult Function(_SetCategoryId value)? setCategoryId, + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -78,16 +117,223 @@ class _$CategoryLoaderEventCopyWithImpl<$Res, $Val extends CategoryLoaderEvent> } /// @nodoc -abstract class _$$GetImplCopyWith<$Res> { - factory _$$GetImplCopyWith(_$GetImpl value, $Res Function(_$GetImpl) then) = - __$$GetImplCopyWithImpl<$Res>; +abstract class _$$GetCategoriesImplCopyWith<$Res> { + factory _$$GetCategoriesImplCopyWith( + _$GetCategoriesImpl value, $Res Function(_$GetCategoriesImpl) then) = + __$$GetCategoriesImplCopyWithImpl<$Res>; + @useResult + $Res call({bool isActive, String? search, bool forceRemote}); } /// @nodoc -class __$$GetImplCopyWithImpl<$Res> - extends _$CategoryLoaderEventCopyWithImpl<$Res, _$GetImpl> - implements _$$GetImplCopyWith<$Res> { - __$$GetImplCopyWithImpl(_$GetImpl _value, $Res Function(_$GetImpl) _then) +class __$$GetCategoriesImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$GetCategoriesImpl> + implements _$$GetCategoriesImplCopyWith<$Res> { + __$$GetCategoriesImplCopyWithImpl( + _$GetCategoriesImpl _value, $Res Function(_$GetCategoriesImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? isActive = null, + Object? search = freezed, + Object? forceRemote = null, + }) { + return _then(_$GetCategoriesImpl( + isActive: null == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool, + search: freezed == search + ? _value.search + : search // ignore: cast_nullable_to_non_nullable + as String?, + forceRemote: null == forceRemote + ? _value.forceRemote + : forceRemote // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$GetCategoriesImpl implements _GetCategories { + const _$GetCategoriesImpl( + {this.isActive = true, this.search, this.forceRemote = false}); + + @override + @JsonKey() + final bool isActive; + @override + final String? search; + @override + @JsonKey() + final bool forceRemote; + + @override + String toString() { + return 'CategoryLoaderEvent.getCategories(isActive: $isActive, search: $search, forceRemote: $forceRemote)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$GetCategoriesImpl && + (identical(other.isActive, isActive) || + other.isActive == isActive) && + (identical(other.search, search) || other.search == search) && + (identical(other.forceRemote, forceRemote) || + other.forceRemote == forceRemote)); + } + + @override + int get hashCode => Object.hash(runtimeType, isActive, search, forceRemote); + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$GetCategoriesImplCopyWith<_$GetCategoriesImpl> get copyWith => + __$$GetCategoriesImplCopyWithImpl<_$GetCategoriesImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, + }) { + return getCategories(isActive, this.search, forceRemote); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, + }) { + return getCategories?.call(isActive, this.search, forceRemote); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, + required TResult orElse(), + }) { + if (getCategories != null) { + return getCategories(isActive, this.search, forceRemote); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, + }) { + return getCategories(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, + }) { + return getCategories?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, + required TResult orElse(), + }) { + if (getCategories != null) { + return getCategories(this); + } + return orElse(); + } +} + +abstract class _GetCategories implements CategoryLoaderEvent { + const factory _GetCategories( + {final bool isActive, + final String? search, + final bool forceRemote}) = _$GetCategoriesImpl; + + bool get isActive; + String? get search; + bool get forceRemote; + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$GetCategoriesImplCopyWith<_$GetCategoriesImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$LoadMoreImplCopyWith<$Res> { + factory _$$LoadMoreImplCopyWith( + _$LoadMoreImpl value, $Res Function(_$LoadMoreImpl) then) = + __$$LoadMoreImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$LoadMoreImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$LoadMoreImpl> + implements _$$LoadMoreImplCopyWith<$Res> { + __$$LoadMoreImplCopyWithImpl( + _$LoadMoreImpl _value, $Res Function(_$LoadMoreImpl) _then) : super(_value, _then); /// Create a copy of CategoryLoaderEvent @@ -96,18 +342,18 @@ class __$$GetImplCopyWithImpl<$Res> /// @nodoc -class _$GetImpl implements _Get { - const _$GetImpl(); +class _$LoadMoreImpl implements _LoadMore { + const _$LoadMoreImpl(); @override String toString() { - return 'CategoryLoaderEvent.get()'; + return 'CategoryLoaderEvent.loadMore()'; } @override bool operator ==(Object other) { return identical(this, other) || - (other.runtimeType == runtimeType && other is _$GetImpl); + (other.runtimeType == runtimeType && other is _$LoadMoreImpl); } @override @@ -116,30 +362,51 @@ class _$GetImpl implements _Get { @override @optionalTypeArgs TResult when({ - required TResult Function() get, - required TResult Function(String categoryId) setCategoryId, + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, }) { - return get(); + return loadMore(); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function()? get, - TResult? Function(String categoryId)? setCategoryId, + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, }) { - return get?.call(); + return loadMore?.call(); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function()? get, - TResult Function(String categoryId)? setCategoryId, + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, required TResult orElse(), }) { - if (get != null) { - return get(); + if (loadMore != null) { + return loadMore(); } return orElse(); } @@ -147,54 +414,216 @@ class _$GetImpl implements _Get { @override @optionalTypeArgs TResult map({ - required TResult Function(_Get value) get, - required TResult Function(_SetCategoryId value) setCategoryId, + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, }) { - return get(this); + return loadMore(this); } @override @optionalTypeArgs TResult? mapOrNull({ - TResult? Function(_Get value)? get, - TResult? Function(_SetCategoryId value)? setCategoryId, + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, }) { - return get?.call(this); + return loadMore?.call(this); } @override @optionalTypeArgs TResult maybeMap({ - TResult Function(_Get value)? get, - TResult Function(_SetCategoryId value)? setCategoryId, + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, required TResult orElse(), }) { - if (get != null) { - return get(this); + if (loadMore != null) { + return loadMore(this); } return orElse(); } } -abstract class _Get implements CategoryLoaderEvent { - const factory _Get() = _$GetImpl; +abstract class _LoadMore implements CategoryLoaderEvent { + const factory _LoadMore() = _$LoadMoreImpl; } /// @nodoc -abstract class _$$SetCategoryIdImplCopyWith<$Res> { - factory _$$SetCategoryIdImplCopyWith( - _$SetCategoryIdImpl value, $Res Function(_$SetCategoryIdImpl) then) = - __$$SetCategoryIdImplCopyWithImpl<$Res>; +abstract class _$$RefreshImplCopyWith<$Res> { + factory _$$RefreshImplCopyWith( + _$RefreshImpl value, $Res Function(_$RefreshImpl) then) = + __$$RefreshImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$RefreshImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$RefreshImpl> + implements _$$RefreshImplCopyWith<$Res> { + __$$RefreshImplCopyWithImpl( + _$RefreshImpl _value, $Res Function(_$RefreshImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$RefreshImpl implements _Refresh { + const _$RefreshImpl(); + + @override + String toString() { + return 'CategoryLoaderEvent.refresh()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$RefreshImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, + }) { + return refresh(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, + }) { + return refresh?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, + required TResult orElse(), + }) { + if (refresh != null) { + return refresh(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, + }) { + return refresh(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, + }) { + return refresh?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, + required TResult orElse(), + }) { + if (refresh != null) { + return refresh(this); + } + return orElse(); + } +} + +abstract class _Refresh implements CategoryLoaderEvent { + const factory _Refresh() = _$RefreshImpl; +} + +/// @nodoc +abstract class _$$SearchImplCopyWith<$Res> { + factory _$$SearchImplCopyWith( + _$SearchImpl value, $Res Function(_$SearchImpl) then) = + __$$SearchImplCopyWithImpl<$Res>; @useResult - $Res call({String categoryId}); + $Res call({String query, bool isActive}); } /// @nodoc -class __$$SetCategoryIdImplCopyWithImpl<$Res> - extends _$CategoryLoaderEventCopyWithImpl<$Res, _$SetCategoryIdImpl> - implements _$$SetCategoryIdImplCopyWith<$Res> { - __$$SetCategoryIdImplCopyWithImpl( - _$SetCategoryIdImpl _value, $Res Function(_$SetCategoryIdImpl) _then) +class __$$SearchImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$SearchImpl> + implements _$$SearchImplCopyWith<$Res> { + __$$SearchImplCopyWithImpl( + _$SearchImpl _value, $Res Function(_$SearchImpl) _then) : super(_value, _then); /// Create a copy of CategoryLoaderEvent @@ -202,77 +631,107 @@ class __$$SetCategoryIdImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? categoryId = null, + Object? query = null, + Object? isActive = null, }) { - return _then(_$SetCategoryIdImpl( - null == categoryId - ? _value.categoryId - : categoryId // ignore: cast_nullable_to_non_nullable + return _then(_$SearchImpl( + query: null == query + ? _value.query + : query // ignore: cast_nullable_to_non_nullable as String, + isActive: null == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool, )); } } /// @nodoc -class _$SetCategoryIdImpl implements _SetCategoryId { - const _$SetCategoryIdImpl(this.categoryId); +class _$SearchImpl implements _Search { + const _$SearchImpl({required this.query, this.isActive = true}); @override - final String categoryId; + final String query; + @override + @JsonKey() + final bool isActive; @override String toString() { - return 'CategoryLoaderEvent.setCategoryId(categoryId: $categoryId)'; + return 'CategoryLoaderEvent.search(query: $query, isActive: $isActive)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$SetCategoryIdImpl && - (identical(other.categoryId, categoryId) || - other.categoryId == categoryId)); + other is _$SearchImpl && + (identical(other.query, query) || other.query == query) && + (identical(other.isActive, isActive) || + other.isActive == isActive)); } @override - int get hashCode => Object.hash(runtimeType, categoryId); + int get hashCode => Object.hash(runtimeType, query, isActive); /// Create a copy of CategoryLoaderEvent /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') - _$$SetCategoryIdImplCopyWith<_$SetCategoryIdImpl> get copyWith => - __$$SetCategoryIdImplCopyWithImpl<_$SetCategoryIdImpl>(this, _$identity); + _$$SearchImplCopyWith<_$SearchImpl> get copyWith => + __$$SearchImplCopyWithImpl<_$SearchImpl>(this, _$identity); @override @optionalTypeArgs TResult when({ - required TResult Function() get, - required TResult Function(String categoryId) setCategoryId, + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, }) { - return setCategoryId(categoryId); + return search(query, isActive); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function()? get, - TResult? Function(String categoryId)? setCategoryId, + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, }) { - return setCategoryId?.call(categoryId); + return search?.call(query, isActive); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function()? get, - TResult Function(String categoryId)? setCategoryId, + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, required TResult orElse(), }) { - if (setCategoryId != null) { - return setCategoryId(categoryId); + if (search != null) { + return search(query, isActive); } return orElse(); } @@ -280,47 +739,643 @@ class _$SetCategoryIdImpl implements _SetCategoryId { @override @optionalTypeArgs TResult map({ - required TResult Function(_Get value) get, - required TResult Function(_SetCategoryId value) setCategoryId, + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, }) { - return setCategoryId(this); + return search(this); } @override @optionalTypeArgs TResult? mapOrNull({ - TResult? Function(_Get value)? get, - TResult? Function(_SetCategoryId value)? setCategoryId, + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, }) { - return setCategoryId?.call(this); + return search?.call(this); } @override @optionalTypeArgs TResult maybeMap({ - TResult Function(_Get value)? get, - TResult Function(_SetCategoryId value)? setCategoryId, + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, required TResult orElse(), }) { - if (setCategoryId != null) { - return setCategoryId(this); + if (search != null) { + return search(this); } return orElse(); } } -abstract class _SetCategoryId implements CategoryLoaderEvent { - const factory _SetCategoryId(final String categoryId) = _$SetCategoryIdImpl; +abstract class _Search implements CategoryLoaderEvent { + const factory _Search({required final String query, final bool isActive}) = + _$SearchImpl; - String get categoryId; + String get query; + bool get isActive; /// Create a copy of CategoryLoaderEvent /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) - _$$SetCategoryIdImplCopyWith<_$SetCategoryIdImpl> get copyWith => + _$$SearchImplCopyWith<_$SearchImpl> get copyWith => throw _privateConstructorUsedError; } +/// @nodoc +abstract class _$$SyncAllImplCopyWith<$Res> { + factory _$$SyncAllImplCopyWith( + _$SyncAllImpl value, $Res Function(_$SyncAllImpl) then) = + __$$SyncAllImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$SyncAllImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$SyncAllImpl> + implements _$$SyncAllImplCopyWith<$Res> { + __$$SyncAllImplCopyWithImpl( + _$SyncAllImpl _value, $Res Function(_$SyncAllImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$SyncAllImpl implements _SyncAll { + const _$SyncAllImpl(); + + @override + String toString() { + return 'CategoryLoaderEvent.syncAll()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$SyncAllImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, + }) { + return syncAll(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, + }) { + return syncAll?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, + required TResult orElse(), + }) { + if (syncAll != null) { + return syncAll(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, + }) { + return syncAll(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, + }) { + return syncAll?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, + required TResult orElse(), + }) { + if (syncAll != null) { + return syncAll(this); + } + return orElse(); + } +} + +abstract class _SyncAll implements CategoryLoaderEvent { + const factory _SyncAll() = _$SyncAllImpl; +} + +/// @nodoc +abstract class _$$GetAllCategoriesImplCopyWith<$Res> { + factory _$$GetAllCategoriesImplCopyWith(_$GetAllCategoriesImpl value, + $Res Function(_$GetAllCategoriesImpl) then) = + __$$GetAllCategoriesImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$GetAllCategoriesImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$GetAllCategoriesImpl> + implements _$$GetAllCategoriesImplCopyWith<$Res> { + __$$GetAllCategoriesImplCopyWithImpl(_$GetAllCategoriesImpl _value, + $Res Function(_$GetAllCategoriesImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$GetAllCategoriesImpl implements _GetAllCategories { + const _$GetAllCategoriesImpl(); + + @override + String toString() { + return 'CategoryLoaderEvent.getAllCategories()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$GetAllCategoriesImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, + }) { + return getAllCategories(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, + }) { + return getAllCategories?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, + required TResult orElse(), + }) { + if (getAllCategories != null) { + return getAllCategories(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, + }) { + return getAllCategories(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, + }) { + return getAllCategories?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, + required TResult orElse(), + }) { + if (getAllCategories != null) { + return getAllCategories(this); + } + return orElse(); + } +} + +abstract class _GetAllCategories implements CategoryLoaderEvent { + const factory _GetAllCategories() = _$GetAllCategoriesImpl; +} + +/// @nodoc +abstract class _$$GetDatabaseStatsImplCopyWith<$Res> { + factory _$$GetDatabaseStatsImplCopyWith(_$GetDatabaseStatsImpl value, + $Res Function(_$GetDatabaseStatsImpl) then) = + __$$GetDatabaseStatsImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$GetDatabaseStatsImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$GetDatabaseStatsImpl> + implements _$$GetDatabaseStatsImplCopyWith<$Res> { + __$$GetDatabaseStatsImplCopyWithImpl(_$GetDatabaseStatsImpl _value, + $Res Function(_$GetDatabaseStatsImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$GetDatabaseStatsImpl implements _GetDatabaseStats { + const _$GetDatabaseStatsImpl(); + + @override + String toString() { + return 'CategoryLoaderEvent.getDatabaseStats()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$GetDatabaseStatsImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, + }) { + return getDatabaseStats(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, + }) { + return getDatabaseStats?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, + required TResult orElse(), + }) { + if (getDatabaseStats != null) { + return getDatabaseStats(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, + }) { + return getDatabaseStats(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, + }) { + return getDatabaseStats?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, + required TResult orElse(), + }) { + if (getDatabaseStats != null) { + return getDatabaseStats(this); + } + return orElse(); + } +} + +abstract class _GetDatabaseStats implements CategoryLoaderEvent { + const factory _GetDatabaseStats() = _$GetDatabaseStatsImpl; +} + +/// @nodoc +abstract class _$$ClearCacheImplCopyWith<$Res> { + factory _$$ClearCacheImplCopyWith( + _$ClearCacheImpl value, $Res Function(_$ClearCacheImpl) then) = + __$$ClearCacheImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$ClearCacheImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$ClearCacheImpl> + implements _$$ClearCacheImplCopyWith<$Res> { + __$$ClearCacheImplCopyWithImpl( + _$ClearCacheImpl _value, $Res Function(_$ClearCacheImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$ClearCacheImpl implements _ClearCache { + const _$ClearCacheImpl(); + + @override + String toString() { + return 'CategoryLoaderEvent.clearCache()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$ClearCacheImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(bool isActive, String? search, bool forceRemote) + getCategories, + required TResult Function() loadMore, + required TResult Function() refresh, + required TResult Function(String query, bool isActive) search, + required TResult Function() syncAll, + required TResult Function() getAllCategories, + required TResult Function() getDatabaseStats, + required TResult Function() clearCache, + }) { + return clearCache(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult? Function()? loadMore, + TResult? Function()? refresh, + TResult? Function(String query, bool isActive)? search, + TResult? Function()? syncAll, + TResult? Function()? getAllCategories, + TResult? Function()? getDatabaseStats, + TResult? Function()? clearCache, + }) { + return clearCache?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(bool isActive, String? search, bool forceRemote)? + getCategories, + TResult Function()? loadMore, + TResult Function()? refresh, + TResult Function(String query, bool isActive)? search, + TResult Function()? syncAll, + TResult Function()? getAllCategories, + TResult Function()? getDatabaseStats, + TResult Function()? clearCache, + required TResult orElse(), + }) { + if (clearCache != null) { + return clearCache(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_GetCategories value) getCategories, + required TResult Function(_LoadMore value) loadMore, + required TResult Function(_Refresh value) refresh, + required TResult Function(_Search value) search, + required TResult Function(_SyncAll value) syncAll, + required TResult Function(_GetAllCategories value) getAllCategories, + required TResult Function(_GetDatabaseStats value) getDatabaseStats, + required TResult Function(_ClearCache value) clearCache, + }) { + return clearCache(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_GetCategories value)? getCategories, + TResult? Function(_LoadMore value)? loadMore, + TResult? Function(_Refresh value)? refresh, + TResult? Function(_Search value)? search, + TResult? Function(_SyncAll value)? syncAll, + TResult? Function(_GetAllCategories value)? getAllCategories, + TResult? Function(_GetDatabaseStats value)? getDatabaseStats, + TResult? Function(_ClearCache value)? clearCache, + }) { + return clearCache?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_GetCategories value)? getCategories, + TResult Function(_LoadMore value)? loadMore, + TResult Function(_Refresh value)? refresh, + TResult Function(_Search value)? search, + TResult Function(_SyncAll value)? syncAll, + TResult Function(_GetAllCategories value)? getAllCategories, + TResult Function(_GetDatabaseStats value)? getDatabaseStats, + TResult Function(_ClearCache value)? clearCache, + required TResult orElse(), + }) { + if (clearCache != null) { + return clearCache(this); + } + return orElse(); + } +} + +abstract class _ClearCache implements CategoryLoaderEvent { + const factory _ClearCache() = _$ClearCacheImpl; +} + /// @nodoc mixin _$CategoryLoaderState { @optionalTypeArgs @@ -328,27 +1383,57 @@ mixin _$CategoryLoaderState { required TResult Function() initial, required TResult Function() loading, required TResult Function( - List categories, String? categoryId) + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) loaded, required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ TResult? Function()? initial, TResult? Function()? loading, - TResult? Function(List categories, String? categoryId)? + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, TResult Function()? loading, - TResult Function(List categories, String? categoryId)? + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -358,6 +1443,10 @@ mixin _$CategoryLoaderState { required TResult Function(_Loading value) loading, required TResult Function(_Loaded value) loaded, required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -366,6 +1455,10 @@ mixin _$CategoryLoaderState { TResult? Function(_Loading value)? loading, TResult? Function(_Loaded value)? loaded, TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -374,6 +1467,10 @@ mixin _$CategoryLoaderState { TResult Function(_Loading value)? loading, TResult Function(_Loaded value)? loaded, TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -444,9 +1541,19 @@ class _$InitialImpl implements _Initial { required TResult Function() initial, required TResult Function() loading, required TResult Function( - List categories, String? categoryId) + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) loaded, required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, }) { return initial(); } @@ -456,9 +1563,19 @@ class _$InitialImpl implements _Initial { TResult? whenOrNull({ TResult? Function()? initial, TResult? Function()? loading, - TResult? Function(List categories, String? categoryId)? + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, }) { return initial?.call(); } @@ -468,9 +1585,19 @@ class _$InitialImpl implements _Initial { TResult maybeWhen({ TResult Function()? initial, TResult Function()? loading, - TResult Function(List categories, String? categoryId)? + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, required TResult orElse(), }) { if (initial != null) { @@ -486,6 +1613,10 @@ class _$InitialImpl implements _Initial { required TResult Function(_Loading value) loading, required TResult Function(_Loaded value) loaded, required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, }) { return initial(this); } @@ -497,6 +1628,10 @@ class _$InitialImpl implements _Initial { TResult? Function(_Loading value)? loading, TResult? Function(_Loaded value)? loaded, TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, }) { return initial?.call(this); } @@ -508,6 +1643,10 @@ class _$InitialImpl implements _Initial { TResult Function(_Loading value)? loading, TResult Function(_Loaded value)? loaded, TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, required TResult orElse(), }) { if (initial != null) { @@ -565,9 +1704,19 @@ class _$LoadingImpl implements _Loading { required TResult Function() initial, required TResult Function() loading, required TResult Function( - List categories, String? categoryId) + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) loaded, required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, }) { return loading(); } @@ -577,9 +1726,19 @@ class _$LoadingImpl implements _Loading { TResult? whenOrNull({ TResult? Function()? initial, TResult? Function()? loading, - TResult? Function(List categories, String? categoryId)? + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, }) { return loading?.call(); } @@ -589,9 +1748,19 @@ class _$LoadingImpl implements _Loading { TResult maybeWhen({ TResult Function()? initial, TResult Function()? loading, - TResult Function(List categories, String? categoryId)? + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, required TResult orElse(), }) { if (loading != null) { @@ -607,6 +1776,10 @@ class _$LoadingImpl implements _Loading { required TResult Function(_Loading value) loading, required TResult Function(_Loaded value) loaded, required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, }) { return loading(this); } @@ -618,6 +1791,10 @@ class _$LoadingImpl implements _Loading { TResult? Function(_Loading value)? loading, TResult? Function(_Loaded value)? loaded, TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, }) { return loading?.call(this); } @@ -629,6 +1806,10 @@ class _$LoadingImpl implements _Loading { TResult Function(_Loading value)? loading, TResult Function(_Loaded value)? loaded, TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, required TResult orElse(), }) { if (loading != null) { @@ -648,7 +1829,13 @@ abstract class _$$LoadedImplCopyWith<$Res> { _$LoadedImpl value, $Res Function(_$LoadedImpl) then) = __$$LoadedImplCopyWithImpl<$Res>; @useResult - $Res call({List categories, String? categoryId}); + $Res call( + {List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery}); } /// @nodoc @@ -665,16 +1852,36 @@ class __$$LoadedImplCopyWithImpl<$Res> @override $Res call({ Object? categories = null, - Object? categoryId = freezed, + Object? hasReachedMax = null, + Object? currentPage = null, + Object? isLoadingMore = null, + Object? isActive = null, + Object? searchQuery = freezed, }) { return _then(_$LoadedImpl( - null == categories + categories: null == categories ? _value._categories : categories // ignore: cast_nullable_to_non_nullable as List, - freezed == categoryId - ? _value.categoryId - : categoryId // ignore: cast_nullable_to_non_nullable + hasReachedMax: null == hasReachedMax + ? _value.hasReachedMax + : hasReachedMax // ignore: cast_nullable_to_non_nullable + as bool, + currentPage: null == currentPage + ? _value.currentPage + : currentPage // ignore: cast_nullable_to_non_nullable + as int, + isLoadingMore: null == isLoadingMore + ? _value.isLoadingMore + : isLoadingMore // ignore: cast_nullable_to_non_nullable + as bool, + isActive: null == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool, + searchQuery: freezed == searchQuery + ? _value.searchQuery + : searchQuery // ignore: cast_nullable_to_non_nullable as String?, )); } @@ -683,7 +1890,13 @@ class __$$LoadedImplCopyWithImpl<$Res> /// @nodoc class _$LoadedImpl implements _Loaded { - const _$LoadedImpl(final List categories, this.categoryId) + const _$LoadedImpl( + {required final List categories, + required this.hasReachedMax, + required this.currentPage, + required this.isLoadingMore, + required this.isActive, + this.searchQuery}) : _categories = categories; final List _categories; @@ -695,11 +1908,19 @@ class _$LoadedImpl implements _Loaded { } @override - final String? categoryId; + final bool hasReachedMax; + @override + final int currentPage; + @override + final bool isLoadingMore; + @override + final bool isActive; + @override + final String? searchQuery; @override String toString() { - return 'CategoryLoaderState.loaded(categories: $categories, categoryId: $categoryId)'; + return 'CategoryLoaderState.loaded(categories: $categories, hasReachedMax: $hasReachedMax, currentPage: $currentPage, isLoadingMore: $isLoadingMore, isActive: $isActive, searchQuery: $searchQuery)'; } @override @@ -709,13 +1930,27 @@ class _$LoadedImpl implements _Loaded { other is _$LoadedImpl && const DeepCollectionEquality() .equals(other._categories, _categories) && - (identical(other.categoryId, categoryId) || - other.categoryId == categoryId)); + (identical(other.hasReachedMax, hasReachedMax) || + other.hasReachedMax == hasReachedMax) && + (identical(other.currentPage, currentPage) || + other.currentPage == currentPage) && + (identical(other.isLoadingMore, isLoadingMore) || + other.isLoadingMore == isLoadingMore) && + (identical(other.isActive, isActive) || + other.isActive == isActive) && + (identical(other.searchQuery, searchQuery) || + other.searchQuery == searchQuery)); } @override - int get hashCode => Object.hash(runtimeType, - const DeepCollectionEquality().hash(_categories), categoryId); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_categories), + hasReachedMax, + currentPage, + isLoadingMore, + isActive, + searchQuery); /// Create a copy of CategoryLoaderState /// with the given fields replaced by the non-null parameter values. @@ -731,11 +1966,22 @@ class _$LoadedImpl implements _Loaded { required TResult Function() initial, required TResult Function() loading, required TResult Function( - List categories, String? categoryId) + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) loaded, required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, }) { - return loaded(categories, categoryId); + return loaded(categories, hasReachedMax, currentPage, isLoadingMore, + isActive, searchQuery); } @override @@ -743,11 +1989,22 @@ class _$LoadedImpl implements _Loaded { TResult? whenOrNull({ TResult? Function()? initial, TResult? Function()? loading, - TResult? Function(List categories, String? categoryId)? + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, }) { - return loaded?.call(categories, categoryId); + return loaded?.call(categories, hasReachedMax, currentPage, isLoadingMore, + isActive, searchQuery); } @override @@ -755,13 +2012,24 @@ class _$LoadedImpl implements _Loaded { TResult maybeWhen({ TResult Function()? initial, TResult Function()? loading, - TResult Function(List categories, String? categoryId)? + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, required TResult orElse(), }) { if (loaded != null) { - return loaded(categories, categoryId); + return loaded(categories, hasReachedMax, currentPage, isLoadingMore, + isActive, searchQuery); } return orElse(); } @@ -773,6 +2041,10 @@ class _$LoadedImpl implements _Loaded { required TResult Function(_Loading value) loading, required TResult Function(_Loaded value) loaded, required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, }) { return loaded(this); } @@ -784,6 +2056,10 @@ class _$LoadedImpl implements _Loaded { TResult? Function(_Loading value)? loading, TResult? Function(_Loaded value)? loaded, TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, }) { return loaded?.call(this); } @@ -795,6 +2071,10 @@ class _$LoadedImpl implements _Loaded { TResult Function(_Loading value)? loading, TResult Function(_Loaded value)? loaded, TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, required TResult orElse(), }) { if (loaded != null) { @@ -806,11 +2086,19 @@ class _$LoadedImpl implements _Loaded { abstract class _Loaded implements CategoryLoaderState { const factory _Loaded( - final List categories, final String? categoryId) = - _$LoadedImpl; + {required final List categories, + required final bool hasReachedMax, + required final int currentPage, + required final bool isLoadingMore, + required final bool isActive, + final String? searchQuery}) = _$LoadedImpl; List get categories; - String? get categoryId; + bool get hasReachedMax; + int get currentPage; + bool get isLoadingMore; + bool get isActive; + String? get searchQuery; /// Create a copy of CategoryLoaderState /// with the given fields replaced by the non-null parameter values. @@ -890,9 +2178,19 @@ class _$ErrorImpl implements _Error { required TResult Function() initial, required TResult Function() loading, required TResult Function( - List categories, String? categoryId) + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) loaded, required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, }) { return error(message); } @@ -902,9 +2200,19 @@ class _$ErrorImpl implements _Error { TResult? whenOrNull({ TResult? Function()? initial, TResult? Function()? loading, - TResult? Function(List categories, String? categoryId)? + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, }) { return error?.call(message); } @@ -914,9 +2222,19 @@ class _$ErrorImpl implements _Error { TResult maybeWhen({ TResult Function()? initial, TResult Function()? loading, - TResult Function(List categories, String? categoryId)? + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? loaded, TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, required TResult orElse(), }) { if (error != null) { @@ -932,6 +2250,10 @@ class _$ErrorImpl implements _Error { required TResult Function(_Loading value) loading, required TResult Function(_Loaded value) loaded, required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, }) { return error(this); } @@ -943,6 +2265,10 @@ class _$ErrorImpl implements _Error { TResult? Function(_Loading value)? loading, TResult? Function(_Loaded value)? loaded, TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, }) { return error?.call(this); } @@ -954,6 +2280,10 @@ class _$ErrorImpl implements _Error { TResult Function(_Loading value)? loading, TResult Function(_Loaded value)? loaded, TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, required TResult orElse(), }) { if (error != null) { @@ -974,3 +2304,770 @@ abstract class _Error implements CategoryLoaderState { _$$ErrorImplCopyWith<_$ErrorImpl> get copyWith => throw _privateConstructorUsedError; } + +/// @nodoc +abstract class _$$SyncingImplCopyWith<$Res> { + factory _$$SyncingImplCopyWith( + _$SyncingImpl value, $Res Function(_$SyncingImpl) then) = + __$$SyncingImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$SyncingImplCopyWithImpl<$Res> + extends _$CategoryLoaderStateCopyWithImpl<$Res, _$SyncingImpl> + implements _$$SyncingImplCopyWith<$Res> { + __$$SyncingImplCopyWithImpl( + _$SyncingImpl _value, $Res Function(_$SyncingImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$SyncingImpl implements _Syncing { + const _$SyncingImpl(); + + @override + String toString() { + return 'CategoryLoaderState.syncing()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$SyncingImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) + loaded, + required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, + }) { + return syncing(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? + loaded, + TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, + }) { + return syncing?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? + loaded, + TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, + required TResult orElse(), + }) { + if (syncing != null) { + return syncing(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + required TResult Function(_Loading value) loading, + required TResult Function(_Loaded value) loaded, + required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, + }) { + return syncing(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + TResult? Function(_Loading value)? loading, + TResult? Function(_Loaded value)? loaded, + TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, + }) { + return syncing?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + TResult Function(_Loading value)? loading, + TResult Function(_Loaded value)? loaded, + TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, + required TResult orElse(), + }) { + if (syncing != null) { + return syncing(this); + } + return orElse(); + } +} + +abstract class _Syncing implements CategoryLoaderState { + const factory _Syncing() = _$SyncingImpl; +} + +/// @nodoc +abstract class _$$SyncSuccessImplCopyWith<$Res> { + factory _$$SyncSuccessImplCopyWith( + _$SyncSuccessImpl value, $Res Function(_$SyncSuccessImpl) then) = + __$$SyncSuccessImplCopyWithImpl<$Res>; + @useResult + $Res call({String message}); +} + +/// @nodoc +class __$$SyncSuccessImplCopyWithImpl<$Res> + extends _$CategoryLoaderStateCopyWithImpl<$Res, _$SyncSuccessImpl> + implements _$$SyncSuccessImplCopyWith<$Res> { + __$$SyncSuccessImplCopyWithImpl( + _$SyncSuccessImpl _value, $Res Function(_$SyncSuccessImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + }) { + return _then(_$SyncSuccessImpl( + null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$SyncSuccessImpl implements _SyncSuccess { + const _$SyncSuccessImpl(this.message); + + @override + final String message; + + @override + String toString() { + return 'CategoryLoaderState.syncSuccess(message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SyncSuccessImpl && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, message); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SyncSuccessImplCopyWith<_$SyncSuccessImpl> get copyWith => + __$$SyncSuccessImplCopyWithImpl<_$SyncSuccessImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) + loaded, + required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, + }) { + return syncSuccess(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? + loaded, + TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, + }) { + return syncSuccess?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? + loaded, + TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, + required TResult orElse(), + }) { + if (syncSuccess != null) { + return syncSuccess(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + required TResult Function(_Loading value) loading, + required TResult Function(_Loaded value) loaded, + required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, + }) { + return syncSuccess(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + TResult? Function(_Loading value)? loading, + TResult? Function(_Loaded value)? loaded, + TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, + }) { + return syncSuccess?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + TResult Function(_Loading value)? loading, + TResult Function(_Loaded value)? loaded, + TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, + required TResult orElse(), + }) { + if (syncSuccess != null) { + return syncSuccess(this); + } + return orElse(); + } +} + +abstract class _SyncSuccess implements CategoryLoaderState { + const factory _SyncSuccess(final String message) = _$SyncSuccessImpl; + + String get message; + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SyncSuccessImplCopyWith<_$SyncSuccessImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$SyncErrorImplCopyWith<$Res> { + factory _$$SyncErrorImplCopyWith( + _$SyncErrorImpl value, $Res Function(_$SyncErrorImpl) then) = + __$$SyncErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({String message}); +} + +/// @nodoc +class __$$SyncErrorImplCopyWithImpl<$Res> + extends _$CategoryLoaderStateCopyWithImpl<$Res, _$SyncErrorImpl> + implements _$$SyncErrorImplCopyWith<$Res> { + __$$SyncErrorImplCopyWithImpl( + _$SyncErrorImpl _value, $Res Function(_$SyncErrorImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + }) { + return _then(_$SyncErrorImpl( + null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$SyncErrorImpl implements _SyncError { + const _$SyncErrorImpl(this.message); + + @override + final String message; + + @override + String toString() { + return 'CategoryLoaderState.syncError(message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SyncErrorImpl && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, message); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SyncErrorImplCopyWith<_$SyncErrorImpl> get copyWith => + __$$SyncErrorImplCopyWithImpl<_$SyncErrorImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) + loaded, + required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, + }) { + return syncError(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? + loaded, + TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, + }) { + return syncError?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? + loaded, + TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, + required TResult orElse(), + }) { + if (syncError != null) { + return syncError(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + required TResult Function(_Loading value) loading, + required TResult Function(_Loaded value) loaded, + required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, + }) { + return syncError(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + TResult? Function(_Loading value)? loading, + TResult? Function(_Loaded value)? loaded, + TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, + }) { + return syncError?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + TResult Function(_Loading value)? loading, + TResult Function(_Loaded value)? loaded, + TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, + required TResult orElse(), + }) { + if (syncError != null) { + return syncError(this); + } + return orElse(); + } +} + +abstract class _SyncError implements CategoryLoaderState { + const factory _SyncError(final String message) = _$SyncErrorImpl; + + String get message; + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SyncErrorImplCopyWith<_$SyncErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$AllCategoriesLoadedImplCopyWith<$Res> { + factory _$$AllCategoriesLoadedImplCopyWith(_$AllCategoriesLoadedImpl value, + $Res Function(_$AllCategoriesLoadedImpl) then) = + __$$AllCategoriesLoadedImplCopyWithImpl<$Res>; + @useResult + $Res call({List categories}); +} + +/// @nodoc +class __$$AllCategoriesLoadedImplCopyWithImpl<$Res> + extends _$CategoryLoaderStateCopyWithImpl<$Res, _$AllCategoriesLoadedImpl> + implements _$$AllCategoriesLoadedImplCopyWith<$Res> { + __$$AllCategoriesLoadedImplCopyWithImpl(_$AllCategoriesLoadedImpl _value, + $Res Function(_$AllCategoriesLoadedImpl) _then) + : super(_value, _then); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? categories = null, + }) { + return _then(_$AllCategoriesLoadedImpl( + null == categories + ? _value._categories + : categories // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc + +class _$AllCategoriesLoadedImpl implements _AllCategoriesLoaded { + const _$AllCategoriesLoadedImpl(final List categories) + : _categories = categories; + + final List _categories; + @override + List get categories { + if (_categories is EqualUnmodifiableListView) return _categories; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_categories); + } + + @override + String toString() { + return 'CategoryLoaderState.allCategoriesLoaded(categories: $categories)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AllCategoriesLoadedImpl && + const DeepCollectionEquality() + .equals(other._categories, _categories)); + } + + @override + int get hashCode => Object.hash( + runtimeType, const DeepCollectionEquality().hash(_categories)); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$AllCategoriesLoadedImplCopyWith<_$AllCategoriesLoadedImpl> get copyWith => + __$$AllCategoriesLoadedImplCopyWithImpl<_$AllCategoriesLoadedImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery) + loaded, + required TResult Function(String message) error, + required TResult Function() syncing, + required TResult Function(String message) syncSuccess, + required TResult Function(String message) syncError, + required TResult Function(List categories) + allCategoriesLoaded, + }) { + return allCategoriesLoaded(categories); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? + loaded, + TResult? Function(String message)? error, + TResult? Function()? syncing, + TResult? Function(String message)? syncSuccess, + TResult? Function(String message)? syncError, + TResult? Function(List categories)? allCategoriesLoaded, + }) { + return allCategoriesLoaded?.call(categories); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function( + List categories, + bool hasReachedMax, + int currentPage, + bool isLoadingMore, + bool isActive, + String? searchQuery)? + loaded, + TResult Function(String message)? error, + TResult Function()? syncing, + TResult Function(String message)? syncSuccess, + TResult Function(String message)? syncError, + TResult Function(List categories)? allCategoriesLoaded, + required TResult orElse(), + }) { + if (allCategoriesLoaded != null) { + return allCategoriesLoaded(categories); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + required TResult Function(_Loading value) loading, + required TResult Function(_Loaded value) loaded, + required TResult Function(_Error value) error, + required TResult Function(_Syncing value) syncing, + required TResult Function(_SyncSuccess value) syncSuccess, + required TResult Function(_SyncError value) syncError, + required TResult Function(_AllCategoriesLoaded value) allCategoriesLoaded, + }) { + return allCategoriesLoaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + TResult? Function(_Loading value)? loading, + TResult? Function(_Loaded value)? loaded, + TResult? Function(_Error value)? error, + TResult? Function(_Syncing value)? syncing, + TResult? Function(_SyncSuccess value)? syncSuccess, + TResult? Function(_SyncError value)? syncError, + TResult? Function(_AllCategoriesLoaded value)? allCategoriesLoaded, + }) { + return allCategoriesLoaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + TResult Function(_Loading value)? loading, + TResult Function(_Loaded value)? loaded, + TResult Function(_Error value)? error, + TResult Function(_Syncing value)? syncing, + TResult Function(_SyncSuccess value)? syncSuccess, + TResult Function(_SyncError value)? syncError, + TResult Function(_AllCategoriesLoaded value)? allCategoriesLoaded, + required TResult orElse(), + }) { + if (allCategoriesLoaded != null) { + return allCategoriesLoaded(this); + } + return orElse(); + } +} + +abstract class _AllCategoriesLoaded implements CategoryLoaderState { + const factory _AllCategoriesLoaded(final List categories) = + _$AllCategoriesLoadedImpl; + + List get categories; + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$AllCategoriesLoadedImplCopyWith<_$AllCategoriesLoadedImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/presentation/home/bloc/category_loader/category_loader_event.dart b/lib/presentation/home/bloc/category_loader/category_loader_event.dart index 8cf7464..88f84dd 100644 --- a/lib/presentation/home/bloc/category_loader/category_loader_event.dart +++ b/lib/presentation/home/bloc/category_loader/category_loader_event.dart @@ -2,7 +2,26 @@ part of 'category_loader_bloc.dart'; @freezed class CategoryLoaderEvent with _$CategoryLoaderEvent { - const factory CategoryLoaderEvent.get() = _Get; - const factory CategoryLoaderEvent.setCategoryId(String categoryId) = - _SetCategoryId; + const factory CategoryLoaderEvent.getCategories({ + @Default(true) bool isActive, + String? search, + @Default(false) bool forceRemote, + }) = _GetCategories; + + const factory CategoryLoaderEvent.loadMore() = _LoadMore; + + const factory CategoryLoaderEvent.refresh() = _Refresh; + + const factory CategoryLoaderEvent.search({ + required String query, + @Default(true) bool isActive, + }) = _Search; + + const factory CategoryLoaderEvent.syncAll() = _SyncAll; + + const factory CategoryLoaderEvent.getAllCategories() = _GetAllCategories; + + const factory CategoryLoaderEvent.getDatabaseStats() = _GetDatabaseStats; + + const factory CategoryLoaderEvent.clearCache() = _ClearCache; } diff --git a/lib/presentation/home/bloc/category_loader/category_loader_state.dart b/lib/presentation/home/bloc/category_loader/category_loader_state.dart index da8f22f..12b0fc5 100644 --- a/lib/presentation/home/bloc/category_loader/category_loader_state.dart +++ b/lib/presentation/home/bloc/category_loader/category_loader_state.dart @@ -3,8 +3,29 @@ part of 'category_loader_bloc.dart'; @freezed class CategoryLoaderState with _$CategoryLoaderState { const factory CategoryLoaderState.initial() = _Initial; + const factory CategoryLoaderState.loading() = _Loading; - const factory CategoryLoaderState.loaded( - List categories, String? categoryId) = _Loaded; + + const factory CategoryLoaderState.loaded({ + required List categories, + required bool hasReachedMax, + required int currentPage, + required bool isLoadingMore, + required bool isActive, + String? searchQuery, + }) = _Loaded; + const factory CategoryLoaderState.error(String message) = _Error; + + // Sync-specific states + const factory CategoryLoaderState.syncing() = _Syncing; + + const factory CategoryLoaderState.syncSuccess(String message) = _SyncSuccess; + + const factory CategoryLoaderState.syncError(String message) = _SyncError; + + // For dropdown/all categories + const factory CategoryLoaderState.allCategoriesLoaded( + List categories, + ) = _AllCategoriesLoaded; } diff --git a/lib/presentation/home/pages/home_page.dart b/lib/presentation/home/pages/home_page.dart index 69ae96f..ce99f69 100644 --- a/lib/presentation/home/pages/home_page.dart +++ b/lib/presentation/home/pages/home_page.dart @@ -1,11 +1,11 @@ // ======================================== -// OFFLINE-ONLY HOMEPAGE - NO API CALLS +// HOMEPAGE - LOCAL DATA ONLY, NO SYNC // lib/presentation/home/pages/home_page.dart // ======================================== import 'dart:developer'; import 'package:enaklo_pos/core/components/flushbar.dart'; -import 'package:enaklo_pos/data/datasources/product/product_local_datasource.dart'; +import 'package:enaklo_pos/data/models/response/category_response_model.dart'; import 'package:enaklo_pos/presentation/home/bloc/category_loader/category_loader_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/current_outlet/current_outlet_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart'; @@ -50,17 +50,10 @@ class _HomePageState extends State { final ScrollController scrollController = ScrollController(); String searchQuery = ''; - // Local database only - Map _databaseStats = {}; - final ProductLocalDatasource _localDatasource = - ProductLocalDatasource.instance; - bool _isLoadingStats = true; - @override void initState() { super.initState(); - _initializeLocalData(); - _loadProducts(); + _loadData(); } @override @@ -70,48 +63,28 @@ class _HomePageState extends State { super.dispose(); } - // Initialize local data only - void _initializeLocalData() { - _loadDatabaseStats(); - } + void _loadData() { + log('๐Ÿ“ฑ Loading data from local database...'); - // Load database statistics - void _loadDatabaseStats() async { - try { - final stats = await _localDatasource.getDatabaseStats(); - if (mounted) { - setState(() { - _databaseStats = stats; - _isLoadingStats = false; - }); - } - log('๐Ÿ“Š Local database stats: $stats'); - } catch (e) { - log('โŒ Error loading local stats: $e'); - setState(() { - _isLoadingStats = false; - }); - } - } + // Load categories from local database + context + .read() + .add(const CategoryLoaderEvent.getCategories()); - void _loadProducts() { - log('๐Ÿ“ฑ Loading products from local database only...'); - - // Load products from local database only + // Load products from local database context .read() .add(const ProductLoaderEvent.getProduct()); // Initialize other components context.read().add(CheckoutEvent.started(widget.items)); - context.read().add(CategoryLoaderEvent.get()); context.read().add(CurrentOutletEvent.currentOutlet()); } - void _refreshLocalData() { + void _refreshData() { log('๐Ÿ”„ Refreshing local data...'); context.read().add(const ProductLoaderEvent.refresh()); - _loadDatabaseStats(); + context.read().add(const CategoryLoaderEvent.refresh()); } void onCategoryTap(int index) { @@ -125,11 +98,9 @@ class _HomePageState extends State { ScrollNotification notification, String? categoryId) { if (notification is ScrollEndNotification && scrollController.position.extentAfter == 0) { - log('๐Ÿ“„ Loading more local products for category: $categoryId'); + log('๐Ÿ“„ Loading more products...'); context.read().add( - ProductLoaderEvent.loadMore( - categoryId: categoryId, - ), + ProductLoaderEvent.loadMore(), ); return true; } @@ -142,7 +113,6 @@ class _HomePageState extends State { listener: (context, state) { state.maybeWhen( orElse: () {}, - loading: () {}, success: () { Future.delayed(Duration(milliseconds: 300), () { AppFlushbar.showSuccess(context, 'Outlet berhasil diubah'); @@ -160,81 +130,33 @@ class _HomePageState extends State { backgroundColor: AppColors.white, body: Column( children: [ - // Local database indicator - _buildLocalModeIndicator(), + // Simple local mode indicator + _buildLocalIndicator(), // Main content Expanded( child: Row( children: [ - // Left panel - Products + // Left panel - Products with Categories Expanded( flex: 3, - child: Align( - alignment: AlignmentDirectional.topStart, - child: BlocBuilder( - builder: (context, state) { - return state.maybeWhen( - orElse: () => - Center(child: CircularProgressIndicator()), - loaded: (categories, categoryId) => Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - // Enhanced home title with local stats - _buildLocalHomeTitle(categoryId), - - // Products section - Expanded( - child: BlocBuilder( - builder: (context, productState) { - return CategoryTabBar( - categories: categories, - tabViews: categories.map((category) { - return SizedBox( - child: productState.maybeWhen( - orElse: () => - _buildLoadingState(), - loading: () => - _buildLoadingState(), - loaded: (products, - hasReachedMax, - currentPage, - isLoadingMore, - categoryId, - searchQuery) { - if (products.isEmpty) { - return _buildEmptyState( - categoryId); - } - return _buildProductGrid( - products, - hasReachedMax, - isLoadingMore, - categoryId, - currentPage, - ); - }, - error: (message) => - _buildErrorState( - message, categoryId), - ), - ); - }).toList(), - ); - }, - ), - ), - ], - ), - ); - }, - ), + child: + BlocBuilder( + builder: (context, categoryState) { + return categoryState.maybeWhen( + orElse: () => _buildCategoryLoadingState(), + loading: () => _buildCategoryLoadingState(), + error: (message) => + _buildCategoryErrorState(message), + loaded: (categories, hasReachedMax, currentPage, + isLoadingMore, isActive, searchQuery) => + _buildCategoryContent(categories), + ); + }, ), ), - // Right panel - Cart (unchanged) + // Right panel - Cart Expanded( flex: 2, child: _buildCartSection(), @@ -249,8 +171,8 @@ class _HomePageState extends State { ); } - // Local mode indicator - Widget _buildLocalModeIndicator() { + // Simple local mode indicator without sync + Widget _buildLocalIndicator() { return Container( width: double.infinity, padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16), @@ -261,9 +183,7 @@ class _HomePageState extends State { SizedBox(width: 8), Expanded( child: Text( - _isLoadingStats - ? 'Mode Lokal - Memuat data...' - : 'Mode Lokal - ${_databaseStats['total_products'] ?? 0} produk tersimpan', + 'Mode Lokal - Data tersimpan di perangkat', style: TextStyle( color: Colors.white, fontSize: 13, @@ -271,18 +191,10 @@ class _HomePageState extends State { ), ), ), - if (_databaseStats.isNotEmpty) ...[ - Text( - '${(_databaseStats['database_size_mb'] ?? 0.0).toStringAsFixed(1)} MB', - style: TextStyle( - color: Colors.white.withOpacity(0.8), - fontSize: 11, - ), - ), - SizedBox(width: 8), - ], + + // Only refresh button InkWell( - onTap: _refreshLocalData, + onTap: _refreshData, child: Container( padding: EdgeInsets.all(4), decoration: BoxDecoration( @@ -297,170 +209,125 @@ class _HomePageState extends State { ); } - // Enhanced home title with local stats only - Widget _buildLocalHomeTitle(String? categoryId) { + Widget _buildCategoryLoadingState() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(color: AppColors.primary), + SizedBox(height: 16), + Text( + 'Memuat kategori...', + style: TextStyle(color: Colors.grey.shade600), + ), + ], + ), + ); + } + + Widget _buildCategoryErrorState(String message) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error_outline, size: 48, color: Colors.red.shade400), + SizedBox(height: 16), + Text('Error Kategori', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)), + SizedBox(height: 8), + Padding( + padding: EdgeInsets.symmetric(horizontal: 32), + child: Text( + message, + textAlign: TextAlign.center, + style: TextStyle(color: Colors.grey.shade600), + ), + ), + SizedBox(height: 16), + Button.filled( + width: 120, + onPressed: () { + context + .read() + .add(const CategoryLoaderEvent.getCategories()); + }, + label: 'Coba Lagi', + ), + ], + ), + ); + } + + Widget _buildCategoryContent(List categories) { + return Column( + children: [ + // Simple home title + _buildSimpleHomeTitle(), + + // Products section with categories + Expanded( + child: BlocBuilder( + builder: (context, productState) { + return CategoryTabBar( + categories: categories, + tabViews: categories.map((category) { + return SizedBox( + child: productState.maybeWhen( + orElse: () => _buildLoadingState(), + loading: () => _buildLoadingState(), + loaded: (products, hasReachedMax, currentPage, + isLoadingMore, categoryId, searchQuery) { + if (products.isEmpty) { + return _buildEmptyState(categoryId); + } + return _buildProductGrid( + products, + hasReachedMax, + isLoadingMore, + categoryId, + currentPage, + ); + }, + error: (message) => + _buildErrorState(message, category.id), + ), + ); + }).toList(), + ); + }, + ), + ), + ], + ); + } + + // Simple home title + Widget _buildSimpleHomeTitle() { return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, border: Border(bottom: BorderSide(color: Colors.grey.shade200)), ), - child: Column( - children: [ - // Original HomeTitle with faster search - HomeTitle( - controller: searchController, - onChanged: (value) { - setState(() { - searchQuery = value; - }); + child: HomeTitle( + controller: searchController, + onChanged: (value) { + setState(() { + searchQuery = value; + }); - // Fast local search - no debounce needed for local data - Future.delayed(Duration(milliseconds: 200), () { - if (value == searchController.text) { - log('๐Ÿ” Local search: "$value"'); - context.read().add( - ProductLoaderEvent.searchProduct( - categoryId: categoryId, - query: value, - ), - ); - } - }); - }, - ), - - // Local database stats - if (_databaseStats.isNotEmpty) ...[ - SizedBox(height: 8), - Row( - children: [ - // Local storage indicator - Container( - padding: EdgeInsets.symmetric(horizontal: 6, vertical: 3), - decoration: BoxDecoration( - color: Colors.blue.shade50, - borderRadius: BorderRadius.circular(10), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.storage, - size: 12, color: Colors.blue.shade600), - SizedBox(width: 3), - Text( - 'Lokal', - style: TextStyle( - fontSize: 10, - color: Colors.blue.shade600, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - - SizedBox(width: 8), - - // Database stats chips - _buildStatChip( - '${_databaseStats['total_products'] ?? 0}', - 'produk', - Icons.inventory_2, - Colors.green, - ), - SizedBox(width: 6), - _buildStatChip( - '${_databaseStats['total_variants'] ?? 0}', - 'varian', - Icons.tune, - Colors.orange, - ), - SizedBox(width: 6), - _buildStatChip( - '${_databaseStats['cache_entries'] ?? 0}', - 'cache', - Icons.memory, - Colors.purple, - ), - - Spacer(), - - // Clear cache button - InkWell( - onTap: () { - _localDatasource.clearExpiredCache(); - _loadDatabaseStats(); - AppFlushbar.showSuccess(context, 'Cache dibersihkan'); - }, - child: Container( - padding: EdgeInsets.all(5), - decoration: BoxDecoration( - color: Colors.grey.shade100, - borderRadius: BorderRadius.circular(5), + // Fast local search + Future.delayed(Duration(milliseconds: 200), () { + if (value == searchController.text) { + log('๐Ÿ” Local search: "$value"'); + context.read().add( + ProductLoaderEvent.searchProduct( + query: value, ), - child: Icon( - Icons.clear_all, - size: 14, - color: Colors.grey.shade600, - ), - ), - ), - SizedBox(width: 4), - // Refresh button - InkWell( - onTap: _refreshLocalData, - child: Container( - padding: EdgeInsets.all(5), - decoration: BoxDecoration( - color: AppColors.primary.withOpacity(0.1), - borderRadius: BorderRadius.circular(5), - ), - child: Icon( - Icons.refresh, - size: 14, - color: AppColors.primary, - ), - ), - ), - ], - ), - ], - ], - ), - ); - } - - Widget _buildStatChip( - String value, String label, IconData icon, Color color) { - return Container( - padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), - decoration: BoxDecoration( - color: color.withOpacity(0.1), - borderRadius: BorderRadius.circular(6), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(icon, size: 10, color: color), - SizedBox(width: 2), - Text( - value, - style: TextStyle( - fontSize: 9, - fontWeight: FontWeight.w600, - color: color, - ), - ), - SizedBox(width: 1), - Text( - label, - style: TextStyle( - fontSize: 8, - color: color.withOpacity(0.8), - ), - ), - ], + ); + } + }); + }, ), ); } @@ -473,7 +340,7 @@ class _HomePageState extends State { CircularProgressIndicator(color: AppColors.primary), SizedBox(height: 16), Text( - 'Memuat data lokal...', + 'Memuat data...', style: TextStyle(color: Colors.grey.shade600), ), ], @@ -491,12 +358,12 @@ class _HomePageState extends State { Text( searchQuery.isNotEmpty ? 'Produk "$searchQuery" tidak ditemukan' - : 'Belum ada data produk lokal', + : 'Belum ada data produk', textAlign: TextAlign.center, ), SizedBox(height: 8), Text( - 'Tambahkan produk ke database lokal terlebih dahulu', + 'Data akan dimuat dari database lokal', style: TextStyle( color: Colors.grey.shade600, fontSize: 12, @@ -542,18 +409,11 @@ class _HomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.error_outline, - size: 48, - color: Colors.red.shade400, - ), + Icon(Icons.error_outline, size: 48, color: Colors.red.shade400), SizedBox(height: 16), Text( - 'Error Database Lokal', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), + 'Error Database', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), SizedBox(height: 8), Padding( @@ -588,61 +448,19 @@ class _HomePageState extends State { ) { return Column( children: [ - // Product count with local indicator + // Simple product count if (products.isNotEmpty) Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6), child: Row( children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 4, vertical: 1), - decoration: BoxDecoration( - color: Colors.blue.shade50, - borderRadius: BorderRadius.circular(4), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.storage, - size: 10, color: Colors.blue.shade600), - SizedBox(width: 2), - Text( - '${products.length}', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Colors.blue.shade600, - ), - ), - ], - ), - ), - SizedBox(width: 6), Text( - 'produk dari database lokal', + '${products.length} produk ditemukan', style: TextStyle( color: Colors.grey.shade600, - fontSize: 11, + fontSize: 12, ), ), - if (currentPage > 1) ...[ - SizedBox(width: 6), - Container( - padding: EdgeInsets.symmetric(horizontal: 4, vertical: 1), - decoration: BoxDecoration( - color: AppColors.primary.withOpacity(0.1), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - 'Hal $currentPage', - style: TextStyle( - color: AppColors.primary, - fontSize: 9, - fontWeight: FontWeight.w500, - ), - ), - ), - ], Spacer(), if (isLoadingMore) SizedBox( @@ -657,7 +475,7 @@ class _HomePageState extends State { ), ), - // Products grid - faster loading from local DB + // Products grid Expanded( child: NotificationListener( onNotification: (notification) => @@ -666,7 +484,7 @@ class _HomePageState extends State { itemCount: products.length, controller: scrollController, padding: const EdgeInsets.all(16), - cacheExtent: 200.0, // Bigger cache for smooth scrolling + cacheExtent: 200.0, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 180, mainAxisSpacing: 30, @@ -683,12 +501,12 @@ class _HomePageState extends State { ), ), - // End of data indicator + // End indicator if (hasReachedMax && products.isNotEmpty) Container( padding: EdgeInsets.all(8), child: Text( - 'Semua produk lokal telah dimuat', + 'Semua produk telah dimuat', style: TextStyle( color: Colors.grey.shade500, fontSize: 11, @@ -786,7 +604,7 @@ class _HomePageState extends State { ), ), - // Payment section (unchanged) + // Payment section Padding( padding: const EdgeInsets.all(16.0).copyWith(top: 0), child: Column( diff --git a/lib/presentation/home/widgets/category_tab_bar.dart b/lib/presentation/home/widgets/category_tab_bar.dart index 5abfaf3..ea4fd25 100644 --- a/lib/presentation/home/widgets/category_tab_bar.dart +++ b/lib/presentation/home/widgets/category_tab_bar.dart @@ -1,6 +1,5 @@ import 'package:enaklo_pos/core/constants/colors.dart'; import 'package:enaklo_pos/data/models/response/category_response_model.dart'; -import 'package:enaklo_pos/presentation/home/bloc/category_loader/category_loader_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -37,9 +36,6 @@ class _CategoryTabBarState extends State context.read().add( ProductLoaderEvent.getProduct(categoryId: selectedCategoryId), ); - context - .read() - .add(CategoryLoaderEvent.setCategoryId(selectedCategoryId ?? "")); } } }); diff --git a/lib/presentation/setting/bloc/get_categories/get_categories_bloc.dart b/lib/presentation/setting/bloc/get_categories/get_categories_bloc.dart index e7ab7f6..e9a307a 100644 --- a/lib/presentation/setting/bloc/get_categories/get_categories_bloc.dart +++ b/lib/presentation/setting/bloc/get_categories/get_categories_bloc.dart @@ -1,5 +1,5 @@ import 'package:bloc/bloc.dart'; -import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart'; +import 'package:enaklo_pos/data/datasources/category/category_remote_datasource.dart'; import 'package:enaklo_pos/data/models/response/category_response_model.dart'; import 'package:freezed_annotation/freezed_annotation.dart';