pmtool / src /components /asset-request /ErrorDisplay.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
4.88 kB
import React from 'react';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { AlertTriangle, RefreshCw, XCircle, AlertCircle } from 'lucide-react';
interface ErrorDisplayProps {
error: Error | string | null;
title?: string;
onRetry?: () => void;
variant?: 'alert' | 'card' | 'inline';
className?: string;
}
/**
* Reusable error display component with retry action
*/
export const ErrorDisplay: React.FC<ErrorDisplayProps> = ({
error,
title = 'Error',
onRetry,
variant = 'alert',
className = '',
}) => {
if (!error) return null;
const errorMessage = typeof error === 'string' ? error : error.message;
const ErrorContent = () => (
<>
<div className="flex items-start gap-3">
<AlertTriangle className="h-5 w-5 mt-0.5" />
<div className="flex-1">
<AlertTitle className="mb-2">{title}</AlertTitle>
<AlertDescription>
<p className="text-sm mb-3">{errorMessage}</p>
{onRetry && (
<Button
variant="outline"
size="sm"
onClick={onRetry}
className="flex items-center gap-2"
>
<RefreshCw className="h-4 w-4" />
Try Again
</Button>
)}
</AlertDescription>
</div>
</div>
</>
);
if (variant === 'card') {
return (
<Card className={className}>
<CardContent className="pt-6">
<Alert variant="destructive">
<ErrorContent />
</Alert>
</CardContent>
</Card>
);
}
if (variant === 'inline') {
return (
<div className={`bg-destructive/15 border border-destructive/20 rounded-md p-4 ${className}`}>
<div className="flex items-start gap-3">
<XCircle className="h-5 w-5 text-destructive mt-0.5" />
<div className="flex-1">
<p className="text-sm text-destructive font-medium mb-2">{title}</p>
<p className="text-sm text-destructive/90 mb-3">{errorMessage}</p>
{onRetry && (
<Button
variant="outline"
size="sm"
onClick={onRetry}
className="flex items-center gap-2 border-destructive/30 hover:bg-destructive/10"
>
<RefreshCw className="h-4 w-4" />
Try Again
</Button>
)}
</div>
</div>
</div>
);
}
return (
<Alert variant="destructive" className={className}>
<ErrorContent />
</Alert>
);
};
interface FormErrorDisplayProps {
errors: Record<string, string>;
className?: string;
}
/**
* Display form validation errors
*/
export const FormErrorDisplay: React.FC<FormErrorDisplayProps> = ({
errors,
className = '',
}) => {
const errorEntries = Object.entries(errors);
if (errorEntries.length === 0) return null;
return (
<div
className={`bg-destructive/15 border border-destructive/20 rounded-md p-4 ${className}`}
role="alert"
aria-live="polite"
>
<div className="flex items-start gap-3">
<AlertCircle className="h-5 w-5 text-destructive mt-0.5 flex-shrink-0" />
<div className="flex-1">
<p className="text-sm text-destructive font-medium mb-2">
Please fix the following errors:
</p>
<ul className="list-disc list-inside space-y-1">
{errorEntries.map(([field, message]) => (
<li key={field} className="text-sm text-destructive/90">
{message}
</li>
))}
</ul>
</div>
</div>
</div>
);
};
interface EmptyStateProps {
icon?: React.ReactNode;
title: string;
description?: string;
action?: {
label: string;
onClick: () => void;
};
className?: string;
}
/**
* Empty state display component
*/
export const EmptyState: React.FC<EmptyStateProps> = ({
icon,
title,
description,
action,
className = '',
}) => {
return (
<div className={`flex flex-col items-center justify-center py-12 text-center ${className}`}>
{icon && <div className="mb-4 text-muted-foreground">{icon}</div>}
<h3 className="text-lg font-semibold mb-2">{title}</h3>
{description && <p className="text-sm text-muted-foreground mb-4 max-w-md">{description}</p>}
{action && (
<Button onClick={action.onClick} variant="outline">
{action.label}
</Button>
)}
</div>
);
};
export default ErrorDisplay;