Merge branch 'main' of https://git.renolation.com/renolation/retail
# Conflicts: # docs/API_RESPONSE_FIX.md # docs/AUTH_UI_SUMMARY.md # docs/AUTO_LOGIN_DEBUG.md # docs/AUTO_LOGIN_FIXED.md # docs/BUILD_STATUS.md # docs/CLEANUP_COMPLETE.md # docs/EXPORT_FILES_SUMMARY.md # docs/RIVERPOD_DI_MIGRATION.md # docs/TEST_AUTO_LOGIN.md # lib/features/categories/data/datasources/category_remote_datasource.dart # lib/features/categories/presentation/providers/categories_provider.dart # lib/features/categories/presentation/providers/categories_provider.g.dart # lib/features/products/data/datasources/product_remote_datasource.dart # lib/features/products/data/models/product_model.dart # lib/features/products/presentation/pages/products_page.dart # lib/features/products/presentation/providers/products_provider.dart # lib/features/products/presentation/providers/products_provider.g.dart
This commit is contained in:
@@ -1,25 +1,12 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import '../models/category_model.dart';
|
||||
import '../../../../core/network/dio_client.dart';
|
||||
import '../../../../core/network/api_response.dart';
|
||||
import '../../../../core/constants/api_constants.dart';
|
||||
import '../../../../core/errors/exceptions.dart';
|
||||
|
||||
/// Category remote data source using API
|
||||
abstract class CategoryRemoteDataSource {
|
||||
/// Get all categories (public endpoint - no auth required)
|
||||
Future<List<CategoryModel>> getAllCategories();
|
||||
|
||||
/// Get single category by ID (public endpoint - no auth required)
|
||||
Future<CategoryModel> getCategoryById(String id);
|
||||
|
||||
/// Get category with its products with pagination (public endpoint)
|
||||
/// Returns Map with 'category' and 'products' with pagination info
|
||||
Future<Map<String, dynamic>> getCategoryWithProducts(
|
||||
String id,
|
||||
int page,
|
||||
int limit,
|
||||
);
|
||||
}
|
||||
|
||||
class CategoryRemoteDataSourceImpl implements CategoryRemoteDataSource {
|
||||
@@ -32,24 +19,15 @@ class CategoryRemoteDataSourceImpl implements CategoryRemoteDataSource {
|
||||
try {
|
||||
final response = await client.get(ApiConstants.categories);
|
||||
|
||||
// Parse API response using ApiResponse model
|
||||
final apiResponse = ApiResponse<List<CategoryModel>>.fromJson(
|
||||
response.data as Map<String, dynamic>,
|
||||
(data) => (data as List<dynamic>)
|
||||
.map((json) => CategoryModel.fromJson(json as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
if (!apiResponse.success) {
|
||||
throw ServerException(
|
||||
apiResponse.message ?? 'Failed to fetch categories',
|
||||
);
|
||||
// API returns: { success: true, data: [...categories...] }
|
||||
if (response.data['success'] == true) {
|
||||
final List<dynamic> data = response.data['data'] ?? [];
|
||||
return data.map((json) => CategoryModel.fromJson(json)).toList();
|
||||
} else {
|
||||
throw ServerException(response.data['message'] ?? 'Failed to fetch categories');
|
||||
}
|
||||
|
||||
return apiResponse.data;
|
||||
} on DioException catch (e) {
|
||||
throw _handleDioError(e);
|
||||
} catch (e) {
|
||||
if (e is ServerException) rethrow;
|
||||
throw ServerException('Failed to fetch categories: $e');
|
||||
}
|
||||
}
|
||||
@@ -59,108 +37,15 @@ class CategoryRemoteDataSourceImpl implements CategoryRemoteDataSource {
|
||||
try {
|
||||
final response = await client.get(ApiConstants.categoryById(id));
|
||||
|
||||
// Parse API response using ApiResponse model
|
||||
final apiResponse = ApiResponse<CategoryModel>.fromJson(
|
||||
response.data as Map<String, dynamic>,
|
||||
(data) => CategoryModel.fromJson(data as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
if (!apiResponse.success) {
|
||||
throw ServerException(
|
||||
apiResponse.message ?? 'Failed to fetch category',
|
||||
);
|
||||
// API returns: { success: true, data: {...category...} }
|
||||
if (response.data['success'] == true) {
|
||||
return CategoryModel.fromJson(response.data['data']);
|
||||
} else {
|
||||
throw ServerException(response.data['message'] ?? 'Category not found');
|
||||
}
|
||||
|
||||
return apiResponse.data;
|
||||
} on DioException catch (e) {
|
||||
throw _handleDioError(e);
|
||||
} catch (e) {
|
||||
if (e is ServerException) rethrow;
|
||||
throw ServerException('Failed to fetch category: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> getCategoryWithProducts(
|
||||
String id,
|
||||
int page,
|
||||
int limit,
|
||||
) async {
|
||||
try {
|
||||
final response = await client.get(
|
||||
'${ApiConstants.categories}/$id/products',
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'limit': limit,
|
||||
},
|
||||
);
|
||||
|
||||
// Parse API response - data contains category with nested products
|
||||
final apiResponse = ApiResponse<Map<String, dynamic>>.fromJson(
|
||||
response.data as Map<String, dynamic>,
|
||||
(data) => data as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
if (!apiResponse.success) {
|
||||
throw ServerException(
|
||||
apiResponse.message ?? 'Failed to fetch category with products',
|
||||
);
|
||||
}
|
||||
|
||||
final responseData = apiResponse.data;
|
||||
|
||||
// Extract category info (excluding products array)
|
||||
final categoryData = Map<String, dynamic>.from(responseData);
|
||||
final products = categoryData.remove('products') as List<dynamic>? ?? [];
|
||||
|
||||
// Create category model from remaining data
|
||||
final category = CategoryModel.fromJson(categoryData);
|
||||
|
||||
return {
|
||||
'category': category,
|
||||
'products': products,
|
||||
'meta': apiResponse.meta?.toJson() ?? {},
|
||||
};
|
||||
} on DioException catch (e) {
|
||||
throw _handleDioError(e);
|
||||
} catch (e) {
|
||||
throw ServerException('Failed to fetch category with products: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle Dio errors and convert to custom exceptions
|
||||
Exception _handleDioError(DioException error) {
|
||||
switch (error.response?.statusCode) {
|
||||
case ApiConstants.statusBadRequest:
|
||||
return ValidationException(
|
||||
error.response?.data['message'] ?? 'Invalid request',
|
||||
);
|
||||
case ApiConstants.statusUnauthorized:
|
||||
return UnauthorizedException(
|
||||
error.response?.data['message'] ?? 'Unauthorized access',
|
||||
);
|
||||
case ApiConstants.statusForbidden:
|
||||
return UnauthorizedException(
|
||||
error.response?.data['message'] ?? 'Access forbidden',
|
||||
);
|
||||
case ApiConstants.statusNotFound:
|
||||
return NotFoundException(
|
||||
error.response?.data['message'] ?? 'Category not found',
|
||||
);
|
||||
case ApiConstants.statusInternalServerError:
|
||||
case ApiConstants.statusBadGateway:
|
||||
case ApiConstants.statusServiceUnavailable:
|
||||
return ServerException(
|
||||
error.response?.data['message'] ?? 'Server error',
|
||||
);
|
||||
default:
|
||||
if (error.type == DioExceptionType.connectionTimeout ||
|
||||
error.type == DioExceptionType.receiveTimeout ||
|
||||
error.type == DioExceptionType.sendTimeout) {
|
||||
return NetworkException('Connection timeout');
|
||||
} else if (error.type == DioExceptionType.connectionError) {
|
||||
return NetworkException('No internet connection');
|
||||
}
|
||||
return ServerException('Unexpected error occurred');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user