/**
* Professional Alert Component with variants and auto-dismiss functionality
*/
import React, { useState, useEffect } from 'react';
import { cn } from '../../utils/cn';
export interface AlertProps {
variant?: 'info' | 'success' | 'warning' | 'error';
title?: string;
children: React.ReactNode;
dismissible?: boolean;
autoDismiss?: boolean;
autoDismissTime?: number;
onDismiss?: () => void;
className?: string;
icon?: React.ReactNode;
}
const Alert: React.FC<AlertProps> = ({
variant = 'info',
title,
children,
dismissible = false,
autoDismiss = false,
autoDismissTime = 5000,
onDismiss,
className,
icon
}) => {
const [visible, setVisible] = useState(true);
const [isExiting, setIsExiting] = useState(false);
const defaultIcons = {
info: (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
</svg>
),
success: (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
),
warning: (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
),
error: (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
)
};
const variantClasses = {
info: {
container: 'bg-blue-50 border-blue-200 text-blue-800',
icon: 'text-blue-600',
title: 'text-blue-900',
button: 'text-blue-600 hover:bg-blue-100'
},
success: {
container: 'bg-green-50 border-green-200 text-green-800',
icon: 'text-green-600',
title: 'text-green-900',
button: 'text-green-600 hover:bg-green-100'
},
warning: {
container: 'bg-yellow-50 border-yellow-200 text-yellow-800',
icon: 'text-yellow-600',
title: 'text-yellow-900',
button: 'text-yellow-600 hover:bg-yellow-100'
},
error: {
container: 'bg-red-50 border-red-200 text-red-800',
icon: 'text-red-600',
title: 'text-red-900',
button: 'text-red-600 hover:bg-red-100'
}
};
useEffect(() => {
if (autoDismiss) {
const timer = setTimeout(() => {
handleDismiss();
}, autoDismissTime);
return () => clearTimeout(timer);
}
}, [autoDismiss, autoDismissTime]);
const handleDismiss = () => {
setIsExiting(true);
setTimeout(() => {
setVisible(false);
onDismiss?.();
}, 200);
};
if (!visible) return null;
const classes = cn(
'p-4',
'rounded-lg',
'border',
'flex',
'items-start',
'space-x-3',
'transition-all',
'duration-200',
'ease-in-out',
isExiting ? 'opacity-0 transform -translate-y-2' : 'opacity-100 transform translate-y-0',
variantClasses[variant].container,
className
);
return (
<div className={classes} role="alert">
<div className={cn('flex-shrink-0', variantClasses[variant].icon)}>
{icon || defaultIcons[variant]}
</div>
<div className="flex-1 min-w-0">
{title && (
<h3 className={cn('text-sm font-medium', variantClasses[variant].title)}>
{title}
</h3>
)}
<div className="text-sm">
{children}
</div>
</div>
{dismissible && (
<button
type="button"
className={cn(
'inline-flex',
'flex-shrink-0',
'p-1.5',
'rounded-md',
'focus:outline-none',
'focus:ring-2',
'focus:ring-offset-2',
variantClasses[variant].button
)}
onClick={handleDismiss}
aria-label="Dismiss"
>
<span className="sr-only">Dismiss</span>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
</svg>
</button>
)}
</div>
);
};
export default Alert;