All files / varjoliitokauppa/context ToastContext.tsx

68% Statements 17/25
57.14% Branches 4/7
46.15% Functions 6/13
83.33% Lines 15/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67                                  1x   1x 2x   2x 1x 1x     1x         2x       2x   2x         1x                                     1x 1x 1x 1x  
'use client';
 
import React, { createContext, useContext, useState, useCallback, useMemo } from 'react';
import { X, CheckCircle, AlertCircle } from 'lucide-react';
 
type ToastType = 'success' | 'error' | 'info';
 
interface Toast {
  id: string;
  message: string;
  type: ToastType;
}
 
interface ToastContextType {
  addToast: (message: string, type?: ToastType) => void;
}
 
const ToastContext = createContext<ToastContextType | undefined>(undefined);
 
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [toasts, setToasts] = useState<Toast[]>([]);
 
  const addToast = useCallback((message: string, type: ToastType = 'success') => {
    const id = Math.random().toString(36).substr(2, 9);
    setToasts((prev) => [...prev, { id, message, type }]);
 
    // Auto dismiss
    setTimeout(() => {
      setToasts((prev) => prev.filter((t) => t.id !== id));
    }, 3000);
  }, []);
 
  const removeToast = useCallback((id: string) => {
    setToasts((prev) => prev.filter((t) => t.id !== id));
  }, []);
 
  const contextValue = useMemo(() => ({ addToast }), [addToast]);
 
  return (
    <ToastContext.Provider value={contextValue}>
      {children}
      <div className="fixed bottom-6 right-6 z-[100] flex flex-col gap-3 pointer-events-none">
        {toasts.map((toast) => (
          <div
            key={toast.id}
            className={`
              pointer-events-auto flex items-center gap-3 px-5 py-4 rounded-xl shadow-2xl min-w-[300px] animate-slide-up
              ${toast.type === 'success' ? 'bg-black text-white' : 'bg-white text-black border border-gray-200'}
            `}
          >
            {toast.type === 'success' ? <CheckCircle size={20} className="text-green-400" /> : <AlertCircle size={20} />}
            <p className="text-sm font-bold flex-grow">{toast.message}</p>
            <button onClick={() => removeToast(toast.id)} className="opacity-50 hover:opacity-100">
              <X size={16} />
            </button>
          </div>
        ))}
      </div>
    </ToastContext.Provider>
  );
};
 
export const useToast = () => {
  const context = useContext(ToastContext);
  Iif (!context) throw new Error("useToast must be used within ToastProvider");
  return context;
};