// admin-settings.js - 관리자 설정 페이지 // 전역 변수 let currentUser = null; let users = []; let filteredUsers = []; let currentEditingUser = null; // DOM 요소 const elements = { // 시간 timeValue: document.getElementById('timeValue'), // 사용자 정보 userName: document.getElementById('userName'), userRole: document.getElementById('userRole'), userInitial: document.getElementById('userInitial'), // 검색 및 필터 userSearch: document.getElementById('userSearch'), filterButtons: document.querySelectorAll('.filter-btn'), // 테이블 usersTableBody: document.getElementById('usersTableBody'), emptyState: document.getElementById('emptyState'), // 버튼 addUserBtn: document.getElementById('addUserBtn'), saveUserBtn: document.getElementById('saveUserBtn'), confirmDeleteBtn: document.getElementById('confirmDeleteBtn'), // 모달 userModal: document.getElementById('userModal'), deleteModal: document.getElementById('deleteModal'), modalTitle: document.getElementById('modalTitle'), // 폼 userForm: document.getElementById('userForm'), userNameInput: document.getElementById('userName'), userIdInput: document.getElementById('userId'), userPasswordInput: document.getElementById('userPassword'), userRoleSelect: document.getElementById('userRole'), userEmailInput: document.getElementById('userEmail'), userPhoneInput: document.getElementById('userPhone'), passwordGroup: document.getElementById('passwordGroup'), // 토스트 toastContainer: document.getElementById('toastContainer') }; // ========== 초기화 ========== // document.addEventListener('DOMContentLoaded', async () => { console.log('🔧 관리자 설정 페이지 초기화 시작'); try { await initializePage(); console.log('✅ 관리자 설정 페이지 초기화 완료'); } catch (error) { console.error('❌ 페이지 초기화 오류:', error); showToast('페이지를 불러오는 중 오류가 발생했습니다.', 'error'); } }); async function initializePage() { // 이벤트 리스너 설정 setupEventListeners(); // 사용자 목록 로드 await loadUsers(); } // ========== 사용자 정보 설정 ========== // function setupUserInfo() { const authData = getAuthData(); if (authData && authData.user) { currentUser = authData.user; // 사용자 이름 설정 if (elements.userName) { elements.userName.textContent = currentUser.name || currentUser.username; } // 사용자 역할 설정 const roleMap = { 'admin': '관리자', 'system': '시스템 관리자', 'leader': '그룹장', 'user': '작업자' }; if (elements.userRole) { elements.userRole.textContent = roleMap[currentUser.role] || '작업자'; } // 아바타 초기값 설정 if (elements.userInitial) { const initial = (currentUser.name || currentUser.username).charAt(0); elements.userInitial.textContent = initial; } console.log('👤 사용자 정보 설정 완료:', currentUser.name); } } function getAuthData() { const token = localStorage.getItem('token'); const user = localStorage.getItem('user'); return { token, user: user ? JSON.parse(user) : null }; } // ========== 시간 업데이트 ========== // function updateCurrentTime() { const now = new Date(); const timeString = now.toLocaleTimeString('ko-KR', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }); if (elements.timeValue) { elements.timeValue.textContent = timeString; } } // ========== 이벤트 리스너 ========== // function setupEventListeners() { // 검색 if (elements.userSearch) { elements.userSearch.addEventListener('input', handleSearch); } // 필터 버튼 elements.filterButtons.forEach(btn => { btn.addEventListener('click', handleFilter); }); // 사용자 추가 버튼 if (elements.addUserBtn) { elements.addUserBtn.addEventListener('click', openAddUserModal); } // 사용자 저장 버튼 if (elements.saveUserBtn) { elements.saveUserBtn.addEventListener('click', saveUser); } // 삭제 확인 버튼 if (elements.confirmDeleteBtn) { elements.confirmDeleteBtn.addEventListener('click', confirmDeleteUser); } // 로그아웃 버튼 const logoutBtn = document.getElementById('logoutBtn'); if (logoutBtn) { logoutBtn.addEventListener('click', handleLogout); } // 프로필 드롭다운 const userProfile = document.getElementById('userProfile'); const profileMenu = document.getElementById('profileMenu'); if (userProfile && profileMenu) { userProfile.addEventListener('click', (e) => { e.stopPropagation(); profileMenu.style.display = profileMenu.style.display === 'block' ? 'none' : 'block'; }); document.addEventListener('click', () => { profileMenu.style.display = 'none'; }); } } // ========== 사용자 관리 ========== // async function loadUsers() { try { console.log('👥 사용자 목록 로딩...'); // 실제 API에서 사용자 데이터 가져오기 const response = await window.apiCall('/users'); users = Array.isArray(response) ? response : (response.data || []); console.log(`✅ 사용자 ${users.length}명 로드 완료`); // 필터링된 사용자 목록 초기화 filteredUsers = [...users]; // 테이블 렌더링 renderUsersTable(); } catch (error) { console.error('❌ 사용자 목록 로딩 오류:', error); showToast('사용자 목록을 불러오는 중 오류가 발생했습니다.', 'error'); users = []; filteredUsers = []; renderUsersTable(); } } function renderUsersTable() { if (!elements.usersTableBody) return; if (filteredUsers.length === 0) { elements.usersTableBody.innerHTML = ''; if (elements.emptyState) { elements.emptyState.style.display = 'block'; } return; } if (elements.emptyState) { elements.emptyState.style.display = 'none'; } elements.usersTableBody.innerHTML = filteredUsers.map(user => `
${(user.name || user.username).charAt(0)}

