refactor: 시스템 전반 코드 리팩토링 및 문의사항 UI 개선
This commit is contained in:
@@ -13,8 +13,7 @@ const CONTINENT_ORDER = { "아시아": 1, "아프리카": 2, "아메리카": 3,
|
||||
// --- 초기화 ---
|
||||
async function init() {
|
||||
console.log("Dashboard Initializing...");
|
||||
const container = document.getElementById('projectAccordion');
|
||||
if (!container) return;
|
||||
if (!document.getElementById('projectAccordion')) return;
|
||||
|
||||
await loadAvailableDates();
|
||||
await loadDataByDate();
|
||||
@@ -23,7 +22,7 @@ async function init() {
|
||||
// --- 데이터 통신 및 로드 ---
|
||||
async function loadAvailableDates() {
|
||||
try {
|
||||
const response = await fetch('/available-dates');
|
||||
const response = await fetch(API.AVAILABLE_DATES);
|
||||
const dates = await response.json();
|
||||
if (dates?.length > 0) {
|
||||
const selectHtml = `
|
||||
@@ -40,7 +39,7 @@ async function loadAvailableDates() {
|
||||
async function loadDataByDate(selectedDate = "") {
|
||||
try {
|
||||
await loadActivityAnalysis(selectedDate);
|
||||
const url = selectedDate ? `/project-data?date=${selectedDate}` : `/project-data?t=${Date.now()}`;
|
||||
const url = selectedDate ? `${API.PROJECT_DATA}?date=${selectedDate}` : `${API.PROJECT_DATA}?t=${Date.now()}`;
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
if (data.error) throw new Error(data.error);
|
||||
@@ -56,7 +55,7 @@ async function loadActivityAnalysis(date = "") {
|
||||
const dashboard = document.getElementById('activityDashboard');
|
||||
if (!dashboard) return;
|
||||
try {
|
||||
const url = date ? `/project-activity?date=${date}` : `/project-activity`;
|
||||
const url = date ? `${API.PROJECT_ACTIVITY}?date=${date}` : API.PROJECT_ACTIVITY;
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
if (data.error) return;
|
||||
@@ -114,20 +113,13 @@ function createProjectHtml(p) {
|
||||
const recentLog = (!logRaw || logRaw === "X" || logRaw === "데이터 없음") ? "기록 없음" : logRaw;
|
||||
const logTime = recentLog !== "기록 없음" ? recentLog.split(',')[0] : "기록 없음";
|
||||
|
||||
// '폴더 자동 삭제' 여부 확인
|
||||
const isStaleLog = recentLog.replace(/\s/g, "").includes("폴더자동삭제");
|
||||
// 파일이 0개 또는 NULL인 경우에만 행 전체 에러(붉은색) 표시
|
||||
const isNoFiles = (files === 0 || files === null);
|
||||
const statusClass = isNoFiles ? "status-error" : "";
|
||||
|
||||
// 로그 텍스트 스타일 결정
|
||||
// 폴더자동삭제는 위험(error), 기록 없음은 주의(warning)
|
||||
let logStyleClass = "";
|
||||
if (isStaleLog) {
|
||||
logStyleClass = "error-text";
|
||||
} else if (recentLog === "기록 없음") {
|
||||
logStyleClass = "warning-text";
|
||||
}
|
||||
if (isStaleLog) logStyleClass = "error-text";
|
||||
else if (recentLog === "기록 없음") logStyleClass = "warning-text";
|
||||
|
||||
const logBoldStyle = isStaleLog ? 'font-weight: 800;' : '';
|
||||
|
||||
@@ -140,14 +132,14 @@ function createProjectHtml(p) {
|
||||
<div class="detail-grid">
|
||||
<div class="detail-section">
|
||||
<h4>참여 인원 상세</h4>
|
||||
<table class="data-table" id="personnel-table">
|
||||
<table class="data-table">
|
||||
<thead><tr><th>이름</th><th>소속</th><th>권한</th></tr></thead>
|
||||
<tbody><tr><td>${admin}</td><td>${dept}</td><td>관리자</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="detail-section">
|
||||
<h4>최근 활동</h4>
|
||||
<table class="data-table" id="activity-table">
|
||||
<table class="data-table">
|
||||
<thead><tr><th>유형</th><th>내용</th><th>일시</th></tr></thead>
|
||||
<tbody><tr><td><span class="badge">로그</span></td><td>동기화 완료</td><td>${logTime}</td></tr></tbody>
|
||||
</table>
|
||||
@@ -166,29 +158,18 @@ function toggleAccordion(h) {
|
||||
}
|
||||
|
||||
function showActivityDetails(status) {
|
||||
const modal = document.getElementById('activityDetailModal'), tbody = document.getElementById('modalTableBody'), title = document.getElementById('modalTitle');
|
||||
const names = { active: '정상', warning: '주의', stale: '방치', unknown: '데이터 없음' };
|
||||
const filtered = (projectActivityDetails || []).filter(d => d.status === status);
|
||||
title.innerText = `${names[status]} 목록 (${filtered.length}개)`;
|
||||
tbody.innerHTML = filtered.map(p => {
|
||||
document.getElementById('modalTitle').innerText = `${names[status]} 목록 (${filtered.length}개)`;
|
||||
document.getElementById('modalTableBody').innerHTML = filtered.map(p => {
|
||||
const o = rawData.find(r => r[0] === p.name);
|
||||
return `<tr class="modal-row" onclick="scrollToProject('${p.name}')"><td><strong>${p.name}</strong></td><td>${o ? o[1] : "-"}</td><td>${o ? o[2] : "-"}</td></tr>`;
|
||||
}).join('');
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeActivityModal() {
|
||||
const modal = document.getElementById('activityDetailModal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
}
|
||||
|
||||
function closeAuthModal() {
|
||||
const modal = document.getElementById('authModal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
ModalManager.open('activityDetailModal');
|
||||
}
|
||||
|
||||
function scrollToProject(name) {
|
||||
closeActivityModal();
|
||||
ModalManager.close('activityDetailModal');
|
||||
const target = Array.from(document.querySelectorAll('.repo-title')).find(t => t.innerText.trim() === name.trim())?.closest('.accordion-header');
|
||||
if (target) {
|
||||
let p = target.parentElement;
|
||||
@@ -208,27 +189,23 @@ function scrollToProject(name) {
|
||||
async function syncData() {
|
||||
if (isCrawling) {
|
||||
if (confirm("크롤링을 중단하시겠습니까?")) {
|
||||
const res = await fetch('/stop-sync');
|
||||
const res = await fetch(API.STOP_SYNC);
|
||||
if ((await res.json()).success) document.getElementById('syncBtn').innerText = "중단 요청 중...";
|
||||
}
|
||||
return;
|
||||
}
|
||||
const modal = document.getElementById('authModal');
|
||||
if (modal) {
|
||||
document.getElementById('authId').value = ''; document.getElementById('authPw').value = '';
|
||||
document.getElementById('authErrorMessage').style.display = 'none';
|
||||
modal.style.display = 'flex'; document.getElementById('authId').focus();
|
||||
}
|
||||
document.getElementById('authId').value = '';
|
||||
document.getElementById('authPw').value = '';
|
||||
document.getElementById('authErrorMessage').style.display = 'none';
|
||||
ModalManager.open('authModal');
|
||||
}
|
||||
|
||||
function closeAuthModal() { document.getElementById('authModal').style.display = 'none'; }
|
||||
|
||||
async function submitAuth() {
|
||||
const id = document.getElementById('authId').value, pw = document.getElementById('authPw').value, err = document.getElementById('authErrorMessage');
|
||||
try {
|
||||
const res = await fetch('/auth/crawl', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: id, password: pw }) });
|
||||
const res = await fetch(API.AUTH_CRAWL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: id, password: pw }) });
|
||||
const data = await res.json();
|
||||
if (data.success) { closeAuthModal(); startCrawlProcess(); }
|
||||
if (data.success) { ModalManager.close('authModal'); startCrawlProcess(); }
|
||||
else { err.innerText = "크롤링을 할 수 없습니다."; err.style.display = 'block'; }
|
||||
} catch { err.innerText = "서버 연결 실패"; err.style.display = 'block'; }
|
||||
}
|
||||
@@ -239,7 +216,7 @@ async function startCrawlProcess() {
|
||||
btn.classList.add('loading'); btn.style.backgroundColor = 'var(--error-color)'; btn.innerHTML = `<span class="spinner"></span> 크롤링 중단`;
|
||||
logC.style.display = 'block'; logB.innerHTML = '<div style="color:#aaa; margin-bottom:10px;">>>> 엔진 초기화 중...</div>';
|
||||
try {
|
||||
const res = await fetch(`/sync`);
|
||||
const res = await fetch(API.SYNC);
|
||||
const reader = res.body.getReader(), decoder = new TextDecoder();
|
||||
while (true) {
|
||||
const { done, value } = await reader.read(); if (done) break;
|
||||
|
||||
Reference in New Issue
Block a user