-
Notifications
You must be signed in to change notification settings - Fork 3
칸반 목록 저장 이슈 해결 #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
칸반 목록 저장 이슈 해결 #73
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { requestJson } from '@/shared/apis/groups/http'; | ||
| import type { Task } from './types'; | ||
|
|
||
| export function updateTask( | ||
| groupId: number, | ||
| taskListId: number, | ||
| taskId: number, | ||
| data: { done?: boolean; name?: string; description?: string }, | ||
| ): Promise<Task> { | ||
| return requestJson<Task>( | ||
| `/groups/${groupId}/task-lists/${taskListId}/tasks/${taskId}`, | ||
| '할 일 수정 실패', | ||
| { | ||
| method: 'PATCH', | ||
| body: JSON.stringify(data), | ||
| }, | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ export function useKanbanDnd( | |
| tasks: KanbanTask[], | ||
| setTasks: React.Dispatch<React.SetStateAction<KanbanTask[]>>, | ||
| columnIds: KanbanStatus[], | ||
| onStatusChange?: (taskId: string, fromStatus: KanbanStatus, toStatus: KanbanStatus) => void, | ||
| ) { | ||
| const [activeTask, setActiveTask] = useState<KanbanTask | null>(null); | ||
|
|
||
|
|
@@ -41,9 +42,13 @@ export function useKanbanDnd( | |
| // over가 컬럼인 경우 (빈 컬럼에 드롭) | ||
| const isOverColumn = columnIds.includes(overId as KanbanStatus); | ||
| if (isOverColumn) { | ||
| setTasks((prev) => | ||
| prev.map((t) => (t.id === activeId ? { ...t, status: overId as KanbanStatus } : t)), | ||
| ); | ||
| const activeTaskItem = tasks.find((t) => t.id === activeId); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| if (activeTaskItem && activeTaskItem.status !== overId) { | ||
| const fromStatus = activeTaskItem.status; | ||
| const toStatus = overId as KanbanStatus; | ||
| setTasks((prev) => prev.map((t) => (t.id === activeId ? { ...t, status: toStatus } : t))); | ||
| onStatusChange?.(activeId, fromStatus, toStatus); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -63,9 +68,10 @@ export function useKanbanDnd( | |
| }); | ||
| } else { | ||
| // 다른 컬럼으로 이동 | ||
| setTasks((prev) => | ||
| prev.map((t) => (t.id === activeId ? { ...t, status: overTaskItem.status } : t)), | ||
| ); | ||
| const fromStatus = activeTaskItem.status; | ||
| const toStatus = overTaskItem.status; | ||
| setTasks((prev) => prev.map((t) => (t.id === activeId ? { ...t, status: toStatus } : t))); | ||
| onStatusChange?.(activeId, fromStatus, toStatus); | ||
| } | ||
| }; | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,35 @@ | ||
| import { useState, useCallback, useMemo } from 'react'; | ||
| import { useRouter } from 'next/navigation'; | ||
| import { useQueries } from '@tanstack/react-query'; | ||
| import { useQueries, useQueryClient } from '@tanstack/react-query'; | ||
| import type { KanbanTask, KanbanStatus, TaskItem } from '../interfaces/team'; | ||
| import type { TaskList } from '../apis/types'; | ||
| import { taskListQueryOptions } from '../queries/useTaskListQuery'; | ||
| import { useCreateTaskListMutation } from '../queries/useCreateTaskListMutation'; | ||
| import { useDeleteTaskListMutation } from '../queries/useDeleteTaskListMutation'; | ||
| import { updateTask } from '../apis/task'; | ||
| import { taskListKeys } from '../queries/queryKeys'; | ||
|
|
||
| function getTodayDateString(): string { | ||
| return new Date().toISOString().split('T')[0]; | ||
| } | ||
|
|
||
| // localStorage에 컬럼 위치를 저장하여 새로고침 후에도 유지 | ||
| function getStoredStatus(groupId: number, taskListId: number): KanbanStatus | null { | ||
| try { | ||
| const stored = localStorage.getItem(`kanban-status-${groupId}-${taskListId}`); | ||
| if (stored === 'todo' || stored === 'inProgress' || stored === 'done') return stored; | ||
| return null; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| function setStoredStatus(groupId: number, taskListId: number, status: KanbanStatus): void { | ||
| try { | ||
| localStorage.setItem(`kanban-status-${groupId}-${taskListId}`, status); | ||
| } catch {} | ||
| } | ||
|
|
||
| function deriveStatus(items: TaskItem[]): KanbanStatus { | ||
| if (items.length === 0) return 'todo'; | ||
| const doneCount = items.filter((item) => item.checked).length; | ||
|
|
@@ -25,6 +44,7 @@ export function useKanbanTasks( | |
| taskLists: Omit<TaskList, 'tasks'>[], | ||
| ) { | ||
| const router = useRouter(); | ||
| const queryClient = useQueryClient(); | ||
| const today = getTodayDateString(); | ||
|
|
||
| // 각 할 일 목록의 태스크를 병렬로 조회 | ||
|
|
@@ -44,11 +64,13 @@ export function useKanbanTasks( | |
| text: task.name, | ||
| checked: task.doneAt !== null, | ||
| })); | ||
| // localStorage 저장값 우선, 없으면 item 완료 비율로 파생 | ||
| const storedStatus = getStoredStatus(groupId, tl.id); | ||
| return { | ||
| id: String(tl.id), | ||
| title: tl.name, | ||
| items, | ||
| status: deriveStatus(items), | ||
| status: storedStatus ?? deriveStatus(items), | ||
| }; | ||
| }); | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
|
|
@@ -118,6 +140,46 @@ export function useKanbanTasks( | |
| // 수정 기능은 할 일 목록 상세 페이지에서 처리 | ||
| const handleUpdateTask = useCallback(() => {}, []); | ||
|
|
||
| // 드래그로 컬럼 이동 시 컬럼 위치를 저장하고, 완료/할 일 이동 시 API로 완료 상태 동기화 | ||
| const handleStatusChange = useCallback( | ||
| async (taskId: string, fromStatus: KanbanStatus, toStatus: KanbanStatus) => { | ||
| if (fromStatus === toStatus) return; | ||
|
|
||
| const task = tasks.find((t) => t.id === taskId); | ||
| const taskListId = Number(taskId); | ||
|
|
||
| // 항목 유무와 관계없이 컬럼 위치를 localStorage에 저장 | ||
| setStoredStatus(groupId, taskListId, toStatus); | ||
|
|
||
| // 진행중으로 이동하거나 항목이 없으면 API 호출 없이 종료 (위치는 이미 저장됨) | ||
| if (!task || task.items.length === 0 || toStatus === 'inProgress') return; | ||
|
|
||
| try { | ||
| if (toStatus === 'done') { | ||
| // 모든 항목 완료 처리 | ||
| await Promise.all( | ||
| task.items.map((item) => | ||
| updateTask(groupId, taskListId, Number(item.id), { done: true }), | ||
| ), | ||
| ); | ||
| } else if (toStatus === 'todo') { | ||
| // 모든 항목 미완료 처리 | ||
| await Promise.all( | ||
| task.items.map((item) => | ||
| updateTask(groupId, taskListId, Number(item.id), { done: false }), | ||
| ), | ||
| ); | ||
| } | ||
| } finally { | ||
| // 성공/실패 관계없이 쿼리를 무효화하여 실제 서버 상태로 동기화 | ||
| await queryClient.invalidateQueries({ | ||
| queryKey: taskListKeys.detail(groupId, taskListId, today), | ||
| }); | ||
| } | ||
| }, | ||
| [tasks, groupId, today, queryClient], | ||
| ); | ||
|
Comment on lines
+144
to
+181
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| return { | ||
| tasks, | ||
| setTasks, | ||
|
|
@@ -127,6 +189,7 @@ export function useKanbanTasks( | |
| handleCardClick, | ||
| handleDeleteTask, | ||
| handleUpdateTask, | ||
| handleStatusChange, | ||
| handleAddTask, | ||
| handleAddListSubmit, | ||
| handleAddListClose, | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
54라는 하드코딩된 값(매직 넘버)을 사용하고 있습니다. 주석으로 설명이 되어 있지만, 이 값은KanbanItem.module.css의 접힌 카드 높이와 암묵적으로 연결되어 있어 유지보수 시 문제를 일으킬 수 있습니다. 예를 들어, CSS에서 카드 높이가 변경되면 이 코드도 함께 수정해야 합니다. 이 값을 공유 상수로 정의하거나 CSS 변수를 통해 관리하여 코드의 일관성과 유지보수성을 높이는 것이 좋습니다.