// 노트 관리 애플리케이션 컴포넌트 window.notesApp = () => ({ // 상태 관리 notes: [], stats: null, loading: false, error: '', // 필터링 searchQuery: '', selectedType: '', publishedOnly: false, selectedNotebook: '', // 노트북 관련 availableNotebooks: [], // 일괄 선택 관련 selectedNotes: [], bulkNotebookId: '', // 노트북 생성 관련 showCreateNotebookModal: false, creatingNotebook: false, newNotebookForm: { name: '', description: '', color: '#3B82F6', icon: 'book' }, // 색상 및 아이콘 옵션 availableColors: [ '#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#06B6D4', '#84CC16', '#F97316', '#EC4899', '#6B7280' ], availableIcons: [ { value: 'book', label: '📖 책' }, { value: 'sticky-note', label: '📝 노트' }, { value: 'lightbulb', label: '💡 아이디어' }, { value: 'graduation-cap', label: '🎓 학습' }, { value: 'briefcase', label: '💼 업무' }, { value: 'heart', label: '❤️ 개인' }, { value: 'code', label: '💻 개발' }, { value: 'palette', label: '🎨 창작' }, { value: 'flask', label: '🧪 연구' }, { value: 'star', label: '⭐ 즐겨찾기' } ], // 검색 디바운스 searchTimeout: null, // 인증 상태 isAuthenticated: false, currentUser: null, // API 클라이언트 api: null, // 초기화 async init() { console.log('🚀 Notes App 초기화 시작'); // API 클라이언트 초기화 this.api = new DocumentServerAPI(); console.log('🔧 API 클라이언트 초기화됨:', this.api); console.log('🔧 getNotebooks 메서드 존재 여부:', typeof this.api.getNotebooks); // URL 파라미터 확인 (노트북 필터) const urlParams = new URLSearchParams(window.location.search); const notebookId = urlParams.get('notebook_id'); const notebookName = urlParams.get('notebook_name'); if (notebookId) { this.selectedNotebook = notebookId; console.log('🔍 노트북 필터 적용:', notebookName || notebookId); } // 인증 상태 확인 await this.checkAuthStatus(); if (this.isAuthenticated) { await this.loadNotebooks(); await this.loadStats(); await this.loadNotes(); } // 헤더 로드 await this.loadHeader(); }, // 인증 상태 확인 async checkAuthStatus() { try { const user = await this.api.getCurrentUser(); this.isAuthenticated = true; this.currentUser = user; console.log('✅ 인증됨:', user.username || user.email); } catch (error) { console.log('❌ 인증되지 않음'); this.isAuthenticated = false; this.currentUser = null; // 로그인 페이지로 리다이렉트하지 않고 메인 페이지로 window.location.href = '/'; } }, // 헤더 로드 async loadHeader() { try { await window.headerLoader.loadHeader(); } catch (error) { console.error('헤더 로드 실패:', error); } }, // 노트북 목록 로드 async loadNotebooks() { try { console.log('📚 노트북 로드 시작...'); console.log('🔧 API 메서드 확인:', typeof this.api.getNotebooks); if (typeof this.api.getNotebooks !== 'function') { throw new Error('getNotebooks 메서드가 존재하지 않습니다'); } // 임시: 직접 API 호출 this.availableNotebooks = await this.api.get('/notebooks/', { active_only: true }); console.log('📚 노트북 로드됨:', this.availableNotebooks.length, '개'); } catch (error) { console.error('노트북 로드 실패:', error); this.availableNotebooks = []; } }, // 통계 정보 로드 async loadStats() { try { this.stats = await this.api.get('/note-documents/stats'); console.log('📊 통계 로드됨:', this.stats); } catch (error) { console.error('통계 로드 실패:', error); } }, // 노트 목록 로드 async loadNotes() { this.loading = true; this.error = ''; try { const queryParams = {}; if (this.searchQuery) { queryParams.search = this.searchQuery; } if (this.selectedType) { queryParams.note_type = this.selectedType; } if (this.publishedOnly) { queryParams.published_only = 'true'; } if (this.selectedNotebook) { if (this.selectedNotebook === 'unassigned') { queryParams.notebook_id = 'null'; } else { queryParams.notebook_id = this.selectedNotebook; } } this.notes = await this.api.getNoteDocuments(queryParams); console.log('📝 노트 로드됨:', this.notes.length, '개'); } catch (error) { console.error('노트 로드 실패:', error); this.error = '노트를 불러오는데 실패했습니다: ' + error.message; this.notes = []; } finally { this.loading = false; } }, // 검색 디바운스 debounceSearch() { clearTimeout(this.searchTimeout); this.searchTimeout = setTimeout(() => { this.loadNotes(); }, 500); }, // 노트 새로고침 async refreshNotes() { await Promise.all([ this.loadStats(), this.loadNotes() ]); }, // 새 노트 생성 createNewNote() { window.location.href = '/note-editor.html'; }, // 노트 보기 (뷰어 페이지로 이동) viewNote(noteId) { window.location.href = `/viewer.html?type=note&id=${noteId}`; }, // 노트 편집 editNote(noteId) { window.location.href = `/note-editor.html?id=${noteId}`; }, // 노트 삭제 async deleteNote(note) { if (!confirm(`"${note.title}" 노트를 삭제하시겠습니까?`)) { return; } try { const response = await fetch(`/api/note-documents/${note.id}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${localStorage.getItem('access_token')}` } }); if (response.ok) { this.showNotification('노트가 삭제되었습니다', 'success'); await this.refreshNotes(); } else { throw new Error('삭제 실패'); } } catch (error) { console.error('노트 삭제 실패:', error); this.showNotification('노트 삭제에 실패했습니다: ' + error.message, 'error'); } }, // 노트 타입 라벨 getNoteTypeLabel(type) { const labels = { 'note': '일반', 'research': '연구', 'summary': '요약', 'idea': '아이디어', 'guide': '가이드', 'reference': '참고' }; return labels[type] || type; }, // 날짜 포맷팅 formatDate(dateString) { if (!dateString) return ''; const date = new Date(dateString); const now = new Date(); const diffTime = Math.abs(now - date); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); if (diffDays === 1) { return '오늘'; } else if (diffDays === 2) { return '어제'; } else if (diffDays <= 7) { return `${diffDays - 1}일 전`; } else { return date.toLocaleDateString('ko-KR', { year: 'numeric', month: 'short', 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); }, // === 일괄 선택 관련 메서드 === // 노트 선택/해제 toggleNoteSelection(noteId) { const index = this.selectedNotes.indexOf(noteId); if (index > -1) { this.selectedNotes.splice(index, 1); } else { this.selectedNotes.push(noteId); } }, // 선택 해제 clearSelection() { this.selectedNotes = []; this.bulkNotebookId = ''; }, // 선택된 노트들을 노트북에 할당 async assignToNotebook() { if (!this.bulkNotebookId || this.selectedNotes.length === 0) { this.showNotification('노트북을 선택하고 노트를 선택해주세요.', 'error'); return; } try { // 각 노트를 업데이트 const updatePromises = this.selectedNotes.map(noteId => this.api.put(`/note-documents/${noteId}`, { notebook_id: this.bulkNotebookId }) ); await Promise.all(updatePromises); this.showNotification(`${this.selectedNotes.length}개 노트가 노트북에 할당되었습니다.`, 'success'); // 선택 해제 및 새로고침 this.clearSelection(); await this.loadNotes(); } catch (error) { console.error('노트북 할당 실패:', error); this.showNotification('노트북 할당에 실패했습니다.', 'error'); } }, // === 노트북 생성 관련 메서드 === // 노트북 생성 모달 닫기 closeCreateNotebookModal() { this.showCreateNotebookModal = false; this.newNotebookForm = { name: '', description: '', color: '#3B82F6', icon: 'book' }; }, // 노트북 생성 및 노트 할당 async createNotebookAndAssign() { if (!this.newNotebookForm.name.trim()) { this.showNotification('노트북 이름을 입력해주세요.', 'error'); return; } this.creatingNotebook = true; try { // 1. 노트북 생성 const newNotebook = await this.api.post('/notebooks/', this.newNotebookForm); console.log('📚 새 노트북 생성됨:', newNotebook.name); // 2. 선택된 노트들이 있으면 할당 if (this.selectedNotes.length > 0) { const updatePromises = this.selectedNotes.map(noteId => this.api.put(`/note-documents/${noteId}`, { notebook_id: newNotebook.id }) ); await Promise.all(updatePromises); console.log(`📝 ${this.selectedNotes.length}개 노트가 새 노트북에 할당됨`); } this.showNotification( `노트북 "${newNotebook.name}"이 생성되었습니다.${this.selectedNotes.length > 0 ? ` ${this.selectedNotes.length}개 노트가 할당되었습니다.` : ''}`, 'success' ); // 3. 정리 및 새로고침 this.closeCreateNotebookModal(); this.clearSelection(); await this.loadNotebooks(); await this.loadNotes(); } catch (error) { console.error('노트북 생성 실패:', error); this.showNotification('노트북 생성에 실패했습니다.', 'error'); } finally { this.creatingNotebook = false; } } }); // 페이지 로드 시 초기화 document.addEventListener('DOMContentLoaded', () => { console.log('📄 Notes 페이지 로드됨'); });