/** * Todo 관리 기능 */ let todos = []; let currentPhoto = null; let currentFilter = 'all'; // 페이지 로드 시 초기화 document.addEventListener('DOMContentLoaded', () => { setupTodoForm(); setupPhotoUpload(); setupFilters(); updateItemCounts(); loadRegisteredItems(); }); // Todo 폼 설정 function setupTodoForm() { const todoForm = document.getElementById('todoForm'); if (todoForm) { todoForm.addEventListener('submit', handleTodoSubmit); } } // 사진 업로드 설정 function setupPhotoUpload() { const cameraInput = document.getElementById('cameraInput'); const galleryInput = document.getElementById('galleryInput'); if (cameraInput) { cameraInput.addEventListener('change', handlePhotoUpload); } if (galleryInput) { galleryInput.addEventListener('change', handlePhotoUpload); } } // 필터 설정 function setupFilters() { // 필터 탭 클릭 이벤트는 HTML에서 onclick으로 처리 } // Todo 제출 처리 async function handleTodoSubmit(event) { event.preventDefault(); const content = document.getElementById('todoContent').value.trim(); if (!content) { alert('할일 내용을 입력해주세요.'); return; } try { showLoading(true); const todoData = { content: content, photo: currentPhoto, status: 'draft', created_at: new Date().toISOString() }; // 임시 저장 (백엔드 구현 전까지) const newTodo = { id: Date.now(), ...todoData, user_id: window.currentUser?.id || 1 }; todos.unshift(newTodo); // 실제 API 호출 (백엔드 구현 후 사용) /* const newTodo = await TodoAPI.createTodo(todoData); todos.unshift(newTodo); */ // 폼 초기화 및 목록 업데이트 clearForm(); loadRegisteredItems(); updateItemCounts(); // 성공 메시지 showToast('항목이 등록되었습니다!', 'success'); } catch (error) { console.error('할일 추가 실패:', error); alert(error.message || '할일 추가에 실패했습니다.'); } finally { showLoading(false); } } // 사진 업로드 처리 async function handlePhotoUpload(event) { const files = event.target.files; if (!files || files.length === 0) return; const file = files[0]; try { showLoading(true); // 이미지 압축 const compressedImage = await ImageUtils.compressImage(file, { maxWidth: 800, maxHeight: 600, quality: 0.8 }); currentPhoto = compressedImage; // 미리보기 표시 const previewContainer = document.getElementById('photoPreview'); const previewImage = document.getElementById('previewImage'); if (previewContainer && previewImage) { previewImage.src = compressedImage; previewContainer.classList.remove('hidden'); } } catch (error) { console.error('이미지 처리 실패:', error); alert('이미지 처리에 실패했습니다.'); } finally { showLoading(false); } } // 카메라 열기 function openCamera() { const cameraInput = document.getElementById('cameraInput'); if (cameraInput) { cameraInput.click(); } } // 갤러리 열기 function openGallery() { const galleryInput = document.getElementById('galleryInput'); if (galleryInput) { galleryInput.click(); } } // 사진 제거 function removePhoto() { currentPhoto = null; const previewContainer = document.getElementById('photoPreview'); const previewImage = document.getElementById('previewImage'); if (previewContainer) { previewContainer.classList.add('hidden'); } if (previewImage) { previewImage.src = ''; } // 파일 입력 초기화 const cameraInput = document.getElementById('cameraInput'); const galleryInput = document.getElementById('galleryInput'); if (cameraInput) cameraInput.value = ''; if (galleryInput) galleryInput.value = ''; } // 폼 초기화 function clearForm() { const todoForm = document.getElementById('todoForm'); if (todoForm) { todoForm.reset(); } removePhoto(); } // Todo 목록 로드 async function loadTodos() { try { // 실제 API 호출 todos = await TodoAPI.getTodos(currentFilter); renderTodos(); } catch (error) { console.error('할일 목록 로드 실패:', error); showToast('할일 목록을 불러오는데 실패했습니다.', 'error'); } } // Todo 목록 렌더링 function renderTodos() { const todoList = document.getElementById('todoList'); const emptyState = document.getElementById('emptyState'); if (!todoList || !emptyState) return; // 필터링 const filteredTodos = todos.filter(todo => { if (currentFilter === 'all') return true; if (currentFilter === 'active') return ['draft', 'scheduled', 'active', 'delayed'].includes(todo.status); if (currentFilter === 'completed') return todo.status === 'completed'; return todo.status === currentFilter; }); // 빈 상태 처리 if (filteredTodos.length === 0) { todoList.innerHTML = ''; emptyState.classList.remove('hidden'); return; } emptyState.classList.add('hidden'); // Todo 항목 렌더링 todoList.innerHTML = filteredTodos.map(todo => `
${todo.photo ? `
첨부 사진
` : ''}

