'use client'; import * as React from 'react'; import clsx from 'clsx'; import { InformationCircleIcon, CheckCircleIcon, ExclamationTriangleIcon, XCircleIcon, XMarkIcon, } from '@heroicons/react/20/solid'; export type AlertTone = 'info' | 'success' | 'warning' | 'error'; export interface AlertProps { /** Farbschema / Typ des Alerts */ tone?: AlertTone; /** Überschrift (z.B. "Attention needed") */ title?: React.ReactNode; /** Beschreibungstext (als string oder JSX) */ description?: React.ReactNode; /** Bullet-List-Einträge wie im "With list"-Beispiel */ listItems?: React.ReactNode[]; /** Eigene Icon-Komponente (null = Icon komplett ausblenden) */ icon?: React.ReactNode | null; /** Bereich für Buttons / Actions unter dem Text */ actions?: React.ReactNode; /** Inhalt rechts (z.B. ein "Details →"-Link) */ rightContent?: React.ReactNode; /** Linker Accent-Border (statt Outlines im Dark Mode) */ accent?: boolean; /** Wenn gesetzt, wird ein Dismiss-X angezeigt und dieser Handler aufgerufen */ onDismiss?: () => void; className?: string; } const toneConfig: Record< AlertTone, { bg: string; outline: string; accentBorder: string; title: string; text: string; icon: string; dismissBtn: string; } > = { info: { bg: 'bg-blue-50 dark:bg-blue-500/10', outline: 'dark:outline dark:outline-blue-500/20', accentBorder: 'border-l-4 border-blue-400 dark:border-blue-500', title: 'text-blue-800 dark:text-blue-200', text: 'text-blue-700 dark:text-blue-300', icon: 'text-blue-400 dark:text-blue-300', dismissBtn: 'inline-flex rounded-md bg-blue-50 p-1.5 text-blue-500 hover:bg-blue-100 ' + 'focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 focus-visible:ring-offset-blue-50 focus-visible:outline-none ' + 'dark:bg-transparent dark:text-blue-400 dark:hover:bg-blue-500/10 ' + 'dark:focus-visible:ring-blue-500 dark:focus-visible:ring-offset-1 dark:focus-visible:ring-offset-blue-900', }, success: { bg: 'bg-green-50 dark:bg-green-500/10', outline: 'dark:outline dark:outline-green-500/20', accentBorder: 'border-l-4 border-green-400 dark:border-green-500', title: 'text-green-800 dark:text-green-200', text: 'text-green-700 dark:text-green-200/85', icon: 'text-green-400 dark:text-green-300', dismissBtn: 'inline-flex rounded-md bg-green-50 p-1.5 text-green-500 hover:bg-green-100 ' + 'focus-visible:ring-2 focus-visible:ring-green-600 focus-visible:ring-offset-2 focus-visible:ring-offset-green-50 focus-visible:outline-none ' + 'dark:bg-transparent dark:text-green-400 dark:hover:bg-green-500/10 ' + 'dark:focus-visible:ring-green-500 dark:focus-visible:ring-offset-1 dark:focus-visible:ring-offset-green-900', }, warning: { bg: 'bg-yellow-50 dark:bg-yellow-500/10', outline: 'dark:outline dark:outline-yellow-500/15', accentBorder: 'border-l-4 border-yellow-400 dark:border-yellow-500', title: 'text-yellow-800 dark:text-yellow-100', text: 'text-yellow-700 dark:text-yellow-100/80', icon: 'text-yellow-400 dark:text-yellow-300', dismissBtn: 'inline-flex rounded-md bg-yellow-50 p-1.5 text-yellow-500 hover:bg-yellow-100 ' + 'focus-visible:ring-2 focus-visible:ring-yellow-600 focus-visible:ring-offset-2 focus-visible:ring-offset-yellow-50 focus-visible:outline-none ' + 'dark:bg-transparent dark:text-yellow-400 dark:hover:bg-yellow-500/10 ' + 'dark:focus-visible:ring-yellow-500 dark:focus-visible:ring-offset-1 dark:focus-visible:ring-offset-yellow-900', }, error: { bg: 'bg-red-50 dark:bg-red-500/15', outline: 'dark:outline dark:outline-red-500/25', accentBorder: 'border-l-4 border-red-400 dark:border-red-500', title: 'text-red-800 dark:text-red-200', text: 'text-red-700 dark:text-red-200/80', icon: 'text-red-400 dark:text-red-400', dismissBtn: 'inline-flex rounded-md bg-red-50 p-1.5 text-red-500 hover:bg-red-100 ' + 'focus-visible:ring-2 focus-visible:ring-red-600 focus-visible:ring-offset-2 focus-visible:ring-offset-red-50 focus-visible:outline-none ' + 'dark:bg-transparent dark:text-red-400 dark:hover:bg-red-500/10 ' + 'dark:focus-visible:ring-red-500 dark:focus-visible:ring-offset-1 dark:focus-visible:ring-offset-red-900', }, }; function getDefaultIcon(tone: AlertTone) { switch (tone) { case 'info': return ( ); case 'success': return ( ); case 'warning': return ( ); case 'error': return ( ); } } function Alerts({ tone = 'info', title, description, listItems, icon, actions, rightContent, accent = false, onDismiss, className, }: AlertProps) { const cfg = toneConfig[tone]; const hasList = !!listItems && listItems.length > 0; const showIcon = icon !== null; return (
{description}
) : ( description )}