2025-10-27 16:40:59 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React, { useState, useCallback } from "react";
|
2025-11-05 16:36:32 +09:00
|
|
|
import {
|
|
|
|
|
ResizableDialog,
|
|
|
|
|
ResizableDialogContent,
|
|
|
|
|
ResizableDialogHeader,
|
|
|
|
|
ResizableDialogTitle,
|
|
|
|
|
ResizableDialogDescription,
|
|
|
|
|
ResizableDialogFooter,
|
|
|
|
|
} from "@/components/ui/resizable-dialog";
|
2025-10-27 16:40:59 +09:00
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { roleAPI, RoleGroup } from "@/lib/api/role";
|
|
|
|
|
import { AlertTriangle } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
interface RoleDeleteModalProps {
|
|
|
|
|
isOpen: boolean;
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
onSuccess?: () => void;
|
|
|
|
|
role: RoleGroup | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 권한 그룹 삭제 확인 모달
|
|
|
|
|
*
|
|
|
|
|
* 기능:
|
|
|
|
|
* - 권한 그룹 삭제 확인
|
|
|
|
|
* - CASCADE 삭제 경고 (멤버, 메뉴 권한)
|
|
|
|
|
*
|
|
|
|
|
* shadcn/ui 표준 확인 모달 디자인
|
|
|
|
|
*/
|
|
|
|
|
export function RoleDeleteModal({ isOpen, onClose, onSuccess, role }: RoleDeleteModalProps) {
|
|
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
|
const [showAlert, setShowAlert] = useState(false);
|
|
|
|
|
const [alertMessage, setAlertMessage] = useState("");
|
|
|
|
|
const [alertType, setAlertType] = useState<"success" | "error">("error");
|
|
|
|
|
|
|
|
|
|
// 알림 표시
|
|
|
|
|
const displayAlert = useCallback((message: string, type: "success" | "error") => {
|
|
|
|
|
setAlertMessage(message);
|
|
|
|
|
setAlertType(type);
|
|
|
|
|
setShowAlert(true);
|
|
|
|
|
setTimeout(() => setShowAlert(false), 3000);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// 삭제 핸들러
|
|
|
|
|
const handleDelete = useCallback(async () => {
|
|
|
|
|
if (!role) return;
|
|
|
|
|
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
const response = await roleAPI.delete(role.objid);
|
|
|
|
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
displayAlert("권한 그룹이 삭제되었습니다.", "success");
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
onClose();
|
|
|
|
|
onSuccess?.();
|
|
|
|
|
}, 1500);
|
|
|
|
|
} else {
|
|
|
|
|
displayAlert(response.message || "삭제에 실패했습니다.", "error");
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("권한 그룹 삭제 오류:", error);
|
|
|
|
|
displayAlert("권한 그룹 삭제 중 오류가 발생했습니다.", "error");
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, [role, onClose, onSuccess, displayAlert]);
|
|
|
|
|
|
|
|
|
|
if (!role) return null;
|
|
|
|
|
|
|
|
|
|
return (
|
2025-11-05 16:36:32 +09:00
|
|
|
<ResizableDialog open={isOpen} onOpenChange={onClose}>
|
|
|
|
|
<ResizableDialogContent className="max-w-[95vw] sm:max-w-[500px]">
|
|
|
|
|
<ResizableDialogHeader>
|
|
|
|
|
<ResizableDialogTitle className="text-base sm:text-lg">권한 그룹 삭제</ResizableDialogTitle>
|
|
|
|
|
</ResizableDialogHeader>
|
2025-10-27 16:40:59 +09:00
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
{/* 경고 메시지 */}
|
|
|
|
|
<div className="rounded-lg border border-orange-300 bg-orange-50 p-4">
|
|
|
|
|
<div className="flex items-start gap-3">
|
|
|
|
|
<AlertTriangle className="mt-0.5 h-5 w-5 flex-shrink-0 text-orange-600" />
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<p className="text-sm font-semibold text-orange-900">정말로 삭제하시겠습니까?</p>
|
|
|
|
|
<p className="text-xs text-orange-800">
|
|
|
|
|
이 작업은 되돌릴 수 없습니다. 권한 그룹을 삭제하면 다음 데이터도 함께 삭제됩니다:
|
|
|
|
|
</p>
|
|
|
|
|
<ul className="list-inside list-disc space-y-1 text-xs text-orange-800">
|
|
|
|
|
<li>연결된 모든 멤버 ({role.memberCount || 0}명)</li>
|
|
|
|
|
<li>설정된 모든 메뉴 권한 ({role.menuCount || 0}개)</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 삭제할 권한 그룹 정보 */}
|
|
|
|
|
<div className="bg-muted/50 rounded-lg border p-4">
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<div className="flex justify-between text-sm">
|
|
|
|
|
<span className="text-muted-foreground">권한 그룹명</span>
|
|
|
|
|
<span className="font-medium">{role.authName}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex justify-between text-sm">
|
|
|
|
|
<span className="text-muted-foreground">권한 코드</span>
|
|
|
|
|
<span className="font-mono font-medium">{role.authCode}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex justify-between text-sm">
|
|
|
|
|
<span className="text-muted-foreground">회사</span>
|
|
|
|
|
<span className="font-medium">{role.companyCode}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{role.memberNames && (
|
|
|
|
|
<div className="border-t pt-2">
|
|
|
|
|
<span className="text-muted-foreground text-xs">멤버:</span>
|
|
|
|
|
<p className="mt-1 text-xs">{role.memberNames}</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 알림 메시지 */}
|
|
|
|
|
{showAlert && (
|
|
|
|
|
<div
|
|
|
|
|
className={`rounded-lg border p-3 text-sm ${
|
|
|
|
|
alertType === "success"
|
|
|
|
|
? "border-green-300 bg-green-50 text-green-800"
|
|
|
|
|
: "border-destructive/50 bg-destructive/10 text-destructive"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{alertMessage}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<DialogFooter className="gap-2 sm:gap-0">
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
onClick={onClose}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
|
|
|
|
|
>
|
|
|
|
|
취소
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant="destructive"
|
|
|
|
|
onClick={handleDelete}
|
|
|
|
|
disabled={isLoading}
|
|
|
|
|
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
|
|
|
|
|
>
|
|
|
|
|
{isLoading ? "삭제중..." : "삭제"}
|
|
|
|
|
</Button>
|
2025-11-05 16:36:32 +09:00
|
|
|
</ResizableDialogFooter>
|
|
|
|
|
</ResizableDialogContent>
|
|
|
|
|
</ResizableDialog>
|
2025-10-27 16:40:59 +09:00
|
|
|
);
|
|
|
|
|
}
|