diff --git a/frontend/app/context/ToastContext.tsx b/frontend/app/context/ToastContext.tsx index 5c4f030..10953a4 100644 --- a/frontend/app/context/ToastContext.tsx +++ b/frontend/app/context/ToastContext.tsx @@ -1,6 +1,12 @@ "use client"; -import { createContext, useContext, useState, useCallback, ReactNode } from "react"; +import { + createContext, + useContext, + useState, + useCallback, + ReactNode, +} from "react"; type ToastType = "success" | "error" | "info"; @@ -11,30 +17,83 @@ interface Toast { } interface ToastContextValue { - showToast: (message: string, type?: ToastType) => void; + showToast: ( + message: string, + type?: ToastType + ) => void; } -const ToastContext = createContext({ - showToast: () => {}, -}); +const ToastContext = + createContext({ + showToast: () => {}, + }); + const MAX_TOASTS = 4; + +function createToast( + message: string, + type: ToastType +): Toast { + return { + id: Date.now(), + message, + type, + }; +} + +function scheduleToastRemoval( + id: number, + removeToast: (id: number) => void +) { + return window.setTimeout(() => { + removeToast(id); + }, 4000); +} + export function useToast() { return useContext(ToastContext); } -export function ToastProvider({ children }: { children: ReactNode }) { - const [toasts, setToasts] = useState([]); +export function ToastProvider({ + children, +}: { + children: ReactNode; +}) { + const [toasts, setToasts] = + useState([]); + + const removeToast = useCallback( + (id: number) => { + setToasts((prev) => + prev.filter( + (toast) => toast.id !== id + ) + ); + }, + [] + ); const showToast = useCallback( - (message: string, type: ToastType = "info") => { - const id = Date.now(); + ( + message: string, + type: ToastType = "info" + ) => { + const toast = createToast( + message, + type + ); + + const { id } = toast; setToasts((prev) => { - const duplicateExists = prev.some( - (toast) => - toast.message === message && - toast.type === type - ); + const duplicateExists = + prev.some( + (existingToast) => + existingToast.message === + message && + existingToast.type === + type + ); if (duplicateExists) { return prev; @@ -42,46 +101,64 @@ export function ToastProvider({ children }: { children: ReactNode }) { const updated = [ ...prev, - { id, message, type }, + toast, ]; - if (updated.length > MAX_TOASTS) { + if ( + updated.length > MAX_TOASTS + ) { updated.shift(); } return updated; }); - setTimeout(() => { - setToasts((prev) => - prev.filter((t) => t.id !== id) - ); - }, 4000); + scheduleToastRemoval( + id, + removeToast + ); }, - [] + [removeToast] ); - const removeToast = (id: number) => { - setToasts((prev) => prev.filter((t) => t.id !== id)); - }; - return ( - + {children} - {/* Toast container — fixed top-right */} +
{toasts.map((toast) => (
- {toast.message} + + {toast.message} + +