- 업로드 시 HTML과 PDF를 별도 폴더에 저장 (/documents/, /pdfs/) - 프론트엔드 필터링을 폴더 경로 기준으로 단순화 - PDF 삭제 시 외래키 참조 해제 로직 추가 - book-documents.js, book-editor.js 필터링 통일 - HTML 문서 목록에서 PDF 완전 분리
230 lines
7.4 KiB
JavaScript
230 lines
7.4 KiB
JavaScript
// PDF 관리 애플리케이션 컴포넌트
|
|
window.pdfManagerApp = () => ({
|
|
// 상태 관리
|
|
pdfDocuments: [],
|
|
allDocuments: [],
|
|
loading: false,
|
|
error: '',
|
|
filterType: 'all', // 'all', 'linked', 'standalone'
|
|
|
|
// 인증 상태
|
|
isAuthenticated: false,
|
|
currentUser: null,
|
|
|
|
// 초기화
|
|
async init() {
|
|
console.log('🚀 PDF Manager App 초기화 시작');
|
|
|
|
// 인증 상태 확인
|
|
await this.checkAuthStatus();
|
|
|
|
if (this.isAuthenticated) {
|
|
await this.loadPDFs();
|
|
}
|
|
|
|
// 헤더 로드
|
|
await this.loadHeader();
|
|
},
|
|
|
|
// 인증 상태 확인
|
|
async checkAuthStatus() {
|
|
try {
|
|
const user = await window.api.getCurrentUser();
|
|
this.isAuthenticated = true;
|
|
this.currentUser = user;
|
|
console.log('✅ 인증됨:', user.username);
|
|
} catch (error) {
|
|
console.log('❌ 인증되지 않음');
|
|
this.isAuthenticated = false;
|
|
this.currentUser = null;
|
|
window.location.href = '/login.html';
|
|
}
|
|
},
|
|
|
|
// 헤더 로드
|
|
async loadHeader() {
|
|
try {
|
|
await window.headerLoader.loadHeader();
|
|
} catch (error) {
|
|
console.error('헤더 로드 실패:', error);
|
|
}
|
|
},
|
|
|
|
// PDF 파일들 로드
|
|
async loadPDFs() {
|
|
this.loading = true;
|
|
this.error = '';
|
|
|
|
try {
|
|
// 모든 문서 가져오기
|
|
this.allDocuments = await window.api.getDocuments();
|
|
|
|
// PDF 파일들만 필터링
|
|
this.pdfDocuments = this.allDocuments.filter(doc =>
|
|
(doc.original_filename && doc.original_filename.toLowerCase().endsWith('.pdf')) ||
|
|
(doc.pdf_path && doc.pdf_path !== '') ||
|
|
(doc.html_path === null && doc.pdf_path) // PDF만 업로드된 경우
|
|
);
|
|
|
|
// 연결 상태 확인
|
|
this.pdfDocuments.forEach(pdf => {
|
|
// 이 PDF를 참조하는 다른 문서가 있는지 확인
|
|
const linkedDocuments = this.allDocuments.filter(doc =>
|
|
doc.matched_pdf_id === pdf.id
|
|
);
|
|
pdf.isLinked = linkedDocuments.length > 0;
|
|
pdf.linkedDocuments = linkedDocuments;
|
|
});
|
|
|
|
console.log('📕 PDF 문서들:', this.pdfDocuments.length, '개');
|
|
|
|
} catch (error) {
|
|
console.error('PDF 로드 실패:', error);
|
|
this.error = 'PDF 파일을 불러오는데 실패했습니다: ' + error.message;
|
|
this.pdfDocuments = [];
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
// 필터링된 PDF 목록
|
|
get filteredPDFs() {
|
|
switch (this.filterType) {
|
|
case 'linked':
|
|
return this.pdfDocuments.filter(pdf => pdf.isLinked);
|
|
case 'standalone':
|
|
return this.pdfDocuments.filter(pdf => !pdf.isLinked);
|
|
default:
|
|
return this.pdfDocuments;
|
|
}
|
|
},
|
|
|
|
// 통계 계산
|
|
get linkedPDFs() {
|
|
return this.pdfDocuments.filter(pdf => pdf.isLinked).length;
|
|
},
|
|
|
|
get standalonePDFs() {
|
|
return this.pdfDocuments.filter(pdf => !pdf.isLinked).length;
|
|
},
|
|
|
|
// PDF 새로고침
|
|
async refreshPDFs() {
|
|
await this.loadPDFs();
|
|
},
|
|
|
|
// PDF 다운로드
|
|
async downloadPDF(pdf) {
|
|
try {
|
|
console.log('📕 PDF 다운로드 시작:', pdf.id);
|
|
|
|
// PDF 파일 다운로드 URL 생성
|
|
const downloadUrl = `/api/documents/${pdf.id}/download`;
|
|
|
|
// 인증 헤더 추가를 위해 fetch 사용
|
|
const response = await fetch(downloadUrl, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('PDF 다운로드에 실패했습니다');
|
|
}
|
|
|
|
// Blob으로 변환하여 다운로드
|
|
const blob = await response.blob();
|
|
const url = window.URL.createObjectURL(blob);
|
|
|
|
// 다운로드 링크 생성 및 클릭
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = pdf.original_filename || `${pdf.title}.pdf`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
|
|
// URL 정리
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
console.log('✅ PDF 다운로드 완료');
|
|
|
|
} catch (error) {
|
|
console.error('❌ PDF 다운로드 실패:', error);
|
|
alert('PDF 다운로드에 실패했습니다: ' + error.message);
|
|
}
|
|
},
|
|
|
|
// PDF 삭제
|
|
async deletePDF(pdf) {
|
|
// 연결된 문서가 있는지 확인
|
|
if (pdf.isLinked && pdf.linkedDocuments.length > 0) {
|
|
const linkedTitles = pdf.linkedDocuments.map(doc => doc.title).join('\n- ');
|
|
const confirmMessage = `이 PDF는 다음 문서들과 연결되어 있습니다:\n\n- ${linkedTitles}\n\n정말 삭제하시겠습니까? 연결된 문서들의 PDF 링크가 해제됩니다.`;
|
|
|
|
if (!confirm(confirmMessage)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!confirm(`"${pdf.title}" PDF 파일을 삭제하시겠습니까?`)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
await window.api.deleteDocument(pdf.id);
|
|
|
|
// 목록에서 제거
|
|
this.pdfDocuments = this.pdfDocuments.filter(p => p.id !== pdf.id);
|
|
|
|
this.showNotification('PDF 파일이 삭제되었습니다', 'success');
|
|
|
|
// 목록 새로고침 (연결 상태 업데이트를 위해)
|
|
await this.loadPDFs();
|
|
|
|
} catch (error) {
|
|
console.error('PDF 삭제 실패:', error);
|
|
this.showNotification('PDF 삭제에 실패했습니다: ' + error.message, 'error');
|
|
}
|
|
},
|
|
|
|
// 날짜 포맷팅
|
|
formatDate(dateString) {
|
|
if (!dateString) return '';
|
|
const date = new Date(dateString);
|
|
return date.toLocaleDateString('ko-KR', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
},
|
|
|
|
// 알림 표시
|
|
showNotification(message, type = 'info') {
|
|
console.log(`${type.toUpperCase()}: ${message}`);
|
|
|
|
// 간단한 토스트 알림 생성
|
|
const toast = document.createElement('div');
|
|
toast.className = `fixed top-4 right-4 px-6 py-3 rounded-lg text-white z-50 ${
|
|
type === 'success' ? 'bg-green-600' :
|
|
type === 'error' ? 'bg-red-600' : 'bg-blue-600'
|
|
}`;
|
|
toast.textContent = message;
|
|
|
|
document.body.appendChild(toast);
|
|
|
|
// 3초 후 제거
|
|
setTimeout(() => {
|
|
if (toast.parentNode) {
|
|
toast.parentNode.removeChild(toast);
|
|
}
|
|
}, 3000);
|
|
}
|
|
});
|
|
|
|
// 페이지 로드 시 초기화
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
console.log('📄 PDF Manager 페이지 로드됨');
|
|
});
|