ERP-node/frontend/components/admin/UserAuthEditModal.tsx

219 lines
7.4 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { userAPI } from "@/lib/api/user";
import { Shield, ShieldCheck, User, Users, Building2, AlertTriangle } from "lucide-react";
interface UserAuthEditModalProps {
isOpen: boolean;
onClose: () => void;
onSuccess?: () => void;
user: any | null;
}
/**
* 사용자 권한 변경 모달
*
* 권한 레벨만 변경 가능 (최고 관리자 전용)
*/
export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuthEditModalProps) {
const [selectedUserType, setSelectedUserType] = useState<string>("");
const [isLoading, setIsLoading] = useState(false);
const [showConfirmation, setShowConfirmation] = useState(false);
// 모달 열릴 때 현재 권한 설정
useEffect(() => {
if (isOpen && user) {
setSelectedUserType(user.userType || "USER");
setShowConfirmation(false);
}
}, [isOpen, user]);
// 권한 정보
const userTypeOptions = [
{
value: "SUPER_ADMIN",
label: "최고 관리자",
description: "모든 회사 관리, DDL 실행, 회사 생성/삭제 가능",
icon: <ShieldCheck className="h-4 w-4" />,
color: "text-purple-600",
},
{
value: "COMPANY_ADMIN",
label: "회사 관리자",
description: "자기 회사 데이터 및 사용자 관리 가능",
icon: <Building2 className="h-4 w-4" />,
color: "text-blue-600",
},
{
value: "USER",
label: "일반 사용자",
description: "자기 회사 데이터 조회/수정만 가능",
icon: <User className="h-4 w-4" />,
color: "text-gray-600",
},
{
value: "GUEST",
label: "게스트",
description: "제한된 조회 권한",
icon: <Users className="h-4 w-4" />,
color: "text-green-600",
},
{
value: "PARTNER",
label: "협력업체",
description: "협력업체 전용 권한",
icon: <Shield className="h-4 w-4" />,
color: "text-orange-600",
},
];
const selectedOption = userTypeOptions.find((opt) => opt.value === selectedUserType);
// 권한 변경 여부 확인
const isUserTypeChanged = user && selectedUserType !== user.userType;
// 권한 변경 처리
const handleSave = async () => {
if (!user || !isUserTypeChanged) {
onClose();
return;
}
// SUPER_ADMIN 변경 시 확인
if (selectedUserType === "SUPER_ADMIN" && !showConfirmation) {
setShowConfirmation(true);
return;
}
setIsLoading(true);
try {
const response = await userAPI.update({
userId: user.userId,
userName: user.userName,
companyCode: user.companyCode,
deptCode: user.deptCode,
userType: selectedUserType,
});
if (response.success) {
onSuccess?.();
} else {
alert(response.message || "권한 변경에 실패했습니다.");
}
} catch (error) {
console.error("권한 변경 오류:", error);
alert("권한 변경 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
if (!user) return null;
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-[95vw] sm:max-w-[500px]">
<DialogHeader>
<DialogTitle className="text-base sm:text-lg"> </DialogTitle>
</DialogHeader>
<div className="space-y-4">
{/* 사용자 정보 */}
<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"> ID</span>
<span className="font-mono font-medium">{user.userId}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground"></span>
<span className="font-medium">{user.userName}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground"></span>
<span className="font-medium">{user.companyName || user.companyCode}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground"> </span>
<span className="font-medium">
{userTypeOptions.find((opt) => opt.value === user.userType)?.label || user.userType}
</span>
</div>
</div>
</div>
{/* 권한 선택 */}
<div className="space-y-2">
<Label htmlFor="userType" className="text-sm font-medium">
<span className="text-red-500">*</span>
</Label>
<Select value={selectedUserType} onValueChange={setSelectedUserType}>
<SelectTrigger className="h-10">
<SelectValue placeholder="권한 선택" />
</SelectTrigger>
<SelectContent>
{userTypeOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
<div className="flex items-center gap-2">
<span className={option.color}>{option.icon}</span>
<span>{option.label}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
{selectedOption && <p className="text-muted-foreground text-xs">{selectedOption.description}</p>}
</div>
{/* SUPER_ADMIN 경고 */}
{showConfirmation && selectedUserType === "SUPER_ADMIN" && (
<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">
, DDL을 , /
. .
</p>
</div>
</div>
</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
onClick={handleSave}
disabled={isLoading || !isUserTypeChanged}
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
>
{isLoading ? "처리중..." : showConfirmation ? "확인 및 저장" : "저장"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}