${todo.content}

${getStatusText(todo.status)} ${formatDate(todo.created_at)}
${todo.status !== 'completed' ? ` ` : ''}
`).join(''); } // Todo 상태 토글 async function toggleTodo(id) { try { const todo = todos.find(t => t.id === id); if (!todo) return; const newStatus = todo.status === 'completed' ? 'active' : 'completed'; // 임시 업데이트 todo.status = newStatus; // 실제 API 호출 (백엔드 구현 후 사용) /* await TodoAPI.updateTodo(id, { status: newStatus }); */ renderTodos(); showToast(newStatus === 'completed' ? '할일을 완료했습니다!' : '할일을 다시 활성화했습니다!', 'success'); } catch (error) { console.error('할일 상태 변경 실패:', error); showToast('상태 변경에 실패했습니다.', 'error'); } } // Todo 삭제 async function deleteTodo(id) { if (!confirm('정말로 이 할일을 삭제하시겠습니까?')) return; try { // 임시 삭제 todos = todos.filter(t => t.id !== id); // 실제 API 호출 (백엔드 구현 후 사용) /* await TodoAPI.deleteTodo(id); */ renderTodos(); showToast('할일이 삭제되었습니다.', 'success'); } catch (error) { console.error('할일 삭제 실패:', error); showToast('삭제에 실패했습니다.', 'error'); } } // Todo 편집 (향후 구현) function editTodo(id) { // TODO: 편집 모달 또는 인라인 편집 구현 console.log('편집 기능 구현 예정:', id); } // 필터 변경 function filterTodos(filter) { currentFilter = filter; // 탭 활성화 상태 변경 document.querySelectorAll('.filter-tab').forEach(tab => { tab.classList.remove('active', 'bg-white', 'text-blue-600'); tab.classList.add('text-gray-600'); }); event.target.classList.add('active', 'bg-white', 'text-blue-600'); event.target.classList.remove('text-gray-600'); renderTodos(); } // 상태 아이콘 반환 function getStatusIcon(status) { const icons = { draft: 'fa-edit', scheduled: 'fa-calendar', active: 'fa-play', completed: 'fa-check', delayed: 'fa-clock' }; return icons[status] || 'fa-circle'; } // 상태 텍스트 반환 function getStatusText(status) { const texts = { draft: '검토 필요', scheduled: '예정됨', active: '진행중', completed: '완료됨', delayed: '지연됨' }; return texts[status] || '알 수 없음'; } // 날짜 포맷팅 function formatDate(dateString) { if (!dateString) return '날짜 없음'; const date = new Date(dateString); if (isNaN(date.getTime())) return '날짜 없음'; const now = new Date(); const diffTime = now - date; const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); if (diffDays === 0) return '오늘'; if (diffDays === 1) return '어제'; if (diffDays < 7) return `${diffDays}일 전`; return date.toLocaleDateString('ko-KR'); } // 토스트 메시지 표시 function showToast(message, type = 'info') { // 간단한 alert으로 대체 (향후 토스트 UI 구현) console.log(`[${type.toUpperCase()}] ${message}`); if (type === 'error') { alert(message); } } // 페이지 이동 함수 function goToPage(pageType) { const pages = { 'todo': 'todo.html', 'calendar': 'calendar.html', 'checklist': 'checklist.html' }; if (pages[pageType]) { window.location.href = pages[pageType]; } else { console.error('Unknown page type:', pageType); } } // 대시보드로 이동 function goToDashboard() { window.location.href = 'upload.html'; } // 분류 센터로 이동 function goToClassify() { window.location.href = 'classify.html'; } // 항목 등록 후 인덱스 업데이트 async function updateItemCounts() { try { // 무한 로딩 방지: 토큰이 없으면 API 요청하지 않음 const token = localStorage.getItem('authToken'); if (!token) { console.log('토큰이 없어서 카운트 업데이트를 건너뜁니다.'); // 토큰이 없으면 0개로 표시 const todoCountEl = document.getElementById('todoCount'); const calendarCountEl = document.getElementById('calendarCount'); const checklistCountEl = document.getElementById('checklistCount'); if (todoCountEl) todoCountEl.textContent = '0개'; if (calendarCountEl) calendarCountEl.textContent = '0개'; if (checklistCountEl) checklistCountEl.textContent = '0개'; return; } // API에서 실제 데이터 가져와서 카운트 const items = await TodoAPI.getTodos(); const todoCount = items.filter(item => item.category === 'todo').length; const calendarCount = items.filter(item => item.category === 'calendar').length; const checklistCount = items.filter(item => item.category === 'checklist').length; const todoCountEl = document.getElementById('todoCount'); const calendarCountEl = document.getElementById('calendarCount'); const checklistCountEl = document.getElementById('checklistCount'); if (todoCountEl) todoCountEl.textContent = `${todoCount}개`; if (calendarCountEl) calendarCountEl.textContent = `${calendarCount}개`; if (checklistCountEl) checklistCountEl.textContent = `${checklistCount}개`; } catch (error) { console.error('항목 카운트 업데이트 실패:', error); // 에러 시 0개로 표시 const todoCountEl = document.getElementById('todoCount'); const calendarCountEl = document.getElementById('calendarCount'); const checklistCountEl = document.getElementById('checklistCount'); if (todoCountEl) todoCountEl.textContent = '0개'; if (calendarCountEl) calendarCountEl.textContent = '0개'; if (checklistCountEl) checklistCountEl.textContent = '0개'; } } // 등록된 항목들 로드 (카테고리가 없는 미분류 항목들) async function loadRegisteredItems() { try { // 무한 로딩 방지: 토큰이 없으면 API 요청하지 않음 const token = localStorage.getItem('authToken'); if (!token) { console.log('토큰이 없어서 API 요청을 건너뜁니다.'); renderRegisteredItems([]); return; } // API에서 모든 항목을 가져와서 카테고리가 없는 것만 필터링 const allItems = await TodoAPI.getTodos(); const unclassifiedItems = allItems.filter(item => !item.category || item.category === null); renderRegisteredItems(unclassifiedItems); } catch (error) { console.error('등록된 항목 로드 실패:', error); renderRegisteredItems([]); } } // 등록된 항목들 렌더링 function renderRegisteredItems(items) { const itemsList = document.getElementById('itemsList'); const emptyState = document.getElementById('emptyState'); if (!itemsList || !emptyState) return; if (!items || items.length === 0) { itemsList.innerHTML = ''; emptyState.classList.remove('hidden'); return; } emptyState.classList.add('hidden'); itemsList.innerHTML = items.map(item => `
${item.image_url ? `
첨부 사진
` : ''}

