Files
edr-platform/apps/edr-passenger-web/backoffice/src/components/ui/ConfirmDialog.tsx

131 lines
4.3 KiB
TypeScript

'use client';
import { useEffect } from 'react';
import { AlertTriangle, AlertCircle, Info, Loader2, X } from 'lucide-react';
import { cn } from '@/lib/utils';
import ActionButton from './ActionButton';
interface ConfirmDialogProps {
isOpen: boolean;
onClose: () => void;
onConfirm: () => void;
title: string;
message: string;
confirmText?: string;
cancelText?: string;
isLoading?: boolean;
isDanger?: boolean;
warning?: string;
error?: string;
}
export default function ConfirmDialog({
isOpen,
onClose,
onConfirm,
title,
message,
confirmText = 'Confirm',
cancelText = 'Cancel',
isLoading = false,
isDanger = false,
warning,
error,
}: ConfirmDialogProps) {
useEffect(() => {
if (!isOpen) return;
const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [isOpen, onClose]);
useEffect(() => {
document.body.style.overflow = isOpen ? 'hidden' : 'unset';
return () => { document.body.style.overflow = 'unset'; };
}, [isOpen]);
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/60 backdrop-blur-sm"
onClick={!isLoading ? onClose : undefined}
/>
{/* Panel */}
<div className={cn(
'relative w-full max-w-md z-10 rounded-2xl shadow-2xl border border-border/50 bg-background animate-fade-up overflow-hidden',
)}>
{/* Top accent */}
<div className={cn(
'h-1 w-full',
isDanger
? 'bg-gradient-to-r from-red-500 to-red-400'
: 'bg-gradient-to-r from-[rgb(20,113,76)] to-emerald-400',
)} />
{/* Header */}
<div className="flex items-start justify-between px-6 pt-5 pb-4">
<div className="flex items-center gap-3">
<div className={cn(
'flex items-center justify-center w-10 h-10 rounded-full shrink-0',
isDanger
? 'bg-red-100 dark:bg-red-900/30'
: 'bg-emerald-100 dark:bg-emerald-900/30',
)}>
{isDanger
? <AlertTriangle className="w-5 h-5 text-red-600 dark:text-red-400" />
: <Info className="w-5 h-5 text-emerald-600 dark:text-emerald-400" />
}
</div>
<h2 className="text-base font-semibold text-foreground">{title}</h2>
</div>
{!isLoading && (
<button
onClick={onClose}
className="flex items-center justify-center w-7 h-7 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors ml-2 shrink-0"
>
<X className="w-4 h-4" />
</button>
)}
</div>
{/* Body */}
<div className="px-6 pb-2 space-y-3">
<p className="text-sm text-muted-foreground leading-relaxed">{message}</p>
{warning && (
<div className="flex gap-3 rounded-xl bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 px-4 py-3">
<AlertTriangle className="h-4 w-4 text-amber-600 dark:text-amber-400 shrink-0 mt-0.5" />
<p className="text-xs text-amber-800 dark:text-amber-300 leading-relaxed">{warning}</p>
</div>
)}
{error && (
<div className="flex gap-3 rounded-xl bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 px-4 py-3">
<AlertCircle className="h-4 w-4 text-red-600 dark:text-red-400 shrink-0 mt-0.5" />
<p className="text-xs text-red-800 dark:text-red-300 leading-relaxed">{error}</p>
</div>
)}
</div>
{/* Footer */}
<div className="flex justify-end gap-2 px-6 py-4 border-t border-border/60 mt-3">
<ActionButton variant="secondary" onClick={onClose} disabled={isLoading}>
{cancelText}
</ActionButton>
<ActionButton
variant={isDanger ? 'danger' : 'primary'}
onClick={onConfirm}
loading={isLoading}
>
{confirmText}
</ActionButton>
</div>
</div>
</div>
);
}