From f01603e0cf80eef5c41d757de66ea081380db62a Mon Sep 17 00:00:00 2001 From: Emanuele <106186915+OneStepAt4time@users.noreply.github.com> Date: Tue, 14 Apr 2026 18:41:38 +0200 Subject: [PATCH] feat(dashboard): toast notification improvements - Type icons (check circle, alert triangle, info, alert circle) - Auto-dismiss progress bar indicator - Clear all button when multiple toasts shown - Accessible with role=alert and aria-labels Closes #1794 --- dashboard/src/components/ToastContainer.tsx | 46 ++++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/dashboard/src/components/ToastContainer.tsx b/dashboard/src/components/ToastContainer.tsx index dc91ca45..013b6e8b 100644 --- a/dashboard/src/components/ToastContainer.tsx +++ b/dashboard/src/components/ToastContainer.tsx @@ -2,7 +2,8 @@ * components/ToastContainer.tsx — Global toast notification renderer. */ -import { X } from 'lucide-react'; +import { useEffect, useState } from 'react'; +import { X, CheckCircle, AlertTriangle, Info, AlertCircle, Trash2 } from 'lucide-react'; import { useToastStore } from '../store/useToastStore'; import type { ToastType } from '../store/useToastStore'; @@ -13,14 +14,42 @@ const TYPE_STYLES: Record = { warning: 'border-yellow-500/50 bg-yellow-950/80 text-yellow-200', }; +const TYPE_ICONS: Record = { + error: AlertCircle, + success: CheckCircle, + info: Info, + warning: AlertTriangle, +}; + +const AUTO_DISMISS_MS = 4000; + function ToastItem({ id, type, title, description }: { id: string; type: ToastType; title: string; description?: string }) { const removeToast = useToastStore((s) => s.removeToast); + const [progress, setProgress] = useState(100); + const Icon = TYPE_ICONS[type]; + + useEffect(() => { + const start = Date.now(); + const interval = setInterval(() => { + const elapsed = Date.now() - start; + const remaining = Math.max(0, 100 - (elapsed / AUTO_DISMISS_MS) * 100); + setProgress(remaining); + if (remaining <= 0) clearInterval(interval); + }, 50); + return () => clearInterval(interval); + }, [id]); return (
+ {/* Progress bar */} +
+

{title}

{description &&

{description}

} @@ -38,11 +67,24 @@ function ToastItem({ id, type, title, description }: { id: string; type: ToastTy export default function ToastContainer() { const toasts = useToastStore((s) => s.toasts); + const removeToast = useToastStore((s) => s.removeToast); if (toasts.length === 0) return null; return (
+ {toasts.length > 1 && ( +
+ +
+ )} {toasts.map((t) => (