Refactor Clash component to use GameModal for game interactions, remove Hello World page, and implement toast notifications for error handling

This commit is contained in:
averel10
2025-11-22 09:44:36 +01:00
parent 3bd61490f1
commit 197d9e6e22
16 changed files with 976 additions and 1088 deletions

View File

@@ -0,0 +1,76 @@
'use client';
import { useEffect, useState } from 'react';
import { Toast, setToastListener } from '@/app/lib/toast';
export default function ToastContainer() {
const [toasts, setToasts] = useState<Toast[]>([]);
useEffect(() => {
setToastListener((toast: Toast) => {
setToasts((prev) => [...prev, toast]);
if (toast.duration && toast.duration > 0) {
const timer = setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== toast.id));
}, toast.duration);
return () => clearTimeout(timer);
}
});
}, []);
const removeToast = (id: string) => {
setToasts((prev) => prev.filter((t) => t.id !== id));
};
const getToastStyles = (type: string) => {
switch (type) {
case 'success':
return 'bg-green-50 dark:bg-green-900 border-green-200 dark:border-green-700 text-green-800 dark:text-green-200';
case 'error':
return 'bg-red-50 dark:bg-red-900 border-red-200 dark:border-red-700 text-red-800 dark:text-red-200';
case 'warning':
return 'bg-yellow-50 dark:bg-yellow-900 border-yellow-200 dark:border-yellow-700 text-yellow-800 dark:text-yellow-200';
case 'info':
default:
return 'bg-blue-50 dark:bg-blue-900 border-blue-200 dark:border-blue-700 text-blue-800 dark:text-blue-200';
}
};
const getIcon = (type: string) => {
switch (type) {
case 'success':
return '✅';
case 'error':
return '❌';
case 'warning':
return '⚠️';
case 'info':
default:
return '';
}
};
return (
<div className="fixed bottom-4 right-4 z-50 space-y-2">
{toasts.map((toast) => (
<div
key={toast.id}
className={`border rounded-lg p-3 shadow-lg animate-fade-in ${getToastStyles(toast.type)} flex items-start gap-3 max-w-sm`}
>
<span className="text-xl flex-shrink-0">{getIcon(toast.type)}</span>
<div className="flex-1">
<p className="text-sm break-words">{toast.message}</p>
</div>
<button
onClick={() => removeToast(toast.id)}
className="flex-shrink-0 text-lg opacity-60 hover:opacity-100 transition-opacity"
>
</button>
</div>
))}
</div>
);
}