import { useState, useEffect, useCallback, useRef } from 'react' import { fetchUsers, fetchRoles, updateUserApi, approveUserApi, rejectUserApi, assignRolesApi, type UserListItem, type RoleWithPermissions, } from '@common/services/authApi' import { getRoleColor, statusLabels } from './adminConstants' // ─── 사용자 관리 패널 ───────────────────────────────────────── function UsersPanel() { const [searchTerm, setSearchTerm] = useState('') const [statusFilter, setStatusFilter] = useState('') const [users, setUsers] = useState([]) const [loading, setLoading] = useState(true) const [allRoles, setAllRoles] = useState([]) const [roleEditUserId, setRoleEditUserId] = useState(null) const [selectedRoleSns, setSelectedRoleSns] = useState([]) const roleDropdownRef = useRef(null) const loadUsers = useCallback(async () => { setLoading(true) try { const data = await fetchUsers(searchTerm || undefined, statusFilter || undefined) setUsers(data) } catch (err) { console.error('사용자 목록 조회 실패:', err) } finally { setLoading(false) } }, [searchTerm, statusFilter]) useEffect(() => { loadUsers() }, [loadUsers]) useEffect(() => { fetchRoles().then(setAllRoles).catch(console.error) }, []) useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (roleDropdownRef.current && !roleDropdownRef.current.contains(e.target as Node)) { setRoleEditUserId(null) } } if (roleEditUserId) { document.addEventListener('mousedown', handleClickOutside) } return () => document.removeEventListener('mousedown', handleClickOutside) }, [roleEditUserId]) const handleUnlock = async (userId: string) => { try { await updateUserApi(userId, { status: 'ACTIVE' }) await loadUsers() } catch (err) { console.error('계정 잠금 해제 실패:', err) } } const handleApprove = async (userId: string) => { try { await approveUserApi(userId) await loadUsers() } catch (err) { console.error('사용자 승인 실패:', err) } } const handleReject = async (userId: string) => { try { await rejectUserApi(userId) await loadUsers() } catch (err) { console.error('사용자 거절 실패:', err) } } const handleDeactivate = async (userId: string) => { try { await updateUserApi(userId, { status: 'INACTIVE' }) await loadUsers() } catch (err) { console.error('사용자 비활성화 실패:', err) } } const handleActivate = async (userId: string) => { try { await updateUserApi(userId, { status: 'ACTIVE' }) await loadUsers() } catch (err) { console.error('사용자 활성화 실패:', err) } } const handleOpenRoleEdit = (user: UserListItem) => { setRoleEditUserId(user.id) setSelectedRoleSns(user.roleSns || []) } const toggleRoleSelection = (roleSn: number) => { setSelectedRoleSns(prev => prev.includes(roleSn) ? prev.filter(s => s !== roleSn) : [...prev, roleSn] ) } const handleSaveRoles = async (userId: string) => { try { await assignRolesApi(userId, selectedRoleSns) await loadUsers() setRoleEditUserId(null) } catch (err) { console.error('역할 할당 실패:', err) } } const formatDate = (dateStr: string | null) => { if (!dateStr) return '-' return new Date(dateStr).toLocaleString('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }) } const pendingCount = users.filter(u => u.status === 'PENDING').length return (

사용자 관리

총 {users.length}명

{pendingCount > 0 && ( 승인대기 {pendingCount}명 )}
setSearchTerm(e.target.value)} className="w-56 px-3 py-2 text-xs bg-bg-2 border border-border rounded-md text-text-1 placeholder-text-3 focus:border-primary-cyan focus:outline-none font-korean" />
{loading ? (
불러오는 중...
) : ( {users.map((user) => { const statusInfo = statusLabels[user.status] || statusLabels.INACTIVE return ( ) })}
이름 계정 소속 역할 인증 상태 최근 로그인 관리
{user.name} {user.account} {user.orgAbbr || '-'}
handleOpenRoleEdit(user)} title="클릭하여 역할 변경" > {user.roles.length > 0 ? user.roles.map((roleCode, idx) => { const color = getRoleColor(roleCode, idx) const roleName = allRoles.find(r => r.code === roleCode)?.name || roleCode return ( {roleName} ) }) : ( 역할 없음 )}
{roleEditUserId === user.id && (
역할 선택
{allRoles.map((role, idx) => { const color = getRoleColor(role.code, idx) return ( ) })}
)}
{user.oauthProvider ? ( Google ) : ( ID/PW )} {statusInfo.label} {formatDate(user.lastLogin)}
{user.status === 'PENDING' && ( <> )} {user.status === 'LOCKED' && ( )} {user.status === 'ACTIVE' && ( )} {(user.status === 'INACTIVE' || user.status === 'REJECTED') && ( )}
)}
) } export default UsersPanel