- 실시간 작업장 현황을 지도로 시각화 - 작업장 관리 페이지에서 정의한 구역 정보 활용 - TBM 작업자 및 방문자 현황 표시 주요 변경사항: - dashboard.html: 작업장 현황 섹션 추가 (기존 작업 현황 테이블 제거) - workplace-status.js: 지도 렌더링 및 데이터 통합 로직 구현 - modern-dashboard.js: 삭제된 DOM 요소 조건부 체크 추가 시각화 방식: - 인원 없음: 회색 테두리 + 작업장 이름 - 내부 작업자: 파란색 영역 + 인원 수 - 외부 방문자: 보라색 영역 + 인원 수 - 둘 다: 초록색 영역 + 총 인원 수 기술 구현: - Canvas API 기반 사각형 영역 렌더링 - map-regions API를 통한 데이터 일관성 보장 - 클릭 이벤트로 상세 정보 모달 표시 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
872 lines
25 KiB
JavaScript
872 lines
25 KiB
JavaScript
// 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 => `
|
||
<tr>
|
||
<td>
|
||
<div class="user-info">
|
||
<div class="user-avatar-small">${(user.name || user.username).charAt(0)}</div>
|
||
<div class="user-details">
|
||
<h4>${user.name || user.username}</h4>
|
||
<p>${user.email || '이메일 없음'}</p>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td><strong>${user.username}</strong></td>
|
||
<td>
|
||
<span class="role-badge ${user.role}">
|
||
${getRoleIcon(user.role)} ${getRoleName(user.role)}
|
||
</span>
|
||
</td>
|
||
<td>
|
||
<span class="status-badge ${user.is_active ? 'active' : 'inactive'}">
|
||
${user.is_active ? '활성' : '비활성'}
|
||
</span>
|
||
</td>
|
||
<td>${formatDate(user.last_login) || '로그인 기록 없음'}</td>
|
||
<td>
|
||
<div class="action-buttons">
|
||
<button class="action-btn edit" onclick="editUser(${user.user_id})">
|
||
수정
|
||
</button>
|
||
${user.role !== 'Admin' && user.role !== 'admin' ? `
|
||
<button class="action-btn permissions" onclick="managePageAccess(${user.user_id})">
|
||
권한
|
||
</button>
|
||
` : ''}
|
||
<button class="action-btn toggle" onclick="toggleUserStatus(${user.user_id})">
|
||
${user.is_active ? '비활성화' : '활성화'}
|
||
</button>
|
||
<button class="action-btn delete" onclick="deleteUser(${user.user_id})">
|
||
삭제
|
||
</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
`).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;
|
||
}
|
||
|
||
// ========== 사용자 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,
|
||
phone: elements.userPhoneInput?.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 = `
|
||
<div class="toast-icon">${iconMap[type] || 'ℹ️'}</div>
|
||
<div class="toast-message">${message}</div>
|
||
<button class="toast-close" onclick="this.parentElement.remove()">×</button>
|
||
`;
|
||
|
||
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 += '<div class="page-access-category">';
|
||
html += '<div class="page-access-category-title">' + catName + '</div>';
|
||
|
||
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 += '<div class="page-access-item"><label>';
|
||
html += '<input type="checkbox" class="page-access-checkbox" ';
|
||
html += 'data-page-id="' + page.id + '" ';
|
||
html += 'data-page-key="' + page.page_key + '" ';
|
||
html += (isChecked ? 'checked ' : '');
|
||
html += (isAlwaysAccessible ? 'disabled ' : '');
|
||
html += '>';
|
||
html += '<span class="page-name">' + page.page_name + '</span>';
|
||
html += '</label></div>';
|
||
});
|
||
|
||
html += '</div>';
|
||
});
|
||
|
||
pageAccessList.innerHTML = html;
|
||
}
|
||
|
||
// 페이지 권한 저장
|
||
async function savePageAccess(userId) {
|
||
try {
|
||
const checkboxes = document.querySelectorAll('.page-access-checkbox:not([disabled])');
|
||
const pageAccessData = [];
|
||
|
||
checkboxes.forEach(checkbox => {
|
||
pageAccessData.push({
|
||
page_id: parseInt(checkbox.dataset.pageId),
|
||
can_access: checkbox.checked ? 1 : 0
|
||
});
|
||
});
|
||
|
||
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 += '<div class="page-access-category">';
|
||
html += '<div class="page-access-category-title">' + catName + '</div>';
|
||
|
||
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 += '<div class="page-access-item"><label>';
|
||
html += '<input type="checkbox" class="page-access-checkbox" ';
|
||
html += 'data-page-id="' + page.id + '" ';
|
||
html += 'data-page-key="' + page.page_key + '" ';
|
||
html += (isChecked ? 'checked ' : '');
|
||
html += (isAlwaysAccessible ? 'disabled ' : '');
|
||
html += '>';
|
||
html += '<span class="page-name">' + page.page_name + '</span>';
|
||
html += '</label></div>';
|
||
});
|
||
|
||
html += '</div>';
|
||
});
|
||
|
||
pageAccessList.innerHTML = html;
|
||
}
|
||
|
||
// 페이지 권한 저장 (모달용)
|
||
async function savePageAccessFromModal() {
|
||
if (!currentPageAccessUser) {
|
||
showToast('사용자 정보가 없습니다.', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await savePageAccess(currentPageAccessUser.user_id);
|
||
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);
|
||
}
|
||
});
|