Files
GSB-Construction/resources/js/Components/ConfirmModal.tsx

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>
);
}