custom_ui/frontend/src/api-enhanced.js

266 lines
7.5 KiB
JavaScript

/**
* Enhanced API wrapper with integrated error handling and notifications
*
* This example shows how to use the error store and notification system together
* to provide comprehensive error handling and user feedback for API calls.
*/
import { useErrorStore } from "@/stores/errors";
import { useNotificationStore } from "@/stores/notifications";
import { useLoadingStore } from "@/stores/loading";
import Api from "./api";
export class ApiWithErrorHandling {
static async makeApiCall(apiFunction, options = {}) {
const {
// Error handling options
componentName = null,
showErrorNotifications = true,
showSuccessNotifications = true,
// Loading options
showLoading = true,
loadingMessage = "Loading...",
// Success options
successMessage = null,
successTitle = "Success",
// Error options
errorTitle = "Error",
customErrorMessage = null,
// Retry options
retryCount = 0,
retryDelay = 1000,
// Operation identifier for tracking
operationKey = null,
} = options;
const errorStore = useErrorStore();
const notificationStore = useNotificationStore();
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
const result = await errorStore.handleApiCall(operation, apiFunction, {
showNotification: showErrorNotifications,
retryCount,
retryDelay,
});
// Show success notification
if (showSuccessNotifications && successMessage) {
notificationStore.addSuccess(successMessage, successTitle);
}
return result;
} catch (error) {
// The error store has already handled the error notification
// But we can add custom handling here if needed
console.error("API call failed:", error);
// Optionally show a custom error message
if (customErrorMessage && !showErrorNotifications) {
notificationStore.addError(customErrorMessage, errorTitle);
}
// Re-throw the error so calling code can handle it if needed
throw error;
} finally {
// Always clear loading state
if (showLoading) {
if (componentName) {
loadingStore.setComponentLoading(componentName, false);
} else {
loadingStore.stopOperation(operation);
}
}
}
}
// 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...",
successMessage: null, // 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...",
successMessage: null, // Don't show success for data fetches
...options,
},
);
}
static async createClient(clientData, options = {}) {
return this.makeApiCall(() => Api.createClient(clientData), {
operationKey: "create-client",
componentName: "form",
loadingMessage: "Creating client...",
successMessage: "Client created successfully!",
errorTitle: "Failed to Create Client",
...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...",
successMessage: null,
...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...",
successMessage: null,
...options,
},
);
}
static async getCityStateByZip(zipcode, options = {}) {
return this.makeApiCall(() => Api.getCityStateByZip(zipcode), {
operationKey: "zip-lookup",
componentName: "form",
loadingMessage: "Looking up location...",
successMessage: null,
errorTitle: "Zip Code Lookup Failed",
customErrorMessage: "Unable to find location for the provided zip code",
retryCount: 2,
retryDelay: 1000,
...options,
});
}
}
/**
* Example usage in Vue components:
*
* <script setup>
* import { ref, onMounted } from 'vue';
* import { ApiWithErrorHandling } from '@/api-enhanced';
* import { useErrorStore } from '@/stores/errors';
* import { useNotificationStore } from '@/stores/notifications';
*
* const errorStore = useErrorStore();
* const notificationStore = useNotificationStore();
*
* const clientData = ref([]);
* const pagination = ref({ page: 0, pageSize: 10 });
* const filters = ref({});
*
* // Example 1: Basic API call with automatic error handling
* const loadClientData = async () => {
* try {
* const result = await ApiWithErrorHandling.getPaginatedClientDetails(
* pagination.value,
* filters.value,
* []
* );
* clientData.value = result.data;
* } catch (error) {
* // Error has already been handled by the error store and notifications shown
* // This catch block is optional - you only need it if you want custom handling
* console.log('Failed to load client data, but user has been notified');
* }
* };
*
* // Example 2: API call with custom options
* const createNewClient = async (formData) => {
* try {
* await ApiWithErrorHandling.createClient(formData, {
* componentName: 'createClientForm',
* successMessage: 'New client added successfully!',
* errorTitle: 'Client Creation Failed',
* customErrorMessage: 'There was an issue creating the client. Please try again.',
* retryCount: 1
* });
*
* // Refresh data after successful creation
* await loadClientData();
* } catch (error) {
* // Handle any additional logic needed on error
* }
* };
*
* // Example 3: Using error store directly for custom error handling
* const handleCustomOperation = async () => {
* await errorStore.withErrorHandling('custom-operation', async () => {
* // Your custom async operation here
* const result = await someApiCall();
* return result;
* }, {
* componentName: 'customComponent',
* showNotification: true,
* rethrow: false // Don't re-throw errors, just handle them
* });
* };
*
* // Example 4: Using notification store directly
* const showCustomNotification = () => {
* notificationStore.addNotification({
* type: 'info',
* title: 'Custom Notification',
* message: 'This is a custom message with actions',
* actions: [
* {
* label: 'Retry',
* variant: 'primary',
* handler: () => loadClientData()
* },
* {
* label: 'Cancel',
* variant: 'secondary'
* }
* ]
* });
* };
*
* onMounted(() => {
* loadClientData();
* });
* </script>
*/
export default ApiWithErrorHandling;