+
â ī¸
+
+ Something went wrong
+
+
+ An unexpected error occurred. Please refresh the page or try again later.
+
+
+
+
+
+
+ {process.env.NODE_ENV === 'development' && this.state.error && (
+
+
+ Error Details (Development)
+
+
+ {this.state.error.stack}
+
+
+ )}
+
+ );
+ }
+
+ return this.props.children;
+ }
+}
\ No newline at end of file
diff --git a/components/Toast.tsx b/components/Toast.tsx
new file mode 100644
index 0000000..26c64b2
--- /dev/null
+++ b/components/Toast.tsx
@@ -0,0 +1,115 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+export type ToastType = 'success' | 'error' | 'warning' | 'info';
+
+export interface Toast {
+ id: string;
+ type: ToastType;
+ title: string;
+ message?: string;
+ duration?: number;
+}
+
+interface ToastProps {
+ toast: Toast;
+ onRemove: (id: string) => void;
+}
+
+export function ToastComponent({ toast, onRemove }: ToastProps) {
+ const [isVisible, setIsVisible] = useState(false);
+
+ useEffect(() => {
+ // Animate in
+ setTimeout(() => setIsVisible(true), 10);
+
+ // Auto remove after duration
+ const timer = setTimeout(() => {
+ setIsVisible(false);
+ setTimeout(() => onRemove(toast.id), 300);
+ }, toast.duration || 5000);
+
+ return () => clearTimeout(timer);
+ }, [toast.id, toast.duration, onRemove]);
+
+ const getToastStyles = () => {
+ switch (toast.type) {
+ case 'success':
+ return 'bg-green-500/20 border-green-500/40 text-green-300';
+ case 'error':
+ return 'bg-red-500/20 border-red-500/40 text-red-300';
+ case 'warning':
+ return 'bg-yellow-500/20 border-yellow-500/40 text-yellow-300';
+ case 'info':
+ return 'bg-blue-500/20 border-blue-500/40 text-blue-300';
+ default:
+ return 'bg-gray-500/20 border-gray-500/40 text-gray-300';
+ }
+ };
+
+ const getIcon = () => {
+ switch (toast.type) {
+ case 'success':
+ return 'â
';
+ case 'error':
+ return 'â';
+ case 'warning':
+ return 'â ī¸';
+ case 'info':
+ return 'âšī¸';
+ default:
+ return 'đĸ';
+ }
+ };
+
+ return (
+