Skip to content
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

Feat: #33 특정 프로젝트 상태 목록 수평 DnD 기능 추가 #40

Merged
merged 9 commits into from
Jul 6, 2024
Original file line number Diff line number Diff line change
@@ -3,14 +3,14 @@ import { IoIosClose } from 'react-icons/io';
import { RiProhibited2Fill, RiProhibited2Line } from 'react-icons/ri';
import { SubmitHandler, useForm } from 'react-hook-form';
import { STATUS_VALIDATION_RULES } from '@constants/formValidationRules';
import type { ColorInfo, TodoStatus, TodoStatusForm } from '@/types/TodoStatusType';
import type { ColorInfo, TaskStatus, TaskStatusForm } from '@/types/TaskStatusType';

type TodoStatusProps = {
todoStatus: TodoStatus[];
type TaskStatusProps = {
taskStatus: TaskStatus[];
onClose: () => void;
};

const DEFAULT_TODO_COLORS = Object.freeze({
const DEFAULT_TASK_COLORS = Object.freeze({
RED: '#c83c00',
YELLOW: '#dab700',
GREEN: '#237700',
@@ -22,14 +22,14 @@ const DEFAULT_TODO_COLORS = Object.freeze({
});

// 색상 정보 취득
function getTodoColors(todoStatus: TodoStatus[]): ColorInfo[] {
function getTaskColors(taskStatus: TaskStatus[]): ColorInfo[] {
const colorMap = new Map();

Object.values(DEFAULT_TODO_COLORS).forEach((color) =>
Object.values(DEFAULT_TASK_COLORS).forEach((color) =>
colorMap.set(color, { color, isDefault: true, isUsable: true }),
);

todoStatus.forEach(({ color }) => {
taskStatus.forEach(({ color }) => {
const colorStatusInfo = colorMap.has(color)
? { ...colorMap.get(color), isUsable: false }
: { color, isDefault: false, isUsable: false };
@@ -39,13 +39,13 @@ function getTodoColors(todoStatus: TodoStatus[]): ColorInfo[] {
return [...colorMap.values()];
}

export default function ModalTodoStatus({ todoStatus, onClose }: TodoStatusProps) {
export default function ModalTodoStatus({ taskStatus, onClose }: TaskStatusProps) {
const {
register,
watch,
handleSubmit,
formState: { errors },
} = useForm<TodoStatusForm>({
} = useForm<TaskStatusForm>({
mode: 'onChange',
defaultValues: {
name: '',
@@ -56,17 +56,17 @@ export default function ModalTodoStatus({ todoStatus, onClose }: TodoStatusProps
const selectedColor = watch('color');

// ToDo: useMemo, useCallback 고려해보기
const colorList = getTodoColors(todoStatus);
const nameList = todoStatus.map((status) => status.name);
const colorNameList = todoStatus.map((status) => status.color);
const colorList = getTaskColors(taskStatus);
const nameList = taskStatus.map((status) => status.name);
const colorNameList = taskStatus.map((status) => status.color);

const handleClickDelete = (color: string) => {
// ToDo: 색상 삭제시 등록된 할일 목록이 있는지 확인하는 로직 추가
// ToDo: 색상 삭제를 위한 네트워크 로직 추가
console.log(`${color} 삭제`);
};

const onSubmit: SubmitHandler<TodoStatusForm> = async (data) => {
const onSubmit: SubmitHandler<TaskStatusForm> = async (data) => {
// ToDo: 색상 생성을 위한 네트워크 로직 추가
console.log(data);
};
11 changes: 0 additions & 11 deletions src/globals.css
Original file line number Diff line number Diff line change
@@ -42,17 +42,6 @@
--color-button: #efefef;
--color-kakao: #f6e04b;

/* todo color */
--color-todo-red: #c83c00;
--color-todo-yellow: #dab700;
--color-todo-green: #237700;
--color-todo-blue: #00c2ff;
--color-todo-orange: #ff7a00;
--color-todo-purple: #db00ff;
--color-todo-pink: #ff0099;
--color-todo-yellow-green: #8fff00;
--color-todo-gray: #d9d9d9;

/* text color */
--text-color-default: #2c2c2c;
--text-color-emphasis: #5e5e5e;
4 changes: 2 additions & 2 deletions src/layouts/page/ProjectLayout.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import ListProject from '@components/sidebar/ListProject';

import ModalPortal from '@components/common/ModalPortal';
import ModalLayout from '@layouts/ModalLayout';
import ModalTodoStatus from '@components/modal/ModalTodoStatus';
import ModalTaskStatus from '@components/modal/ModalTaskStatus';

const dummy = {
teamName: '김찌와 소주',
@@ -84,7 +84,7 @@ export default function ProjectLayout() {
{showStateModal && (
<ModalPortal>
<ModalLayout onClose={() => setShowStateModal(false)}>
<ModalTodoStatus onClose={() => setShowStateModal(false)} todoStatus={dummyColor.state} />
<ModalTaskStatus onClose={() => setShowStateModal(false)} taskStatus={dummyColor.state} />
</ModalLayout>
</ModalPortal>
)}
10 changes: 5 additions & 5 deletions src/mocks/mockData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { TodoStatus } from '@/types/TodoStatusType';
import type { TodoWithStatus } from '@/types/TodoType';
import type { TaskStatus } from '@/types/TaskStatusType';
import type { TaskWithStatus } from '@/types/TaskType';

export const USER_DUMMY = [
{
@@ -22,7 +22,7 @@ export const USER_DUMMY = [
},
];

export const STATUS_DUMMY: TodoStatus[] = [
export const STATUS_DUMMY: TaskStatus[] = [
{
statusId: 1,
name: 'To Do',
@@ -43,7 +43,7 @@ export const STATUS_DUMMY: TodoStatus[] = [
},
];

export const TODO_DUMMY: TodoWithStatus[] = [
export const TASK_DUMMY: TaskWithStatus[] = [
{
statusId: 1,
name: 'To Do',
@@ -113,7 +113,7 @@ export const TODO_DUMMY: TodoWithStatus[] = [
tasks: [
{
taskId: 1,
name: 'todo 상태 추가 모달 작업하기',
name: 'task 상태 추가 모달 작업하기',
order: 1,
userId: 2,
files: [],
30 changes: 15 additions & 15 deletions src/pages/project/KanbanPage.tsx
Original file line number Diff line number Diff line change
@@ -3,9 +3,9 @@ import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
import { BsPencil } from 'react-icons/bs';
import deepClone from '@utils/deepClone';

import { TODO_DUMMY } from '@mocks/mockData';
import { TASK_DUMMY } from '@mocks/mockData';
import type { DropResult } from '@hello-pangea/dnd';
import type { Todo, TodoWithStatus } from '@/types/TodoType';
import type { Task, TaskWithStatus } from '@/types/TaskType';

// ToDo: 유틸리티로 분리할지 고려하기
function generatorPrefixId(id: number | string, prefix: string, delimiter: string = '-') {
@@ -17,7 +17,7 @@ function parserPrefixId(prefixId: string, delimiter: string = '-') {
return result[result.length - 1];
}

function createChangedTodo(todo: TodoWithStatus[], dropResult: DropResult, isSameStatus: boolean) {
function createChangedStatusTasks(statusTasks: TaskWithStatus[], dropResult: DropResult, isSameStatus: boolean) {
const { source, destination, draggableId } = dropResult;

// ToDo: 메세지 포맷 정하고 수정하기
@@ -27,45 +27,45 @@ function createChangedTodo(todo: TodoWithStatus[], dropResult: DropResult, isSam
const destinationStatusId = Number(parserPrefixId(destination.droppableId));
const taskId = Number(parserPrefixId(draggableId));

const newTodo = deepClone(todo);
const { tasks: sourceTasks } = newTodo.find((data) => data.statusId === sourceStatusId)! as TodoWithStatus;
const newStatusTasks = deepClone(statusTasks);
const { tasks: sourceTasks } = newStatusTasks.find((data) => data.statusId === sourceStatusId)! as TaskWithStatus;
const { tasks: destinationTasks } = isSameStatus
? { tasks: sourceTasks }
: (newTodo.find((data) => data.statusId === destinationStatusId)! as TodoWithStatus);
const task = sourceTasks.find((data) => data.taskId === taskId)! as Todo;
: (newStatusTasks.find((data) => data.statusId === destinationStatusId)! as TaskWithStatus);
const task = sourceTasks.find((data) => data.taskId === taskId)! as Task;

sourceTasks.splice(source.index, 1);
destinationTasks.splice(destination.index, 0, task);

sourceTasks.forEach((task, index) => (task.order = index + 1));
if (!isSameStatus) destinationTasks.forEach((task, index) => (task.order = index + 1));

return newTodo;
return newStatusTasks;
}

// ToDo: 할일 상태 Vertical DnD 추가할 것
// ToDo: DnD시 가시성을 위한 애니메이션 처리 추가할 것
// ToDo: 칸반보드 ItemList, Item 컴포넌트로 분리할 것
export default function KanbanPage() {
const [todo, setTodo] = useState<TodoWithStatus[]>(TODO_DUMMY);
const [statusTasks, setStatusTasks] = useState<TaskWithStatus[]>(TASK_DUMMY);

const handleDragEnd = (dropResult: DropResult) => {
const { source, destination } = dropResult;

if (!destination) return;

const isSameStatus = source.droppableId === destination.droppableId;
const isSameTodo = source.index === destination.index;
if (isSameStatus && isSameTodo) return;
const isSameTask = source.index === destination.index;
if (isSameStatus && isSameTask) return;

const newTodo = createChangedTodo(todo, dropResult, isSameStatus);
setTodo(newTodo);
const newStatusTasks = createChangedStatusTasks(statusTasks, dropResult, isSameStatus);
setStatusTasks(newStatusTasks);
};

return (
<section className="flex grow gap-10 pt-10">
<DragDropContext onDragEnd={handleDragEnd}>
{todo.map((data) => {
{statusTasks.map((data) => {
const { statusId, name, color, tasks } = data;
const droppableId = generatorPrefixId(statusId, 'status');
return (
@@ -77,7 +77,7 @@ export default function KanbanPage() {
</span>
</header>
<div className="grow">
<Droppable droppableId={droppableId} type="TODO">
<Droppable droppableId={droppableId} type="TASK">
{(dropProvided) => {
return (
<article
4 changes: 2 additions & 2 deletions src/types/TodoStatusType.tsx → src/types/TaskStatusType.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// ToDo: API 설계 완료시 데이터 타입 변경할 것
export type TodoStatus = {
export type TaskStatus = {
statusId: number;
name: string;
color: string;
order: number;
};

export type TodoStatusForm = {
export type TaskStatusForm = {
name: string;
color: string;
};
6 changes: 3 additions & 3 deletions src/types/TodoType.tsx → src/types/TaskType.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TodoStatus } from './TodoStatusType';
import { TaskStatus } from './TaskStatusType';

// ToDo: API 설계 완료시 데이터 타입 변경할 것
export type Todo = {
export type Task = {
taskId: number;
name: string;
order: number;
@@ -11,4 +11,4 @@ export type Todo = {
endDate: string;
};

export type TodoWithStatus = TodoStatus & { tasks: Todo[] };
export type TaskWithStatus = TaskStatus & { tasks: Task[] };
12 changes: 1 addition & 11 deletions tailwind.config.js
Original file line number Diff line number Diff line change
@@ -59,17 +59,7 @@ export default {
selected: 'var(--color-selected)',
scroll: 'var(--color-scroll)',
button: 'var(--color-button)',
kako: 'var(--color-kakao)',
todo: {
red: 'var(--color-todo-red)',
yellow: 'var(--color-todo-yellow)',
green: 'var(--color-todo-green)',
blue: 'var(--color-todo-blue)',
orange: 'var(--color-todo-orange)',
purple: 'var(--color-todo-purple)',
'yellow-green': 'var(--color-todo-yellow-green)',
gray: 'var(--color-todo-gray)',
},
kakao: 'var(--color-kakao)',
},
},
},