first commit
This commit is contained in:
162
lib/view/account/product.dart
Normal file
162
lib/view/account/product.dart
Normal file
@@ -0,0 +1,162 @@
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../../domain/provider/product/all_products.dart';
|
||||
import '../../modules/product/product.dart';
|
||||
|
||||
class ProductsController extends GetxController {
|
||||
final ProductsProvider provider = ProductsProvider();
|
||||
var isConnected = true.obs;
|
||||
var isLoading = false.obs;
|
||||
var productResponse = Rxn<ProductResponse>();
|
||||
var selectedIndex = 0.obs;
|
||||
var searchQuery = ''.obs;
|
||||
var isSearching = false.obs;
|
||||
|
||||
/// In-memory cache: key is "categoryId_tenantId"
|
||||
final Map<String, ProductResponse> _cache = {};
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
|
||||
|
||||
// Listen for connectivity changes
|
||||
Connectivity().onConnectivityChanged.listen((status) {
|
||||
isConnected.value = (status != ConnectivityResult.none);
|
||||
});
|
||||
|
||||
}
|
||||
Future<bool> hasInternet() async {
|
||||
try {
|
||||
final response = await http.get(Uri.parse('https://www.google.com'))
|
||||
.timeout(const Duration(seconds: 5));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<void> fetchProducts(int categoryId, int tenantId, int locationId) async {
|
||||
final cacheKey = '${categoryId}_${tenantId}_$locationId'; // ✅ Include locationId in cache key
|
||||
|
||||
// 1️⃣ Use cache if available
|
||||
if (_cache.containsKey(cacheKey)) {
|
||||
productResponse.value = _cache[cacheKey];
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
bool connected = await hasInternet();
|
||||
if (!connected) {
|
||||
isLoading.value = false;
|
||||
isConnected = false.obs;
|
||||
return; // Stop fetching
|
||||
}
|
||||
|
||||
// 2️⃣ Otherwise fetch from API
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
final response = await provider.getProductsBySubCategory(
|
||||
categoryId: categoryId,
|
||||
tenantId: tenantId,
|
||||
locationId: locationId, // ✅ Pass locationId to API
|
||||
);
|
||||
|
||||
productResponse.value = response;
|
||||
|
||||
// 3️⃣ Save in cache
|
||||
_cache[cacheKey] = response!;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Force refresh API and update cache
|
||||
Future<void> refreshProducts(int categoryId, int tenantId, int locationId) async {
|
||||
final cacheKey = '${categoryId}_${tenantId}_$locationId'; // ✅ Include locationId
|
||||
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
final response = await provider.getProductsBySubCategory(
|
||||
categoryId: categoryId,
|
||||
tenantId: tenantId,
|
||||
locationId: locationId, // ✅ Pass locationId to API
|
||||
);
|
||||
|
||||
productResponse.value = response;
|
||||
|
||||
// ✅ Update cache with new key
|
||||
_cache[cacheKey] = response!;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns products depending on search query and selected subcategory
|
||||
List<Product> get filteredProducts {
|
||||
// Check if nested data exists (main API)
|
||||
final details = productResponse.value?.data?.details;
|
||||
if (details != null && details.isNotEmpty) {
|
||||
if (searchQuery.value.isEmpty) {
|
||||
final selectedDetail = details[selectedIndex.value];
|
||||
return selectedDetail.products ?? [];
|
||||
}
|
||||
|
||||
List<Product> allProducts = [];
|
||||
for (var detail in details) {
|
||||
allProducts.addAll(detail.products ?? []);
|
||||
}
|
||||
return allProducts
|
||||
.where((p) =>
|
||||
(p.productname ?? '')
|
||||
.toLowerCase()
|
||||
.contains(searchQuery.value.toLowerCase()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// If flat details exist (variants API)
|
||||
final variantDetails = productResponse.value?.details ?? [];
|
||||
if (variantDetails.isNotEmpty) {
|
||||
if (searchQuery.value.isEmpty) return variantDetails;
|
||||
|
||||
return variantDetails
|
||||
.where((p) =>
|
||||
(p.productname ?? '')
|
||||
.toLowerCase()
|
||||
.contains(searchQuery.value.toLowerCase()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
// NEW: Dedicated method for subcategory-specific screen
|
||||
List<Product> getProductsBySubcategory(String subCategoryName) {
|
||||
final details = productResponse.value?.data?.details ?? [];
|
||||
|
||||
if (details.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Find matching subcategory (case-insensitive, trimmed for safety)
|
||||
final matchingDetail = details.firstWhere(
|
||||
(detail) =>
|
||||
(detail.subcategoryname ?? '').trim().toLowerCase() ==
|
||||
subCategoryName.trim().toLowerCase(),
|
||||
orElse: () => Detail(), // fallback - make sure Detail() is valid in your modules
|
||||
);
|
||||
|
||||
// Return the products of that subcategory (or empty if no match)
|
||||
return matchingDetail.products ?? [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user