const { useState, useEffect } = React; function Todo() { // 상태 관리 const [todos, setTodos] = useState([]); const [activeTodos, setActiveTodos] = useState([]); const [completedTodos, setCompletedTodos] = useState([]); const [isLoading, setIsLoading] = useState(false); const [currentTab, setCurrentTab] = useState('active'); const [currentEditId, setCurrentEditId] = useState(null); // 모달 상태 const [showAddModal, setShowAddModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); // 폼 상태 const [todoForm, setTodoForm] = useState({ title: '', remark: '', expire: '', seqno: 0, flag: false, request: '', status: '0' }); const [editForm, setEditForm] = useState({ idx: 0, title: '', remark: '', expire: '', seqno: 0, flag: false, request: '', status: '0' }); // 컴포넌트 마운트시 할일 목록 로드 useEffect(() => { loadTodos(); }, []); // 할일 목록을 활성/완료로 분리 useEffect(() => { const active = todos.filter(todo => (todo.status || '0') !== '5'); const completed = todos.filter(todo => (todo.status || '0') === '5'); setActiveTodos(active); setCompletedTodos(completed); }, [todos]); // 할일 목록 로드 const loadTodos = async () => { setIsLoading(true); try { const response = await fetch('/Todo/GetTodos'); const data = await response.json(); if (data.Success) { setTodos(data.Data || []); } else { showNotification(data.Message || '할일 목록을 불러올 수 없습니다.', 'error'); } } catch (error) { console.error('할일 목록 로드 중 오류:', error); showNotification('서버 연결에 실패했습니다.', 'error'); } finally { setIsLoading(false); } }; // 새 할일 추가 const addTodo = async (e) => { e.preventDefault(); if (!todoForm.remark.trim()) { showNotification('할일 내용을 입력해주세요.', 'error'); return; } setIsLoading(true); try { const response = await fetch('/Todo/CreateTodo', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ...todoForm, seqno: parseInt(todoForm.seqno), expire: todoForm.expire || null, request: todoForm.request || null }) }); const data = await response.json(); if (data.Success) { setShowAddModal(false); setTodoForm({ title: '', remark: '', expire: '', seqno: 0, flag: false, request: '', status: '0' }); loadTodos(); showNotification(data.Message || '할일이 추가되었습니다.', 'success'); } else { showNotification(data.Message || '할일 추가에 실패했습니다.', 'error'); } } catch (error) { console.error('할일 추가 중 오류:', error); showNotification('서버 연결에 실패했습니다.', 'error'); } finally { setIsLoading(false); } }; // 할일 수정 const updateTodo = async () => { if (!editForm.remark.trim()) { showNotification('할일 내용을 입력해주세요.', 'error'); return; } setIsLoading(true); try { const response = await fetch('/Todo/UpdateTodo', { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ...editForm, seqno: parseInt(editForm.seqno), expire: editForm.expire || null, request: editForm.request || null }) }); const data = await response.json(); if (data.Success) { setShowEditModal(false); setCurrentEditId(null); loadTodos(); showNotification(data.Message || '할일이 수정되었습니다.', 'success'); } else { showNotification(data.Message || '할일 수정에 실패했습니다.', 'error'); } } catch (error) { console.error('할일 수정 중 오류:', error); showNotification('서버 연결에 실패했습니다.', 'error'); } finally { setIsLoading(false); } }; // 상태 업데이트 (편집 모달에서 바로 서버에 반영) const updateTodoStatus = async (status) => { if (!currentEditId) return; const formData = { ...editForm, status: status, seqno: parseInt(editForm.seqno), expire: editForm.expire || null, request: editForm.request || null }; if (!formData.remark.trim()) { showNotification('할일 내용을 입력해주세요.', 'error'); return; } setIsLoading(true); try { const response = await fetch('/Todo/UpdateTodo', { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData) }); const data = await response.json(); if (data.Success) { setShowEditModal(false); setCurrentEditId(null); loadTodos(); showNotification(`상태가 '${getStatusText(status)}'(으)로 변경되었습니다.`, 'success'); } else { showNotification(data.Message || '상태 변경에 실패했습니다.', 'error'); } } catch (error) { console.error('상태 변경 중 오류:', error); showNotification('서버 연결에 실패했습니다.', 'error'); } finally { setIsLoading(false); } }; // 할일 편집 모달 열기 const editTodo = async (id) => { setCurrentEditId(id); setIsLoading(true); try { const response = await fetch(`/Todo/GetTodo?id=${id}`); const data = await response.json(); if (data.Success && data.Data) { const todo = data.Data; setEditForm({ idx: todo.idx, title: todo.title || '', remark: todo.remark || '', expire: todo.expire ? new Date(todo.expire).toISOString().split('T')[0] : '', seqno: todo.seqno || 0, flag: todo.flag || false, request: todo.request || '', status: todo.status || '0' }); setShowEditModal(true); } else { showNotification(data.Message || '할일 정보를 불러올 수 없습니다.', 'error'); } } catch (error) { console.error('할일 조회 중 오류:', error); showNotification('서버 연결에 실패했습니다.', 'error'); } finally { setIsLoading(false); } }; // 할일 삭제 const deleteTodo = async (id) => { if (!confirm('정말로 이 할일을 삭제하시겠습니까?')) { return; } setIsLoading(true); try { const response = await fetch(`/Todo/DeleteTodo?id=${id}`, { method: 'DELETE' }); const data = await response.json(); if (data.Success) { loadTodos(); showNotification(data.Message || '할일이 삭제되었습니다.', 'success'); } else { showNotification(data.Message || '할일 삭제에 실패했습니다.', 'error'); } } catch (error) { console.error('할일 삭제 중 오류:', error); showNotification('서버 연결에 실패했습니다.', 'error'); } finally { setIsLoading(false); } }; // 유틸리티 함수들 const getStatusClass = (status) => { switch(status) { case '0': return 'bg-gray-500/20 text-gray-300'; case '1': return 'bg-primary-500/20 text-primary-300'; case '2': return 'bg-danger-500/20 text-danger-300'; case '3': return 'bg-warning-500/20 text-warning-300'; case '5': return 'bg-success-500/20 text-success-300'; default: return 'bg-white/10 text-white/50'; } }; const getStatusText = (status) => { switch(status) { case '0': return '대기'; case '1': return '진행'; case '2': return '취소'; case '3': return '보류'; case '5': return '완료'; default: return '대기'; } }; const getSeqnoClass = (seqno) => { switch(seqno) { case 1: return 'bg-primary-500/20 text-primary-300'; case 2: return 'bg-warning-500/20 text-warning-300'; case 3: return 'bg-danger-500/20 text-danger-300'; default: return 'bg-white/10 text-white/50'; } }; const getSeqnoText = (seqno) => { switch(seqno) { case 1: return '중요'; case 2: return '매우 중요'; case 3: return '긴급'; default: return '보통'; } }; const formatDate = (dateString) => { if (!dateString) return '-'; return new Date(dateString).toLocaleDateString('ko-KR'); }; // 알림 표시 함수 const showNotification = (message, type = 'info') => { const colors = { info: 'bg-blue-500/90 backdrop-blur-sm', success: 'bg-green-500/90 backdrop-blur-sm', warning: 'bg-yellow-500/90 backdrop-blur-sm', error: 'bg-red-500/90 backdrop-blur-sm' }; const notification = document.createElement('div'); notification.className = `fixed top-4 right-4 ${colors[type]} text-white px-4 py-3 rounded-lg z-50 transition-all duration-300 transform translate-x-0 opacity-100 shadow-lg border border-white/20`; notification.innerHTML = `
| 진행상태 | 플래그 | 제목 | 내용 | 요청자 | 중요도 | 만료일 | 작업 |
|---|---|---|---|---|---|---|---|
|
데이터를 불러오는 중...
|
|||||||
| 진행중인 할일이 없습니다 | |||||||
| 진행상태 | 플래그 | 제목 | 내용 | 요청자 | 중요도 | 만료일 | 완료일 | 작업 |
|---|---|---|---|---|---|---|---|---|
|
데이터를 불러오는 중...
|
||||||||
| 완료된 할일이 없습니다 | ||||||||