${item.title}

등록: ${formatDate(item.created_at)} ${item.category ? ` ${getCategoryText(item.category)} ` : ` 미분류 `}
`).join(''); } // 분류 모달 표시 function showClassificationModal(itemId) { console.log('분류 모달 표시:', itemId); // 기존 항목 정보 가져오기 const item = todos.find(t => t.id == itemId) || { title: '', description: '' }; // 모달 HTML 생성 const modalHtml = `

항목 분류 및 편집

`; // 모달을 body에 추가 document.body.insertAdjacentHTML('beforeend', modalHtml); // 현재 선택된 카테고리 변수 (기본값: todo) window.selectedCategory = 'todo'; // 기본적으로 todo 카테고리 선택 상태로 표시 setTimeout(() => { selectCategory('todo'); }, 100); // 모달 외부 클릭 시 닫기 const modal = document.getElementById('classificationModal'); modal.addEventListener('click', (e) => { if (e.target === modal) { closeClassificationModal(); } }); // ESC 키로 모달 닫기 const handleEscKey = (e) => { if (e.key === 'Escape') { closeClassificationModal(); document.removeEventListener('keydown', handleEscKey); } }; document.addEventListener('keydown', handleEscKey); } // 카테고리 선택 function selectCategory(category) { // 이전 선택 해제 document.querySelectorAll('.category-btn').forEach(btn => { btn.classList.remove('border-blue-500', 'border-orange-500', 'border-green-500', 'bg-blue-50', 'bg-orange-50', 'bg-green-50'); btn.classList.add('border-gray-200'); }); // 새 선택 적용 const selectedBtn = document.querySelector(`[data-category="${category}"]`); if (selectedBtn) { selectedBtn.classList.remove('border-gray-200'); if (category === 'todo') { selectedBtn.classList.add('border-blue-500', 'bg-blue-50'); } else if (category === 'calendar') { selectedBtn.classList.add('border-orange-500', 'bg-orange-50'); } else if (category === 'checklist') { selectedBtn.classList.add('border-green-500', 'bg-green-50'); } } // 날짜 섹션 표시/숨김 const dateSection = document.getElementById('dateSection'); const dateLabel = document.getElementById('dateLabel'); if (category === 'checklist') { dateSection.classList.add('hidden'); } else { dateSection.classList.remove('hidden'); if (category === 'todo') { dateLabel.textContent = '시작 날짜'; } else if (category === 'calendar') { dateLabel.textContent = '마감 날짜'; } } window.selectedCategory = category; } // 분류 모달 닫기 function closeClassificationModal() { const modal = document.getElementById('classificationModal'); if (modal) { modal.remove(); } window.selectedCategory = null; } // 분류 저장 async function saveClassification(itemId) { const title = document.getElementById('itemTitle').value.trim(); const description = document.getElementById('itemDescription').value.trim(); const priority = document.getElementById('itemPriority').value; const date = document.getElementById('itemDate').value; if (!title) { alert('제목을 입력해주세요.'); return; } if (!window.selectedCategory) { alert('분류를 선택해주세요.'); return; } try { // API 호출하여 항목 업데이트 const updateData = { title: title, description: description, category: window.selectedCategory, priority: priority }; // 날짜 설정 (체크리스트가 아닌 경우) if (window.selectedCategory !== 'checklist' && date) { updateData.due_date = date + 'T09:00:00Z'; // 기본 시간 설정 } await TodoAPI.updateTodo(itemId, updateData); // 성공 메시지 showToast(`항목이 ${getCategoryText(window.selectedCategory)}(으)로 분류되었습니다.`, 'success'); // 모달 닫기 closeClassificationModal(); // todo가 아닌 다른 카테고리로 변경한 경우에만 페이지 이동 if (window.selectedCategory !== 'todo') { setTimeout(() => { goToPage(window.selectedCategory); }, 1000); // 토스트 메시지를 보여준 후 이동 } else { // todo 카테고리인 경우 인덱스 페이지에서 목록만 새로고침 loadRegisteredItems(); updateItemCounts(); } } catch (error) { console.error('분류 저장 실패:', error); alert('분류 저장에 실패했습니다.'); } } // 항목 분류 (기존 함수 - 호환성 유지) function classifyItem(itemId, category) { console.log('항목 분류:', itemId, category); goToPage(category); } // 분류별 색상 function getCategoryColor(category) { const colors = { 'todo': 'bg-blue-100 text-blue-800', 'calendar': 'bg-orange-100 text-orange-800', 'checklist': 'bg-green-100 text-green-800' }; return colors[category] || 'bg-gray-100 text-gray-800'; } // 분류별 텍스트 function getCategoryText(category) { const texts = { 'todo': 'Todo', 'calendar': '캘린더', 'checklist': '체크리스트' }; return texts[category] || '미분류'; } // 전역으로 사용 가능하도록 export window.loadTodos = loadTodos; window.openCamera = openCamera; window.openGallery = openGallery; window.removePhoto = removePhoto; window.clearForm = clearForm; window.toggleTodo = toggleTodo; window.deleteTodo = deleteTodo; window.editTodo = editTodo; window.filterTodos = filterTodos; window.goToPage = goToPage; window.goToDashboard = goToDashboard; window.goToClassify = goToClassify; window.showClassificationModal = showClassificationModal; window.updateItemCounts = updateItemCounts;