diff --git a/admin/script.js b/admin/script.js
index d6576ba..f2ecb83 100644
--- a/admin/script.js
+++ b/admin/script.js
@@ -975,28 +975,44 @@ class FileManager {
const tbody = document.getElementById('fileList');
const sortBy = document.getElementById('sortBy').value;
- let sortedFiles = [...this.files];
+ // 검색 및 필터 적용 (메인 페이지와 동일하게)
+ const searchTerm = document.getElementById('searchInput') ? document.getElementById('searchInput').value.toLowerCase().trim() : '';
+ const categoryFilter = document.getElementById('categoryFilter') ? document.getElementById('categoryFilter').value : '';
- switch (sortBy) {
- case 'title':
- sortedFiles.sort((a, b) => a.title.localeCompare(b.title));
- break;
- case 'category':
- sortedFiles.sort((a, b) => a.category.localeCompare(b.category));
- break;
- case 'date':
- default:
- sortedFiles.sort((a, b) => new Date(b.created_at || b.createdAt) - new Date(a.created_at || a.createdAt));
- break;
+ let filteredFiles = [...this.files];
+
+ if (searchTerm) {
+ filteredFiles = filteredFiles.filter(file =>
+ file.title.toLowerCase().includes(searchTerm) ||
+ file.description.toLowerCase().includes(searchTerm) ||
+ (file.tags && file.tags.some(tag => tag.toLowerCase().includes(searchTerm)))
+ );
}
+
+ if (categoryFilter) {
+ filteredFiles = filteredFiles.filter(file => file.category === categoryFilter);
+ }
+
+ // 정렬
+ const sortedFiles = 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 || b.createdAt) - new Date(a.created_at || a.createdAt);
+ }
+ });
- this.allFiles = sortedFiles; // 전체 파일 목록 저장
+ this.allFiles = sortedFiles;
this.updatePagination();
if (sortedFiles.length === 0) {
tbody.innerHTML = `
- 📂 등록된 자료가 없습니다. 새 자료를 추가해보세요! |
+ 📂 조건에 맞는 자료가 없습니다. |
`;
return;
@@ -1026,7 +1042,7 @@ class FileManager {
- ${this.escapeHtml(file.title)}
+ ${this.escapeHtml(file.title)}
${file.description ? ` ${this.escapeHtml(file.description.substring(0, 80))}${file.description.length > 80 ? '...' : ''}` : ''}
${file.tags && file.tags.length > 0 ?
` ${file.tags.map(tag => `#${this.escapeHtml(tag)}`).join('')} ` : ''
@@ -1035,9 +1051,15 @@ class FileManager {
|
${hasAttachments ?
- ` ${file.files.map((f, index) =>
- `${this.getFileIcon(f.name || f.original_name || 'unknown')}`
- ).join(' ')} ` :
+ `${file.files.map((f, index) =>
+ `
+ ${this.getFileIcon(f.name || f.original_name || 'unknown')}
+
+ ${this.escapeHtml(f.name || f.original_name || '파일')}
+ ${this.formatFileSize(f.size || 0)}
+
+ `
+ ).join('')} ` :
`-`
}
|
@@ -1104,23 +1126,23 @@ class FileManager {
// 페이지네이션 관련 함수들
updatePagination() {
+ const totalPages = Math.max(1, Math.ceil(this.allFiles.length / 10));
const pagination = document.getElementById('pagination');
const prevBtn = document.getElementById('prevPage');
const nextBtn = document.getElementById('nextPage');
const pageInfo = document.getElementById('pageInfo');
- const totalFiles = this.allFiles.length;
- const itemsPerPage = 10;
- const totalPages = Math.ceil(totalFiles / itemsPerPage);
+ // 항상 페이지네이션을 표시
+ if (pagination) pagination.style.display = 'flex';
- if (totalPages <= 1) {
- pagination.style.display = 'none';
- } else {
- pagination.style.display = 'flex';
- prevBtn.disabled = this.currentPage <= 1;
- nextBtn.disabled = this.currentPage >= totalPages;
- pageInfo.textContent = `${this.currentPage} / ${totalPages}`;
- }
+ // 페이지 버튼 상태 업데이트
+ if (prevBtn) prevBtn.disabled = this.currentPage <= 1;
+ if (nextBtn) nextBtn.disabled = this.currentPage >= totalPages || this.allFiles.length === 0;
+
+ // 페이지 정보 표시 (아이템이 없어도 1/1로 표시)
+ const displayTotalPages = this.allFiles.length === 0 ? 1 : totalPages;
+ const displayCurrentPage = this.allFiles.length === 0 ? 1 : this.currentPage;
+ if (pageInfo) pageInfo.textContent = `${displayCurrentPage} / ${displayTotalPages}`;
}
goToPrevPage() {
@@ -1145,22 +1167,141 @@ class FileManager {
viewFile(id) {
const file = this.files.find(f => f.id === id);
if (!file) return;
+
+ this.showDetailView(file);
+ }
+
+ showDetailView(file) {
+ // 메인 컨테이너 숨기기
+ const container = document.querySelector('.container');
+ container.style.display = 'none';
- // 간단한 알림으로 파일 정보 표시
- let info = `📄 ${file.title}\n\n`;
- info += `📁 카테고리: ${file.category}\n`;
- info += `📅 등록일: ${new Date(file.created_at || file.createdAt).toLocaleDateString('ko-KR')}\n`;
- if (file.description) info += `📝 설명: ${file.description}\n`;
- if (file.tags && file.tags.length > 0) info += `🏷️ 태그: ${file.tags.join(', ')}\n`;
- if (file.files && file.files.length > 0) {
- info += `\n📎 첨부파일 (${file.files.length}개):\n`;
- file.files.forEach((attachment, index) => {
- const icon = this.getFileIcon(attachment.name || attachment.original_name || 'unknown');
- info += ` ${index + 1}. ${icon} ${attachment.name || attachment.original_name || '파일'}\n`;
- });
+ // 상세보기 컨테이너 생성
+ const detailContainer = document.createElement('div');
+ detailContainer.className = 'detail-container';
+ detailContainer.id = 'detailContainer';
+
+ const createdDate = new Date(file.created_at || file.createdAt).toLocaleDateString('ko-KR');
+ const updatedDate = new Date(file.updated_at || file.updatedAt).toLocaleDateString('ko-KR');
+
+ detailContainer.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+ ${file.category}
+
+
+
+
+
+
+ ${file.description ? this.escapeHtml(file.description) : '설명이 없습니다.'}
+
+
+
+ ${file.tags && file.tags.length > 0 ? `
+
+
+
+
+ ${file.tags.map(tag => `#${this.escapeHtml(tag)}`).join('')}
+
+
+
` : ''}
+
+ ${file.files && file.files.length > 0 ? `
+
+
+
+
+ ${file.files.map((f, index) => `
+
+ ${this.getFileIcon(f.name || f.original_name || 'unknown')}
+ ${this.escapeHtml(f.name || f.original_name || '파일')}
+
+
+ `).join('')}
+
+
+
+
+
+
` : `
+
+
+
+ 첨부된 파일이 없습니다.
+
+
`}
+
+
+
+
${createdDate}
+
+
+ ${createdDate !== updatedDate ? `
+
+
+
${updatedDate}
+
` : ''}
+
+
+
+
+ `;
+
+ document.body.appendChild(detailContainer);
+ }
+
+ hideDetailView() {
+ const detailContainer = document.getElementById('detailContainer');
+ if (detailContainer) {
+ detailContainer.remove();
}
- alert(info);
+ // 메인 컨테이너 다시 보이기
+ const container = document.querySelector('.container');
+ container.style.display = 'block';
+ }
+
+ editFileFromDetail(id) {
+ this.hideDetailView();
+ this.editFile(id);
+ }
+
+ async deleteFileFromDetail(id) {
+ if (confirm('정말로 이 자료를 삭제하시겠습니까?')) {
+ await this.deleteFile(id);
+ this.hideDetailView();
+ }
}
editFile(id) {
@@ -1331,49 +1472,11 @@ class FileManager {
const searchTerm = document.getElementById('searchInput').value.toLowerCase().trim();
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.some(tag => tag.toLowerCase().includes(searchTerm))
- );
- }
-
- if (categoryFilter) {
- filteredFiles = filteredFiles.filter(file => file.category === categoryFilter);
- }
-
- this.renderFilteredFiles(filteredFiles);
+ // 검색 시 첫 페이지로 리셋
+ this.currentPage = 1;
+ this.renderFiles();
}
- renderFilteredFiles(files) {
- const container = document.getElementById('fileList');
- const sortBy = document.getElementById('sortBy').value;
-
- let sortedFiles = [...files];
-
- switch (sortBy) {
- case 'title':
- sortedFiles.sort((a, b) => a.title.localeCompare(b.title));
- break;
- case 'category':
- sortedFiles.sort((a, b) => a.category.localeCompare(b.category));
- break;
- case 'date':
- default:
- sortedFiles.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
- break;
- }
-
- if (sortedFiles.length === 0) {
- container.innerHTML = '🔍 검색 결과가 없습니다. 다른 키워드로 검색해보세요!
';
- return;
- }
-
- container.innerHTML = sortedFiles.map(file => this.createFileHTML(file)).join('');
- }
clearForm() {
document.getElementById('fileForm').reset();
diff --git a/admin/styles.css b/admin/styles.css
index 1e87c07..724e5a4 100644
--- a/admin/styles.css
+++ b/admin/styles.css
@@ -385,7 +385,7 @@ header p {
}
.board-table th {
- padding: 15px 12px;
+ padding: 12px;
text-align: center;
font-weight: 600;
font-size: 0.95rem;
@@ -410,17 +410,18 @@ header p {
}
.board-table td {
- padding: 12px;
+ padding: 8px 12px;
text-align: center;
vertical-align: middle;
font-size: 0.9rem;
+ line-height: 1.4;
}
/* 컬럼 너비 설정 */
.col-no { width: 60px; }
.col-category { width: 100px; }
.col-title { width: auto; min-width: 200px; text-align: left; }
-.col-attachment { width: 80px; }
+.col-attachment { width: 220px; text-align: center; }
.col-date { width: 120px; }
.col-actions { width: 150px; }
@@ -431,7 +432,7 @@ header p {
text-decoration: none;
cursor: pointer;
display: block;
- padding: 8px;
+ padding: 4px 6px;
border-radius: 4px;
transition: all 0.2s ease;
}
@@ -465,16 +466,61 @@ header p {
.attachment-icons {
display: flex;
- gap: 2px;
- justify-content: center;
+ flex-direction: column;
+ gap: 4px;
align-items: center;
- flex-wrap: wrap;
- font-size: 1rem;
+ font-size: 0.85rem;
line-height: 1.2;
+ max-height: 100px;
+ overflow-y: auto;
+ padding: 2px;
}
-.attachment-icons span {
- display: inline-block;
+.attachment-file-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 2px;
+ cursor: pointer;
+ padding: 3px 4px;
+ border-radius: 3px;
+ transition: background-color 0.2s ease;
+ justify-content: center;
+ white-space: nowrap;
+}
+
+.attachment-file-item:hover {
+ background-color: #f0f9ff;
+}
+
+.attachment-file-icon {
+ font-size: 1.1rem;
+ width: auto;
+ text-align: left;
+ flex-shrink: 0;
+ margin-right: 1px;
+}
+
+.attachment-file-info {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ flex: 1;
+ min-width: 0;
+ margin-left: 0;
+}
+
+.attachment-file-name {
+ font-weight: 500;
+ color: #374151;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-size: 0.8rem;
+}
+
+.attachment-file-size {
+ color: #6b7280;
+ font-size: 0.7rem;
}
.attachment-icon-clickable {
@@ -496,15 +542,15 @@ header p {
/* 액션 버튼 */
.action-buttons {
display: flex;
- gap: 5px;
+ gap: 3px;
justify-content: center;
align-items: center;
}
.action-btn {
- padding: 6px 12px;
+ padding: 4px 8px;
border: none;
- border-radius: 4px;
+ border-radius: 3px;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.2s ease;
@@ -549,6 +595,298 @@ header p {
font-size: 1.1rem;
}
+/* 상세보기 페이지 스타일 */
+.detail-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ z-index: 1000;
+ overflow-y: auto;
+}
+
+.detail-section {
+ background: rgba(255, 255, 255, 0.95);
+ padding: 30px;
+ border-radius: 15px;
+ margin-bottom: 30px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+}
+
+.detail-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 30px;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.detail-header h2 {
+ color: #4a5568;
+ font-size: 2rem;
+ margin: 0;
+ flex: 1;
+}
+
+.detail-actions {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.edit-detail-btn {
+ padding: 10px 20px;
+ background: #3b82f6;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 0.95rem;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.edit-detail-btn:hover {
+ background: #2563eb;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
+}
+
+.delete-detail-btn {
+ padding: 10px 20px;
+ background: #ef4444;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 0.95rem;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.delete-detail-btn:hover {
+ background: #dc2626;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
+}
+
+.back-btn {
+ padding: 12px 24px;
+ background: #667eea;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 1rem;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.back-btn:hover {
+ background: #5a67d8;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+}
+
+.detail-content {
+ display: grid;
+ gap: 20px;
+}
+
+.detail-info {
+ display: grid;
+ gap: 25px;
+}
+
+.info-group {
+ display: grid;
+ gap: 10px;
+}
+
+.info-group label {
+ font-weight: 600;
+ color: #4a5568;
+ font-size: 1.1rem;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.info-value {
+ padding: 15px;
+ background: #f8fafc;
+ border-radius: 8px;
+ border: 2px solid #e2e8f0;
+ font-size: 1rem;
+ line-height: 1.6;
+}
+
+.info-value.description {
+ min-height: 80px;
+ white-space: pre-wrap;
+}
+
+.info-value.no-files {
+ color: #9ca3af;
+ font-style: italic;
+}
+
+.tags-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.tag {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ color: white;
+ padding: 6px 12px;
+ border-radius: 20px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ border: none;
+}
+
+.attachments-list {
+ display: grid;
+ gap: 12px;
+ margin-bottom: 20px;
+}
+
+.attachment-item {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ padding: 15px;
+ background: white;
+ border-radius: 8px;
+ border: 1px solid #e5e7eb;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ transition: all 0.2s ease;
+}
+
+.attachment-item:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-1px);
+}
+
+.attachment-icon {
+ font-size: 1.5rem;
+ min-width: 30px;
+ text-align: center;
+}
+
+.attachment-name {
+ flex: 1;
+ font-weight: 500;
+ color: #374151;
+ word-break: break-all;
+}
+
+.download-single-btn {
+ background: #10b981;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 0.9rem;
+ font-weight: 500;
+ transition: all 0.2s ease;
+}
+
+.download-single-btn:hover {
+ background: #059669;
+ transform: scale(1.05);
+}
+
+.attachment-actions {
+ padding-top: 15px;
+ border-top: 1px solid #e5e7eb;
+ text-align: center;
+}
+
+.download-all-btn {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ color: white;
+ border: none;
+ padding: 12px 24px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 1rem;
+ font-weight: 600;
+ transition: all 0.3s ease;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.download-all-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3);
+}
+
+/* 모바일 반응형 */
+@media (max-width: 768px) {
+ .detail-section {
+ padding: 20px;
+ margin: 10px;
+ border-radius: 10px;
+ }
+
+ .detail-header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .detail-header h2 {
+ font-size: 1.5rem;
+ }
+
+ .detail-actions {
+ width: 100%;
+ justify-content: stretch;
+ }
+
+ .detail-actions button {
+ flex: 1;
+ min-width: 0;
+ }
+
+ .back-btn {
+ order: 3;
+ width: 100%;
+ justify-content: center;
+ margin-top: 10px;
+ }
+
+ .attachment-item {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ }
+
+ .download-single-btn {
+ align-self: stretch;
+ text-align: center;
+ }
+}
+
/* 파일 업로드 영역 */
.file-upload-area {
position: relative;
diff --git a/script.js b/script.js
index 0925f61..9ab1282 100644
--- a/script.js
+++ b/script.js
@@ -65,7 +65,7 @@ class FileManager {
const fileList = document.getElementById('fileList');
const sortBy = document.getElementById('sortBy').value;
- // 정렬
+ // 정렬 (관리자 페이지와 동일하게)
const sortedFiles = [...this.filteredFiles].sort((a, b) => {
switch (sortBy) {
case 'title':
@@ -74,7 +74,7 @@ class FileManager {
return a.category.localeCompare(b.category);
case 'date':
default:
- return new Date(b.createdAt) - new Date(a.createdAt);
+ return new Date(b.created_at || b.createdAt) - new Date(a.created_at || a.createdAt);
}
});
@@ -118,9 +118,15 @@ class FileManager {
${hasAttachments ?
- ` ${file.files.map((f, index) =>
- `${this.getFileIcon(f.name || f.original_name || 'unknown')}`
- ).join(' ')} ` :
+ `${file.files.map((f, index) =>
+ `
+ ${this.getFileIcon(f.name || f.original_name || 'unknown')}
+
+ ${this.escapeHtml(f.name || f.original_name || '파일')}
+ ${this.formatFileSize(f.size || 0)}
+
+ `
+ ).join('')} ` :
`-`
}
|
@@ -151,25 +157,132 @@ class FileManager {
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];
+ }
+
viewFileInfo(id) {
const file = this.files.find(f => f.id === id);
if (!file) return;
- let info = `📋 자료 정보\n\n`;
- info += `📌 제목: ${file.title}\n`;
- info += `📂 카테고리: ${file.category}\n`;
- info += `📅 등록일: ${new Date(file.created_at || file.createdAt).toLocaleDateString('ko-KR')}\n`;
- if (file.description) info += `📝 설명: ${file.description}\n`;
- if (file.tags && file.tags.length > 0) info += `🏷️ 태그: ${file.tags.join(', ')}\n`;
- if (file.files && file.files.length > 0) {
- info += `\n📎 첨부파일 (${file.files.length}개):\n`;
- file.files.forEach((attachment, index) => {
- const icon = this.getFileIcon(attachment.name || attachment.original_name || 'unknown');
- info += ` ${index + 1}. ${icon} ${attachment.name || attachment.original_name || '파일'}\n`;
- });
+ this.showDetailView(file);
+ }
+
+ showDetailView(file) {
+ // 메인 컨테이너 숨기기
+ const container = document.querySelector('.container');
+ container.style.display = 'none';
+
+ // 상세보기 컨테이너 생성
+ const detailContainer = document.createElement('div');
+ detailContainer.className = 'detail-container';
+ detailContainer.id = 'detailContainer';
+
+ const createdDate = new Date(file.created_at || file.createdAt).toLocaleDateString('ko-KR');
+ const updatedDate = new Date(file.updated_at || file.updatedAt).toLocaleDateString('ko-KR');
+
+ detailContainer.innerHTML = `
+
+
+ 📋 자료 상세보기
+ 등록된 자료의 상세 정보를 확인하세요
+
+
+
+
+
+
+
+
+
+
+ ${file.category}
+
+
+
+
+
+
+ ${file.description ? this.escapeHtml(file.description) : '설명이 없습니다.'}
+
+
+
+ ${file.tags && file.tags.length > 0 ? `
+
+
+
+
+ ${file.tags.map(tag => `#${this.escapeHtml(tag)}`).join('')}
+
+
+
` : ''}
+
+ ${file.files && file.files.length > 0 ? `
+
+
+
+
+ ${file.files.map((f, index) => `
+
+ ${this.getFileIcon(f.name || f.original_name || 'unknown')}
+ ${this.escapeHtml(f.name || f.original_name || '파일')}
+
+
+ `).join('')}
+
+
+
+
+
+
` : `
+
+
+
+ 첨부된 파일이 없습니다.
+
+
`}
+
+
+
+
${createdDate}
+
+
+ ${createdDate !== updatedDate ? `
+
+
+
${updatedDate}
+
` : ''}
+
+
+
+
+ `;
+
+ document.body.appendChild(detailContainer);
+ }
+
+ hideDetailView() {
+ const detailContainer = document.getElementById('detailContainer');
+ if (detailContainer) {
+ detailContainer.remove();
}
- alert(info);
+ // 메인 컨테이너 다시 보이기
+ const container = document.querySelector('.container');
+ container.style.display = 'block';
}
async downloadFiles(id) {
@@ -262,20 +375,23 @@ class FileManager {
}
updatePagination() {
- const totalPages = Math.ceil(this.filteredFiles.length / this.itemsPerPage);
+ 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 (totalPages <= 1) {
- pagination.style.display = 'none';
- } else {
- pagination.style.display = 'flex';
- prevBtn.disabled = this.currentPage <= 1;
- nextBtn.disabled = this.currentPage >= totalPages;
- pageInfo.textContent = `${this.currentPage} / ${totalPages}`;
- }
+ // 항상 페이지네이션을 표시
+ pagination.style.display = 'flex';
+
+ // 페이지 버튼 상태 업데이트
+ prevBtn.disabled = this.currentPage <= 1;
+ nextBtn.disabled = this.currentPage >= totalPages || this.filteredFiles.length === 0;
+
+ // 페이지 정보 표시 (아이템이 없어도 1/1로 표시)
+ const displayTotalPages = this.filteredFiles.length === 0 ? 1 : totalPages;
+ const displayCurrentPage = this.filteredFiles.length === 0 ? 1 : this.currentPage;
+ pageInfo.textContent = `${displayCurrentPage} / ${displayTotalPages}`;
}
goToPrevPage() {
@@ -299,7 +415,14 @@ class FileManager {
loadFiles() {
try {
const stored = localStorage.getItem('fileManagerData');
- return stored ? JSON.parse(stored) : [];
+ const files = stored ? JSON.parse(stored) : [];
+
+ // 기존 localStorage 데이터의 호환성을 위해 컴럼명 변환
+ return files.map(file => ({
+ ...file,
+ created_at: file.created_at || file.createdAt || new Date().toISOString(),
+ updated_at: file.updated_at || file.updatedAt || new Date().toISOString()
+ }));
} catch (error) {
console.error('파일 로드 중 오류:', error);
return [];
diff --git a/styles.css b/styles.css
index dd9690b..c10cf1e 100644
--- a/styles.css
+++ b/styles.css
@@ -40,45 +40,6 @@ header p {
}
/* 페이지네이션 스타일 */
-.pagination {
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 20px;
- margin-top: 30px;
- padding: 20px;
- background: rgba(255, 255, 255, 0.9);
- border-radius: 10px;
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
-}
-
-.page-btn {
- background: #4299e1;
- color: white;
- border: none;
- padding: 10px 20px;
- border-radius: 8px;
- cursor: pointer;
- font-size: 1rem;
- transition: all 0.3s ease;
-}
-
-.page-btn:hover:not(:disabled) {
- background: #3182ce;
- transform: translateY(-2px);
-}
-
-.page-btn:disabled {
- background: #a0aec0;
- cursor: not-allowed;
- transform: none;
-}
-
-#pageInfo {
- font-weight: 600;
- color: #4a5568;
- font-size: 1.1rem;
-}
.auth-section {
margin-top: 20px;
@@ -426,7 +387,7 @@ header p {
}
.board-table th {
- padding: 15px 12px;
+ padding: 12px;
text-align: center;
font-weight: 600;
font-size: 0.95rem;
@@ -451,17 +412,18 @@ header p {
}
.board-table td {
- padding: 12px;
+ padding: 8px 12px;
text-align: center;
vertical-align: middle;
font-size: 0.9rem;
+ line-height: 1.4;
}
/* 컬럼 너비 설정 */
.col-no { width: 60px; }
.col-category { width: 100px; }
.col-title { width: auto; min-width: 200px; text-align: left; }
-.col-attachment { width: 80px; }
+.col-attachment { width: 220px; text-align: center; }
.col-date { width: 120px; }
.col-actions { width: 150px; }
@@ -472,7 +434,7 @@ header p {
text-decoration: none;
cursor: pointer;
display: block;
- padding: 8px;
+ padding: 4px 6px;
border-radius: 4px;
transition: all 0.2s ease;
}
@@ -506,16 +468,61 @@ header p {
.attachment-icons {
display: flex;
- gap: 2px;
- justify-content: center;
+ flex-direction: column;
+ gap: 4px;
align-items: center;
- flex-wrap: wrap;
- font-size: 1rem;
+ font-size: 0.85rem;
line-height: 1.2;
+ max-height: 100px;
+ overflow-y: auto;
+ padding: 2px;
}
-.attachment-icons span {
- display: inline-block;
+.attachment-file-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 2px;
+ cursor: pointer;
+ padding: 3px 4px;
+ border-radius: 3px;
+ transition: background-color 0.2s ease;
+ justify-content: center;
+ white-space: nowrap;
+}
+
+.attachment-file-item:hover {
+ background-color: #f0f9ff;
+}
+
+.attachment-file-icon {
+ font-size: 1.1rem;
+ width: auto;
+ text-align: left;
+ flex-shrink: 0;
+ margin-right: 1px;
+}
+
+.attachment-file-info {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ flex: 1;
+ min-width: 0;
+ margin-left: 0;
+}
+
+.attachment-file-name {
+ font-weight: 500;
+ color: #374151;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-size: 0.8rem;
+}
+
+.attachment-file-size {
+ color: #6b7280;
+ font-size: 0.7rem;
}
.attachment-icon-clickable {
@@ -537,15 +544,15 @@ header p {
/* 액션 버튼 */
.action-buttons {
display: flex;
- gap: 5px;
+ gap: 3px;
justify-content: center;
align-items: center;
}
.action-btn {
- padding: 6px 12px;
+ padding: 4px 8px;
border: none;
- border-radius: 4px;
+ border-radius: 3px;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.2s ease;
@@ -575,39 +582,288 @@ header p {
display: flex;
justify-content: center;
align-items: center;
- gap: 15px;
- margin-top: 20px;
- padding: 20px 0;
+ gap: 20px;
+ margin-top: 30px;
+ padding: 20px;
+ background: rgba(255, 255, 255, 0.9);
+ border-radius: 10px;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
.page-btn {
- padding: 8px 16px;
+ padding: 12px 20px;
border: 2px solid #e2e8f0;
- border-radius: 6px;
+ border-radius: 8px;
background: white;
color: #4a5568;
cursor: pointer;
- font-size: 0.9rem;
- font-weight: 500;
- transition: all 0.2s ease;
+ font-size: 1rem;
+ font-weight: 600;
+ transition: all 0.3s ease;
+ min-width: 80px;
}
.page-btn:hover:not(:disabled) {
border-color: #667eea;
color: #667eea;
background: #f0f4ff;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
.page-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
color: #9ca3af;
+ transform: none;
+ box-shadow: none;
}
#pageInfo {
+ font-weight: 700;
+ color: #4a5568;
+ font-size: 1.2rem;
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ padding: 8px 16px;
+ border: 2px solid #667eea;
+ border-radius: 8px;
+ background-color: rgba(102, 126, 234, 0.1);
+ min-width: 80px;
+ text-align: center;
+}
+
+/* 상세보기 페이지 스타일 */
+.detail-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ z-index: 1000;
+ overflow-y: auto;
+}
+
+.detail-section {
+ background: rgba(255, 255, 255, 0.95);
+ padding: 30px;
+ border-radius: 15px;
+ margin-bottom: 30px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+}
+
+.detail-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 30px;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.detail-header h2 {
+ color: #4a5568;
+ font-size: 2rem;
+ margin: 0;
+ flex: 1;
+}
+
+.back-btn {
+ padding: 12px 24px;
+ background: #667eea;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 1rem;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.back-btn:hover {
+ background: #5a67d8;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+}
+
+.detail-content {
+ display: grid;
+ gap: 20px;
+}
+
+.detail-info {
+ display: grid;
+ gap: 25px;
+}
+
+.info-group {
+ display: grid;
+ gap: 10px;
+}
+
+.info-group label {
font-weight: 600;
color: #4a5568;
+ font-size: 1.1rem;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.info-value {
+ padding: 15px;
+ background: #f8fafc;
+ border-radius: 8px;
+ border: 2px solid #e2e8f0;
font-size: 1rem;
+ line-height: 1.6;
+}
+
+.info-value.description {
+ min-height: 80px;
+ white-space: pre-wrap;
+}
+
+.info-value.no-files {
+ color: #9ca3af;
+ font-style: italic;
+}
+
+.tags-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.tag {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ color: white;
+ padding: 6px 12px;
+ border-radius: 20px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ border: none;
+}
+
+.attachments-list {
+ display: grid;
+ gap: 12px;
+ margin-bottom: 20px;
+}
+
+.attachment-item {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ padding: 15px;
+ background: white;
+ border-radius: 8px;
+ border: 1px solid #e5e7eb;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ transition: all 0.2s ease;
+}
+
+.attachment-item:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-1px);
+}
+
+.attachment-icon {
+ font-size: 1.5rem;
+ min-width: 30px;
+ text-align: center;
+}
+
+.attachment-name {
+ flex: 1;
+ font-weight: 500;
+ color: #374151;
+ word-break: break-all;
+}
+
+.download-single-btn {
+ background: #10b981;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 0.9rem;
+ font-weight: 500;
+ transition: all 0.2s ease;
+}
+
+.download-single-btn:hover {
+ background: #059669;
+ transform: scale(1.05);
+}
+
+.attachment-actions {
+ padding-top: 15px;
+ border-top: 1px solid #e5e7eb;
+ text-align: center;
+}
+
+.download-all-btn {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ color: white;
+ border: none;
+ padding: 12px 24px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 1rem;
+ font-weight: 600;
+ transition: all 0.3s ease;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.download-all-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3);
+}
+
+/* 모바일 반응형 */
+@media (max-width: 768px) {
+ .detail-section {
+ padding: 20px;
+ margin: 10px;
+ border-radius: 10px;
+ }
+
+ .detail-header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .detail-header h2 {
+ font-size: 1.5rem;
+ }
+
+ .back-btn {
+ width: 100%;
+ justify-content: center;
+ }
+
+ .attachment-item {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ }
+
+ .download-single-btn {
+ align-self: stretch;
+ text-align: center;
+ }
}
.file-list {