diff --git a/internal_frontend/app/customers/page.tsx b/internal_frontend/app/customers/page.tsx index ecdd5b0..04c3db6 100644 --- a/internal_frontend/app/customers/page.tsx +++ b/internal_frontend/app/customers/page.tsx @@ -25,6 +25,7 @@ import {NewCustomerModal} from "@/components/customers/modal/NewCustomerModal"; import {Customer} from "@/services/customers/entities/customer"; import Link from "next/link"; import {useErrorHandler} from "@/components/error-boundary"; +import {showError, showInfoToast} from "@/lib/ui/showToast"; export default function CustomersPage() { const [customers, setCustomers] = useState([]); @@ -39,14 +40,17 @@ export default function CustomersPage() { fetch('/api/customers') .then(async (response) => { if (!response.ok) { + showError("Failed to fetch customers data") throw new Error(`Failed to fetch customers: ${response.statusText}`); } return response.json(); }) .then((data) => { + showInfoToast("Customers data loaded") setCustomers(data); }) .catch((error) => { + showError("Failed to fetch customers data (1)") handleError(error); setCustomers([]); }) diff --git a/internal_frontend/components/error-boundary.tsx b/internal_frontend/components/error-boundary.tsx index 063f7c5..931aac5 100644 --- a/internal_frontend/components/error-boundary.tsx +++ b/internal_frontend/components/error-boundary.tsx @@ -1,137 +1,131 @@ "use client"; import React from "react"; -import { showError } from "@/lib/ui/showError"; -import { Button } from "@/components/ui/button"; +import {Button} from "@/components/ui/button"; import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, } from "@/components/ui/dialog"; interface ErrorBoundaryState { - hasError: boolean; - error: Error | null; - errorInfo: React.ErrorInfo | null; + hasError: boolean; + error: Error | null; + errorInfo: React.ErrorInfo | null; } interface ErrorBoundaryProps { - children: React.ReactNode; - fallback?: React.ComponentType<{ error: Error; reset: () => void }>; + children: React.ReactNode; + fallback?: React.ComponentType<{ error: Error; reset: () => void }>; } export class ErrorBoundary extends React.Component< - ErrorBoundaryProps, - ErrorBoundaryState + ErrorBoundaryProps, + ErrorBoundaryState > { - constructor(props: ErrorBoundaryProps) { - super(props); - this.state = { - hasError: false, - error: null, - errorInfo: null, - }; - } - - static getDerivedStateFromError(error: Error): Partial { - return { - hasError: true, - error, - }; - } - - componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { - console.error("ErrorBoundary caught an error:", error, errorInfo); - - this.setState({ - error, - errorInfo, - }); - - // Show error toast - showError(error); - } - - handleReset = () => { - this.setState({ - hasError: false, - error: null, - errorInfo: null, - }); - }; - - render() { - if (this.state.hasError) { - if (this.props.fallback) { - const FallbackComponent = this.props.fallback; - return ( - - ); - } - - return ( - - ); + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { + hasError: false, + error: null, + errorInfo: null, + }; } - return this.props.children; - } + static getDerivedStateFromError(error: Error): Partial { + return { + hasError: true, + error, + }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error("ErrorBoundary caught an error:", error, errorInfo); + + this.setState({ + error, + errorInfo, + }); + } + + handleReset = () => { + this.setState({ + hasError: false, + error: null, + errorInfo: null, + }); + }; + + render() { + if (this.state.hasError) { + if (this.props.fallback) { + const FallbackComponent = this.props.fallback; + return ( + + ); + } + + return ( + + ); + } + + return this.props.children; + } } interface ErrorDialogProps { - error: Error | null; - onReset: () => void; - open: boolean; + error: Error | null; + onReset: () => void; + open: boolean; } -function ErrorDialog({ error, onReset, open }: ErrorDialogProps) { - return ( - {}}> - - - Ein Fehler ist aufgetreten - - Es ist ein unerwarteter Fehler aufgetreten. Bitte versuchen Sie es erneut. - - - - {error && ( -
-

- {error.message} -

-
- )} +function ErrorDialog({error, onReset, open}: ErrorDialogProps) { + return ( + { + }}> + + + Ein Fehler ist aufgetreten + + Es ist ein unerwarteter Fehler aufgetreten. Bitte versuchen Sie es erneut. + + - - - - - - - ); + {error && ( +
+

+ {error.message} +

+
+ )} + + + + + +
+
+ ); } // Hook for handling async errors export function useErrorHandler() { - const handleError = React.useCallback((error: unknown) => { - console.error("Async error caught:", error); - showError(error); - }, []); - - return handleError; -} \ No newline at end of file + return React.useCallback((error: unknown) => { + console.error("Async error caught:", error); + }, []); +} diff --git a/internal_frontend/lib/ui/showError.ts b/internal_frontend/lib/ui/showError.ts deleted file mode 100644 index 2f3f95d..0000000 --- a/internal_frontend/lib/ui/showError.ts +++ /dev/null @@ -1,16 +0,0 @@ -// lib/ui/showError.ts -import {toast} from "sonner"; - -export function showError(error: unknown, fallback = "Ein unbekannter Fehler ist aufgetreten") { - let message: string; - - if (error instanceof Error) { - message = error.message; - } else if (typeof error === "string") { - message = error; - } else { - message = fallback; - } - - toast.error(message); -} diff --git a/internal_frontend/lib/ui/showToast.ts b/internal_frontend/lib/ui/showToast.ts new file mode 100644 index 0000000..b83f615 --- /dev/null +++ b/internal_frontend/lib/ui/showToast.ts @@ -0,0 +1,71 @@ +// lib/ui/showToast.ts +import { toast } from "sonner"; + +// Generic toast function +export function showToast(message: string, type: 'success' | 'error' | 'info' | 'warning' = 'info') { + const options = { + dismissible: true, + closeButton: true, + }; + + switch (type) { + case 'success': + toast.success(message, options); + break; + case 'error': + toast.error(message, options); + break; + case 'info': + toast.info(message, options); + break; + case 'warning': + toast.warning(message, options); + break; + default: + toast(message, options); + } +} + +// Specific toast functions with appropriate error handling +export function showErrorToast(error: unknown, fallback = "Ein unbekannter Fehler ist aufgetreten") { + let message: string; + + if (error instanceof Error) { + message = error.message; + } else if (typeof error === "string") { + message = error; + } else { + message = fallback; + } + + toast.error(message, { + dismissible: true, + closeButton: true, + }); +} + +export function showSuccessToast(message: string) { + toast.success(message, { + dismissible: true, + closeButton: true, + }); +} + +export function showInfoToast(message: string) { + toast.info(message, { + dismissible: true, + closeButton: true, + }); +} + +export function showWarningToast(message: string) { + toast.warning(message, { + dismissible: true, + closeButton: true, + }); +} + +// Backward compatibility - keep the original showError function +export function showError(error: unknown, fallback = "Ein unbekannter Fehler ist aufgetreten") { + showErrorToast(error, fallback); +} \ No newline at end of file