212 lines
7.4 KiB
TypeScript
212 lines
7.4 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import React, { useState, useEffect } from "react";
|
||
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, 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>
|
||
|
|
);
|
||
|
|
}
|