163 lines
4.6 KiB
Dart
163 lines
4.6 KiB
Dart
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 ?? [];
|
||
}
|
||
}
|