266 lines
7.5 KiB
JavaScript
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;
|