288 lines
8.7 KiB
JavaScript
288 lines
8.7 KiB
JavaScript
import { API, getAuthHeaders, ensureAuthenticated } from '/js/api-config.js';
|
|
|
|
// 인증 확인
|
|
const token = ensureAuthenticated();
|
|
|
|
const accessLabels = {
|
|
worker: '작업자',
|
|
group_leader: '그룹장',
|
|
support_team: '지원팀',
|
|
admin: '관리자',
|
|
system: '시스템'
|
|
};
|
|
|
|
// 현재 사용자 정보 가져오기
|
|
const currentUser = JSON.parse(localStorage.getItem('user') || '{}');
|
|
const isSystemUser = currentUser.access_level === 'system';
|
|
|
|
function createRow(item, cols, delHandler) {
|
|
const tr = document.createElement('tr');
|
|
cols.forEach(key => {
|
|
const td = document.createElement('td');
|
|
td.textContent = item[key] || '-';
|
|
tr.appendChild(td);
|
|
});
|
|
const delBtn = document.createElement('button');
|
|
delBtn.textContent = '삭제';
|
|
delBtn.className = 'btn-delete';
|
|
delBtn.onclick = () => delHandler(item);
|
|
const td = document.createElement('td');
|
|
td.appendChild(delBtn);
|
|
tr.appendChild(td);
|
|
return tr;
|
|
}
|
|
|
|
// 내 비밀번호 변경
|
|
const myPasswordForm = document.getElementById('myPasswordForm');
|
|
myPasswordForm?.addEventListener('submit', async e => {
|
|
e.preventDefault();
|
|
|
|
const currentPassword = document.getElementById('currentPassword').value;
|
|
const newPassword = document.getElementById('newPassword').value;
|
|
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
|
|
// 새 비밀번호 확인
|
|
if (newPassword !== confirmPassword) {
|
|
alert('❌ 새 비밀번호가 일치하지 않습니다.');
|
|
return;
|
|
}
|
|
|
|
// 비밀번호 강도 검사
|
|
if (newPassword.length < 6) {
|
|
alert('❌ 비밀번호는 최소 6자 이상이어야 합니다.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(`${API}/auth/change-password`, {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify({
|
|
currentPassword,
|
|
newPassword
|
|
})
|
|
});
|
|
|
|
const result = await res.json();
|
|
|
|
if (res.ok && result.success) {
|
|
showToast('✅ 비밀번호가 변경되었습니다.');
|
|
myPasswordForm.reset();
|
|
|
|
// 3초 후 로그인 페이지로 이동
|
|
setTimeout(() => {
|
|
alert('비밀번호가 변경되어 다시 로그인해주세요.');
|
|
localStorage.removeItem('token');
|
|
localStorage.removeItem('user');
|
|
window.location.href = '/index.html';
|
|
}, 2000);
|
|
} else {
|
|
alert('❌ 비밀번호 변경 실패: ' + (result.error || '현재 비밀번호가 올바르지 않습니다.'));
|
|
}
|
|
} catch (error) {
|
|
console.error('Password change error:', error);
|
|
alert('🚨 서버 오류: ' + error.message);
|
|
}
|
|
});
|
|
|
|
// 시스템 권한자만 볼 수 있는 사용자 비밀번호 변경 섹션
|
|
if (isSystemUser) {
|
|
const systemCard = document.getElementById('systemPasswordChangeCard');
|
|
if (systemCard) {
|
|
systemCard.style.display = 'block';
|
|
}
|
|
|
|
// 사용자 비밀번호 변경 (시스템 권한자)
|
|
const userPasswordForm = document.getElementById('userPasswordForm');
|
|
userPasswordForm?.addEventListener('submit', async e => {
|
|
e.preventDefault();
|
|
|
|
const targetUserId = document.getElementById('targetUserId').value;
|
|
const newPassword = document.getElementById('targetNewPassword').value;
|
|
|
|
if (!targetUserId) {
|
|
alert('❌ 사용자를 선택해주세요.');
|
|
return;
|
|
}
|
|
|
|
if (newPassword.length < 6) {
|
|
alert('❌ 비밀번호는 최소 6자 이상이어야 합니다.');
|
|
return;
|
|
}
|
|
|
|
if (!confirm('정말로 이 사용자의 비밀번호를 변경하시겠습니까?')) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(`${API}/auth/admin/change-password`, {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify({
|
|
userId: targetUserId,
|
|
newPassword
|
|
})
|
|
});
|
|
|
|
const result = await res.json();
|
|
|
|
if (res.ok && result.success) {
|
|
showToast('✅ 사용자 비밀번호가 변경되었습니다.');
|
|
userPasswordForm.reset();
|
|
} else {
|
|
alert('❌ 비밀번호 변경 실패: ' + (result.error || '권한이 없습니다.'));
|
|
}
|
|
} catch (error) {
|
|
console.error('Admin password change error:', error);
|
|
alert('🚨 서버 오류: ' + error.message);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 사용자 등록
|
|
const userForm = document.getElementById('userForm');
|
|
userForm?.addEventListener('submit', async e => {
|
|
e.preventDefault();
|
|
const body = {
|
|
username: document.getElementById('username').value.trim(),
|
|
password: document.getElementById('password').value.trim(),
|
|
name: document.getElementById('name').value.trim(),
|
|
access_level: document.getElementById('access_level').value,
|
|
worker_id: document.getElementById('worker_id').value || null
|
|
};
|
|
|
|
try {
|
|
const res = await fetch(`${API}/auth/register`, {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify(body)
|
|
});
|
|
const result = await res.json();
|
|
if (res.ok && result.success) {
|
|
showToast('✅ 등록 완료');
|
|
userForm.reset();
|
|
loadUsers();
|
|
} else {
|
|
alert('❌ 실패: ' + (result.error || '알 수 없는 오류'));
|
|
}
|
|
} catch (error) {
|
|
console.error('Registration error:', error);
|
|
alert('🚨 서버 오류: ' + error.message);
|
|
}
|
|
});
|
|
|
|
async function loadUsers() {
|
|
const tbody = document.getElementById('userTableBody');
|
|
tbody.innerHTML = '<tr><td colspan="6">불러오는 중...</td></tr>';
|
|
|
|
try {
|
|
const res = await fetch(`${API}/auth/users`, {
|
|
headers: getAuthHeaders()
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`HTTP error! status: ${res.status}`);
|
|
}
|
|
|
|
const list = await res.json();
|
|
tbody.innerHTML = '';
|
|
|
|
if (Array.isArray(list)) {
|
|
// 시스템 권한자용 사용자 선택 옵션도 업데이트
|
|
if (isSystemUser) {
|
|
const targetUserSelect = document.getElementById('targetUserId');
|
|
if (targetUserSelect) {
|
|
targetUserSelect.innerHTML = '<option value="">사용자 선택</option>';
|
|
list.forEach(user => {
|
|
// 본인은 제외
|
|
if (user.user_id !== currentUser.user_id) {
|
|
const opt = document.createElement('option');
|
|
opt.value = user.user_id;
|
|
opt.textContent = `${user.name} (${user.username})`;
|
|
targetUserSelect.appendChild(opt);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
list.forEach(item => {
|
|
item.access_level = accessLabels[item.access_level] || item.access_level;
|
|
item.worker_id = item.worker_id || '-';
|
|
const row = createRow(item, [
|
|
'user_id', 'username', 'name', 'access_level', 'worker_id'
|
|
], async u => {
|
|
if (!confirm('삭제하시겠습니까?')) return;
|
|
try {
|
|
const delRes = await fetch(`${API}/auth/users/${u.user_id}`, {
|
|
method: 'DELETE',
|
|
headers: getAuthHeaders()
|
|
});
|
|
if (delRes.ok) {
|
|
showToast('✅ 삭제 완료');
|
|
loadUsers();
|
|
} else {
|
|
alert('❌ 삭제 실패');
|
|
}
|
|
} catch (error) {
|
|
alert('🚨 삭제 중 오류 발생');
|
|
}
|
|
});
|
|
tbody.appendChild(row);
|
|
});
|
|
} else {
|
|
tbody.innerHTML = '<tr><td colspan="6">데이터 형식 오류</td></tr>';
|
|
}
|
|
} catch (error) {
|
|
console.error('Load users error:', error);
|
|
tbody.innerHTML = '<tr><td colspan="6">로드 실패: ' + error.message + '</td></tr>';
|
|
}
|
|
}
|
|
|
|
async function loadWorkerOptions() {
|
|
const select = document.getElementById('worker_id');
|
|
if (!select) return;
|
|
|
|
try {
|
|
const res = await fetch(`${API}/workers`, {
|
|
headers: getAuthHeaders()
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`HTTP error! status: ${res.status}`);
|
|
}
|
|
|
|
const workers = await res.json();
|
|
if (Array.isArray(workers)) {
|
|
workers.forEach(w => {
|
|
const opt = document.createElement('option');
|
|
opt.value = w.worker_id;
|
|
opt.textContent = `${w.worker_name} (${w.worker_id})`;
|
|
select.appendChild(opt);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.warn('작업자 목록 불러오기 실패:', error);
|
|
}
|
|
}
|
|
|
|
function showToast(message) {
|
|
const toast = document.createElement('div');
|
|
toast.textContent = message;
|
|
toast.style.position = 'fixed';
|
|
toast.style.bottom = '30px';
|
|
toast.style.left = '50%';
|
|
toast.style.transform = 'translateX(-50%)';
|
|
toast.style.background = '#323232';
|
|
toast.style.color = '#fff';
|
|
toast.style.padding = '10px 20px';
|
|
toast.style.borderRadius = '6px';
|
|
toast.style.fontSize = '14px';
|
|
toast.style.zIndex = 9999;
|
|
document.body.appendChild(toast);
|
|
setTimeout(() => toast.remove(), 2000);
|
|
}
|
|
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
loadUsers();
|
|
loadWorkerOptions();
|
|
}); |