src
This commit is contained in:
parent
13e2d46d6f
commit
afe5879b62
3 changed files with 129 additions and 1 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -31,7 +31,6 @@ develop-eggs/
|
||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
lib/
|
|
||||||
lib64/
|
lib64/
|
||||||
parts/
|
parts/
|
||||||
sdist/
|
sdist/
|
||||||
|
|
|
||||||
123
admin/src/lib/error-utils.ts
Normal file
123
admin/src/lib/error-utils.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
import type { AxiosError } from 'axios'
|
||||||
|
|
||||||
|
export interface ApiErrorResponse {
|
||||||
|
error?: string
|
||||||
|
message?: string
|
||||||
|
details?: string
|
||||||
|
field?: string
|
||||||
|
code?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats an API error into a user-friendly message
|
||||||
|
*/
|
||||||
|
export function formatApiError(error: unknown): string {
|
||||||
|
const axiosError = error as AxiosError<ApiErrorResponse>
|
||||||
|
|
||||||
|
// Network error (no response from server)
|
||||||
|
if (!axiosError.response) {
|
||||||
|
if (axiosError.code === 'ECONNABORTED') {
|
||||||
|
return 'Request timeout: The server took too long to respond. Please check your connection and try again.'
|
||||||
|
}
|
||||||
|
if (axiosError.message?.includes('Network Error')) {
|
||||||
|
return 'Network error: Unable to connect to the server. Please check your internet connection.'
|
||||||
|
}
|
||||||
|
return 'Connection error: Unable to reach the server. Please check your connection and try again.'
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = axiosError.response.status
|
||||||
|
const data = axiosError.response.data
|
||||||
|
|
||||||
|
// Get error message from response
|
||||||
|
const message = data?.message || data?.error || 'An unknown error occurred'
|
||||||
|
|
||||||
|
// Add more context based on status code
|
||||||
|
switch (status) {
|
||||||
|
case 400:
|
||||||
|
if (data?.field) {
|
||||||
|
return `Validation error: ${message} (field: ${data.field})`
|
||||||
|
}
|
||||||
|
if (data?.details) {
|
||||||
|
return `Bad request: ${message}. ${data.details}`
|
||||||
|
}
|
||||||
|
return `Invalid request: ${message}`
|
||||||
|
|
||||||
|
case 401:
|
||||||
|
return 'Authentication required: Please log in again.'
|
||||||
|
|
||||||
|
case 403:
|
||||||
|
return 'Access denied: You do not have permission to perform this action.'
|
||||||
|
|
||||||
|
case 404:
|
||||||
|
// Make 404 errors more specific based on the endpoint
|
||||||
|
{
|
||||||
|
const url = axiosError.config?.url || ''
|
||||||
|
if (url.includes('/cards/')) {
|
||||||
|
return 'Card not found: The requested card does not exist or has been deleted.'
|
||||||
|
}
|
||||||
|
if (url.includes('/packs/')) {
|
||||||
|
return 'Pack not found: The requested pack does not exist or has been deleted.'
|
||||||
|
}
|
||||||
|
if (url.includes('/users/')) {
|
||||||
|
return 'User not found: The requested user does not exist.'
|
||||||
|
}
|
||||||
|
return `Resource not found: ${message}`
|
||||||
|
}
|
||||||
|
|
||||||
|
case 409:
|
||||||
|
return `Conflict: ${message}. The resource may already exist or be in use.`
|
||||||
|
|
||||||
|
case 422:
|
||||||
|
if (data?.details) {
|
||||||
|
return `Validation failed: ${message}. ${data.details}`
|
||||||
|
}
|
||||||
|
return `Validation error: ${message}`
|
||||||
|
|
||||||
|
case 429:
|
||||||
|
return 'Too many requests: Please wait a moment and try again.'
|
||||||
|
|
||||||
|
case 500:
|
||||||
|
return `Server error: ${message}. Please try again later or contact support if the problem persists.`
|
||||||
|
|
||||||
|
case 502:
|
||||||
|
return 'Bad gateway: The server is temporarily unavailable. Please try again later.'
|
||||||
|
|
||||||
|
case 503:
|
||||||
|
return 'Service unavailable: The server is temporarily down for maintenance. Please try again later.'
|
||||||
|
|
||||||
|
case 504:
|
||||||
|
return 'Gateway timeout: The server took too long to respond. Please try again.'
|
||||||
|
|
||||||
|
default:
|
||||||
|
return `${message} (Error ${status})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a detailed error message with additional context for specific operations
|
||||||
|
*/
|
||||||
|
export function getDetailedErrorMessage(
|
||||||
|
error: unknown,
|
||||||
|
operation: string,
|
||||||
|
resource?: string
|
||||||
|
): string {
|
||||||
|
const baseMessage = formatApiError(error)
|
||||||
|
const axiosError = error as AxiosError<ApiErrorResponse>
|
||||||
|
|
||||||
|
// Add operation context
|
||||||
|
let contextMessage = baseMessage
|
||||||
|
|
||||||
|
if (operation && resource) {
|
||||||
|
contextMessage = `Failed to ${operation} ${resource}: ${baseMessage}`
|
||||||
|
} else if (operation) {
|
||||||
|
contextMessage = `Failed to ${operation}: ${baseMessage}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add field-specific errors if available
|
||||||
|
if (axiosError.response?.data?.field) {
|
||||||
|
const field = axiosError.response.data.field
|
||||||
|
contextMessage += ` (Field: ${field})`
|
||||||
|
}
|
||||||
|
|
||||||
|
return contextMessage
|
||||||
|
}
|
||||||
6
admin/src/lib/utils.ts
Normal file
6
admin/src/lib/utils.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { type ClassValue, clsx } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue