Skip to content

Commit

Permalink
Merge pull request #325 from boostcampwm2023/feature/drag-and-drop
Browse files Browse the repository at this point in the history
feaT: 태스크 드래그 앤 드롭 기능 구현
  • Loading branch information
surinkwon authored Aug 13, 2024
2 parents 7defc0f + 2df2706 commit 9564a94
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 89 deletions.
38 changes: 10 additions & 28 deletions frontend/src/components/backlog/StoryBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import { MouseEvent } from "react";
import { Socket } from "socket.io-client";
import { useOutletContext } from "react-router-dom";
import useShowDetail from "../../hooks/pages/backlog/useShowDetail";
import { BacklogStatusType, EpicCategoryDTO } from "../../types/DTO/backlogDTO";
import EpicDropdown from "./EpicDropdown";
import BacklogStatusChip from "./BacklogStatusChip";
import CategoryChip from "./CategoryChip";
import ChevronDown from "../../assets/icons/chevron-down.svg?react";
import ChevronRight from "../../assets/icons/chevron-right.svg?react";
import TaskContainer from "./TaskContainer";
import TaskHeader from "./TaskHeader";
import BacklogStatusDropdown from "./BacklogStatusDropdown";
import useStoryEmitEvent from "../../hooks/pages/backlog/useStoryEmitEvent";
import ConfirmModal from "../common/ConfirmModal";
import useShowDetail from "../../hooks/pages/backlog/useShowDetail";
import useBacklogInputChange from "../../hooks/pages/backlog/useBacklogInputChange";
import { MouseEvent } from "react";
import { MOUSE_KEY } from "../../constants/event";
import useStoryEmitEvent from "../../hooks/pages/backlog/useStoryEmitEvent";
import useDropdownState from "../../hooks/common/dropdown/useDropdownState";
import TrashCan from "../../assets/icons/trash-can.svg?react";
import { useModal } from "../../hooks/common/modal/useModal";
import ConfirmModal from "../common/ConfirmModal";
import EpicDropdown from "./EpicDropdown";
import TaskCreateBlock from "./TaskCreateBlock";
import ChevronRight from "../../assets/icons/chevron-right.svg?react";
import ChevronDown from "../../assets/icons/chevron-down.svg?react";
import TrashCan from "../../assets/icons/trash-can.svg?react";
import { MOUSE_KEY } from "../../constants/event";
import { BacklogStatusType, EpicCategoryDTO } from "../../types/DTO/backlogDTO";

interface StoryBlockProps {
id: number;
Expand All @@ -27,11 +24,8 @@ interface StoryBlockProps {
point: number | null;
progress: number;
status: BacklogStatusType;
children: React.ReactNode;
taskExist: boolean;
epicList?: EpicCategoryDTO[];
finished?: boolean;
lastTaskRankValue?: string;
}

const StoryBlock = ({
Expand All @@ -43,9 +37,6 @@ const StoryBlock = ({
status,
taskExist,
epicList,
finished = false,
lastTaskRankValue,
children,
}: StoryBlockProps) => {
const { socket }: { socket: Socket } = useOutletContext();
const { showDetail, handleShowDetail } = useShowDetail();
Expand Down Expand Up @@ -280,15 +271,6 @@ const StoryBlock = ({
</button>
</div>
)}
{showDetail && (
<TaskContainer>
<TaskHeader />
{children}
{!finished && (
<TaskCreateBlock storyId={id} {...{ lastTaskRankValue }} />
)}
</TaskContainer>
)}
</>
);
};
Expand Down
36 changes: 36 additions & 0 deletions frontend/src/components/backlog/StoryDragContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { DragEvent } from "react";

interface StoryDragContainerProps {
index: number;
setRef: (index: number) => (element: HTMLDivElement) => void;
onDragStart: () => void;
onDragEnd: (event: DragEvent) => void;
currentlyDraggedOver: boolean;
children: React.ReactNode;
}

const StoryDragContainer = ({
index,
setRef,
onDragStart,
onDragEnd,
currentlyDraggedOver,
children,
}: StoryDragContainerProps) => (
<div
className="relative"
ref={setRef(index)}
draggable={true}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
<div
className={`${
currentlyDraggedOver ? "w-full h-1 bg-blue-400" : ""
} absolute`}
/>
{children}
</div>
);

export default StoryDragContainer;
41 changes: 41 additions & 0 deletions frontend/src/components/backlog/TaskDragContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { DragEvent } from "react";

interface TaskDragContainerProps {
storyIndex: number;
taskIndex: number;
setRef: (
storyIndex: number,
taskIndex: number
) => (element: HTMLDivElement) => void;
onDragStart: () => void;
onDragEnd: (event: DragEvent) => void;
currentlyDraggedOver: boolean;
children: React.ReactNode;
}

const TaskDragContainer = ({
storyIndex,
taskIndex,
setRef,
onDragStart,
onDragEnd,
currentlyDraggedOver,
children,
}: TaskDragContainerProps) => (
<div
className="relative"
ref={setRef(storyIndex, taskIndex)}
draggable={true}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
<div
className={`${
currentlyDraggedOver ? "w-full h-1 bg-blue-400" : ""
} absolute`}
/>
{children}
</div>
);

export default TaskDragContainer;
52 changes: 51 additions & 1 deletion frontend/src/hooks/pages/backlog/useBacklogSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const useBacklogSocket = (socket: Socket) => {
if (content.epicId) {
setBacklog((prevBacklog) => {
let targetStory: StoryDTO | null = null;
backlog.epicList.some((epic) => {
prevBacklog.epicList.some((epic) => {
const foundStory = epic.storyList.find(
(story) => story.id === content.id
);
Expand Down Expand Up @@ -168,6 +168,56 @@ const useBacklogSocket = (socket: Socket) => {
});
break;
case BacklogSocketTaskAction.UPDATE:
if (content.storyId) {
setBacklog((prevBacklog) => {
let targetTask: TaskDTO | null = null;
prevBacklog.epicList.some((epic) => {
epic.storyList.some((story) => {
const foundTask = story.taskList.find(
(task) => task.id === content.id
);

if (foundTask) {
targetTask = { ...foundTask };
return true;
}

return false;
});

if (targetTask) {
return true;
}

return false;
});

if (!targetTask) {
return prevBacklog;
}

const newEpicList = prevBacklog.epicList.map((epic) => {
const newStoryList = epic.storyList.map((story) => {
const newTaskList = story.taskList.filter(
(task) => task.id !== content.id
);

if (story.id === content.storyId) {
newTaskList.push({
...targetTask,
...content,
} as TaskDTO);
}
return { ...story, taskList: newTaskList };
});

return { ...epic, storyList: newStoryList };
});
return { epicList: newEpicList };
});
break;
}

setBacklog((prevBacklog) => {
const newEpicList = prevBacklog.epicList.map((epic) => {
const newStoryList = epic.storyList.map((story) => {
Expand Down
22 changes: 9 additions & 13 deletions frontend/src/pages/backlog/EpicPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const EpicPage = () => {
{...backlog.epicList.map(
({ id: epicId, name, color, rankValue, storyList }) => (
<EpicBlock
storyExist={storyList.length > 1}
storyExist={storyList.length > 0}
epic={{ id: epicId, name, color, rankValue }}
>
{...storyList.map(({ id, title, point, status, taskList }) => {
Expand All @@ -40,19 +40,15 @@ const EpicPage = () => {
: 0;

return (
<StoryBlock
{...{ id, title, point, status }}
epic={{ id: epicId, name, color, rankValue }}
progress={progress}
taskExist={taskList.length > 0}
lastTaskRankValue={
taskList.length
? taskList[taskList.length - 1].rankValue
: undefined
}
>
<>
<StoryBlock
{...{ id, title, point, status }}
epic={{ id: epicId, name, color, rankValue }}
progress={progress}
taskExist={taskList.length > 0}
/>
{...taskList.map((task) => <TaskBlock {...task} />)}
</StoryBlock>
</>
);
})}
{showDetail ? (
Expand Down
18 changes: 9 additions & 9 deletions frontend/src/pages/backlog/FinishedStoryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ const FinishedStoryPage = () => {
: 0;

return (
<StoryBlock
{...{ id, title, point, status }}
epic={epic}
progress={progress}
taskExist={taskList.length > 0}
epicList={epicCategoryList}
finished={true}
>
<>
<StoryBlock
{...{ id, title, point, status }}
epic={epic}
progress={progress}
taskExist={taskList.length > 0}
epicList={epicCategoryList}
/>
{...taskList.map((task) => <TaskBlock {...task} />)}
</StoryBlock>
</>
);
})}
</div>
Expand Down
Loading

0 comments on commit 9564a94

Please sign in to comment.