update review api.

This commit is contained in:
Phuoc Nguyen
2025-11-17 17:54:32 +07:00
parent 0798b28db5
commit 0841e3bf3d
23 changed files with 4856 additions and 209 deletions

View File

@@ -0,0 +1,178 @@
/// Providers: Reviews
///
/// Riverpod providers for review management.
library;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/features/reviews/data/datasources/reviews_remote_datasource.dart';
import 'package:worker/features/reviews/data/repositories/reviews_repository_impl.dart';
import 'package:worker/features/reviews/domain/entities/review.dart';
import 'package:worker/features/reviews/domain/entities/review_statistics.dart';
import 'package:worker/features/reviews/domain/repositories/reviews_repository.dart';
import 'package:worker/features/reviews/domain/usecases/delete_review.dart';
import 'package:worker/features/reviews/domain/usecases/get_product_reviews.dart';
import 'package:worker/features/reviews/domain/usecases/submit_review.dart';
part 'reviews_provider.g.dart';
// ============================================================================
// Data Layer Providers
// ============================================================================
/// Provider for reviews remote data source
@riverpod
Future<ReviewsRemoteDataSource> reviewsRemoteDataSource(
Ref ref,
) async {
final dioClient = await ref.watch(dioClientProvider.future);
return ReviewsRemoteDataSourceImpl(dioClient);
}
/// Provider for reviews repository
@riverpod
Future<ReviewsRepository> reviewsRepository(Ref ref) async {
final remoteDataSource = await ref.watch(reviewsRemoteDataSourceProvider.future);
return ReviewsRepositoryImpl(remoteDataSource);
}
// ============================================================================
// Use Case Providers
// ============================================================================
/// Provider for get product reviews use case
@riverpod
Future<GetProductReviews> getProductReviews(Ref ref) async {
final repository = await ref.watch(reviewsRepositoryProvider.future);
return GetProductReviews(repository);
}
/// Provider for submit review use case
@riverpod
Future<SubmitReview> submitReview(Ref ref) async {
final repository = await ref.watch(reviewsRepositoryProvider.future);
return SubmitReview(repository);
}
/// Provider for delete review use case
@riverpod
Future<DeleteReview> deleteReview(Ref ref) async {
final repository = await ref.watch(reviewsRepositoryProvider.future);
return DeleteReview(repository);
}
// ============================================================================
// State Providers
// ============================================================================
/// Provider for fetching reviews for a specific product
///
/// This is a family provider that takes a product ID and returns
/// the list of reviews for that product.
///
/// Usage:
/// ```dart
/// final reviewsAsync = ref.watch(productReviewsProvider('PRODUCT_ID'));
/// ```
@riverpod
Future<List<Review>> productReviews(
Ref ref,
String itemId,
) async {
final getProductReviewsUseCase = await ref.watch(getProductReviewsProvider.future);
return await getProductReviewsUseCase(
itemId: itemId,
limitPageLength: 50, // Fetch more reviews
limitStart: 0,
);
}
/// Provider for review statistics (from API)
///
/// Gets statistics directly from API including:
/// - Total feedback count
/// - Average rating (0-5 scale, already calculated by server)
///
/// This is more efficient than calculating client-side
@riverpod
Future<ReviewStatistics> productReviewStatistics(
Ref ref,
String itemId,
) async {
final repository = await ref.watch(reviewsRepositoryProvider.future);
return await repository.getProductReviewStatistics(itemId: itemId);
}
/// Provider for average rating (convenience wrapper)
///
/// Gets the average rating from API statistics
/// Returns 0.0 if there are no reviews.
@riverpod
Future<double> productAverageRating(
Ref ref,
String itemId,
) async {
final stats = await ref.watch(productReviewStatisticsProvider(itemId).future);
return stats.averageRating;
}
/// Provider for counting reviews (convenience wrapper)
///
/// Gets the total count from API statistics
@riverpod
Future<int> productReviewCount(
Ref ref,
String itemId,
) async {
final stats = await ref.watch(productReviewStatisticsProvider(itemId).future);
return stats.totalFeedback;
}
/// Provider for checking if user can submit a review
///
/// This can be extended to check if user has already reviewed
/// the product and enforce one-review-per-user policy.
///
/// For now, it always returns true.
@riverpod
Future<bool> canSubmitReview(
Ref ref,
String itemId,
) async {
// TODO: Implement logic to check if user already reviewed this product
// This would require user email from auth state
return true;
}
// ============================================================================
// Helper Functions
// ============================================================================
/// Convert star rating (1-5) to API rating (0-1)
///
/// Example:
/// - 1 star = 0.2
/// - 2 stars = 0.4
/// - 3 stars = 0.6
/// - 4 stars = 0.8
/// - 5 stars = 1.0
double starsToApiRating(int stars) {
if (stars < 1 || stars > 5) {
throw ArgumentError('Stars must be between 1 and 5. Got: $stars');
}
return stars / 5.0;
}
/// Convert API rating (0-1) to star rating (1-5)
///
/// Example:
/// - 0.2 = 1 star
/// - 0.5 = 2.5 stars (rounded to 3)
/// - 1.0 = 5 stars
int apiRatingToStars(double rating) {
if (rating < 0 || rating > 1) {
throw ArgumentError('Rating must be between 0 and 1. Got: $rating');
}
return (rating * 5).round();
}

