load products

This commit is contained in:
Phuoc Nguyen
2025-11-10 17:02:26 +07:00
parent 453984cd57
commit b367d405c4
15 changed files with 635 additions and 83 deletions

View File

@@ -4,10 +4,9 @@
library;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/features/products/data/datasources/products_local_datasource.dart';
import 'package:worker/features/products/data/repositories/products_repository_impl.dart';
import 'package:worker/features/products/domain/entities/category.dart';
import 'package:worker/features/products/domain/usecases/get_categories.dart';
import 'package:worker/features/products/presentation/providers/products_provider.dart';
part 'categories_provider.g.dart';
@@ -28,8 +27,8 @@ part 'categories_provider.g.dart';
/// ```
@riverpod
Future<List<Category>> categories(Ref ref) async {
final localDataSource = const ProductsLocalDataSourceImpl();
final repository = ProductsRepositoryImpl(localDataSource: localDataSource);
// Get products repository with injected dependencies
final repository = await ref.watch(productsRepositoryProvider.future);
final useCase = GetCategories(repository);
return await useCase();

View File

@@ -92,4 +92,4 @@ final class CategoriesProvider
}
}
String _$categoriesHash() => r'6de35d3271d6d6572d9cdf5ed68edd26036115fc';
String _$categoriesHash() => r'811c668d2624bbc198bc7c563ed14c7d9ffc390b';

View File

