refactor: System2/3, User Management SSO 인증 통합
- System2 신고: SSO JWT 인증 전환, API base 정리 - System3 부적합: SSO 인증 매니저 통합, 권한 체계 정비 - User Management: SSO 토큰 기반 사용자 관리 API 연동 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ const API_BASE = '/api';
|
||||
/* ===== Token ===== */
|
||||
function _cookieGet(n) { const m = document.cookie.match(new RegExp('(?:^|; )' + n + '=([^;]*)')); return m ? decodeURIComponent(m[1]) : null; }
|
||||
function _cookieRemove(n) { let c = n + '=; path=/; max-age=0'; if (location.hostname.includes('technicalkorea.net')) c += '; domain=.technicalkorea.net'; document.cookie = c; }
|
||||
function getToken() { return _cookieGet('sso_token') || localStorage.getItem('sso_token') || localStorage.getItem('access_token'); }
|
||||
function getToken() { return _cookieGet('sso_token') || localStorage.getItem('sso_token'); }
|
||||
function getLoginUrl() {
|
||||
const h = location.hostname;
|
||||
if (h.includes('technicalkorea.net')) return location.protocol + '//tkfb.technicalkorea.net/login?redirect=' + encodeURIComponent(location.href);
|
||||
@@ -27,11 +27,14 @@ function showToast(msg, type = 'success') {
|
||||
document.querySelector('.toast-message')?.remove();
|
||||
const el = document.createElement('div');
|
||||
el.className = `toast-message fixed bottom-4 right-4 px-4 py-3 rounded-lg text-white z-[10000] shadow-lg ${type==='success'?'bg-emerald-500':'bg-red-500'}`;
|
||||
el.innerHTML = `<i class="fas ${type==='success'?'fa-check-circle':'fa-exclamation-circle'} mr-2"></i>${msg}`;
|
||||
el.innerHTML = `<i class="fas ${type==='success'?'fa-check-circle':'fa-exclamation-circle'} mr-2"></i>${escapeHtml(msg)}`;
|
||||
document.body.appendChild(el);
|
||||
setTimeout(() => { el.classList.add('opacity-0'); setTimeout(() => el.remove(), 300); }, 3000);
|
||||
}
|
||||
|
||||
/* ===== Escape ===== */
|
||||
function escapeHtml(str) { if (!str) return ''; const d = document.createElement('div'); d.textContent = str; return d.innerHTML; }
|
||||
|
||||
/* ===== Helpers ===== */
|
||||
const DEPT_FALLBACK = { production:'생산', quality:'품질', purchasing:'구매', design:'설계', sales:'영업' };
|
||||
let departmentsCache = [];
|
||||
@@ -54,7 +57,8 @@ function escHtml(s) { if (!s) return ''; const d = document.createElement('div')
|
||||
/* ===== Logout ===== */
|
||||
function doLogout() {
|
||||
if (!confirm('로그아웃?')) return;
|
||||
_cookieRemove('sso_token'); localStorage.removeItem('sso_token'); localStorage.removeItem('access_token'); localStorage.removeItem('currentUser');
|
||||
_cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token');
|
||||
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k));
|
||||
location.href = getLoginUrl();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ async function loadProjects() {
|
||||
projectsLoaded = true;
|
||||
displayProjects();
|
||||
} catch (err) {
|
||||
document.getElementById('projectList').innerHTML = `<div class="text-red-500 text-center py-6"><i class="fas fa-exclamation-triangle text-xl"></i><p class="text-sm mt-2">${err.message}</p></div>`;
|
||||
document.getElementById('projectList').innerHTML = `<div class="text-red-500 text-center py-6"><i class="fas fa-exclamation-triangle text-xl"></i><p class="text-sm mt-2">${escapeHtml(err.message)}</p></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,18 +23,18 @@ function displayProjects() {
|
||||
c.innerHTML = projects.map(p => `
|
||||
<div class="flex items-center justify-between p-2.5 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="text-sm font-medium text-gray-800 truncate"><i class="fas fa-folder mr-1.5 text-gray-400 text-xs"></i>${p.project_name}</div>
|
||||
<div class="text-sm font-medium text-gray-800 truncate"><i class="fas fa-folder mr-1.5 text-gray-400 text-xs"></i>${escapeHtml(p.project_name)}</div>
|
||||
<div class="text-xs text-gray-500 flex items-center gap-1.5 mt-0.5 flex-wrap">
|
||||
<span class="font-mono">${p.job_no}</span>
|
||||
${p.site?`<span class="px-1.5 py-0.5 rounded bg-amber-50 text-amber-600">${p.site}</span>`:''}
|
||||
${p.pm?`<span class="px-1.5 py-0.5 rounded bg-slate-50 text-slate-500">${p.pm}</span>`:''}
|
||||
<span class="font-mono">${escapeHtml(p.job_no)}</span>
|
||||
${p.site?`<span class="px-1.5 py-0.5 rounded bg-amber-50 text-amber-600">${escapeHtml(p.site)}</span>`:''}
|
||||
${p.pm?`<span class="px-1.5 py-0.5 rounded bg-slate-50 text-slate-500">${escapeHtml(p.pm)}</span>`:''}
|
||||
${statusBadge(p.project_status, p.is_active)}
|
||||
${p.due_date?`<span class="text-gray-400">${formatDate(p.due_date)}</span>`:''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-1 ml-2 flex-shrink-0">
|
||||
<button onclick="editProject(${p.project_id})" class="p-1.5 text-slate-500 hover:text-slate-700 hover:bg-slate-200 rounded" title="편집"><i class="fas fa-pen-to-square text-xs"></i></button>
|
||||
${p.is_active?`<button onclick="deactivateProject(${p.project_id},'${p.project_name.replace(/'/g,"\\'")}')" class="p-1.5 text-red-400 hover:text-red-600 hover:bg-red-100 rounded" title="비활성화"><i class="fas fa-ban text-xs"></i></button>`:''}
|
||||
${p.is_active?`<button onclick="deactivateProject(${p.project_id},'${escapeHtml(p.project_name).replace(/'/g,"\\'")}')" class="p-1.5 text-red-400 hover:text-red-600 hover:bg-red-100 rounded" title="비활성화"><i class="fas fa-ban text-xs"></i></button>`:''}
|
||||
</div>
|
||||
</div>`).join('');
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ function populateTaskWorkTypeSelect() {
|
||||
const val = sel.value;
|
||||
sel.innerHTML = '<option value="">미지정</option>';
|
||||
taskWorkTypes.forEach(wt => {
|
||||
sel.innerHTML += `<option value="${wt.id}">${wt.category ? wt.category + ' > ' : ''}${wt.name}</option>`;
|
||||
sel.innerHTML += `<option value="${wt.id}">${wt.category ? escapeHtml(wt.category) + ' > ' : ''}${escapeHtml(wt.name)}</option>`;
|
||||
});
|
||||
sel.value = val;
|
||||
}
|
||||
|
||||
@@ -246,11 +246,11 @@ function openVacBalanceModal(editId) {
|
||||
// 작업자 셀렉트
|
||||
const wSel = document.getElementById('vbWorker');
|
||||
wSel.innerHTML = '<option value="">선택</option>';
|
||||
vacWorkers.forEach(w => { wSel.innerHTML += `<option value="${w.worker_id}">${w.worker_name}</option>`; });
|
||||
vacWorkers.forEach(w => { wSel.innerHTML += `<option value="${w.worker_id}">${escapeHtml(w.worker_name)}</option>`; });
|
||||
// 유형 셀렉트
|
||||
const tSel = document.getElementById('vbType');
|
||||
tSel.innerHTML = '<option value="">선택</option>';
|
||||
vacTypes.filter(t => t.is_active).forEach(t => { tSel.innerHTML += `<option value="${t.id}">${t.type_name} (${t.type_code})</option>`; });
|
||||
vacTypes.filter(t => t.is_active).forEach(t => { tSel.innerHTML += `<option value="${t.id}">${escapeHtml(t.type_name)} (${escapeHtml(t.type_code)})</option>`; });
|
||||
if (editId) {
|
||||
const b = vacBalances.find(x => x.id === editId);
|
||||
if (!b) return;
|
||||
|
||||
Reference in New Issue
Block a user