View File

@@ -0,0 +1,762 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'reviews_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Provider for reviews remote data source
@ProviderFor(reviewsRemoteDataSource)
const reviewsRemoteDataSourceProvider = ReviewsRemoteDataSourceProvider._();
/// Provider for reviews remote data source
final class ReviewsRemoteDataSourceProvider
extends
$FunctionalProvider<
AsyncValue<ReviewsRemoteDataSource>,
ReviewsRemoteDataSource,
FutureOr<ReviewsRemoteDataSource>
>
with
$FutureModifier<ReviewsRemoteDataSource>,
$FutureProvider<ReviewsRemoteDataSource> {
/// Provider for reviews remote data source
const ReviewsRemoteDataSourceProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'reviewsRemoteDataSourceProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$reviewsRemoteDataSourceHash();
@$internal
@override
$FutureProviderElement<ReviewsRemoteDataSource> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<ReviewsRemoteDataSource> create(Ref ref) {
return reviewsRemoteDataSource(ref);
}
}
String _$reviewsRemoteDataSourceHash() =>
r'482e19a7e1096a814c2f3b4632866d662dbfc51a';
/// Provider for reviews repository
@ProviderFor(reviewsRepository)
const reviewsRepositoryProvider = ReviewsRepositoryProvider._();
/// Provider for reviews repository
final class ReviewsRepositoryProvider
extends
$FunctionalProvider<
AsyncValue<ReviewsRepository>,
ReviewsRepository,
FutureOr<ReviewsRepository>
>
with
$FutureModifier<ReviewsRepository>,
$FutureProvider<ReviewsRepository> {
/// Provider for reviews repository
const ReviewsRepositoryProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'reviewsRepositoryProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$reviewsRepositoryHash();
@$internal
@override
$FutureProviderElement<ReviewsRepository> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<ReviewsRepository> create(Ref ref) {
return reviewsRepository(ref);
}
}
String _$reviewsRepositoryHash() => r'4f4a7ec3d4450f0dd0cf10a05bd666444e74879b';
/// Provider for get product reviews use case
@ProviderFor(getProductReviews)
const getProductReviewsProvider = GetProductReviewsProvider._();
/// Provider for get product reviews use case
final class GetProductReviewsProvider
extends
$FunctionalProvider<
AsyncValue<GetProductReviews>,
GetProductReviews,
FutureOr<GetProductReviews>
>
with
$FutureModifier<GetProductReviews>,
$FutureProvider<GetProductReviews> {
/// Provider for get product reviews use case
const GetProductReviewsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'getProductReviewsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$getProductReviewsHash();
@$internal
@override
$FutureProviderElement<GetProductReviews> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<GetProductReviews> create(Ref ref) {
return getProductReviews(ref);
}
}
String _$getProductReviewsHash() => r'0ad92df95d39333aeb1b2e24946862507c911bdc';
/// Provider for submit review use case
@ProviderFor(submitReview)
const submitReviewProvider = SubmitReviewProvider._();
/// Provider for submit review use case
final class SubmitReviewProvider
extends
$FunctionalProvider<
AsyncValue<SubmitReview>,
SubmitReview,
FutureOr<SubmitReview>
>
with $FutureModifier<SubmitReview>, $FutureProvider<SubmitReview> {
/// Provider for submit review use case
const SubmitReviewProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'submitReviewProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$submitReviewHash();
@$internal
@override
$FutureProviderElement<SubmitReview> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<SubmitReview> create(Ref ref) {
return submitReview(ref);
}
}
String _$submitReviewHash() => r'617eaa6ccd168a597517f6a828d03100bb9508f1';
/// Provider for delete review use case
@ProviderFor(deleteReview)
const deleteReviewProvider = DeleteReviewProvider._();
/// Provider for delete review use case
final class DeleteReviewProvider
extends
$FunctionalProvider<
AsyncValue<DeleteReview>,
DeleteReview,
FutureOr<DeleteReview>
>
with $FutureModifier<DeleteReview>, $FutureProvider<DeleteReview> {
/// Provider for delete review use case
const DeleteReviewProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'deleteReviewProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$deleteReviewHash();
@$internal
@override
$FutureProviderElement<DeleteReview> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<DeleteReview> create(Ref ref) {
return deleteReview(ref);
}
}
String _$deleteReviewHash() => r'13b6f2529258f7db56cc1fce89a6a1af417a74b3';
/// Provider for fetching reviews for a specific product
///
/// This is a family provider that takes a product ID and returns
/// the list of reviews for that product.
///
/// Usage:
/// ```dart
/// final reviewsAsync = ref.watch(productReviewsProvider('PRODUCT_ID'));
/// ```
@ProviderFor(productReviews)
const productReviewsProvider = ProductReviewsFamily._();
/// Provider for fetching reviews for a specific product
///
/// This is a family provider that takes a product ID and returns
/// the list of reviews for that product.
///
/// Usage:
/// ```dart
/// final reviewsAsync = ref.watch(productReviewsProvider('PRODUCT_ID'));
/// ```
final class ProductReviewsProvider
extends
$FunctionalProvider<
AsyncValue<List<Review>>,
List<Review>,
FutureOr<List<Review>>
>
with $FutureModifier<List<Review>>, $FutureProvider<List<Review>> {
/// Provider for fetching reviews for a specific product
///
/// This is a family provider that takes a product ID and returns
/// the list of reviews for that product.
///
/// Usage:
/// ```dart
/// final reviewsAsync = ref.watch(productReviewsProvider('PRODUCT_ID'));
/// ```
const ProductReviewsProvider._({
required ProductReviewsFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'productReviewsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$productReviewsHash();
@override
String toString() {
return r'productReviewsProvider'
''
'($argument)';
}
@$internal
@override
$FutureProviderElement<List<Review>> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<List<Review>> create(Ref ref) {
final argument = this.argument as String;
return productReviews(ref, argument);
}
@override
bool operator ==(Object other) {
return other is ProductReviewsProvider && other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$productReviewsHash() => r'e7a4da21c3d98c2f3c297b73df943b66ef4a56d5';
/// Provider for fetching reviews for a specific product
///
/// This is a family provider that takes a product ID and returns
/// the list of reviews for that product.
///
/// Usage:
/// ```dart
/// final reviewsAsync = ref.watch(productReviewsProvider('PRODUCT_ID'));
/// ```
final class ProductReviewsFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<List<Review>>, String> {
const ProductReviewsFamily._()
: super(
retry: null,
name: r'productReviewsProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
/// Provider for fetching reviews for a specific product
///
/// This is a family provider that takes a product ID and returns
/// the list of reviews for that product.
///
/// Usage:
/// ```dart
/// final reviewsAsync = ref.watch(productReviewsProvider('PRODUCT_ID'));
/// ```
ProductReviewsProvider call(String itemId) =>
ProductReviewsProvider._(argument: itemId, from: this);
@override
String toString() => r'productReviewsProvider';
}
/// Provider for review statistics (from API)
///
/// Gets statistics directly from API including:
/// - Total feedback count
/// - Average rating (0-5 scale, already calculated by server)
///
/// This is more efficient than calculating client-side
@ProviderFor(productReviewStatistics)
const productReviewStatisticsProvider = ProductReviewStatisticsFamily._();
/// Provider for review statistics (from API)
///
/// Gets statistics directly from API including:
/// - Total feedback count
/// - Average rating (0-5 scale, already calculated by server)
///
/// This is more efficient than calculating client-side
final class ProductReviewStatisticsProvider
extends
$FunctionalProvider<
AsyncValue<ReviewStatistics>,
ReviewStatistics,
FutureOr<ReviewStatistics>
>
with $FutureModifier<ReviewStatistics>, $FutureProvider<ReviewStatistics> {
/// Provider for review statistics (from API)
///
/// Gets statistics directly from API including:
/// - Total feedback count
/// - Average rating (0-5 scale, already calculated by server)
///
/// This is more efficient than calculating client-side
const ProductReviewStatisticsProvider._({
required ProductReviewStatisticsFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'productReviewStatisticsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$productReviewStatisticsHash();
@override
String toString() {
return r'productReviewStatisticsProvider'
''
'($argument)';
}
@$internal
@override
$FutureProviderElement<ReviewStatistics> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<ReviewStatistics> create(Ref ref) {
final argument = this.argument as String;
return productReviewStatistics(ref, argument);
}
@override
bool operator ==(Object other) {
return other is ProductReviewStatisticsProvider &&
other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$productReviewStatisticsHash() =>
r'ed3780192e285c6ff2ac8f7ee0b7cb6f3696e2b8';
/// Provider for review statistics (from API)
///
/// Gets statistics directly from API including:
/// - Total feedback count
/// - Average rating (0-5 scale, already calculated by server)
///
/// This is more efficient than calculating client-side
final class ProductReviewStatisticsFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<ReviewStatistics>, String> {
const ProductReviewStatisticsFamily._()
: super(
retry: null,
name: r'productReviewStatisticsProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
/// Provider for review statistics (from API)
///
/// Gets statistics directly from API including:
/// - Total feedback count
/// - Average rating (0-5 scale, already calculated by server)
///
/// This is more efficient than calculating client-side
ProductReviewStatisticsProvider call(String itemId) =>
ProductReviewStatisticsProvider._(argument: itemId, from: this);
@override
String toString() => r'productReviewStatisticsProvider';
}
/// Provider for average rating (convenience wrapper)
///
/// Gets the average rating from API statistics
/// Returns 0.0 if there are no reviews.
@ProviderFor(productAverageRating)
const productAverageRatingProvider = ProductAverageRatingFamily._();
/// Provider for average rating (convenience wrapper)
///
/// Gets the average rating from API statistics
/// Returns 0.0 if there are no reviews.
final class ProductAverageRatingProvider
extends $FunctionalProvider<AsyncValue<double>, double, FutureOr<double>>
with $FutureModifier<double>, $FutureProvider<double> {
/// Provider for average rating (convenience wrapper)
///
/// Gets the average rating from API statistics
/// Returns 0.0 if there are no reviews.
const ProductAverageRatingProvider._({
required ProductAverageRatingFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'productAverageRatingProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$productAverageRatingHash();
@override
String toString() {
return r'productAverageRatingProvider'
''
'($argument)';
}
@$internal
@override
$FutureProviderElement<double> $createElement($ProviderPointer pointer) =>
$FutureProviderElement(pointer);
@override
FutureOr<double> create(Ref ref) {
final argument = this.argument as String;
return productAverageRating(ref, argument);
}
@override
bool operator ==(Object other) {
return other is ProductAverageRatingProvider && other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$productAverageRatingHash() =>
r'59e765e5004c93386999b60499430b1ae2b081a9';
/// Provider for average rating (convenience wrapper)
///
/// Gets the average rating from API statistics
/// Returns 0.0 if there are no reviews.
final class ProductAverageRatingFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<double>, String> {
const ProductAverageRatingFamily._()
: super(
retry: null,
name: r'productAverageRatingProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
/// Provider for average rating (convenience wrapper)
///
/// Gets the average rating from API statistics
/// Returns 0.0 if there are no reviews.
ProductAverageRatingProvider call(String itemId) =>
ProductAverageRatingProvider._(argument: itemId, from: this);
@override
String toString() => r'productAverageRatingProvider';
}
/// Provider for counting reviews (convenience wrapper)
///
/// Gets the total count from API statistics
@ProviderFor(productReviewCount)
const productReviewCountProvider = ProductReviewCountFamily._();
/// Provider for counting reviews (convenience wrapper)
///
/// Gets the total count from API statistics
final class ProductReviewCountProvider
extends $FunctionalProvider<AsyncValue<int>, int, FutureOr<int>>
with $FutureModifier<int>, $FutureProvider<int> {
/// Provider for counting reviews (convenience wrapper)
///
/// Gets the total count from API statistics
const ProductReviewCountProvider._({
required ProductReviewCountFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'productReviewCountProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$productReviewCountHash();
@override
String toString() {
return r'productReviewCountProvider'
''
'($argument)';
}
@$internal
@override
$FutureProviderElement<int> $createElement($ProviderPointer pointer) =>
$FutureProviderElement(pointer);
@override
FutureOr<int> create(Ref ref) {
final argument = this.argument as String;
return productReviewCount(ref, argument);
}
@override
bool operator ==(Object other) {
return other is ProductReviewCountProvider && other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$productReviewCountHash() =>
r'93aba289b0a51286244ff3e4aebc417e79273113';
/// Provider for counting reviews (convenience wrapper)
///
/// Gets the total count from API statistics
final class ProductReviewCountFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<int>, String> {
const ProductReviewCountFamily._()
: super(
retry: null,
name: r'productReviewCountProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
/// Provider for counting reviews (convenience wrapper)
///
/// Gets the total count from API statistics
ProductReviewCountProvider call(String itemId) =>
ProductReviewCountProvider._(argument: itemId, from: this);
@override
String toString() => r'productReviewCountProvider';
}
/// Provider for checking if user can submit a review
///
/// This can be extended to check if user has already reviewed
/// the product and enforce one-review-per-user policy.
///
/// For now, it always returns true.
@ProviderFor(canSubmitReview)
const canSubmitReviewProvider = CanSubmitReviewFamily._();
/// Provider for checking if user can submit a review
///
/// This can be extended to check if user has already reviewed
/// the product and enforce one-review-per-user policy.
///
/// For now, it always returns true.
final class CanSubmitReviewProvider
extends $FunctionalProvider<AsyncValue<bool>, bool, FutureOr<bool>>
with $FutureModifier<bool>, $FutureProvider<bool> {
/// Provider for checking if user can submit a review
///
/// This can be extended to check if user has already reviewed
/// the product and enforce one-review-per-user policy.
///
/// For now, it always returns true.
const CanSubmitReviewProvider._({
required CanSubmitReviewFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'canSubmitReviewProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$canSubmitReviewHash();
@override
String toString() {
return r'canSubmitReviewProvider'
''
'($argument)';
}
@$internal
@override
$FutureProviderElement<bool> $createElement($ProviderPointer pointer) =>
$FutureProviderElement(pointer);
@override
FutureOr<bool> create(Ref ref) {
final argument = this.argument as String;
return canSubmitReview(ref, argument);
}
@override
bool operator ==(Object other) {
return other is CanSubmitReviewProvider && other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$canSubmitReviewHash() => r'01506e31ea6fdf22658850750ae29416e53355bf';
/// Provider for checking if user can submit a review
///
/// This can be extended to check if user has already reviewed
/// the product and enforce one-review-per-user policy.
///
/// For now, it always returns true.
final class CanSubmitReviewFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<bool>, String> {
const CanSubmitReviewFamily._()
: super(
retry: null,
name: r'canSubmitReviewProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
/// Provider for checking if user can submit a review
///
/// This can be extended to check if user has already reviewed
/// the product and enforce one-review-per-user policy.
///
/// For now, it always returns true.
CanSubmitReviewProvider call(String itemId) =>
CanSubmitReviewProvider._(argument: itemId, from: this);
@override
String toString() => r'canSubmitReviewProvider';
}