From 849897b14193ef0d067bddb8c490e03303b03e27 Mon Sep 17 00:00:00 2001 From: EENE Dashboard Date: Tue, 2 Jun 2026 16:53:57 +0900 Subject: [PATCH] feat: showProgress toggle and auto display flags on task type move Co-authored-by: Cursor --- backend/prisma/schema.prisma | 1 + backend/src/routes/tasks.ts | 6 ++-- frontend/src/components/common/TaskModal.tsx | 32 +++++++++++++++---- .../components/dashboard/DepartmentColumn.tsx | 2 ++ .../src/components/dashboard/TaskCard.tsx | 14 ++++---- .../src/components/dashboard/TaskManager.tsx | 2 ++ frontend/src/lib/taskType.ts | 12 +++++++ frontend/src/pages/DashboardPage.tsx | 8 +++-- frontend/src/types/index.ts | 1 + 9 files changed, 62 insertions(+), 16 deletions(-) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index da8f80e..aec7db9 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -56,6 +56,7 @@ model Task { showDescription Boolean @default(true) showStatus Boolean @default(true) showIssue Boolean @default(true) + showProgress Boolean @default(true) keywords String? creatorId String assigneeId String? diff --git a/backend/src/routes/tasks.ts b/backend/src/routes/tasks.ts index 845fba7..70615e2 100644 --- a/backend/src/routes/tasks.ts +++ b/backend/src/routes/tasks.ts @@ -57,7 +57,7 @@ router.post('/', async (req, res, next) => { try { const { title, description, status, priority, quarter, category, section, tag, taskType, progress, issueNote, startDate, dueDate, assigneeId, showDate, - showDescription, showStatus, showIssue, keywords } = + showDescription, showStatus, showIssue, showProgress, keywords } = req.body as Record; if (!title || !quarter) { @@ -83,6 +83,7 @@ router.post('/', async (req, res, next) => { showDescription: showDescription !== undefined ? showDescription === 'true' || showDescription === true : true, showStatus: showStatus !== undefined ? showStatus === 'true' || showStatus === true : true, showIssue: showIssue !== undefined ? showIssue === 'true' || showIssue === true : true, + showProgress: showProgress !== undefined ? showProgress === 'true' || showProgress === true : true, keywords: keywords || null, assigneeId: assigneeId || null, creatorId: (req.body as Record).creatorId ?? 'system', @@ -103,7 +104,7 @@ router.patch('/:id', async (req, res, next) => { const { title, description, status, priority, quarter, category, section, tag, taskType, progress, issueNote, startDate, dueDate, assigneeId, showDate, - showDescription, showStatus, showIssue, keywords } = + showDescription, showStatus, showIssue, showProgress, keywords } = req.body as Record; const task = await prisma.task.update({ @@ -127,6 +128,7 @@ router.patch('/:id', async (req, res, next) => { ...(showDescription !== undefined && { showDescription: showDescription === true || showDescription === 'true' }), ...(showStatus !== undefined && { showStatus: showStatus === true || showStatus === 'true' }), ...(showIssue !== undefined && { showIssue: showIssue === true || showIssue === 'true' }), + ...(showProgress !== undefined && { showProgress: showProgress === true || showProgress === 'true' }), ...(keywords !== undefined && { keywords: keywords || null }), }, }); diff --git a/frontend/src/components/common/TaskModal.tsx b/frontend/src/components/common/TaskModal.tsx index 61b7e48..cd8b184 100644 --- a/frontend/src/components/common/TaskModal.tsx +++ b/frontend/src/components/common/TaskModal.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { createPortal } from 'react-dom'; import type { Task } from '../../types'; -import { normalizeTaskType } from '../../lib/taskType'; +import { normalizeTaskType, displayFlagsForTaskType } from '../../lib/taskType'; const STATUS_OPTIONS = [ { value: 'TODO', label: '대기' }, @@ -26,6 +26,7 @@ export interface TaskFormData { showDescription: boolean; showStatus: boolean; showIssue: boolean; + showProgress: boolean; keywords: string; } @@ -62,6 +63,7 @@ export function TaskModal({ mode, task, defaultSection = 'HR', defaultQuarter = showDescription: task?.showDescription ?? true, showStatus: task?.showStatus ?? true, showIssue: task?.showIssue ?? true, + showProgress: task?.showProgress ?? true, keywords: task?.keywords ?? '', }); @@ -130,7 +132,14 @@ export function TaskModal({ mode, task, defaultSection = 'HR', defaultQuarter =
- +
+ + +
{task.title} - = 70 ? 'text-emerald-500' : - task.progress >= 40 ? 'text-blue-400' : 'text-orange-400' - }`}> - {task.progress}% - + {task.showProgress !== false && ( + = 70 ? 'text-emerald-500' : + task.progress >= 40 ? 'text-blue-400' : 'text-orange-400' + }`}> + {task.progress}% + + )}
diff --git a/frontend/src/components/dashboard/TaskManager.tsx b/frontend/src/components/dashboard/TaskManager.tsx index 84bd5fa..3552f35 100644 --- a/frontend/src/components/dashboard/TaskManager.tsx +++ b/frontend/src/components/dashboard/TaskManager.tsx @@ -71,6 +71,7 @@ export function TaskManager({ tasks, sectionOptions, quarter, onClose }: TaskMan showDescription: data.showDescription, showStatus: data.showStatus, showIssue: data.showIssue, + showProgress: data.showProgress, keywords: data.keywords || null, quarter: data.quarter, priority: 'MEDIUM', @@ -92,6 +93,7 @@ export function TaskManager({ tasks, sectionOptions, quarter, onClose }: TaskMan showDescription: data.showDescription, showStatus: data.showStatus, showIssue: data.showIssue, + showProgress: data.showProgress, keywords: data.keywords || null, }, }); diff --git a/frontend/src/lib/taskType.ts b/frontend/src/lib/taskType.ts index 9a501f3..fa41e97 100644 --- a/frontend/src/lib/taskType.ts +++ b/frontend/src/lib/taskType.ts @@ -14,3 +14,15 @@ export function normalizeTaskType(taskType: string | null | undefined): string { if (taskType === '상시업무') return '기반업무'; return taskType; } + +/** 실행과제 카드에 표시할 필드 플래그 (기반업무는 모두 숨김) */ +export function displayFlagsForTaskType(taskType: string | null | undefined) { + const visible = isProjectTask(taskType); + return { + showDate: visible, + showDescription: visible, + showStatus: visible, + showIssue: visible, + showProgress: visible, + }; +} diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx index 4e34b57..559a26c 100644 --- a/frontend/src/pages/DashboardPage.tsx +++ b/frontend/src/pages/DashboardPage.tsx @@ -18,7 +18,7 @@ import { DepartmentColumn } from '../components/dashboard/DepartmentColumn'; import { TaskManager } from '../components/dashboard/TaskManager'; import { useSocket } from '../contexts/SocketContext'; import { sendTaskSelected, openDetailWindow } from '../lib/dualMonitor'; -import { isRoutineTask } from '../lib/taskType'; +import { isRoutineTask, displayFlagsForTaskType } from '../lib/taskType'; const QUARTER = '2026-Q2'; const SECTIONS = ['인사관리', '학습성장', '운영지원', '전산관리'] as const; @@ -134,8 +134,10 @@ export default function DashboardPage() { if (targetSection !== srcSection) updateData.section = targetSection; if (areaType === 'routine' && !isRoutineTask(draggedTask.taskType)) { updateData.taskType = '기반업무'; + Object.assign(updateData, displayFlagsForTaskType('기반업무')); } else if (areaType === 'project' && isRoutineTask(draggedTask.taskType)) { updateData.taskType = '실행과제'; + Object.assign(updateData, displayFlagsForTaskType('실행과제')); } if (Object.keys(updateData).length > 0) { patchTask.mutate({ id: activeId, data: updateData }); @@ -154,7 +156,9 @@ export default function DashboardPage() { const updateData: Record = {}; if (dstSection !== srcSection) updateData.section = dstSection; if (typeChanged) { - updateData.taskType = isRoutineTask(overTask.taskType) ? '기반업무' : '실행과제'; + const newType = isRoutineTask(overTask.taskType) ? '기반업무' : '실행과제'; + updateData.taskType = newType; + Object.assign(updateData, displayFlagsForTaskType(newType)); } patchTask.mutate({ id: activeId, data: updateData }); if (dstSection !== srcSection) return; diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 870edf2..0c09cb0 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -31,6 +31,7 @@ export interface Task { showDescription: boolean; showStatus: boolean; showIssue: boolean; + showProgress: boolean; keywords: string | null; creatorId: string; assigneeId: string | null;