import 'package:flutter/material.dart'; import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/modules/site_request_module/models/dropdown_models.dart'; import 'package:test_sa/modules/site_request_module/models/request_type_model.dart'; import 'package:test_sa/modules/site_request_module/models/site_request_model.dart'; import 'package:test_sa/models/lookup.dart'; import 'dart:convert'; import 'package:http/http.dart'; /// Provider for managing site request operations including: /// - Fetching request types /// - Managing user's current sites /// - Submitting site requests /// - Fetching and paginating request history class SiteRequestProvider extends ChangeNotifier { // Constants static const int _defaultPageSize = 10; static const String _errorFetchFailed = 'Failed to fetch data'; static const String _errorSubmitFailed = 'Failed to submit request'; // Loading states bool _loading = false; bool _isLoadingMore = false; // Data List _requestTypes = []; List _userCurrentSites = []; List _requestHistory = []; // Pagination int _currentPage = 1; int _pageSize = _defaultPageSize; bool _hasMoreData = true; // Error handling String? _errorMessage; int? stateCode; // Getters bool get loading => _loading; bool get isLoadingMore => _isLoadingMore; String? get errorMessage => _errorMessage; int get currentPage => _currentPage; bool get hasMoreData => _hasMoreData; List get requestTypes => _requestTypes; List get userCurrentSites => _userCurrentSites; List get requestHistory => _requestHistory; // User helper methods String getCurrentUserEmployeeId(UserProvider userProvider) => userProvider.user?.userID ?? ''; String getCurrentUserFullName(UserProvider userProvider) => userProvider.user?.username ?? ''; String getCurrentUserEmail(UserProvider userProvider) => userProvider.user?.email ?? ''; String getCurrentUserMobile(UserProvider userProvider) => userProvider.user?.phoneNumber ?? ''; String getCurrentUserExtension(UserProvider userProvider) => userProvider.user?.extensionNo ?? ''; String getCurrentUserRole(UserProvider userProvider) { if (userProvider.user?.userRoles != null && userProvider.user!.userRoles!.isNotEmpty) { return userProvider.user!.userRoles!.first.name ?? ''; } return ''; } List getCurrentUserAssignedSites(UserProvider userProvider) => []; bool hasAssignedSites(List assignedSites) => assignedSites.isNotEmpty; // Setter set loading(bool value) { _loading = value; notifyListeners(); } /// Fetches available request types from the server Future fetchRequestTypes() async { if (_loading) return -2; return _performApiCall( apiCall: () => ApiManager.instance.get(URLs.getSiteRequestTypes, enableToastMessage: false), onSuccess: (responseData) { List listJson = responseData['data'] ?? responseData; _requestTypes = listJson .map((item) => Lookup.fromJson(item)) .map((lookup) => RequestTypeModel( id: lookup.id?.toString() ?? lookup.value?.toString() ?? '', name: lookup.name ?? '', value: lookup.value, )) .toList(); }, errorMessage: 'Failed to fetch request types', ); } /// Fetches user's current sites for Change/Remove Site operations Future fetchUserCurrentSites(String userId) async { if (_loading) return -2; return _performApiCall( apiCall: () => ApiManager.instance.post(URLs.getUserSites(userId), showToast: false, body: {}), onSuccess: (responseData) { List listJson = responseData['data'] ?? []; _userCurrentSites = listJson .map((item) => SiteRequestSiteModel( id: item['siteId']?.toString() ?? '', name: item['siteName'] ?? '', )) .toList(); }, errorMessage: 'Failed to fetch user sites', ); } /// Submits a site request to the server Future submitSiteRequest(SiteRequestModel request, UserProvider userProvider) async { loading = true; _errorMessage = null; notifyListeners(); try { final isChangeSiteRequest = _requestTypes.any((type) => type.identifier == request.requestType && (type.name?.toLowerCase().contains('change') ?? false)); final requestBody = _buildRequestBody(request, userProvider, isChangeSiteRequest); final response = await ApiManager.instance.post(URLs.submitSiteRequest, body: requestBody); if (response.statusCode >= 200 && response.statusCode < 300) { _handleSubmitSuccess(response, request); return true; } else { _handleSubmitError(response); return false; } } catch (error) { _errorMessage = '$_errorSubmitFailed: $error'; loading = false; notifyListeners(); return false; } } /// Fetches paginated site requests Future fetchSiteRequests({ int? pageSize, int? pageNumber, bool loadMore = false, required UserProvider userProvider, }) async { final size = pageSize ?? _pageSize; final page = pageNumber ?? (loadMore ? _currentPage + 1 : 1); if (size <= 0 || page < 1) { _errorMessage = 'Invalid pagination parameters'; notifyListeners(); return; } _setLoadingState(loadMore); try { final response = await ApiManager.instance.post( URLs.getSiteRequests, body: { "pageSize": size, "pageNumber": page, "userId": getCurrentUserEmployeeId(userProvider), }, ); if (response.statusCode == 200) { _handleFetchSuccess(response, page, size, loadMore); } else { _errorMessage = _errorFetchFailed; } } catch (e) { _errorMessage = 'Error: ${e.toString()}'; } finally { _resetLoadingState(); } } /// Loads more site requests (pagination) Future loadMoreSiteRequests({required UserProvider userProvider}) async { if (!_hasMoreData || _isLoadingMore) return; await fetchSiteRequests(loadMore: true, userProvider: userProvider); } /// Refreshes site requests (resets to first page) Future refreshSiteRequests({required UserProvider userProvider}) async { await fetchSiteRequests(pageNumber: 1, pageSize: _pageSize, userProvider: userProvider); } /// Resets provider state void reset() { _loading = false; _errorMessage = null; _requestHistory.clear(); notifyListeners(); } // Private helper methods /// Generic API call handler to reduce duplication Future _performApiCall({ required Future Function() apiCall, required void Function(dynamic) onSuccess, required String errorMessage, }) async { _loading = true; _errorMessage = null; notifyListeners(); try { final response = await apiCall(); stateCode = response.statusCode; if (response.statusCode >= 200 && response.statusCode < 300) { final responseData = json.decode(response.body); onSuccess(responseData); } else { _errorMessage = errorMessage; } _loading = false; notifyListeners(); return response.statusCode; } catch (error) { _loading = false; stateCode = -1; _errorMessage = errorMessage; notifyListeners(); return -1; } } Map _buildRequestBody( SiteRequestModel request, UserProvider userProvider, bool isChangeSiteRequest, ) { final siteIdsArray = request.siteIds.asMap().entries.map((entry) { final index = entry.key; return { "id": 0, "siteId": int.tryParse(request.siteIds[index]) ?? 0, "siteName": request.siteNames[index], "oldSiteId": (isChangeSiteRequest && request.oldSiteIds != null && index < request.oldSiteIds!.length) ? int.tryParse(request.oldSiteIds![index]) ?? 0 : 0, "oldSitName": (isChangeSiteRequest && request.oldSiteNames != null && index < request.oldSiteNames!.length) ? request.oldSiteNames![index] : "" }; }).toList(); final departmentIdsArray = (request.departmentIds != null && request.departmentIds!.isNotEmpty) ? request.departmentIds!.asMap().entries.map((entry) { return { "id": 0, "departmentId": int.tryParse(request.departmentIds![entry.key]) ?? 0, "departmentName": request.departmentNames![entry.key], }; }).toList() : []; return { "id": 0, "userId": getCurrentUserEmployeeId(userProvider), "requestTypeId": int.tryParse(request.requestType) ?? 0, "siteIds": siteIdsArray, "departmentIds": departmentIdsArray, }; } void _handleSubmitSuccess(Response response, SiteRequestModel request) { try { final responseData = json.decode(response.body); final requestWithId = request.copyWith( id: responseData['id']?.toString() ?? 'REQ_${DateTime.now().millisecondsSinceEpoch}', status: responseData['status']?.toString() ?? 'Pending', requestDate: DateTime.now(), ); _requestHistory.insert(0, requestWithId); } catch (e) { _errorMessage = 'Request submitted but failed to parse response'; } finally { loading = false; notifyListeners(); } } void _handleSubmitError(Response response) { try { final errorData = json.decode(response.body); _errorMessage = errorData['message'] ?? _errorSubmitFailed; } catch (e) { _errorMessage = _errorSubmitFailed; } loading = false; notifyListeners(); } void _setLoadingState(bool loadMore) { if (loadMore) { _isLoadingMore = true; } else { loading = true; _currentPage = 1; _hasMoreData = true; _requestHistory.clear(); } _errorMessage = null; notifyListeners(); } void _handleFetchSuccess(Response response, int page, int size, bool loadMore) { final data = json.decode(response.body); if (data['isSuccess'] == true) { final siteRequests = (data['data'] as List?) ?.map((item) => SiteRequestModel.fromJson(item)) .toList() ?? []; if (loadMore) { _requestHistory.addAll(siteRequests); } else { _requestHistory.clear(); _requestHistory.addAll(siteRequests); } _currentPage = page; _hasMoreData = siteRequests.length >= size; } else { _errorMessage = data['message'] ?? _errorFetchFailed; } } void _resetLoadingState() { loading = false; _isLoadingMore = false; notifyListeners(); } }