/** * Enhanced API wrapper with PrimeVue Toast integration * * This provides a cleaner, simpler API error handling system using PrimeVue Toast * instead of a complex custom notification system. */ import { useErrorStore } from "@/stores/errors"; import { useLoadingStore } from "@/stores/loading"; import Api from "./api"; export class ApiWithToast { static async makeApiCall(apiFunction, options = {}) { const { // Error handling options componentName = null, showErrorToast = true, showSuccessToast = false, // Loading options showLoading = true, loadingMessage = "Loading...", // Success options successMessage = null, // Error options customErrorMessage = null, // Retry options retryCount = 0, retryDelay = 1000, // Operation identifier for tracking operationKey = null, rethrow = false, } = options; const errorStore = useErrorStore(); const loadingStore = useLoadingStore(); // Generate operation key if not provided const operation = operationKey || `api-${Date.now()}`; try { // Clear any existing errors if (componentName) { errorStore.clearComponentError(componentName); } // Show loading state if (showLoading) { if (componentName) { loadingStore.setComponentLoading(componentName, true, loadingMessage); } else { loadingStore.startOperation(operation, loadingMessage); } } // Make the API call with retry logic let attempt = 0; while (attempt <= retryCount) { try { const result = await apiFunction(); // Show success toast if requested if (showSuccessToast && successMessage) { errorStore.setSuccess(successMessage); } return result; } catch (error) { attempt++; if (attempt <= retryCount) { // Wait before retry await new Promise((resolve) => setTimeout(resolve, retryDelay)); continue; } // Final attempt failed - handle error if (componentName) { errorStore.setComponentError(componentName, error, false); } // Show error toast if (showErrorToast) { const errorMessage = customErrorMessage || this.extractErrorMessage(error); errorStore.setGlobalError(new Error(errorMessage)); } // Rethrow error if requested (default is to rethrow) if (rethrow) { throw error; } // If not rethrowing, return null to indicate failure return null; } } } finally { // Always clear loading state if (showLoading) { if (componentName) { loadingStore.setComponentLoading(componentName, false); } else { loadingStore.stopOperation(operation); } } } } // Helper to extract meaningful error messages static extractErrorMessage(error) { if (typeof error === "string") { return error; } if (error?.response?.data?.message) { return error.response.data.message; } if (error?.message) { return error.message; } if (error?.request) { return "Network error - please check your connection"; } return "An unexpected error occurred"; } // Convenience methods for common API operations static async getClientStatusCounts(options = {}) { return this.makeApiCall(() => Api.getClientStatusCounts(), { operationKey: "client-status-counts", componentName: "clients", loadingMessage: "Loading client status data...", showSuccessToast: false, // Don't show success for data fetches ...options, }); } static async getPaginatedClientDetails(paginationParams, filters, sorting, options = {}) { return this.makeApiCall( () => Api.getPaginatedClientDetails(paginationParams, filters, sorting), { operationKey: "client-table-data", componentName: "dataTable", loadingMessage: "Loading client data...", showSuccessToast: false, ...options, }, ); } static async createClient(clientData, options = {}) { return this.makeApiCall(() => Api.createClient(clientData), { operationKey: "create-client", componentName: "form", loadingMessage: "Creating client...", showSuccessToast: true, successMessage: "Client created successfully!", ...options, }); } static async getPaginatedJobDetails(paginationParams, filters, sorting, options = {}) { return this.makeApiCall( () => Api.getPaginatedJobDetails(paginationParams, filters, sorting), { operationKey: "job-table-data", componentName: "jobs", loadingMessage: "Loading job data...", showSuccessToast: false, ...options, }, ); } static async getPaginatedWarrantyData(paginationParams, filters, sorting, options = {}) { return this.makeApiCall( () => Api.getPaginatedWarrantyData(paginationParams, filters, sorting), { operationKey: "warranty-table-data", componentName: "warranties", loadingMessage: "Loading warranty data...", showSuccessToast: false, ...options, }, ); } static async getCityStateByZip(zipcode, options = {}) { return this.makeApiCall(() => Api.getCityStateByZip(zipcode), { operationKey: "zip-lookup", componentName: "form", loadingMessage: "Looking up location...", showSuccessToast: false, customErrorMessage: "Unable to find location for the provided zip code", retryCount: 2, retryDelay: 1000, ...options, }); } } /** * Simple usage examples: * * // Basic API call with automatic toast notifications * try { * const result = await ApiWithToast.getPaginatedClientDetails(pagination, filters, []); * // Success - data loaded, no toast shown for data fetches * } catch (error) { * // Error toast automatically shown, component error set * } * * // Create operation with success toast * try { * await ApiWithToast.createClient(formData); * // Success toast shown automatically: "Client created successfully!" * } catch (error) { * // Error toast shown automatically with appropriate message * } * * // Custom options * await ApiWithToast.makeApiCall( * () => someApiCall(), * { * componentName: 'myComponent', * showSuccessToast: true, * successMessage: 'Operation completed!', * retryCount: 3, * customErrorMessage: 'Custom error message' * } * ); */ export default ApiWithToast;