mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 00:38:11 +00:00
131 lines
4.3 KiB
TypeScript
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>
|
|
);
|
|
}
|