add notifiaction handling, error handling
This commit is contained in:
parent
ce708f5209
commit
1af288aa62
21 changed files with 4864 additions and 224 deletions
265
frontend/src/api-enhanced.js
Normal file
265
frontend/src/api-enhanced.js
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
/**
|
||||
* 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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue