let rawData = []; const continentMap = { "라오스": "아시아", "미얀마": "아시아", "베트남": "아시아", "사우디아라비아": "아시아", "우즈베키스탄": "아시아", "이라크": "아시아", "캄보디아": "아시아", "키르기스스탄": "아시아", "파키스탄": "아시아", "필리핀": "아시아", "아르헨티나": "아메리카", "온두라스": "아메리카", "볼리비아": "아메리카", "콜롬비아": "아메리카", "파라과이": "아메리카", "페루": "아메리카", "엘살바도르": "아메리카", "가나": "아프리카", "기니": "아프리카", "우간다": "아프리카", "에티오피아": "아프리카", "탄자니아": "아프리카" }; const continentOrder = { "아시아": 1, "아프리카": 2, "아메리카": 3, "지사": 4 }; async function init() { const container = document.getElementById('projectAccordion'); if (!container) return; // 서버에서 최신 sheet.csv 데이터 가져오기 (캐시 방지 위해 timestamp 추가) try { const response = await fetch(`/project-data?t=${new Date().getTime()}`); rawData = await response.json(); console.log("Loaded rawData:", rawData); if (rawData.error) throw new Error(rawData.error); } catch (e) { console.error("데이터 로드 실패:", e); alert("데이터를 가져오는 데 실패했습니다."); return; } container.innerHTML = ''; // 초기화 const groupedData = {}; rawData.forEach((item, index) => { const projectName = item[0]; let continent = ""; let country = ""; if (projectName.endsWith("사무소")) { continent = "지사"; country = projectName.split(" ")[0]; } else if (projectName.startsWith("메콩유역")) { country = "캄보디아"; continent = "아시아"; } else { country = projectName.split(" ")[0]; continent = continentMap[country] || "기타"; } if (!groupedData[continent]) groupedData[continent] = {}; if (!groupedData[continent][country]) groupedData[continent][country] = []; groupedData[continent][country].push({ item, index }); }); const sortedContinents = Object.keys(groupedData).sort((a, b) => (continentOrder[a] || 99) - (continentOrder[b] || 99)); sortedContinents.forEach(continent => { const continentGroup = document.createElement('div'); continentGroup.className = 'continent-group'; let continentHtml = `
${continent}
`; const sortedCountries = Object.keys(groupedData[continent]).sort((a, b) => a.localeCompare(b)); sortedCountries.forEach(country => { continentHtml += `
${country}
프로젝트명
담당부서
담당자
파일수
최근로그
`; const sortedProjects = groupedData[continent][country].sort((a, b) => a.item[0].localeCompare(b.item[0])); sortedProjects.forEach(({ item, index }) => { const projectName = item[0]; const dept = item[1]; const admin = item[2]; const recentLogRaw = item[3]; const fileCount = item[4]; const recentLog = recentLogRaw === "X" ? "기록 없음" : recentLogRaw; const logTime = recentLog !== "기록 없음" ? recentLog.split(',')[0] : "기록 없음"; let statusClass = ""; if (fileCount === 0) statusClass = "status-error"; else if (recentLog === "기록 없음") statusClass = "status-warning"; continentHtml += `
${projectName}
${dept}
${admin}
${fileCount}
${recentLog}

참여 인원 상세

이름소속사용자권한
${admin}${dept}관리자
김철수${dept}부관리자
박지민${dept}일반참여자
최유리${dept}참관자

최근 문의사항 및 파일 변경 로그

유형내용일시
로그데이터 동기화 완료${logTime}
문의프로젝트 접근 권한 요청2026-02-23
파일설계도면 v2.pdf 업로드2026-02-22
`; }); continentHtml += `
`; }); continentHtml += `
`; continentGroup.innerHTML = continentHtml; container.appendChild(continentGroup); }); const allContinents = container.querySelectorAll('.continent-group'); allContinents.forEach(continent => { continent.classList.add('active'); }); const allCountries = container.querySelectorAll('.country-group'); allCountries.forEach(country => { country.classList.add('active'); }); } function toggleGroup(header) { const group = header.parentElement; group.classList.toggle('active'); } function toggleAccordion(header) { const item = header.parentElement; const container = item.parentElement; const allItems = container.querySelectorAll('.accordion-item'); allItems.forEach(el => { if (el !== item) el.classList.remove('active'); }); item.classList.toggle('active'); } async function syncData() { const btn = document.getElementById('syncBtn'); const logConsole = document.getElementById('logConsole'); const logBody = document.getElementById('logBody'); btn.classList.add('loading'); btn.innerHTML = ` 동기화 중 (진행 상황 확인 중...)`; btn.disabled = true; logConsole.style.display = 'block'; logBody.innerHTML = ''; function addLog(msg) { const logItem = document.createElement('div'); logItem.innerText = `[${new Date().toLocaleTimeString()}] ${msg}`; logBody.appendChild(logItem); logConsole.scrollTop = logConsole.scrollHeight; } try { const response = await fetch(`/sync`); const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const payload = JSON.parse(line.substring(6)); if (payload.type === 'log') { addLog(payload.message); } else if (payload.type === 'done') { const newData = payload.data; newData.forEach(scrapedItem => { const target = rawData.find(item => item[0].replace(/\s/g, '').includes(scrapedItem.projectName.replace(/\s/g, '')) || scrapedItem.projectName.replace(/\s/g, '').includes(item[0].replace(/\s/g, '')) ); if (target) { if (scrapedItem.recentLog !== "기존데이터유지") { target[3] = scrapedItem.recentLog; } target[4] = scrapedItem.fileCount; } }); document.getElementById('projectAccordion').innerHTML = ''; init(); addLog(">>> 모든 동기화 작업이 완료되었습니다!"); alert(`총 ${newData.length}개 프로젝트 동기화 완료!`); logConsole.style.display = 'none'; } } } } } catch (e) { addLog(`오류 발생: ${e.message}`); alert("서버 연결 실패. 백엔드 서버가 실행 중인지 확인하세요."); console.error(e); } finally { btn.classList.remove('loading'); btn.innerHTML = ` 데이터 동기화 (크롤링)`; btn.disabled = false; } } document.addEventListener('DOMContentLoaded', init);