import { useState } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { createPortal } from 'react-dom'; import { apiClient, getApiErrorMessage } from '../../lib/apiClient'; import { TaskModal } from '../common/TaskModal'; import type { TaskFormData } from '../common/TaskModal'; import type { Task } from '../../types'; import { isProjectTask, isRoutineTask } from '../../lib/taskType'; const STATUS_LABEL: Record = { IN_PROGRESS: '진행', REVIEW: '보류', TODO: '대기', DONE: '완료', CANCELLED: '취소', }; const STATUS_STYLE: Record = { IN_PROGRESS: 'bg-blue-100 text-blue-700', REVIEW: 'bg-orange-100 text-orange-700', TODO: 'bg-gray-100 text-gray-600', DONE: 'bg-emerald-100 text-emerald-700', CANCELLED: 'bg-gray-100 text-gray-400', }; const SECTIONS = ['인사관리', '학습성장', '운영지원', '전산관리'] as const; interface TaskManagerProps { tasks: Task[]; sectionOptions: { value: string; label: string }[]; quarter: string; onClose: () => void; } export function TaskManager({ tasks, sectionOptions, quarter, onClose }: TaskManagerProps) { const queryClient = useQueryClient(); const [filterSection, setFilterSection] = useState('전체'); const [filterType, setFilterType] = useState('전체'); const [modalMode, setModalMode] = useState<'add' | 'edit' | null>(null); const [editingTask, setEditingTask] = useState(null); const create = useMutation({ mutationFn: (data: Record) => apiClient.post('/tasks', data), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['tasks'] }), }); const patch = useMutation({ mutationFn: ({ id, data }: { id: string; data: Record }) => apiClient.patch(`/tasks/${id}`, data), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['tasks'] }), }); const remove = useMutation({ mutationFn: (id: string) => apiClient.delete(`/tasks/${id}`), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['tasks'] }), }); const matchType = (taskType: string | null | undefined, filter: string) => { if (filter === '전체') return true; if (filter === '실행과제') return isProjectTask(taskType); if (filter === '기반업무') return isRoutineTask(taskType); return taskType === filter; }; const filtered = tasks.filter((t) => { if (filterSection !== '전체' && t.section !== filterSection) return false; if (!matchType(t.taskType, filterType)) return false; return true; }); const handleAdd = async (data: TaskFormData) => { try { await create.mutateAsync({ title: data.title, section: data.section || null, tag: data.tag || null, taskType: data.taskType || null, status: data.status, progress: data.progress, description: data.description || null, issueNote: data.issueNote || null, startDate: data.startDate || null, dueDate: data.dueDate || null, showDate: data.showDate, showDescription: data.showDescription, showStatus: data.showStatus, showIssue: data.showIssue, showProgress: data.showProgress, keywords: data.keywords || null, quarter: data.quarter, priority: 'MEDIUM', }); setModalMode(null); } catch (err: unknown) { alert(getApiErrorMessage(err, '업무 추가에 실패했습니다.')); } }; const handleEdit = (data: TaskFormData) => { if (!editingTask) return; patch.mutate({ id: editingTask.id, data: { title: data.title, section: data.section || null, tag: data.tag || null, taskType: data.taskType || null, status: data.status, progress: data.progress, description: data.description || null, issueNote: data.issueNote || null, startDate: data.startDate || null, dueDate: data.dueDate || null, showDate: data.showDate, showDescription: data.showDescription, showStatus: data.showStatus, showIssue: data.showIssue, showProgress: data.showProgress, keywords: data.keywords || null, }, }); setModalMode(null); setEditingTask(null); }; const handleDelete = (task: Task) => { if (window.confirm(`"${task.title}" 업무를 삭제하시겠습니까?`)) { remove.mutate(task.id); } }; return createPortal(
e.stopPropagation()} > {/* 헤더 */}

📋 업무관리

{/* 부문 필터 */}
{['전체', ...SECTIONS].map((s) => ( ))}
{/* 유형 필터 */}
{['전체', '실행과제', '기반업무'].map((t) => ( ))}
{/* 테이블 */}
{filtered.length === 0 ? ( ) : filtered.map((task) => ( ))}
부문 유형 업무명 내용 기간 진행률 상태 이슈
업무 없음
{task.section ?? '-'} {isRoutineTask(task.taskType) ? '기반업무' : '실행과제'} {task.title} {task.description ?? '-'} {task.startDate || task.dueDate ? `${task.startDate?.slice(0,10) ?? '?'} ~ ${task.dueDate?.slice(0,10) ?? '?'}` : '-'} = 70 ? 'text-emerald-500' : task.progress >= 40 ? 'text-blue-500' : 'text-orange-400' }`}>{task.progress}% {STATUS_LABEL[task.status]} {task.issueNote ?? ''}
{/* 하단 요약 */}
전체 {filtered.length} 실행과제 {filtered.filter(t => isProjectTask(t.taskType)).length} 기반업무 {filtered.filter(t => isRoutineTask(t.taskType)).length}
{modalMode === 'add' && ( setModalMode(null)} /> )} {modalMode === 'edit' && editingTask && ( { setModalMode(null); setEditingTask(null); }} /> )}
, document.body ); }