File size: 3,548 Bytes
4badc3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
 * Error Handling Utilities
 * Provides standardized error handling with toast notifications
 */
window.ErrorHandler = window.ErrorHandler || {};

/**
 * Safely execute an async function with error handling
 * @param {Function} fn - Async function to execute
 * @param {string} errorMessage - User-friendly error message prefix
 * @param {object} options - Additional options
 * @param {boolean} options.rethrow - Whether to rethrow the error after handling (default: false)
 * @param {Function} options.onError - Custom error handler callback
 * @returns {Promise<any>} Result of the function or undefined on error
 */
window.ErrorHandler.safeAsync = async function(fn, errorMessage = null, options = {}) {
    const { rethrow = false, onError = null } = options;
    const store = Alpine.store('global');
    const defaultErrorMessage = errorMessage || store.t('operationFailed');

    try {
        return await fn();
    } catch (error) {
        // Log error for debugging
        console.error(`[ErrorHandler] ${defaultErrorMessage}:`, error);

        // Show toast notification
        const fullMessage = `${defaultErrorMessage}: ${error.message || store.t('unknownError')}`;
        store.showToast(fullMessage, 'error');

        // Call custom error handler if provided
        if (onError && typeof onError === 'function') {
            try {
                onError(error);
            } catch (handlerError) {
                console.error('[ErrorHandler] Custom error handler failed:', handlerError);
            }
        }

        // Rethrow if requested
        if (rethrow) {
            throw error;
        }

        return undefined;
    }
};

/**
 * Show an error toast notification
 * @param {string} message - Error message
 * @param {Error} error - Optional error object
 */
window.ErrorHandler.showError = function(message, error = null) {
    const store = Alpine.store('global');
    const fullMessage = error ? `${message}: ${error.message}` : message;
    store.showToast(fullMessage, 'error');
};

/**
 * Execute an async function with automatic loading state management
 * @param {Function} asyncFn - Async function to execute
 * @param {object} context - Component context (this) that contains the loading state
 * @param {string} loadingKey - Name of the loading state property (default: 'loading')
 * @param {object} options - Additional options (same as safeAsync)
 * @returns {Promise<any>} Result of the function or undefined on error
 *
 * @example
 * // In your Alpine component:
 * async refreshAccount(email) {
 *   return await window.ErrorHandler.withLoading(async () => {
 *     const response = await window.utils.request(`/api/accounts/${email}/refresh`, { method: 'POST' });
 *     this.$store.global.showToast('Account refreshed', 'success');
 *     return response;
 *   }, this, 'refreshing');
 * }
 *
 * // In HTML:
 * // <button @click="refreshAccount(email)" :disabled="refreshing">
 * //   <i class="fas fa-sync-alt" :class="{ 'fa-spin': refreshing }"></i>
 * //   Refresh
 * // </button>
 */
window.ErrorHandler.withLoading = async function(asyncFn, context, loadingKey = 'loading', options = {}) {
    // Set loading state to true
    context[loadingKey] = true;

    try {
        // Execute the async function with error handling
        const result = await window.ErrorHandler.safeAsync(asyncFn, options.errorMessage, options);
        return result;
    } finally {
        // Always reset loading state, even if there was an error
        context[loadingKey] = false;
    }
};