123 lines
4.9 KiB
TypeScript
123 lines
4.9 KiB
TypeScript
import React from 'react';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
} from '@/Components/ui/dialog';
|
|
import { Button } from '@/Components/ui/button';
|
|
import { AlertTriangle, Info, CheckCircle2, Send, HelpCircle, Loader2 } from 'lucide-react';
|
|
|
|
interface ConfirmModalProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
title?: string;
|
|
message?: string;
|
|
confirmText?: string;
|
|
cancelText?: string;
|
|
variant?: 'default' | 'destructive' | 'warning' | 'info';
|
|
loading?: boolean;
|
|
onConfirm: () => void;
|
|
onCancel?: () => void;
|
|
}
|
|
|
|
export function ConfirmModal({
|
|
open,
|
|
onOpenChange,
|
|
title = 'Are you sure?',
|
|
message = 'Please confirm if you wish to proceed with this action.',
|
|
confirmText = 'Confirm',
|
|
cancelText = 'Cancel',
|
|
variant = 'info',
|
|
loading = false,
|
|
onConfirm,
|
|
onCancel,
|
|
}: ConfirmModalProps) {
|
|
const renderIcon = () => {
|
|
switch (variant) {
|
|
case 'destructive':
|
|
return <AlertTriangle className="h-6 w-6 text-rose-600 shrink-0" />;
|
|
case 'warning':
|
|
return <AlertTriangle className="h-6 w-6 text-amber-500 shrink-0" />;
|
|
case 'info':
|
|
return <Send className="h-5 w-5 text-slate-800 shrink-0" />;
|
|
default:
|
|
return <CheckCircle2 className="h-6 w-6 text-emerald-600 shrink-0" />;
|
|
}
|
|
};
|
|
|
|
const confirmButtonClass = () => {
|
|
switch (variant) {
|
|
case 'destructive':
|
|
return 'bg-rose-600 hover:bg-rose-700 text-white font-medium shadow-sm';
|
|
case 'warning':
|
|
return 'bg-amber-600 hover:bg-amber-700 text-white font-medium shadow-sm';
|
|
case 'info':
|
|
return 'bg-slate-900 hover:bg-slate-800 text-white font-medium shadow-sm';
|
|
default:
|
|
return 'bg-emerald-600 hover:bg-emerald-700 text-white font-medium shadow-sm';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={(val) => { if (!loading) onOpenChange(val); }}>
|
|
<DialogContent
|
|
showCloseButton={!loading}
|
|
className="sm:max-w-md bg-white border border-slate-200/80 shadow-2xl rounded-2xl p-0 overflow-hidden"
|
|
>
|
|
<div className="p-6 flex items-start gap-3.5">
|
|
<div className={`h-10 w-10 rounded-xl flex items-center justify-center shrink-0 mt-0.5 ${
|
|
variant === 'destructive' ? 'bg-rose-50 text-rose-600 border border-rose-150' :
|
|
variant === 'warning' ? 'bg-amber-50 text-amber-600 border border-amber-150' :
|
|
variant === 'info' ? 'bg-slate-100 text-slate-800 border border-slate-200' :
|
|
'bg-emerald-50 text-emerald-600 border border-emerald-150'
|
|
}`}>
|
|
{renderIcon()}
|
|
</div>
|
|
<div className="flex-1 space-y-1">
|
|
<DialogTitle className="text-base font-bold text-slate-900 tracking-tight leading-snug">
|
|
{title}
|
|
</DialogTitle>
|
|
<DialogDescription className="text-xs text-slate-600 leading-relaxed font-normal">
|
|
{message}
|
|
</DialogDescription>
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter className="px-6 py-3.5 bg-slate-50/90 border-t border-slate-100/80 flex flex-row items-center justify-end gap-2.5 rounded-b-2xl">
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
disabled={loading}
|
|
className="border-slate-200 bg-white text-slate-700 hover:bg-slate-100 hover:text-slate-900 font-medium px-4 text-xs h-8.5 rounded-lg shadow-2xs"
|
|
onClick={() => {
|
|
if (onCancel) onCancel();
|
|
onOpenChange(false);
|
|
}}
|
|
>
|
|
{cancelText}
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
size="sm"
|
|
disabled={loading}
|
|
className={`px-4 text-xs h-8.5 rounded-lg transition-all flex items-center justify-center gap-1.5 ${confirmButtonClass()}`}
|
|
onClick={() => {
|
|
onConfirm();
|
|
if (!loading) {
|
|
onOpenChange(false);
|
|
}
|
|
}}
|
|
>
|
|
{loading && <Loader2 className="h-3.5 w-3.5 animate-spin" />}
|
|
{confirmText}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|