arbret/frontend/app/utils/error-handling.ts
counterweight 3beb23a765
refactor(frontend): improve code quality and maintainability
- Extract API error handling utility (utils/error-handling.ts)
  - Centralize error message extraction logic
  - Add type guards for API errors
  - Replace duplicated error handling across components

- Create reusable Toast component (components/Toast.tsx)
  - Extract toast notification logic from profile page
  - Support auto-dismiss functionality
  - Consistent styling with shared styles

- Extract form validation debouncing hook (hooks/useDebouncedValidation.ts)
  - Reusable debounced validation logic
  - Clean timeout management
  - Used in profile page for form validation

- Consolidate duplicate styles (styles/auth-form.ts)
  - Use shared style tokens instead of duplicating values
  - Reduce code duplication between auth-form and shared styles

- Extract loading state component (components/LoadingState.tsx)
  - Standardize loading UI across pages
  - Replace duplicated loading JSX patterns
  - Used in profile, exchange, and trades pages

- Fix useRequireAuth dependency array
  - Remove unnecessary hasPermission from dependencies
  - Add eslint-disable comment with explanation
  - Improve hook stability and performance

All frontend tests pass. Linting passes.
2025-12-25 19:04:45 +01:00

50 lines
1.6 KiB
TypeScript

import { ApiError } from "../api";
/**
* Extract a user-friendly error message from an API error or generic error.
* Handles ApiError instances with structured data, regular Error instances, and unknown errors.
*
* @param err - The error to extract a message from
* @param fallback - Default message if extraction fails (default: "An error occurred")
* @returns A user-friendly error message string
*/
export function extractApiErrorMessage(
err: unknown,
fallback: string = "An error occurred"
): string {
if (err instanceof ApiError) {
if (err.data && typeof err.data === "object") {
const data = err.data as { detail?: string };
return data.detail || err.message || fallback;
}
return err.message || fallback;
}
if (err instanceof Error) {
return err.message;
}
return fallback;
}
/**
* Type guard to check if an error is an ApiError with structured detail data.
*/
export function isApiErrorWithDetail(
err: unknown
): err is ApiError & { data: { detail?: string } } {
return err instanceof ApiError && err.data !== undefined && typeof err.data === "object";
}
/**
* Extract field errors from a 422 validation error response.
* Returns undefined if the error doesn't contain field errors.
*/
export function extractFieldErrors(
err: unknown
): { detail?: { field_errors?: Record<string, string> } } | undefined {
if (err instanceof ApiError && err.status === 422) {
if (err.data && typeof err.data === "object") {
return err.data as { detail?: { field_errors?: Record<string, string> } };
}
}
return undefined;
}