class AdminFileManager {
constructor() {
this.files = [];
this.categories = [];
this.currentPage = 1;
this.itemsPerPage = 10;
this.filteredFiles = [];
this.currentEditId = null;
this.currentEditCategoryId = null;
this.currentUser = null;
this.isLoggedIn = false;
this.init();
}
async init() {
console.log('๐ Admin FileManager ์ด๊ธฐํ ์์');
try {
this.bindEvents();
await this.checkSession();
this.updateUI();
} catch (error) {
console.error('์ด๊ธฐํ ์ค๋ฅ:', error);
this.showNotification('์ด๊ธฐํ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
}
}
bindEvents() {
// ๋ก๊ทธ์ธ ์ด๋ฒคํธ
const loginBtn = document.getElementById('loginBtn');
const adminPassword = document.getElementById('adminPassword');
if (loginBtn) {
loginBtn.addEventListener('click', () => this.handleLogin());
}
if (adminPassword) {
adminPassword.addEventListener('keyup', (e) => {
if (e.key === 'Enter') this.handleLogin();
});
}
// ๋ก๊ทธ์์ ์ด๋ฒคํธ
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
logoutBtn.addEventListener('click', () => this.handleLogout());
}
// ๊ฒ์ ๋ฐ ์ ๋ ฌ ์ด๋ฒคํธ
const searchBtn = document.getElementById('searchBtn');
const searchInput = document.getElementById('searchInput');
const categoryFilter = document.getElementById('categoryFilter');
const sortBy = document.getElementById('sortBy');
if (searchBtn) searchBtn.addEventListener('click', () => this.handleSearch());
if (searchInput) {
searchInput.addEventListener('keyup', (e) => {
if (e.key === 'Enter') this.handleSearch();
});
}
if (categoryFilter) categoryFilter.addEventListener('change', () => this.handleSearch());
if (sortBy) sortBy.addEventListener('change', () => this.handleSearch());
// ํญ ์ ํ ์ด๋ฒคํธ
this.bindTabEvents();
// ํ์ผ ๊ด๋ฆฌ ์ด๋ฒคํธ
this.bindFileEvents();
// ์นดํ
๊ณ ๋ฆฌ ๊ด๋ฆฌ ์ด๋ฒคํธ
this.bindCategoryEvents();
// ํ์ด์ง๋ค์ด์
์ด๋ฒคํธ
this.bindPaginationEvents();
}
bindTabEvents() {
const fileTabBtn = document.getElementById('fileTabBtn');
const categoryTabBtn = document.getElementById('categoryTabBtn');
const fileTab = document.getElementById('fileTab');
const categoryTab = document.getElementById('categoryTab');
if (fileTabBtn && categoryTabBtn && fileTab && categoryTab) {
fileTabBtn.addEventListener('click', () => {
// ํญ ๋ฒํผ ํ์ฑํ ์ํ ๋ณ๊ฒฝ
fileTabBtn.classList.add('active');
categoryTabBtn.classList.remove('active');
// ํญ ์ปจํ
์ธ ํ์/์จ๊น
fileTab.classList.add('active');
categoryTab.classList.remove('active');
});
categoryTabBtn.addEventListener('click', () => {
// ํญ ๋ฒํผ ํ์ฑํ ์ํ ๋ณ๊ฒฝ
categoryTabBtn.classList.add('active');
fileTabBtn.classList.remove('active');
// ํญ ์ปจํ
์ธ ํ์/์จ๊น
categoryTab.classList.add('active');
fileTab.classList.remove('active');
// ์นดํ
๊ณ ๋ฆฌ ๋ชฉ๋ก ๋ ๋๋ง
this.renderCategoryList();
});
}
}
bindCategoryEvents() {
// ์นดํ
๊ณ ๋ฆฌ ์ถ๊ฐ ํผ
const categoryForm = document.getElementById('categoryForm');
if (categoryForm) {
categoryForm.addEventListener('submit', (e) => {
e.preventDefault();
this.handleAddCategory();
});
}
// ์นดํ
๊ณ ๋ฆฌ ์ทจ์ ๋ฒํผ
const cancelCategoryBtn = document.getElementById('cancelCategoryBtn');
if (cancelCategoryBtn) {
cancelCategoryBtn.addEventListener('click', () => this.resetCategoryForm());
}
// ๋ชจ๋ฌ ์ด๋ฒคํธ
this.bindModalEvents();
}
bindModalEvents() {
// ์์ ๋ชจ๋ฌ ๋ซ๊ธฐ
const closeModal = document.getElementById('closeModal');
if (closeModal) {
closeModal.addEventListener('click', () => {
document.getElementById('editModal').style.display = 'none';
});
}
// ์นดํ
๊ณ ๋ฆฌ ์์ ๋ชจ๋ฌ ๋ซ๊ธฐ
const closeCategoryModal = document.getElementById('closeCategoryModal');
if (closeCategoryModal) {
closeCategoryModal.addEventListener('click', () => {
document.getElementById('editCategoryModal').style.display = 'none';
});
}
// ์์ ํผ ์ ์ถ
const editForm = document.getElementById('editForm');
if (editForm) {
editForm.addEventListener('submit', (e) => {
e.preventDefault();
this.handleUpdateFile();
});
}
// ์นดํ
๊ณ ๋ฆฌ ์์ ํผ ์ ์ถ
const editCategoryForm = document.getElementById('editCategoryForm');
if (editCategoryForm) {
editCategoryForm.addEventListener('submit', (e) => {
e.preventDefault();
this.handleUpdateCategory();
});
}
}
bindFileEvents() {
// ํ์ผ ์ถ๊ฐ ํผ
const fileForm = document.getElementById('fileForm');
if (fileForm) {
fileForm.addEventListener('submit', (e) => {
e.preventDefault();
this.handleAddFile();
});
}
// ์ทจ์ ๋ฒํผ
const cancelBtn = document.getElementById('cancelBtn');
if (cancelBtn) {
cancelBtn.addEventListener('click', () => this.resetForm());
}
// ํ์ผ ์
๋ก๋ ์์ญ
this.setupFileUpload();
}
bindEditModalEvents() {
console.log('bindEditModalEvents ํธ์ถ๋จ, ์ด๋ฏธ ๋ฐ์ธ๋ฉ๋ ์ํ:', this.editModalEventsBound);
// ์ด๋ฒคํธ๊ฐ ์ด๋ฏธ ๋ฐ์ธ๋ฉ๋์๋์ง ํ์ธ
if (this.editModalEventsBound) {
console.log('์ด๋ฏธ ๋ฐ์ธ๋ฉ๋จ, ์คํต');
return;
}
// ๊ธฐ์กด ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ ๊ฑฐ (์ค๋ณต ๋ฐฉ์ง)
const editForm = document.getElementById('editForm');
if (editForm && this.editFormHandler) {
editForm.removeEventListener('submit', this.editFormHandler);
}
// ์์ ํผ ์ ์ถ ์ด๋ฒคํธ ํธ๋ค๋ฌ ์์ฑ ๋ฐ ๋ฐ์ธ๋ฉ
this.editFormHandler = (e) => {
e.preventDefault();
console.log('editForm ์ ์ถ๋จ');
// ์ค๋ณต ์คํ ๋ฐฉ์ง
if (this.isUpdating) {
console.log('์ด๋ฏธ ์
๋ฐ์ดํธ ์ค์
๋๋ค.');
return;
}
this.handleUpdateFile();
};
if (editForm) {
console.log('editForm ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ');
editForm.addEventListener('submit', this.editFormHandler);
}
// ์์ ๋ชจ๋ฌ ๋ซ๊ธฐ
const closeModal = document.getElementById('closeModal');
if (closeModal) {
console.log('closeModal ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ');
closeModal.addEventListener('click', () => {
console.log('closeModal ํด๋ฆญ๋จ');
document.getElementById('editModal').style.display = 'none';
this.currentEditId = null;
this.filesToDelete = [];
// ์ ํ์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ด๊ธฐํ
this.updateNewFilesPreview([]);
const newAttachmentsInput = document.getElementById('newAttachments');
if (newAttachmentsInput) {
newAttachmentsInput.value = '';
}
});
}
// ํ์ผ ์ ํ ๋ฒํผ๊ณผ ๋๋๊ทธ&๋๋กญ ์์ญ
const fileSelectBtn = document.getElementById('fileSelectBtn');
const fileInput = document.getElementById('newAttachments');
const dropZone = document.getElementById('fileDropZone');
if (fileSelectBtn && fileInput && dropZone) {
console.log('์๋ก์ด ํ์ผ ์ ํ ์ธํฐํ์ด์ค ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ');
// ํ์ผ ์ ํ ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ
fileSelectBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
console.log('ํ์ผ ์ ํ ๋ฒํผ ํด๋ฆญ๋จ');
fileInput.click();
});
// ๋๋กญ์กด ํด๋ฆญ ์ด๋ฒคํธ
dropZone.addEventListener('click', (e) => {
if (e.target === dropZone || e.target.closest('.drop-zone-content')) {
fileInput.click();
}
});
// ํ์ผ ์
๋ ฅ ๋ณ๊ฒฝ ์ด๋ฒคํธ
fileInput.addEventListener('change', (e) => {
console.log('ํ์ผ ์ ํ๋จ, ๊ฐ์:', e.target.files.length);
this.updateNewFilesPreview(e.target.files);
});
// ๋๋๊ทธ&๋๋กญ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ
this.bindDragAndDropEvents(dropZone, fileInput);
} else {
console.error('์๋ก์ด ํ์ผ ์ ํ ์์๋ค์ ์ฐพ์ ์ ์์:', {
fileSelectBtn: !!fileSelectBtn,
fileInput: !!fileInput,
dropZone: !!dropZone
});
}
// ๋ฐ์ธ๋ฉ ์๋ฃ ํ๋๊ทธ ์ค์
this.editModalEventsBound = true;
console.log('์ด๋ฒคํธ ๋ฐ์ธ๋ฉ ์๋ฃ');
}
// ๋๋๊ทธ&๋๋กญ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ
bindDragAndDropEvents(dropZone, fileInput) {
console.log('๋๋๊ทธ&๋๋กญ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ ์์');
// ๋๋๊ทธ ์ง์
dropZone.addEventListener('dragenter', (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.add('dragover');
});
// ๋๋๊ทธ ์ค๋ฒ
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.add('dragover');
});
// ๋๋๊ทธ ๋๊ฐ
dropZone.addEventListener('dragleave', (e) => {
e.preventDefault();
e.stopPropagation();
// ์์ ํ ๋ฒ์ด๋ฌ์ ๋๋ง ํด๋์ค ์ ๊ฑฐ
if (!dropZone.contains(e.relatedTarget)) {
dropZone.classList.remove('dragover');
}
});
// ํ์ผ ๋๋กญ
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.remove('dragover');
const files = e.dataTransfer.files;
console.log('ํ์ผ ๋๋กญ๋จ, ๊ฐ์:', files.length);
if (files.length > 0) {
// ํ์ผ ์
๋ ฅ์ ๋๋กญ๋ ํ์ผ๋ค ์ค์
fileInput.files = files;
this.updateNewFilesPreview(files);
}
});
console.log('๋๋๊ทธ&๋๋กญ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ ์๋ฃ');
}
// ์๋ก์ด ํ์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์
๋ฐ์ดํธ
updateNewFilesPreview(files) {
const previewContainer = document.getElementById('newFilesPreview');
if (!files || files.length === 0) {
previewContainer.classList.remove('show');
previewContainer.innerHTML = '';
return;
}
previewContainer.classList.add('show');
previewContainer.innerHTML = '';
Array.from(files).forEach((file, index) => {
const fileItem = document.createElement('div');
fileItem.className = 'preview-file-item';
const fileIcon = this.getFileIcon(file.name);
const fileSize = this.formatFileSize(file.size);
fileItem.innerHTML = `
${fileIcon}
${this.escapeHtml(file.name)}
${fileSize}
`;
// ์ ๊ฑฐ ๋ฒํผ ์ด๋ฒคํธ
const removeBtn = fileItem.querySelector('.preview-file-remove');
removeBtn.addEventListener('click', () => {
this.removeFileFromPreview(index);
});
previewContainer.appendChild(fileItem);
});
}
// ํ์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์์ ์ ๊ฑฐ
removeFileFromPreview(indexToRemove) {
const fileInput = document.getElementById('newAttachments');
const dt = new DataTransfer();
Array.from(fileInput.files).forEach((file, index) => {
if (index !== indexToRemove) {
dt.items.add(file);
}
});
fileInput.files = dt.files;
this.updateNewFilesPreview(fileInput.files);
}
// ์ ์ฒจ๋ถํ์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ์ (๊ธฐ์กด ํจ์ - ํธํ์ฑ ์ ์ง)
showAttachmentPreview(files) {
const container = document.querySelector('.attachment-preview');
if (!container) {
// ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ปจํ
์ด๋๊ฐ ์์ผ๋ฉด ์์ฑ
const previewDiv = document.createElement('div');
previewDiv.className = 'attachment-preview';
document.querySelector('.new-attachment-section').appendChild(previewDiv);
}
const preview = document.querySelector('.attachment-preview');
if (files.length === 0) {
preview.style.display = 'none';
return;
}
let previewText = `์ ํ๋ ํ์ผ (${files.length}๊ฐ): `;
const fileNames = Array.from(files).map(file => file.name).slice(0, 3);
if (files.length > 3) {
fileNames.push(`์ธ ${files.length - 3}๊ฐ`);
}
previewText += fileNames.join(', ');
preview.innerHTML = previewText;
preview.style.display = 'block';
}
bindPaginationEvents() {
const prevBtn = document.getElementById('prevPage');
const nextBtn = document.getElementById('nextPage');
if (prevBtn) prevBtn.addEventListener('click', () => this.goToPrevPage());
if (nextBtn) nextBtn.addEventListener('click', () => this.goToNextPage());
}
setupFileUpload() {
const fileUploadArea = document.getElementById('fileUploadArea');
const fileUpload = document.getElementById('fileUpload');
if (fileUploadArea && fileUpload) {
// ํด๋ฆญ์ผ๋ก ํ์ผ ์ ํ
fileUploadArea.addEventListener('click', () => {
fileUpload.click();
});
// ํ์ผ ์ ํ ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
fileUpload.addEventListener('change', (e) => {
this.handleFileSelection(e.target.files);
});
// ๋๋๊ทธ ์ค ๋๋กญ
fileUploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
fileUploadArea.classList.add('drag-over');
});
fileUploadArea.addEventListener('dragleave', () => {
fileUploadArea.classList.remove('drag-over');
});
fileUploadArea.addEventListener('drop', (e) => {
e.preventDefault();
fileUploadArea.classList.remove('drag-over');
this.handleFileSelection(e.dataTransfer.files);
});
}
}
async checkSession() {
try {
const response = await fetch('/api/auth/session');
if (response.ok) {
const data = await response.json();
if (data.user) {
this.currentUser = data.user;
this.isLoggedIn = true;
await this.loadData();
}
}
} catch (error) {
console.log('์ธ์
ํ์ธ ์คํจ:', error);
}
}
async handleLogin() {
const email = document.getElementById('adminEmail').value.trim();
const password = document.getElementById('adminPassword').value;
const loginBtn = document.getElementById('loginBtn');
if (!email || !password) {
this.showNotification('์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.', 'error');
return;
}
try {
loginBtn.disabled = true;
loginBtn.textContent = '๋ก๊ทธ์ธ ์ค...';
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (response.ok) {
this.currentUser = data.user;
this.isLoggedIn = true;
this.showNotification('๋ก๊ทธ์ธ๋์์ต๋๋ค!', 'success');
await this.loadData();
this.updateUI();
} else {
throw new Error(data.message || '๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค.');
}
} catch (error) {
console.error('๋ก๊ทธ์ธ ์ค๋ฅ:', error);
this.showNotification(error.message || '๋ก๊ทธ์ธ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
} finally {
loginBtn.disabled = false;
loginBtn.textContent = '๋ก๊ทธ์ธ';
}
}
async handleLogout() {
try {
await fetch('/api/auth/logout', { method: 'POST' });
this.currentUser = null;
this.isLoggedIn = false;
this.files = [];
this.categories = [];
this.showNotification('๋ก๊ทธ์์๋์์ต๋๋ค.', 'info');
this.updateUI();
// ํผ ์ด๊ธฐํ
document.getElementById('adminPassword').value = '';
} catch (error) {
console.error('๋ก๊ทธ์์ ์ค๋ฅ:', error);
this.showNotification('๋ก๊ทธ์์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
}
}
async loadData() {
if (!this.isLoggedIn) return;
try {
// ํ์ผ ๋ชฉ๋ก ๋ก๋
const filesData = await window.AdminAPI.Files.getAll();
this.files = filesData.data || [];
console.log('ํ์ผ ๋ก๋ ์๋ฃ:', this.files.length, '๊ฐ');
// ์นดํ
๊ณ ๋ฆฌ ๋ชฉ๋ก ๋ก๋
const categoriesData = await window.AdminAPI.Categories.getAll();
this.categories = categoriesData.data || [];
console.log('์นดํ
๊ณ ๋ฆฌ ๋ก๋ ์๋ฃ:', this.categories.length, '๊ฐ');
console.log('์นดํ
๊ณ ๋ฆฌ ๋ฐ์ดํฐ:', this.categories);
this.filteredFiles = [...this.files];
this.renderFiles();
this.updatePagination();
this.updateCategoryOptions();
} catch (error) {
console.error('๋ฐ์ดํฐ ๋ก๋ ์ค๋ฅ:', error);
this.showNotification('๋ฐ์ดํฐ ๋ก๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
}
}
updateUI() {
const loginSection = document.getElementById('loginSection');
const adminSection = document.getElementById('adminSection');
const adminPanel = document.getElementById('adminPanel');
const adminUserEmail = document.getElementById('adminUserEmail');
if (this.isLoggedIn) {
// ๋ก๊ทธ์ธ ์ํ
if (loginSection) loginSection.style.display = 'none';
if (adminSection) adminSection.style.display = 'flex';
if (adminPanel) adminPanel.style.display = 'block';
if (adminUserEmail) adminUserEmail.textContent = this.currentUser.email;
} else {
// ๋ก๊ทธ์์ ์ํ
if (loginSection) loginSection.style.display = 'flex';
if (adminSection) adminSection.style.display = 'none';
if (adminPanel) adminPanel.style.display = 'none';
}
}
updateCategoryOptions() {
const categorySelects = ['fileCategory', 'categoryFilter', 'editCategory'];
categorySelects.forEach(selectId => {
const select = document.getElementById(selectId);
if (!select) return;
// ๊ธฐ์กด ์ต์
์ ๊ฑฐ (์ฒซ ๋ฒ์งธ ์ต์
์ ์ธ)
while (select.children.length > 1) {
select.removeChild(select.lastChild);
}
// ์นดํ
๊ณ ๋ฆฌ ์ต์
์ถ๊ฐ
this.categories.forEach(category => {
const option = document.createElement('option');
option.value = category.name;
option.textContent = category.name;
select.appendChild(option);
});
});
}
handleSearch() {
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
const categoryFilter = document.getElementById('categoryFilter').value;
let filteredFiles = this.files;
if (searchTerm) {
filteredFiles = filteredFiles.filter(file =>
file.title.toLowerCase().includes(searchTerm) ||
file.description.toLowerCase().includes(searchTerm) ||
(file.tags && this.parseJsonTags(file.tags).some(tag => tag.toLowerCase().includes(searchTerm)))
);
}
if (categoryFilter) {
filteredFiles = filteredFiles.filter(file => file.category === categoryFilter);
}
this.filteredFiles = filteredFiles;
this.currentPage = 1;
this.renderFiles();
this.updatePagination();
}
parseJsonTags(tags) {
try {
if (typeof tags === 'string') {
return JSON.parse(tags);
}
return Array.isArray(tags) ? tags : [];
} catch (error) {
return [];
}
}
renderFiles() {
const fileList = document.getElementById('fileList');
const sortBy = document.getElementById('sortBy').value;
if (!fileList) return;
// ์ ๋ ฌ
const sortedFiles = [...this.filteredFiles].sort((a, b) => {
switch (sortBy) {
case 'title':
return a.title.localeCompare(b.title);
case 'category':
return a.category.localeCompare(b.category);
case 'date':
default:
return new Date(b.created_at) - new Date(a.created_at);
}
});
// ํ์ด์ง๋ค์ด์
์ ์ฉ
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
const endIndex = startIndex + this.itemsPerPage;
const paginatedFiles = sortedFiles.slice(startIndex, endIndex);
if (sortedFiles.length === 0) {
fileList.innerHTML = `
๐ ์กฐ๊ฑด์ ๋ง๋ ์๋ฃ๊ฐ ์์ต๋๋ค. |
`;
return;
}
fileList.innerHTML = paginatedFiles.map((file, index) =>
this.createFileRowHTML(file, startIndex + index + 1)
).join('');
}
createFileRowHTML(file, rowNumber) {
const createdDate = new Date(file.created_at).toLocaleDateString('ko-KR');
const hasAttachments = file.files && file.files.length > 0;
const tags = this.parseJsonTags(file.tags);
return `
${rowNumber} |
${file.category}
|
${this.escapeHtml(file.title)}
${file.description ? ` ${this.escapeHtml(file.description)}` : ''}
${tags.length > 0 ?
` ${tags.map(tag => `#${this.escapeHtml(tag)}`).join('')} ` : ''
}
|
${hasAttachments ?
`
${file.files.map((f, index) =>
`
${this.getFileIcon(f.original_name || 'unknown')}
${this.escapeHtml(f.original_name || 'ํ์ผ')}
`
).join('')}
` :
`-`
}
|
${createdDate} |
${hasAttachments ?
`` :
''
}
|
`;
}
async handleAddFile() {
if (!this.isLoggedIn) {
this.showNotification('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.', 'error');
return;
}
const title = document.getElementById('fileTitle').value.trim();
const description = document.getElementById('fileDescription').value.trim();
const category = document.getElementById('fileCategory').value;
const tags = document.getElementById('fileTags').value.trim();
const fileInput = document.getElementById('fileUpload');
if (!title || !category) {
this.showNotification('์ ๋ชฉ๊ณผ ์นดํ
๊ณ ๋ฆฌ๋ ํ์์
๋๋ค.', 'error');
return;
}
try {
// ๋ก๋ฉ ์ํ ํ์
this.showLoadingState('ํ์ผ ๋ฑ๋ก ์ค...', true);
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = true;
submitBtn.textContent = '๋ฑ๋ก ์ค...';
const formData = new FormData();
formData.append('title', title);
formData.append('description', description);
formData.append('category', category);
formData.append('tags', JSON.stringify(tags ? tags.split(',').map(tag => tag.trim()).filter(tag => tag) : []));
// ํ์ผ ์ถ๊ฐ
if (fileInput.files.length > 0) {
for (const file of fileInput.files) {
formData.append('files', file);
}
}
const response = await fetch('/api/files', {
method: 'POST',
body: formData
});
const data = await response.json();
if (response.ok) {
this.showNotification('ํ์ผ์ด ์ฑ๊ณต์ ์ผ๋ก ๋ฑ๋ก๋์์ต๋๋ค!', 'success');
this.resetForm();
await this.loadData();
} else {
throw new Error(data.message || 'ํ์ผ ๋ฑ๋ก์ ์คํจํ์ต๋๋ค.');
}
} catch (error) {
console.error('ํ์ผ ์ถ๊ฐ ์ค๋ฅ:', error);
this.showNotification(error.message || 'ํ์ผ ๋ฑ๋ก ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
} finally {
// ๋ก๋ฉ ์ํ ํด์ ๋ฐ ๋ฒํผ ๋ณต์
this.hideLoadingState();
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = false;
submitBtn.textContent = '๐ค ์ถ๊ฐ';
}
}
async deleteFile(id) {
if (!confirm('์ ๋ง๋ก ์ด ํ์ผ์ ์ญ์ ํ์๊ฒ ์ต๋๊น?')) {
return;
}
try {
const response = await fetch(`/api/files/${id}`, {
method: 'DELETE'
});
if (response.ok) {
this.showNotification('ํ์ผ์ด ์ญ์ ๋์์ต๋๋ค.', 'success');
await this.loadData();
} else {
const data = await response.json();
throw new Error(data.message || 'ํ์ผ ์ญ์ ์ ์คํจํ์ต๋๋ค.');
}
} catch (error) {
console.error('ํ์ผ ์ญ์ ์ค๋ฅ:', error);
this.showNotification(error.message || 'ํ์ผ ์ญ์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
}
}
editFile(id) {
const file = this.files.find(f => f.id === id);
if (!file) {
this.showNotification('ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค.', 'error');
return;
}
// ์์ ๋ชจ๋ฌ์ ๋ฐ์ดํฐ ์ฑ์ฐ๊ธฐ
document.getElementById('editTitle').value = file.title;
document.getElementById('editDescription').value = file.description || '';
document.getElementById('editCategory').value = file.category;
const tags = this.parseJsonTags(file.tags);
document.getElementById('editTags').value = tags.join(', ');
// ๊ธฐ์กด ์ฒจ๋ถํ์ผ ํ์
this.renderExistingAttachments(file.files || []);
// ์ฒจ๋ถํ์ผ ์ญ์ ๋ชฉ๋ก ์ด๊ธฐํ
this.filesToDelete = [];
// ์ ํ์ผ ์
๋ ฅ ์ด๊ธฐํ
const newAttachmentsInput = document.getElementById('newAttachments');
if (newAttachmentsInput) {
newAttachmentsInput.value = '';
}
// ์ ํ์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ด๊ธฐํ
this.updateNewFilesPreview([]);
// ์์ ๋ชจ๋ฌ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ (ํ๋ฒ๋ง)
if (!this.editModalEventsBound) {
this.bindEditModalEvents();
}
this.currentEditId = id;
document.getElementById('editModal').style.display = 'flex';
}
// ๊ธฐ์กด ์ฒจ๋ถํ์ผ ๋ ๋๋ง
renderExistingAttachments(attachments) {
const container = document.getElementById('existingAttachments');
const noFilesIndicator = document.getElementById('noExistingFiles');
if (!attachments || attachments.length === 0) {
noFilesIndicator.style.display = 'block';
// ๊ธฐ์กด ํ์ผ ์์ดํ
๋ค ์ ๊ฑฐ
const existingItems = container.querySelectorAll('.attachment-item');
existingItems.forEach(item => item.remove());
return;
}
noFilesIndicator.style.display = 'none';
// ๊ธฐ์กด ์์ดํ
๋ค ์ ๊ฑฐ
const existingItems = container.querySelectorAll('.attachment-item');
existingItems.forEach(item => item.remove());
// ์๋ก์ด ์ฒจ๋ถํ์ผ ์์ดํ
๋ค ์ถ๊ฐ
attachments.forEach((file, index) => {
const fileIcon = this.getFileIcon(file.original_name);
const fileSize = this.formatFileSize(file.file_size);
const attachmentItem = document.createElement('div');
attachmentItem.className = 'attachment-item';
attachmentItem.setAttribute('data-attachment-id', file.id);
attachmentItem.innerHTML = `
${fileIcon}
${this.escapeHtml(file.original_name)}
${fileSize}
`;
container.appendChild(attachmentItem);
});
}
// ์ฒจ๋ถํ์ผ ์ญ์ ํ์
markAttachmentForDeletion(attachmentId, buttonElement) {
const attachmentItem = buttonElement.closest('.attachment-item');
if (!this.filesToDelete) {
this.filesToDelete = [];
}
if (this.filesToDelete.includes(attachmentId)) {
// ์ญ์ ์ทจ์
this.filesToDelete = this.filesToDelete.filter(id => id !== attachmentId);
attachmentItem.style.opacity = '1';
attachmentItem.style.textDecoration = 'none';
buttonElement.innerHTML = '๐๏ธ ์ญ์ ';
buttonElement.style.background = '#ef4444';
} else {
// ์ญ์ ํ์
this.filesToDelete.push(attachmentId);
attachmentItem.style.opacity = '0.5';
attachmentItem.style.textDecoration = 'line-through';
buttonElement.innerHTML = 'โถ ์ทจ์';
buttonElement.style.background = '#6b7280';
}
}
// ํ์ผ ํฌ๊ธฐ ํฌ๋งทํ
formatFileSize(bytes) {
if (!bytes) return '0 B';
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}
async handleUpdateFile() {
if (!this.currentEditId) {
this.showNotification('์์ ํ ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค.', 'error');
return;
}
// ์ค๋ณต ์คํ ๋ฐฉ์ง ํ๋๊ทธ ์ค์
if (this.isUpdating) {
console.log('์ด๋ฏธ ์
๋ฐ์ดํธ ์ค์
๋๋ค.');
return;
}
this.isUpdating = true;
const title = document.getElementById('editTitle').value.trim();
const description = document.getElementById('editDescription').value.trim();
const category = document.getElementById('editCategory').value;
const tags = document.getElementById('editTags').value.trim();
if (!title || !category) {
this.isUpdating = false; // ํ๋๊ทธ ํด์
this.showNotification('์ ๋ชฉ๊ณผ ์นดํ
๊ณ ๋ฆฌ๋ ํ์์
๋๋ค.', 'error');
return;
}
try {
// ๋ก๋ฉ ์ํ ํ์ ์์
this.showLoadingState('์์ ์ค...', true);
// FormData๋ฅผ ์ฌ์ฉํ์ฌ ํ์ผ๊ณผ ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ์ ์ก
const formData = new FormData();
formData.append('title', title);
formData.append('description', description);
formData.append('category', category);
formData.append('tags', JSON.stringify(tags ? tags.split(',').map(tag => tag.trim()).filter(tag => tag) : []));
// ์ญ์ ํ ์ฒจ๋ถํ์ผ ID๋ค
if (this.filesToDelete && this.filesToDelete.length > 0) {
formData.append('filesToDelete', JSON.stringify(this.filesToDelete));
}
// ์๋ก ์ถ๊ฐํ ์ฒจ๋ถํ์ผ๋ค
const newFileInput = document.getElementById('newAttachments');
if (newFileInput && newFileInput.files.length > 0) {
for (const file of newFileInput.files) {
formData.append('files', file);
}
}
// API ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ์ฌ ํ์ผ ์
๋ฐ์ดํธ
const data = await window.AdminAPI.Files.update(this.currentEditId, formData);
this.showNotification('ํ์ผ์ด ์ฑ๊ณต์ ์ผ๋ก ์์ ๋์์ต๋๋ค!', 'success');
document.getElementById('editModal').style.display = 'none';
this.currentEditId = null;
this.filesToDelete = [];
// ์ ํ์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ด๊ธฐํ
this.updateNewFilesPreview([]);
const newAttachmentsInput = document.getElementById('newAttachments');
if (newAttachmentsInput) {
newAttachmentsInput.value = '';
}
await this.loadData();
} catch (error) {
console.error('ํ์ผ ์์ ์ค๋ฅ:', error);
this.showNotification(error.message || 'ํ์ผ ์์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
} finally {
// ์์
์๋ฃ ํ ํ๋๊ทธ ํด์ ๋ฐ ๋ก๋ฉ ์ํ ์ ๊ฑฐ
this.isUpdating = false;
this.hideLoadingState();
}
}
async downloadFiles(id) {
console.log('downloadFiles ํธ์ถ๋จ:', id);
const file = this.files.find(f => f.id === id);
console.log('์ฐพ์ ํ์ผ:', file);
if (!file || !file.files || file.files.length === 0) {
console.log('์ฒจ๋ถํ์ผ ์์');
this.showNotification('์ฒจ๋ถํ์ผ์ด ์์ต๋๋ค.', 'error');
return;
}
try {
console.log('๋ค์ด๋ก๋ ์์, ํ์ผ ๊ฐ์:', file.files.length);
if (file.files.length === 1) {
// ๋จ์ผ ํ์ผ: ์ง์ ๋ค์ด๋ก๋
console.log('๋จ์ผ ํ์ผ ๋ค์ด๋ก๋');
await this.downloadSingleFile(id, 0);
this.showNotification('ํ์ผ ๋ค์ด๋ก๋ ์๋ฃ', 'success');
} else {
// ๋ค์ค ํ์ผ: ๊ฐ๊ฐ ๋ค์ด๋ก๋
console.log('๋ค์ค ํ์ผ ๋ค์ด๋ก๋');
for (let i = 0; i < file.files.length; i++) {
console.log(`ํ์ผ ${i + 1}/${file.files.length} ๋ค์ด๋ก๋ ์ค`);
await this.downloadSingleFile(id, i);
// ์งง์ ๋๋ ์ด๋ฅผ ์ถ๊ฐํ์ฌ ๋ธ๋ผ์ฐ์ ๊ฐ ๋ค์ด๋ก๋๋ฅผ ์ฒ๋ฆฌํ ์๊ฐ์ ์ค
await new Promise(resolve => setTimeout(resolve, 500));
}
this.showNotification(`${file.files.length}๊ฐ ํ์ผ ๋ค์ด๋ก๋ ์๋ฃ`, 'success');
}
} catch (error) {
console.error('ํ์ผ ๋ค์ด๋ก๋ ์ค๋ฅ:', error);
this.showNotification(`๋ค์ด๋ก๋ ์ค๋ฅ: ${error.message}`, 'error');
}
}
async downloadSingleFile(fileId, attachmentIndex) {
try {
// ๋ค์ด๋ก๋ ์์ ๋ก๋ฉ ํ์
this.showLoadingState('๋ค์ด๋ก๋ ์ค๋น ์ค...', false);
console.log('downloadSingleFile ํธ์ถ๋จ:', fileId, attachmentIndex);
const file = this.files.find(f => f.id === fileId);
console.log('์ฐพ์ ํ์ผ:', file);
if (!file || !file.files[attachmentIndex]) {
console.log('ํ์ผ ๋๋ ์ฒจ๋ถํ์ผ์ ์ฐพ์ ์ ์์');
throw new Error('ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค.');
}
const attachmentId = file.files[attachmentIndex].id;
const downloadUrl = `/api/download/${fileId}/${attachmentId}`;
console.log('๋ค์ด๋ก๋ URL:', downloadUrl);
const response = await fetch(downloadUrl, {
credentials: 'include'
});
console.log('์๋ต ์ํ:', response.status, response.statusText);
if (!response.ok) {
const errorText = await response.text();
console.log('์๋ต ์ค๋ฅ:', errorText);
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
}
console.log('๋ค์ด๋ก๋ ์์...');
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
// ํ์ผ๋ช
์ ์๋ฒ์์ ์ ์ก๋ ์ ๋ณด์์ ์ถ์ถ (๊ฐ์ ๋ ๋ฐฉ์)
const contentDisposition = response.headers.get('Content-Disposition');
let filename = file.files[attachmentIndex].original_name || `download_${Date.now()}`;
console.log('๐ ๋ค์ด๋ก๋ ํ์ผ๋ช
์ฒ๋ฆฌ:', {
original_name: file.files[attachmentIndex].original_name,
content_disposition: contentDisposition,
default_filename: filename
});
if (contentDisposition) {
// RFC 5987 filename* ํ๋ผ๋ฏธํฐ๋ฅผ ์ฐ์ ์ฒ๋ฆฌ (UTF-8 ์ง์)
const filenameStarMatch = contentDisposition.match(/filename\*=UTF-8''([^;]+)/);
if (filenameStarMatch) {
filename = decodeURIComponent(filenameStarMatch[1]);
console.log('๐ UTF-8 ํ์ผ๋ช
์ถ์ถ:', filename);
} else {
// ์ผ๋ฐ filename ํ๋ผ๋ฏธํฐ ์ฒ๋ฆฌ
const filenameMatch = contentDisposition.match(/filename="?([^";\r\n]+)"?/);
if (filenameMatch) {
filename = filenameMatch[1];
console.log('๐ ๊ธฐ๋ณธ ํ์ผ๋ช
์ถ์ถ:', filename);
}
}
}
// ํ์ผ๋ช
์ด ์ฌ์ ํ ๋น์ด์๋ค๋ฉด ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ
if (!filename || filename.trim() === '') {
filename = file.files[attachmentIndex].original_name || `download_${Date.now()}`;
console.log('๐ ๊ธฐ๋ณธ ํ์ผ๋ช
์ฌ์ฉ:', filename);
}
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
console.log('๋ค์ด๋ก๋ ์๋ฃ');
this.hideLoadingState();
} catch (error) {
console.error('downloadSingleFile ์ค๋ฅ:', error);
this.hideLoadingState();
throw error;
}
}
handleFileSelection(files) {
const selectedFiles = document.getElementById('selectedFiles');
if (!selectedFiles) return;
selectedFiles.innerHTML = '';
Array.from(files).forEach(file => {
const fileItem = document.createElement('div');
fileItem.className = 'selected-file-item';
fileItem.innerHTML = `
${this.getFileIcon(file.name)}
${this.escapeHtml(file.name)}
${this.formatFileSize(file.size)}
`;
selectedFiles.appendChild(fileItem);
});
}
getFileIcon(fileName) {
const ext = fileName.split('.').pop().toLowerCase();
const iconMap = {
'pdf': '๐',
'doc': '๐', 'docx': '๐',
'xls': '๐', 'xlsx': '๐',
'ppt': '๐ฝ๏ธ', 'pptx': '๐ฝ๏ธ',
'jpg': '๐ผ๏ธ', 'jpeg': '๐ผ๏ธ', 'png': '๐ผ๏ธ', 'gif': '๐ผ๏ธ',
'mp4': '๐ฅ', 'avi': '๐ฅ', 'mov': '๐ฅ',
'mp3': '๐ต', 'wav': '๐ต',
'zip': '๐ฆ', 'rar': '๐ฆ',
'txt': '๐'
};
return iconMap[ext] || '๐';
}
formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}
resetForm() {
document.getElementById('fileTitle').value = '';
document.getElementById('fileDescription').value = '';
document.getElementById('fileCategory').value = '';
document.getElementById('fileTags').value = '';
document.getElementById('fileUpload').value = '';
const selectedFiles = document.getElementById('selectedFiles');
if (selectedFiles) selectedFiles.innerHTML = '';
}
updatePagination() {
const totalPages = Math.max(1, Math.ceil(this.filteredFiles.length / this.itemsPerPage));
const pagination = document.getElementById('pagination');
const prevBtn = document.getElementById('prevPage');
const nextBtn = document.getElementById('nextPage');
const pageInfo = document.getElementById('pageInfo');
if (!pagination) return;
pagination.style.display = 'flex';
if (prevBtn) prevBtn.disabled = this.currentPage <= 1;
if (nextBtn) nextBtn.disabled = this.currentPage >= totalPages || this.filteredFiles.length === 0;
const displayTotalPages = this.filteredFiles.length === 0 ? 1 : totalPages;
const displayCurrentPage = this.filteredFiles.length === 0 ? 1 : this.currentPage;
if (pageInfo) pageInfo.textContent = `${displayCurrentPage} / ${displayTotalPages}`;
}
goToPrevPage() {
if (this.currentPage > 1) {
this.currentPage--;
this.renderFiles();
this.updatePagination();
}
}
goToNextPage() {
const totalPages = Math.ceil(this.filteredFiles.length / this.itemsPerPage);
if (this.currentPage < totalPages) {
this.currentPage++;
this.renderFiles();
this.updatePagination();
}
}
showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
border-radius: 8px;
color: white;
font-weight: 500;
z-index: 10000;
max-width: 400px;
background: ${type === 'success' ? '#48bb78' : type === 'error' ? '#f56565' : '#4299e1'};
`;
document.body.appendChild(notification);
setTimeout(() => {
if (document.body.contains(notification)) {
document.body.removeChild(notification);
}
}, 3000);
}
showFileDetail(id) {
const file = this.files.find(f => f.id === id);
if (!file) {
this.showNotification('ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค.', 'error');
return;
}
const tags = this.parseJsonTags(file.tags);
const createdDate = new Date(file.created_at).toLocaleString('ko-KR');
const updatedDate = new Date(file.updated_at).toLocaleString('ko-KR');
const modalHTML = `
${this.escapeHtml(file.title)}
${file.description ? `
${this.escapeHtml(file.description)}
` : ''}
${file.category}
${tags.length > 0 ? `
${tags.map(tag => `#${this.escapeHtml(tag)}`).join('')}
` : ''}
${file.files && file.files.length > 0 ? `
${file.files.map((f, index) => `
${this.getFileIcon(f.original_name || 'unknown')}
${this.escapeHtml(f.original_name || 'ํ์ผ')}
`).join('')}
` : `
์ฒจ๋ถํ์ผ์ด ์์ต๋๋ค.
`}
${createdDate}
${updatedDate}
`;
document.body.insertAdjacentHTML('beforeend', modalHTML);
}
async handleAddCategory() {
const categoryName = document.getElementById('categoryName').value.trim();
if (!categoryName) {
this.showNotification('์นดํ
๊ณ ๋ฆฌ ์ด๋ฆ์ ์
๋ ฅํด์ฃผ์ธ์.', 'error');
return;
}
// ์ค๋ณต ํ์ธ
if (this.categories.some(cat => cat.name === categoryName)) {
this.showNotification('์ด๋ฏธ ์กด์ฌํ๋ ์นดํ
๊ณ ๋ฆฌ์
๋๋ค.', 'error');
return;
}
try {
const data = await window.AdminAPI.Categories.create(categoryName);
this.showNotification('์นดํ
๊ณ ๋ฆฌ๊ฐ ์ถ๊ฐ๋์์ต๋๋ค!', 'success');
this.resetCategoryForm();
await this.loadData();
this.renderCategoryList();
} catch (error) {
console.error('์นดํ
๊ณ ๋ฆฌ ์ถ๊ฐ ์ค๋ฅ:', error);
this.showNotification(error.message || '์นดํ
๊ณ ๋ฆฌ ์ถ๊ฐ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
}
}
resetCategoryForm() {
document.getElementById('categoryName').value = '';
}
renderCategoryList() {
console.log('renderCategoryList ํธ์ถ๋จ');
console.log('ํ์ฌ ์นดํ
๊ณ ๋ฆฌ ๋ฐ์ดํฐ:', this.categories);
const categoryList = document.getElementById('categoryList');
if (!categoryList) {
console.error('categoryList ์์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค');
return;
}
if (this.categories.length === 0) {
console.log('์นดํ
๊ณ ๋ฆฌ๊ฐ ์์ด์ ๋น ๋ฉ์์ง ํ์');
categoryList.innerHTML = '๋ฑ๋ก๋ ์นดํ
๊ณ ๋ฆฌ๊ฐ ์์ต๋๋ค.
';
return;
}
console.log('์นดํ
๊ณ ๋ฆฌ ๋ชฉ๋ก ๋ ๋๋ง ์์');
categoryList.innerHTML = this.categories.map(category => {
console.log('์นดํ
๊ณ ๋ฆฌ ๋ ๋๋ง:', category);
return `
${this.escapeHtml(category.name)}
`;
}).join('');
console.log('์นดํ
๊ณ ๋ฆฌ ๋ชฉ๋ก ๋ ๋๋ง ์๋ฃ');
}
editCategory(id) {
console.log('editCategory ํธ์ถ๋จ, ID:', id, 'Type:', typeof id);
console.log('์ ์ฒด ์นดํ
๊ณ ๋ฆฌ ๋ชฉ๋ก:', this.categories);
const category = this.categories.find(c => {
console.log('๋น๊ต:', c.id, 'vs', id, 'Type:', typeof c.id, 'vs', typeof id);
return c.id == id; // == ์ฌ์ฉ์ผ๋ก ํ์
๋ณํ ํ์ฉ
});
console.log('์ฐพ์ ์นดํ
๊ณ ๋ฆฌ:', category);
if (!category) {
console.error('์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ID:', id);
this.showNotification('์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.', 'error');
return;
}
document.getElementById('editCategoryName').value = category.name;
this.currentEditCategoryId = id;
document.getElementById('editCategoryModal').style.display = 'flex';
}
async handleUpdateCategory() {
if (!this.currentEditCategoryId) {
this.showNotification('์์ ํ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.', 'error');
return;
}
const categoryName = document.getElementById('editCategoryName').value.trim();
if (!categoryName) {
this.showNotification('์นดํ
๊ณ ๋ฆฌ ์ด๋ฆ์ ์
๋ ฅํด์ฃผ์ธ์.', 'error');
return;
}
// ์ค๋ณต ํ์ธ (์๊ธฐ ์์ ์ ์ธ)
if (this.categories.some(cat => cat.name === categoryName && cat.id !== this.currentEditCategoryId)) {
this.showNotification('์ด๋ฏธ ์กด์ฌํ๋ ์นดํ
๊ณ ๋ฆฌ์
๋๋ค.', 'error');
return;
}
try {
const data = await window.AdminAPI.Categories.update(this.currentEditCategoryId, categoryName);
this.showNotification('์นดํ
๊ณ ๋ฆฌ๊ฐ ์์ ๋์์ต๋๋ค!', 'success');
document.getElementById('editCategoryModal').style.display = 'none';
this.currentEditCategoryId = null;
await this.loadData();
this.renderCategoryList();
} catch (error) {
console.error('์นดํ
๊ณ ๋ฆฌ ์์ ์ค๋ฅ:', error);
this.showNotification(error.message || '์นดํ
๊ณ ๋ฆฌ ์์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
}
}
async deleteCategory(id) {
console.log('deleteCategory ํธ์ถ๋จ, ID:', id, 'Type:', typeof id);
console.log('์ ์ฒด ์นดํ
๊ณ ๋ฆฌ ๋ชฉ๋ก:', this.categories);
const category = this.categories.find(c => {
console.log('๋น๊ต:', c.id, 'vs', id, 'Type:', typeof c.id, 'vs', typeof id);
return c.id == id; // == ์ฌ์ฉ์ผ๋ก ํ์
๋ณํ ํ์ฉ
});
console.log('์ฐพ์ ์นดํ
๊ณ ๋ฆฌ:', category);
if (!category) {
console.error('์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ID:', id);
this.showNotification('์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.', 'error');
return;
}
// ํด๋น ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ํ์ผ์ด ์๋์ง ํ์ธ
const filesUsingCategory = this.files.filter(file => file.category === category.name);
if (filesUsingCategory.length > 0) {
if (!confirm(`์ด ์นดํ
๊ณ ๋ฆฌ๋ ${filesUsingCategory.length}๊ฐ์ ํ์ผ์์ ์ฌ์ฉ ์ค์
๋๋ค.\n์ ๋ง๋ก ์ญ์ ํ์๊ฒ ์ต๋๊น? (์ฌ์ฉ ์ค์ธ ํ์ผ๋ค์ ์นดํ
๊ณ ๋ฆฌ๊ฐ '๊ธฐํ'๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค.)`)) {
return;
}
} else {
if (!confirm(`์ ๋ง๋ก '${category.name}' ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ญ์ ํ์๊ฒ ์ต๋๊น?`)) {
return;
}
}
try {
await window.AdminAPI.Categories.delete(id);
this.showNotification('์นดํ
๊ณ ๋ฆฌ๊ฐ ์ญ์ ๋์์ต๋๋ค.', 'success');
await this.loadData();
this.renderCategoryList();
} catch (error) {
console.error('์นดํ
๊ณ ๋ฆฌ ์ญ์ ์ค๋ฅ:', error);
this.showNotification(error.message || '์นดํ
๊ณ ๋ฆฌ ์ญ์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.', 'error');
}
}
// ๋ก๋ฉ ์ํ ํ์
showLoadingState(message = '์ฒ๋ฆฌ ์ค...', disableForm = false) {
console.log('๐ ๋ก๋ฉ ์ํ ํ์:', message);
// ๊ธฐ์กด ๋ก๋ฉ ์ธ๋์ผ์ดํฐ ์ ๊ฑฐ
this.hideLoadingState();
// ๋ก๋ฉ ์ค๋ฒ๋ ์ด ์์ฑ
const loadingOverlay = document.createElement('div');
loadingOverlay.id = 'loadingOverlay';
loadingOverlay.className = 'loading-overlay';
loadingOverlay.innerHTML = `
`;
document.body.appendChild(loadingOverlay);
// ํผ ๋นํ์ฑํ (์ ํ์ )
if (disableForm) {
const forms = document.querySelectorAll('form, button');
forms.forEach(element => {
element.style.pointerEvents = 'none';
element.style.opacity = '0.6';
});
}
}
// ๋ก๋ฉ ์ํ ์จ๊น
hideLoadingState() {
console.log('โ
๋ก๋ฉ ์ํ ํด์ ');
// ๋ก๋ฉ ์ค๋ฒ๋ ์ด ์ ๊ฑฐ
const loadingOverlay = document.getElementById('loadingOverlay');
if (loadingOverlay) {
loadingOverlay.remove();
}
// ํผ ํ์ฑํ
const forms = document.querySelectorAll('form, button');
forms.forEach(element => {
element.style.pointerEvents = '';
element.style.opacity = '';
});
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
// ์ ์ญ ์ธ์คํด์ค ์์ฑ
let adminManager;
document.addEventListener('DOMContentLoaded', () => {
adminManager = new AdminFileManager();
window.adminManager = adminManager; // ์ ์ญ ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก
});