inogen/app/components/ui/error-alert.tsx

224 lines
5.7 KiB
TypeScript

import React from "react";
import { cn } from "~/lib/utils";
import { AlertCircle, X, RefreshCw } from "lucide-react";
import { Button } from "./button";
interface ErrorAlertProps {
title?: string;
message: string;
variant?: "error" | "warning" | "info";
dismissible?: boolean;
onDismiss?: () => void;
onRetry?: () => void;
retryLabel?: string;
className?: string;
icon?: React.ReactNode;
actions?: React.ReactNode;
}
export function ErrorAlert({
title,
message,
variant = "error",
dismissible = false,
onDismiss,
onRetry,
retryLabel = "تلاش مجدد",
className,
icon,
actions,
}: ErrorAlertProps) {
const variants = {
error: {
container:
"bg-red-50 border-red-200 dark:bg-red-950/20 dark:border-red-800/30",
icon: "text-red-500 dark:text-red-400",
title: "text-red-800 dark:text-red-200",
message: "text-red-700 dark:text-red-300",
button:
"text-red-700 hover:text-red-800 dark:text-red-300 dark:hover:text-red-200",
},
warning: {
container:
"bg-yellow-50 border-yellow-200 dark:bg-yellow-950/20 dark:border-yellow-800/30",
icon: "text-yellow-500 dark:text-yellow-400",
title: "text-yellow-800 dark:text-yellow-200",
message: "text-yellow-700 dark:text-yellow-300",
button:
"text-yellow-700 hover:text-yellow-800 dark:text-yellow-300 dark:hover:text-yellow-200",
},
info: {
container:
"bg-blue-50 border-blue-200 dark:bg-blue-950/20 dark:border-blue-800/30",
icon: "text-blue-500 dark:text-blue-400",
title: "text-blue-800 dark:text-blue-200",
message: "text-blue-700 dark:text-blue-300",
button:
"text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200",
},
};
const variantStyles = variants[variant];
const defaultIcon = <AlertCircle className="h-5 w-5" />;
return (
<div
className={cn(
"relative rounded-lg border p-4 transition-all duration-200 animate-slide-down",
variantStyles.container,
className,
)}
role="alert"
aria-live="polite"
>
<div className="flex items-start gap-3">
{/* Icon */}
<div className={cn("flex-shrink-0 mt-0.5", variantStyles.icon)}>
{icon || defaultIcon}
</div>
{/* Content */}
<div className="flex-1 min-w-0">
{title && (
<h3
className={cn(
"text-sm font-medium font-persian mb-1",
variantStyles.title,
)}
>
{title}
</h3>
)}
<div
className={cn(
"text-sm font-persian leading-relaxed",
variantStyles.message,
)}
>
{message}
</div>
{/* Actions */}
{(onRetry || actions) && (
<div className="mt-3 flex items-center gap-2">
{onRetry && (
<Button
variant="ghost"
size="sm"
onClick={onRetry}
className={cn(
"h-8 px-3 font-persian text-xs",
variantStyles.button,
)}
>
<RefreshCw className="h-3 w-3 ml-1" />
{retryLabel}
</Button>
)}
{actions}
</div>
)}
</div>
{/* Dismiss Button */}
{dismissible && onDismiss && (
<button
onClick={onDismiss}
className={cn(
"flex-shrink-0 rounded-md p-1.5 transition-colors duration-200 hover:bg-black/5 dark:hover:bg-white/5",
variantStyles.button,
)}
aria-label="بستن پیام"
>
<X className="h-4 w-4" />
</button>
)}
</div>
</div>
);
}
interface InlineErrorProps {
message: string;
className?: string;
}
export function InlineError({ message, className }: InlineErrorProps) {
return (
<div
className={cn(
"flex items-center gap-2 text-sm text-destructive font-persian",
className,
)}
role="alert"
aria-live="polite"
>
<AlertCircle className="h-4 w-4 flex-shrink-0" />
<span>{message}</span>
</div>
);
}
interface FormErrorProps {
title?: string;
errors: string[];
className?: string;
onRetry?: () => void;
retryLabel?: string;
}
export function FormError({
title = "خطا در ارسال فرم",
errors,
className,
onRetry,
retryLabel = "تلاش مجدد",
}: FormErrorProps) {
if (errors.length === 0) return null;
const message =
errors.length === 1
? errors[0]
: errors.map((error, index) => `${error}`).join("\n");
return (
<ErrorAlert
title={title}
message={message}
variant="error"
onRetry={onRetry}
retryLabel={retryLabel}
className={className}
/>
);
}
interface ConnectionErrorProps {
onRetry?: () => void;
className?: string;
}
export function ConnectionError({ onRetry, className }: ConnectionErrorProps) {
return (
<ErrorAlert
title="خطا در اتصال"
message="خطا در برقراری ارتباط با سرور. لطفاً اتصال اینترنت خود را بررسی کنید."
variant="error"
onRetry={onRetry}
retryLabel="تلاش مجدد"
className={className}
/>
);
}
interface ValidationErrorProps {
message: string;
className?: string;
}
export function ValidationError({ message, className }: ValidationErrorProps) {
return (
<ErrorAlert message={message} variant="warning" className={className} />
);
}