/** * 할일관리 애플리케이션 */ console.log('📋 할일관리 JavaScript 로드 완료'); function todosApp() { return { // 상태 관리 loading: false, activeTab: 'active', // draft, active, scheduled, completed // 할일 데이터 todos: [], stats: { total_count: 0, draft_count: 0, scheduled_count: 0, active_count: 0, completed_count: 0, delayed_count: 0, completion_rate: 0 }, // 입력 폼 newTodoContent: '', // 모달 상태 showScheduleModal: false, showDelayModal: false, showCommentModal: false, showSplitModal: false, // 현재 선택된 할일 currentTodo: null, currentTodoComments: [], // 폼 데이터 scheduleForm: { start_date: '', estimated_minutes: 30 }, delayForm: { delayed_until: '' }, commentForm: { content: '' }, splitForm: { subtasks: ['', ''], estimated_minutes_per_task: [30, 30] }, // 계산된 속성들 get draftTodos() { return this.todos.filter(todo => todo.status === 'draft'); }, get activeTodos() { return this.todos.filter(todo => todo.status === 'active'); }, get scheduledTodos() { return this.todos.filter(todo => todo.status === 'scheduled'); }, get completedTodos() { return this.todos.filter(todo => todo.status === 'completed'); }, // 초기화 async init() { console.log('📋 할일관리 초기화 중...'); // API 로드 대기 let retryCount = 0; while (!window.api && retryCount < 50) { await new Promise(resolve => setTimeout(resolve, 100)); retryCount++; } if (!window.api) { console.error('❌ API가 로드되지 않았습니다.'); return; } await this.loadTodos(); await this.loadStats(); // 주기적으로 활성 할일 업데이트 (1분마다) setInterval(() => { this.loadActiveTodos(); }, 60000); }, // 할일 목록 로드 async loadTodos() { try { this.loading = true; const response = await window.api.get('/todos/'); this.todos = response || []; console.log(`✅ ${this.todos.length}개 할일 로드 완료`); } catch (error) { console.error('❌ 할일 목록 로드 실패:', error); alert('할일 목록을 불러오는 중 오류가 발생했습니다.'); } finally { this.loading = false; } }, // 활성 할일 로드 (시간 체크 포함) async loadActiveTodos() { try { const response = await window.api.get('/todos/active'); const activeTodos = response || []; // 기존 todos에서 active 상태 업데이트 this.todos = this.todos.map(todo => { const activeVersion = activeTodos.find(active => active.id === todo.id); return activeVersion || todo; }); // 새로 활성화된 할일들 추가 activeTodos.forEach(activeTodo => { if (!this.todos.find(todo => todo.id === activeTodo.id)) { this.todos.push(activeTodo); } }); await this.loadStats(); } catch (error) { console.error('❌ 활성 할일 로드 실패:', error); } }, // 통계 로드 async loadStats() { const stats = { total_count: this.todos.length, draft_count: this.todos.filter(t => t.status === 'draft').length, scheduled_count: this.todos.filter(t => t.status === 'scheduled').length, active_count: this.todos.filter(t => t.status === 'active').length, completed_count: this.todos.filter(t => t.status === 'completed').length, delayed_count: this.todos.filter(t => t.status === 'delayed').length }; stats.completion_rate = stats.total_count > 0 ? Math.round((stats.completed_count / stats.total_count) * 100) : 0; this.stats = stats; }, // 새 할일 생성 async createTodo() { if (!this.newTodoContent.trim()) return; try { this.loading = true; const response = await window.api.post('/todos/', { content: this.newTodoContent.trim() }); this.todos.unshift(response); this.newTodoContent = ''; await this.loadStats(); console.log('✅ 새 할일 생성 완료'); // 검토필요 탭으로 이동 this.activeTab = 'draft'; } catch (error) { console.error('❌ 할일 생성 실패:', error); alert('할일 생성 중 오류가 발생했습니다.'); } finally { this.loading = false; } }, // 일정 설정 모달 열기 openScheduleModal(todo) { this.currentTodo = todo; this.scheduleForm = { start_date: this.formatDateTimeLocal(new Date()), estimated_minutes: 30 }; this.showScheduleModal = true; }, // 일정 설정 모달 닫기 closeScheduleModal() { this.showScheduleModal = false; this.currentTodo = null; }, // 할일 일정 설정 async scheduleTodo() { if (!this.currentTodo || !this.scheduleForm.start_date) return; try { const response = await window.api.post(`/todos/${this.currentTodo.id}/schedule`, { start_date: new Date(this.scheduleForm.start_date).toISOString(), estimated_minutes: parseInt(this.scheduleForm.estimated_minutes) }); // 할일 업데이트 const index = this.todos.findIndex(t => t.id === this.currentTodo.id); if (index !== -1) { this.todos[index] = response; } await this.loadStats(); this.closeScheduleModal(); console.log('✅ 할일 일정 설정 완료'); } catch (error) { console.error('❌ 일정 설정 실패:', error); if (error.message.includes('split')) { alert('2시간 이상의 작업은 분할하는 것을 권장합니다.'); } else { alert('일정 설정 중 오류가 발생했습니다.'); } } }, // 할일 완료 async completeTodo(todoId) { try { const response = await window.api.put(`/todos/${todoId}/complete`); // 할일 업데이트 const index = this.todos.findIndex(t => t.id === todoId); if (index !== -1) { this.todos[index] = response; } await this.loadStats(); console.log('✅ 할일 완료'); } catch (error) { console.error('❌ 할일 완료 실패:', error); alert('할일 완료 처리 중 오류가 발생했습니다.'); } }, // 지연 모달 열기 openDelayModal(todo) { this.currentTodo = todo; this.delayForm = { delayed_until: this.formatDateTimeLocal(new Date(Date.now() + 24 * 60 * 60 * 1000)) // 내일 }; this.showDelayModal = true; }, // 지연 모달 닫기 closeDelayModal() { this.showDelayModal = false; this.currentTodo = null; }, // 할일 지연 async delayTodo() { if (!this.currentTodo || !this.delayForm.delayed_until) return; try { const response = await window.api.put(`/todos/${this.currentTodo.id}/delay`, { delayed_until: new Date(this.delayForm.delayed_until).toISOString() }); // 할일 업데이트 const index = this.todos.findIndex(t => t.id === this.currentTodo.id); if (index !== -1) { this.todos[index] = response; } await this.loadStats(); this.closeDelayModal(); console.log('✅ 할일 지연 설정 완료'); } catch (error) { console.error('❌ 할일 지연 실패:', error); alert('할일 지연 설정 중 오류가 발생했습니다.'); } }, // 댓글 모달 열기 async openCommentModal(todo) { this.currentTodo = todo; this.commentForm = { content: '' }; try { const response = await window.api.get(`/todos/${todo.id}/comments`); this.currentTodoComments = response || []; } catch (error) { console.error('❌ 댓글 로드 실패:', error); this.currentTodoComments = []; } this.showCommentModal = true; }, // 댓글 모달 닫기 closeCommentModal() { this.showCommentModal = false; this.currentTodo = null; this.currentTodoComments = []; }, // 댓글 추가 async addComment() { if (!this.currentTodo || !this.commentForm.content.trim()) return; try { const response = await window.api.post(`/todos/${this.currentTodo.id}/comments`, { content: this.commentForm.content.trim() }); this.currentTodoComments.push(response); this.commentForm.content = ''; // 할일의 댓글 수 업데이트 const index = this.todos.findIndex(t => t.id === this.currentTodo.id); if (index !== -1) { this.todos[index].comment_count = this.currentTodoComments.length; } console.log('✅ 댓글 추가 완료'); } catch (error) { console.error('❌ 댓글 추가 실패:', error); alert('댓글 추가 중 오류가 발생했습니다.'); } }, // 분할 모달 열기 openSplitModal(todo) { this.currentTodo = todo; this.splitForm = { subtasks: ['', ''], estimated_minutes_per_task: [30, 30] }; this.showSplitModal = true; }, // 분할 모달 닫기 closeSplitModal() { this.showSplitModal = false; this.currentTodo = null; }, // 할일 분할 async splitTodo() { if (!this.currentTodo) return; const validSubtasks = this.splitForm.subtasks.filter(s => s.trim()); const validMinutes = this.splitForm.estimated_minutes_per_task.slice(0, validSubtasks.length); if (validSubtasks.length < 2) { alert('최소 2개의 하위 작업이 필요합니다.'); return; } try { const response = await window.api.post(`/todos/${this.currentTodo.id}/split`, { subtasks: validSubtasks, estimated_minutes_per_task: validMinutes }); // 원본 할일 제거하고 분할된 할일들 추가 this.todos = this.todos.filter(t => t.id !== this.currentTodo.id); this.todos.unshift(...response); await this.loadStats(); this.closeSplitModal(); console.log('✅ 할일 분할 완료'); } catch (error) { console.error('❌ 할일 분할 실패:', error); alert('할일 분할 중 오류가 발생했습니다.'); } }, // 댓글 토글 async toggleComments(todoId) { const todo = this.todos.find(t => t.id === todoId); if (todo) { await this.openCommentModal(todo); } }, // 유틸리티 함수들 formatDate(dateString) { if (!dateString) return ''; const date = new Date(dateString); const now = new Date(); const diffMs = now - date; const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMins / 60); const diffDays = Math.floor(diffHours / 24); if (diffMins < 1) return '방금 전'; if (diffMins < 60) return `${diffMins}분 전`; if (diffHours < 24) return `${diffHours}시간 전`; if (diffDays < 7) return `${diffDays}일 전`; return date.toLocaleDateString('ko-KR'); }, formatDateTimeLocal(date) { const d = new Date(date); d.setMinutes(d.getMinutes() - d.getTimezoneOffset()); return d.toISOString().slice(0, 16); } }; } console.log('📋 할일관리 컴포넌트 등록 완료');