| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- "use client";
- import { useEffect } from "react";
- interface AlertModalProps {
- isOpen: boolean;
- onClose: () => void;
- title?: string;
- message: string;
- type?: "info" | "success" | "warning" | "error";
- confirmText?: string;
- showCancel?: boolean;
- cancelText?: string;
- onConfirm?: () => void;
- onCancel?: () => void;
- }
- export default function AlertModal({
- isOpen,
- onClose,
- title,
- message,
- type = "info",
- confirmText = "확인",
- showCancel = false,
- cancelText = "취소",
- onConfirm,
- onCancel,
- }: AlertModalProps) {
- useEffect(() => {
- const handleEscape = (event: KeyboardEvent) => {
- if (event.key === "Escape") {
- onClose();
- }
- };
- if (isOpen) {
- document.addEventListener("keydown", handleEscape);
- document.body.style.overflow = "hidden";
- }
- return () => {
- document.removeEventListener("keydown", handleEscape);
- document.body.style.overflow = "unset";
- };
- }, [isOpen, onClose]);
- const handleConfirm = () => {
- if (onConfirm) {
- onConfirm();
- } else {
- onClose();
- }
- };
- const handleCancel = () => {
- if (onCancel) {
- onCancel();
- } else {
- onClose();
- }
- };
- const getIcon = () => {
- switch (type) {
- case "success":
- return (
- <div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
- <svg className="h-6 w-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7" />
- </svg>
- </div>
- );
- case "warning":
- return (
- <div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-yellow-100">
- <svg className="h-6 w-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
- />
- </svg>
- </div>
- );
- case "error":
- return (
- <div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
- <svg className="h-6 w-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- d="M6 18L18 6M6 6l12 12"
- />
- </svg>
- </div>
- );
- default:
- return (
- <div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-blue-100">
- <svg className="h-6 w-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
- />
- </svg>
- </div>
- );
- }
- };
- const getButtonColor = () => {
- switch (type) {
- case "success":
- return "bg-green-600 hover:bg-green-700 focus:ring-green-500";
- case "warning":
- return "bg-yellow-600 hover:bg-yellow-700 focus:ring-yellow-500";
- case "error":
- return "bg-red-600 hover:bg-red-700 focus:ring-red-500";
- default:
- return "bg-blue-600 hover:bg-blue-700 focus:ring-blue-500";
- }
- };
- if (!isOpen) return null;
- return (
- <div
- className="fixed inset-0 z-50 overflow-y-auto"
- aria-labelledby="modal-title"
- role="dialog"
- aria-modal="true"
- >
- <div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
- <div className="bg-opacity-75 fixed inset-0 bg-gray-500 transition-opacity" onClick={onClose}></div>
- <span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
- ​
- </span>
- <div className="relative inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle">
- <div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
- <div className="sm:flex sm:items-start">
- <div className="mt-3 w-full text-center sm:mt-0 sm:text-left">
- {getIcon()}
- {title && (
- <h3
- className="mb-2 text-center text-lg leading-6 font-medium text-gray-900"
- id="modal-title"
- >
- {title}
- </h3>
- )}
- <div className="mt-2">
- <p className="text-center text-sm whitespace-pre-line text-gray-500">{message}</p>
- </div>
- </div>
- </div>
- </div>
- <div className="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
- <button
- type="button"
- className={`inline-flex w-full justify-center rounded-md border border-transparent px-4 py-2 text-base font-medium text-white shadow-sm focus:ring-2 focus:ring-offset-2 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm ${getButtonColor()}`}
- onClick={handleConfirm}
- >
- {confirmText}
- </button>
- {showCancel && (
- <button
- type="button"
- className="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
- onClick={handleCancel}
- >
- {cancelText}
- </button>
- )}
- </div>
- </div>
- </div>
- </div>
- );
- }
|