${user.name || user.username}

${user.email || '이메일 없음'}

${user.username} ${getRoleIcon(user.role)} ${getRoleName(user.role)} ${user.is_active ? '활성' : '비활성'} ${formatDate(user.last_login) || '로그인 기록 없음'}
${user.role !== 'Admin' && user.role !== 'admin' ? ` ` : ''}
`).join(''); } function getRoleIcon(role) { const icons = { admin: '👑', leader: '👨‍💼', user: '👤' }; return icons[role] || '👤'; } function getRoleName(role) { const names = { admin: '관리자', leader: '그룹장', user: '작업자' }; return names[role] || '작업자'; } function formatDate(dateString) { if (!dateString) return null; const date = new Date(dateString); return date.toLocaleDateString('ko-KR', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } // ========== 검색 및 필터링 ========== // function handleSearch(e) { const searchTerm = e.target.value.toLowerCase(); filteredUsers = users.filter(user => { return (user.name && user.name.toLowerCase().includes(searchTerm)) || (user.username && user.username.toLowerCase().includes(searchTerm)) || (user.email && user.email.toLowerCase().includes(searchTerm)); }); renderUsersTable(); } function handleFilter(e) { const filterType = e.target.dataset.filter; // 활성 버튼 변경 elements.filterButtons.forEach(btn => btn.classList.remove('active')); e.target.classList.add('active'); // 필터링 if (filterType === 'all') { filteredUsers = [...users]; } else { filteredUsers = users.filter(user => user.role === filterType); } renderUsersTable(); } // ========== 모달 관리 ========== // function openAddUserModal() { currentEditingUser = null; if (elements.modalTitle) { elements.modalTitle.textContent = '새 사용자 추가'; } // 폼 초기화 if (elements.userForm) { elements.userForm.reset(); } // 비밀번호 필드 표시 if (elements.passwordGroup) { elements.passwordGroup.style.display = 'block'; } if (elements.userPasswordInput) { elements.userPasswordInput.required = true; } if (elements.userModal) { elements.userModal.style.display = 'flex'; } } function editUser(userId) { const user = users.find(u => u.user_id === userId); if (!user) return; currentEditingUser = user; if (elements.modalTitle) { elements.modalTitle.textContent = '사용자 정보 수정'; } // 역할 이름을 HTML select option value로 변환 const roleToValueMap = { 'Admin': 'admin', 'System Admin': 'admin', 'User': 'user', 'Guest': 'user' }; // 폼에 데이터 채우기 if (elements.userNameInput) elements.userNameInput.value = user.name || ''; if (elements.userIdInput) elements.userIdInput.value = user.username || ''; if (elements.userRoleSelect) elements.userRoleSelect.value = roleToValueMap[user.role] || 'user'; if (elements.userEmailInput) elements.userEmailInput.value = user.email || ''; if (elements.userPhoneInput) elements.userPhoneInput.value = user.phone || ''; // 비밀번호 필드 숨기기 (수정 시에는 선택사항) if (elements.passwordGroup) { elements.passwordGroup.style.display = 'none'; } if (elements.userPasswordInput) { elements.userPasswordInput.required = false; } if (elements.userModal) { elements.userModal.style.display = 'flex'; } } function closeUserModal() { if (elements.userModal) { elements.userModal.style.display = 'none'; } currentEditingUser = null; } function deleteUser(userId) { const user = users.find(u => u.user_id === userId); if (!user) return; currentEditingUser = user; if (elements.deleteModal) { elements.deleteModal.style.display = 'flex'; } } function closeDeleteModal() { if (elements.deleteModal) { elements.deleteModal.style.display = 'none'; } currentEditingUser = null; } // ========== 비밀번호 초기화 ========== // async function resetPassword(userId, username) { if (!confirm(`${username} 사용자의 비밀번호를 000000으로 초기화하시겠습니까?`)) { return; } try { const response = await window.apiCall(`/users/${userId}/reset-password`, 'POST'); if (response.success) { showToast(`${username}의 비밀번호가 000000으로 초기화되었습니다.`, 'success'); } else { showToast(response.message || '비밀번호 초기화에 실패했습니다.', 'error'); } } catch (error) { console.error('비밀번호 초기화 오류:', error); showToast('비밀번호 초기화 중 오류가 발생했습니다.', 'error'); } } window.resetPassword = resetPassword; // ========== 사용자 CRUD ========== // async function saveUser() { try { const formData = { name: elements.userNameInput?.value, username: elements.userIdInput?.value, role: elements.userRoleSelect?.value, // HTML select value는 이미 'admin' 또는 'user' email: elements.userEmailInput?.value }; console.log('저장할 데이터:', formData); // 유효성 검사 if (!formData.name || !formData.username || !formData.role) { showToast('필수 항목을 모두 입력해주세요.', 'error'); return; } // 비밀번호 처리 if (!currentEditingUser && elements.userPasswordInput?.value) { formData.password = elements.userPasswordInput.value; } else if (currentEditingUser && elements.userPasswordInput?.value) { formData.password = elements.userPasswordInput.value; } let response; if (currentEditingUser) { // 수정 response = await window.apiCall(`/users/${currentEditingUser.user_id}`, 'PUT', formData); } else { // 생성 response = await window.apiCall('/users', 'POST', formData); } if (response.success || response.user_id) { const action = currentEditingUser ? '수정' : '생성'; showToast(`사용자가 성공적으로 ${action}되었습니다.`, 'success'); closeUserModal(); await loadUsers(); } else { throw new Error(response.message || '사용자 저장에 실패했습니다.'); } } catch (error) { console.error('사용자 저장 오류:', error); showToast(`사용자 저장 중 오류가 발생했습니다: ${error.message}`, 'error'); } } async function confirmDeleteUser() { if (!currentEditingUser) return; try { const response = await window.apiCall(`/users/${currentEditingUser.user_id}`, 'DELETE'); if (response.success) { showToast('사용자가 성공적으로 삭제되었습니다.', 'success'); closeDeleteModal(); await loadUsers(); } else { throw new Error(response.message || '사용자 삭제에 실패했습니다.'); } } catch (error) { console.error('사용자 삭제 오류:', error); showToast(`사용자 삭제 중 오류가 발생했습니다: ${error.message}`, 'error'); } } async function toggleUserStatus(userId) { try { const user = users.find(u => u.user_id === userId); if (!user) return; const newStatus = !user.is_active; const response = await window.apiCall(`/users/${userId}/status`, 'PUT', { is_active: newStatus }); if (response.success) { const action = newStatus ? '활성화' : '비활성화'; showToast(`사용자가 성공적으로 ${action}되었습니다.`, 'success'); await loadUsers(); } else { throw new Error(response.message || '사용자 상태 변경에 실패했습니다.'); } } catch (error) { console.error('사용자 상태 변경 오류:', error); showToast(`사용자 상태 변경 중 오류가 발생했습니다: ${error.message}`, 'error'); } } // ========== 로그아웃 ========== // function handleLogout() { if (confirm('로그아웃하시겠습니까?')) { localStorage.clear(); window.location.href = '/index.html'; } } // ========== 토스트 알림 ========== // function showToast(message, type = 'info', duration = 3000) { if (!elements.toastContainer) return; const toast = document.createElement('div'); toast.className = `toast ${type}`; const iconMap = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' }; toast.innerHTML = `
${iconMap[type] || 'ℹ️'}
${message}
`; elements.toastContainer.appendChild(toast); // 자동 제거 setTimeout(() => { if (toast.parentElement) { toast.remove(); } }, duration); } // ========== 전역 함수 (HTML에서 호출) ========== // window.editUser = editUser; window.deleteUser = deleteUser; window.toggleUserStatus = toggleUserStatus; window.closeUserModal = closeUserModal; window.closeDeleteModal = closeDeleteModal; // ========== 페이지 권한 관리 ========== // let allPages = []; let userPageAccess = []; // 모든 페이지 목록 로드 async function loadAllPages() { try { const response = await apiCall('/pages'); allPages = response.data || response || []; console.log('📄 페이지 목록 로드:', allPages.length, '개'); } catch (error) { console.error('❌ 페이지 목록 로드 오류:', error); allPages = []; } } // 사용자의 페이지 권한 로드 async function loadUserPageAccess(userId) { try { const response = await apiCall(`/users/${userId}/page-access`); userPageAccess = response.data?.pageAccess || []; console.log(`👤 사용자 ${userId} 페이지 권한 로드:`, userPageAccess.length, '개'); } catch (error) { console.error('❌ 사용자 페이지 권한 로드 오류:', error); userPageAccess = []; } } // 페이지 권한 체크박스 렌더링 function renderPageAccessList(userRole) { const pageAccessList = document.getElementById('pageAccessList'); const pageAccessGroup = document.getElementById('pageAccessGroup'); if (!pageAccessList || !pageAccessGroup) return; // Admin 사용자는 권한 설정 불필요 if (userRole === 'admin') { pageAccessGroup.style.display = 'none'; return; } pageAccessGroup.style.display = 'block'; // 카테고리별로 페이지 그룹화 const pagesByCategory = { 'work': [], 'admin': [], 'common': [], 'profile': [] }; allPages.forEach(page => { const category = page.category || 'common'; if (pagesByCategory[category]) { pagesByCategory[category].push(page); } }); const categoryNames = { 'common': '공통', 'work': '작업', 'admin': '관리', 'profile': '프로필' }; // HTML 생성 let html = ''; Object.keys(pagesByCategory).forEach(category => { const pages = pagesByCategory[category]; if (pages.length === 0) return; const catName = categoryNames[category] || category; html += '
'; html += '
' + catName + '
'; pages.forEach(page => { // 프로필과 대시보드는 모든 사용자가 접근 가능하므로 체크박스 비활성화 const isAlwaysAccessible = page.page_key === 'dashboard' || page.page_key.startsWith('profile.'); const isChecked = userPageAccess.find(p => p.page_id === page.id && p.can_access === 1) || isAlwaysAccessible; html += '
'; }); html += '
'; }); pageAccessList.innerHTML = html; } // 페이지 권한 저장 async function savePageAccess(userId, containerId = null) { try { // 특정 컨테이너가 지정되면 그 안에서만 체크박스 선택 const container = containerId ? document.getElementById(containerId) : document; const checkboxes = container.querySelectorAll('.page-access-checkbox:not([disabled])'); // 중복 page_id 제거 (Map 사용) const pageAccessMap = new Map(); checkboxes.forEach(checkbox => { const pageId = parseInt(checkbox.dataset.pageId); pageAccessMap.set(pageId, { page_id: pageId, can_access: checkbox.checked ? 1 : 0 }); }); const pageAccessData = Array.from(pageAccessMap.values()); console.log('📤 페이지 권한 저장:', userId, pageAccessData); await apiCall(`/users/${userId}/page-access`, 'PUT', { pageAccess: pageAccessData }); console.log('✅ 페이지 권한 저장 완료'); } catch (error) { console.error('❌ 페이지 권한 저장 오류:', error); throw error; } } // editUser 함수를 수정하여 페이지 권한 로드 추가 const originalEditUser = window.editUser; window.editUser = async function(userId) { // 페이지 목록이 없으면 로드 if (allPages.length === 0) { await loadAllPages(); } // 원래 editUser 함수 실행 if (originalEditUser) { originalEditUser(userId); } // 사용자의 페이지 권한 로드 await loadUserPageAccess(userId); // 사용자 정보 가져오기 const user = users.find(u => u.user_id === userId); if (!user) return; // 페이지 권한 체크박스 렌더링 const roleToValueMap = { 'Admin': 'admin', 'System Admin': 'admin', 'User': 'user', 'Guest': 'user' }; const userRole = roleToValueMap[user.role] || 'user'; renderPageAccessList(userRole); }; // saveUser 함수를 수정하여 페이지 권한 저장 추가 const originalSaveUser = window.saveUser; window.saveUser = async function() { try { // 원래 saveUser 함수 실행 if (originalSaveUser) { await originalSaveUser(); } // 사용자 편집 시에만 페이지 권한 저장 if (currentEditingUser && currentEditingUser.user_id) { const userRole = document.getElementById('userRole')?.value; // Admin이 아닌 경우에만 페이지 권한 저장 if (userRole !== 'admin') { await savePageAccess(currentEditingUser.user_id); } } } catch (error) { console.error('❌ 저장 오류:', error); throw error; } }; // ========== 페이지 권한 관리 모달 ========== // let currentPageAccessUser = null; // 페이지 권한 관리 모달 열기 async function managePageAccess(userId) { try { // 페이지 목록이 없으면 로드 if (allPages.length === 0) { await loadAllPages(); } // 사용자 정보 가져오기 const user = users.find(u => u.user_id === userId); if (!user) { showToast('사용자를 찾을 수 없습니다.', 'error'); return; } currentPageAccessUser = user; // 사용자의 페이지 권한 로드 await loadUserPageAccess(userId); // 모달 정보 업데이트 const userName = user.name || user.username; document.getElementById('pageAccessModalTitle').textContent = userName + ' - 페이지 권한 관리'; document.getElementById('pageAccessUserName').textContent = userName; document.getElementById('pageAccessUserRole').textContent = getRoleName(user.role); document.getElementById('pageAccessUserAvatar').textContent = userName.charAt(0); // 페이지 권한 체크박스 렌더링 renderPageAccessModalList(); // 모달 표시 document.getElementById('pageAccessModal').style.display = 'flex'; } catch (error) { console.error('❌ 페이지 권한 관리 모달 오류:', error); showToast('페이지 권한 관리를 열 수 없습니다.', 'error'); } } // 페이지 권한 모달 닫기 function closePageAccessModal() { document.getElementById('pageAccessModal').style.display = 'none'; currentPageAccessUser = null; } // 페이지 권한 체크박스 렌더링 (모달용) function renderPageAccessModalList() { const pageAccessList = document.getElementById('pageAccessModalList'); if (!pageAccessList) return; // 카테고리별로 페이지 그룹화 const pagesByCategory = { 'work': [], 'admin': [], 'common': [], 'profile': [] }; allPages.forEach(page => { const category = page.category || 'common'; if (pagesByCategory[category]) { pagesByCategory[category].push(page); } }); const categoryNames = { 'common': '공통', 'work': '작업', 'admin': '관리', 'profile': '프로필' }; // HTML 생성 let html = ''; Object.keys(pagesByCategory).forEach(category => { const pages = pagesByCategory[category]; if (pages.length === 0) return; const catName = categoryNames[category] || category; html += '
'; html += '
' + catName + '
'; pages.forEach(page => { // 프로필과 대시보드는 모든 사용자가 접근 가능 const isAlwaysAccessible = page.page_key === 'dashboard' || page.page_key.startsWith('profile.'); const isChecked = userPageAccess.find(p => p.page_id === page.id && p.can_access === 1) || isAlwaysAccessible; html += '
'; }); html += '
'; }); pageAccessList.innerHTML = html; } // 페이지 권한 저장 (모달용) async function savePageAccessFromModal() { if (!currentPageAccessUser) { showToast('사용자 정보가 없습니다.', 'error'); return; } try { // 모달 컨테이너 지정 await savePageAccess(currentPageAccessUser.user_id, 'pageAccessModalList'); showToast('페이지 권한이 저장되었습니다.', 'success'); // 캐시 삭제 (사용자가 다시 로그인하거나 페이지 새로고침 필요) localStorage.removeItem('userPageAccess'); closePageAccessModal(); } catch (error) { console.error('❌ 페이지 권한 저장 오류:', error); showToast('페이지 권한 저장에 실패했습니다.', 'error'); } } // 전역 함수로 등록 window.managePageAccess = managePageAccess; window.closePageAccessModal = closePageAccessModal; // 저장 버튼 이벤트 리스너 document.addEventListener('DOMContentLoaded', () => { const saveBtn = document.getElementById('savePageAccessBtn'); if (saveBtn) { saveBtn.addEventListener('click', savePageAccessFromModal); } });