Files
static/app/static/js/upload.js
KWON 7121f250bc feat: Flask 애플리케이션 모듈화 및 웹 대시보드 구현
- Flask Blueprint 아키텍처로 전환 (dashboard, upload, backup, status)
- app.py 681줄  95줄로 축소 (86% 감소)
- HTML 템플릿 모듈화 (base.html + 기능별 templates)
- CSS/JS 파일 분리 (common + 기능별 파일)
- 대시보드 기능 추가 (통계, 주간 예보, 방문객 추이)
- 파일 업로드 웹 인터페이스 구현
- 백업/복구 관리 UI 구현
- Docker 배포 환경 개선
- .gitignore 업데이트 (uploads, backups, cache 등)
2025-12-26 17:31:37 +09:00

188 lines
5.1 KiB
JavaScript

/* ===== 파일 업로드 JavaScript ===== */
const FILE_LIST = [];
/**
* 파일 업로드 UI를 초기화합니다.
*/
function initializeUploadUI() {
setupDropZone();
setupFileButton();
checkSystemStatus();
}
/**
* 드롭존을 설정합니다.
*/
function setupDropZone() {
const dropZone = document.getElementById('drop-zone');
if (!dropZone) return;
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
handleFiles(e.dataTransfer.files);
});
}
/**
* 파일 선택 버튼을 설정합니다.
*/
function setupFileButton() {
const fileSelectBtn = document.getElementById('file-select-btn');
if (!fileSelectBtn) return;
fileSelectBtn.addEventListener('click', () => {
const input = document.createElement('input');
input.type = 'file';
input.multiple = true;
input.accept = '.xlsx,.xls,.csv';
input.addEventListener('change', (e) => {
handleFiles(e.target.files);
});
input.click();
});
}
/**
* 파일들을 처리합니다.
* @param {FileList} files - 선택된 파일들
*/
function handleFiles(files) {
for (let file of files) {
FILE_LIST.push(file);
}
updateFileList();
}
/**
* 파일 목록을 화면에 업데이트합니다.
*/
function updateFileList() {
const fileListDiv = document.getElementById('file-list');
let html = '';
FILE_LIST.forEach((file, index) => {
html += `
<div class="file-item">
<div>
<div class="file-name">
<i class="bi bi-file-earmark"></i> ${file.name}
</div>
<small style="color: #999;">${formatFileSize(file.size)} MB</small>
</div>
<i class="bi bi-x-circle file-item-remove" onclick="removeFile(${index})"></i>
</div>
`;
});
fileListDiv.innerHTML = html;
}
/**
* 파일을 목록에서 제거합니다.
* @param {number} index - 제거할 파일의 인덱스
*/
function removeFile(index) {
FILE_LIST.splice(index, 1);
updateFileList();
}
/**
* 파일 목록을 비웁니다.
*/
function clearFileList() {
FILE_LIST.length = 0;
updateFileList();
document.getElementById('upload-result').innerHTML = '';
}
/**
* 파일들을 업로드합니다.
*/
async function uploadFiles() {
if (FILE_LIST.length === 0) {
showAlert('업로드할 파일을 선택하세요.', 'warning');
return;
}
const formData = new FormData();
FILE_LIST.forEach(file => {
formData.append('files', file);
});
document.getElementById('upload-progress').style.display = 'block';
document.getElementById('upload-btn').disabled = true;
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
let resultHtml = data.success ?
'<div class="alert alert-success">' :
'<div class="alert alert-warning">';
resultHtml += '<strong>업로드 완료!</strong><br>';
data.files.forEach(file => {
const icon = file.status === 'success' ? '✓' : '✗';
resultHtml += `${icon} ${file.filename}: ${file.message}<br>`;
});
resultHtml += '</div>';
document.getElementById('upload-result').innerHTML = resultHtml;
showAlert('업로드가 완료되었습니다.', 'success');
setTimeout(() => {
clearFileList();
// 대시보드 새로고침
if (typeof loadDashboard === 'function') {
loadDashboard();
}
}, 2000);
} catch (error) {
showAlert('업로드 실패: ' + error.message, 'danger');
} finally {
document.getElementById('upload-progress').style.display = 'none';
document.getElementById('upload-btn').disabled = false;
}
}
/**
* 시스템 상태를 확인합니다.
*/
async function checkSystemStatus() {
try {
const data = await apiCall('/api/status');
const dbStatus = document.getElementById('db-status');
const uploadStatus = document.getElementById('upload-folder-status');
if (dbStatus) {
dbStatus.textContent = data.database ? '연결됨' : '연결 안됨';
dbStatus.className = `badge ${data.database ? 'bg-success' : 'bg-danger'}`;
}
if (uploadStatus) {
uploadStatus.textContent = data.upload_folder ? '정상' : '오류';
uploadStatus.className = `badge ${data.upload_folder ? 'bg-success' : 'bg-danger'}`;
}
} catch (error) {
console.error('시스템 상태 확인 실패:', error);
}
}