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