@@ -2,12 +2,17 @@
///
/// Manages the state of products data using Riverpod.
/// Provides filtered products based on category and search query.
/// Fetches data from Frappe ERPNext API via remote data source.
library;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/core/services/frappe_auth_provider.dart';
import 'package:worker/features/products/data/datasources/products_local_datasource.dart';
import 'package:worker/features/products/data/datasources/products_remote_datasource.dart';
import 'package:worker/features/products/data/repositories/products_repository_impl.dart';
import 'package:worker/features/products/domain/entities/product.dart';
import 'package:worker/features/products/domain/repositories/products_repository.dart';
import 'package:worker/features/products/domain/usecases/get_products.dart';
import 'package:worker/features/products/domain/usecases/search_products.dart';
import 'package:worker/features/products/presentation/providers/selected_category_provider.dart';
@@ -15,10 +20,36 @@ import 'package:worker/features/products/presentation/providers/search_query_pro
part 'products_provider.g.dart';
/// Products Local DataSource Provider
@riverpod
ProductsLocalDataSource productsLocalDataSource(Ref ref) {
return const ProductsLocalDataSourceImpl();
}
/// Products Remote DataSource Provider
@riverpod
Future<ProductsRemoteDataSource> productsRemoteDataSource(Ref ref) async {
final dioClient = await ref.watch(dioClientProvider.future);
final frappeAuthService = ref.watch(frappeAuthServiceProvider);
return ProductsRemoteDataSource(dioClient, frappeAuthService);
}
/// Products Repository Provider
@riverpod
Future<ProductsRepository> productsRepository(Ref ref) async {
final localDataSource = ref.watch(productsLocalDataSourceProvider);
final remoteDataSource = await ref.watch(productsRemoteDataSourceProvider.future);
return ProductsRepositoryImpl(
localDataSource: localDataSource,
remoteDataSource: remoteDataSource,
);
}
/// Products Provider
///
/// Fetches and filters products based on selected category and search query.
/// Automatically updates when category or search query changes.
/// Data is fetched from Frappe ERPNext API.
///
/// Usage:
/// ```dart
@@ -38,9 +69,8 @@ class Products extends _$Products {
final selectedCategory = ref.watch(selectedCategoryProvider);
final searchQuery = ref.watch(searchQueryProvider);
// Initialize dependencies
final localDataSource = const ProductsLocalDataSourceImpl();
final repository = ProductsRepositoryImpl(localDataSource: localDataSource);
// Get repository with injected data sources
final repository = await ref.watch(productsRepositoryProvider.future);
// Apply filters
List<Product> products;
@@ -78,10 +108,10 @@ class Products extends _$Products {
///
/// Provides all products without any filtering.
/// Useful for product selection dialogs, etc.
/// Fetches from Frappe ERPNext API.
@riverpod
Future<List<Product>> allProducts(Ref ref) async {
final localDataSource = const ProductsLocalDataSourceImpl();
final repository = ProductsRepositoryImpl(localDataSource: localDataSource);
final repository = await ref.watch(productsRepositoryProvider.future);
final getProductsUseCase = GetProducts(repository);
return await getProductsUseCase();

View File

@@ -8,10 +8,158 @@ part of 'products_provider.dart';
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Products Local DataSource Provider
@ProviderFor(productsLocalDataSource)
const productsLocalDataSourceProvider = ProductsLocalDataSourceProvider._();
/// Products Local DataSource Provider
final class ProductsLocalDataSourceProvider
extends
$FunctionalProvider<
ProductsLocalDataSource,
ProductsLocalDataSource,
ProductsLocalDataSource
>
with $Provider<ProductsLocalDataSource> {
/// Products Local DataSource Provider
const ProductsLocalDataSourceProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'productsLocalDataSourceProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$productsLocalDataSourceHash();
@$internal
@override
$ProviderElement<ProductsLocalDataSource> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
ProductsLocalDataSource create(Ref ref) {
return productsLocalDataSource(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ProductsLocalDataSource value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ProductsLocalDataSource>(value),
);
}
}
String _$productsLocalDataSourceHash() =>
r'c9f87b0affb7b86b890c38175b2b5ff328b7cfa4';
/// Products Remote DataSource Provider
@ProviderFor(productsRemoteDataSource)
const productsRemoteDataSourceProvider = ProductsRemoteDataSourceProvider._();
/// Products Remote DataSource Provider
final class ProductsRemoteDataSourceProvider
extends
$FunctionalProvider<
AsyncValue<ProductsRemoteDataSource>,
ProductsRemoteDataSource,
FutureOr<ProductsRemoteDataSource>
>
with
$FutureModifier<ProductsRemoteDataSource>,
$FutureProvider<ProductsRemoteDataSource> {
/// Products Remote DataSource Provider
const ProductsRemoteDataSourceProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'productsRemoteDataSourceProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$productsRemoteDataSourceHash();
@$internal
@override
$FutureProviderElement<ProductsRemoteDataSource> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<ProductsRemoteDataSource> create(Ref ref) {
return productsRemoteDataSource(ref);
}
}
String _$productsRemoteDataSourceHash() =>
r'4f08cec50d95b05954ca18a7b04d5f09d1ffd059';
/// Products Repository Provider
@ProviderFor(productsRepository)
const productsRepositoryProvider = ProductsRepositoryProvider._();
/// Products Repository Provider
final class ProductsRepositoryProvider
extends
$FunctionalProvider<
AsyncValue<ProductsRepository>,
ProductsRepository,
FutureOr<ProductsRepository>
>
with
$FutureModifier<ProductsRepository>,
$FutureProvider<ProductsRepository> {
/// Products Repository Provider
const ProductsRepositoryProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'productsRepositoryProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$productsRepositoryHash();
@$internal
@override
$FutureProviderElement<ProductsRepository> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<ProductsRepository> create(Ref ref) {
return productsRepository(ref);
}
}
String _$productsRepositoryHash() =>
r'f1ddd3ec17bbbb70e87a8d47e7c58f681c80c2ae';
/// Products Provider
///
/// Fetches and filters products based on selected category and search query.
/// Automatically updates when category or search query changes.
/// Data is fetched from Frappe ERPNext API.
///
/// Usage:
/// ```dart
@@ -31,6 +179,7 @@ const productsProvider = ProductsProvider._();
///
/// Fetches and filters products based on selected category and search query.
/// Automatically updates when category or search query changes.
/// Data is fetched from Frappe ERPNext API.
///
/// Usage:
/// ```dart
@@ -48,6 +197,7 @@ final class ProductsProvider
///
/// Fetches and filters products based on selected category and search query.
/// Automatically updates when category or search query changes.
/// Data is fetched from Frappe ERPNext API.
///
/// Usage:
/// ```dart
@@ -78,12 +228,13 @@ final class ProductsProvider
Products create() => Products();
}
String _$productsHash() => r'0f1b32d2c14b9d8d600ffb0270f54d32af753e1f';
String _$productsHash() => r'b892402a88484d301cdabd1fde5822ddd29538bf';
/// Products Provider
///
/// Fetches and filters products based on selected category and search query.
/// Automatically updates when category or search query changes.
/// Data is fetched from Frappe ERPNext API.
///
/// Usage:
/// ```dart
@@ -119,6 +270,7 @@ abstract class _$Products extends $AsyncNotifier<List<Product>> {
///
/// Provides all products without any filtering.
/// Useful for product selection dialogs, etc.
/// Fetches from Frappe ERPNext API.
@ProviderFor(allProducts)
const allProductsProvider = AllProductsProvider._();
@@ -127,6 +279,7 @@ const allProductsProvider = AllProductsProvider._();
///
/// Provides all products without any filtering.
/// Useful for product selection dialogs, etc.
/// Fetches from Frappe ERPNext API.
final class AllProductsProvider
extends
@@ -140,6 +293,7 @@ final class AllProductsProvider
///
/// Provides all products without any filtering.
/// Useful for product selection dialogs, etc.
/// Fetches from Frappe ERPNext API.
const AllProductsProvider._()
: super(
from: null,
@@ -166,4 +320,4 @@ final class AllProductsProvider
}
}
String _$allProductsHash() => r'a02e989ad36e644d9b62e681b3ced88e10e4d4c3';
String _$allProductsHash() => r'402d7c6e8d119c7c7eab5e696fb8163831259def';

View File

@@ -61,7 +61,7 @@ class ProductCard extends ConsumerWidget {
top: Radius.circular(ProductCardSpecs.borderRadius),
),
child: CachedNetworkImage(
imageUrl: product.imageUrl,
imageUrl: product.thumbnail ?? '',
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,

View File

@@ -48,7 +48,7 @@ class _ImageGallerySectionState extends State<ImageGallerySection> {
}
void _open360View() {
if (widget.product.link360 != null) {
if (widget.product.customLink360 != null) {
// TODO: Open in browser
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(