Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 27x 542x 515x 27x 27x | // src/components/ApiErrorAlert.tsx
import {type ReactElement} from 'react';
import {Alert, AlertTitle} from '@mui/material';
import type {SerializedError} from '@reduxjs/toolkit';
import type {FetchBaseQueryError} from '@reduxjs/toolkit/query';
/**
* Standard API error response format from backend
*/
interface ApiErrorData {
success: false;
error: string; // Error type: "Authentication Error", "Validation Error", etc.
message: string; // Specific message: "Invalid credentials", "Email already exists", etc.
}
interface ApiErrorAlertProps {
/**
* Error from RTK Query mutation/query
*/
error: FetchBaseQueryError | SerializedError | undefined;
/**
* Optional custom fallback message when error format is unexpected
*/
fallbackMessage?: string;
}
/**
* Type guard to check if error data matches our API error format
*/
function isApiErrorData(data: unknown): data is ApiErrorData {
return (
typeof data === 'object' &&
data !== null &&
'error' in data &&
'message' in data &&
typeof (data as ApiErrorData).error === 'string' &&
typeof (data as ApiErrorData).message === 'string'
);
}
/**
* Reusable error alert component for displaying API errors consistently.
*
* Handles the standard backend error format:
* { success: false, error: "Error Type", message: "Specific details" }
*
* @example
* ```tsx
* const [login, { error }] = useLoginMutation();
*
* return (
* <>
* <ApiErrorAlert error={error} />
* {// rest of form}
* </>
* );
* ```
*/
export default function ApiErrorAlert({
error,
fallbackMessage = 'An unexpected error occurred. Please try again.'
}: ApiErrorAlertProps): ReactElement | null {
if (error === undefined) {
return null;
}
// Handle FetchBaseQueryError with our standard API format
Eif ('data' in error && isApiErrorData(error.data)) {
return (
<Alert severity="error" sx={{width: '100%', mb: 2}}>
<AlertTitle>{error.data.error}</AlertTitle>
{error.data.message}
</Alert>
);
}
// Handle FetchBaseQueryError with status but no standard format
if ('status' in error) {
// Check for network errors
if (error.status === 'FETCH_ERROR') {
return (
<Alert severity="error" sx={{width: '100%', mb: 2}}>
<AlertTitle>Network Error</AlertTitle>
Unable to connect to the server. Please check your connection.
</Alert>
);
}
// Handle other HTTP errors
return (
<Alert severity="error" sx={{width: '100%', mb: 2}}>
<AlertTitle>Error</AlertTitle>
{fallbackMessage}
</Alert>
);
}
// Handle SerializedError (unexpected errors)
if ('message' in error && typeof error.message === 'string') {
return (
<Alert severity="error" sx={{width: '100%', mb: 2}}>
<AlertTitle>Error</AlertTitle>
{error.message}
</Alert>
);
}
// Fallback for completely unknown error shapes
return (
<Alert severity="error" sx={{width: '100%', mb: 2}}>
<AlertTitle>Error</AlertTitle>
{fallbackMessage}
</Alert>
);
} |