Skip to content

Commit

Permalink
Feat: #63 수행자 목록 UI 추가 & 수행자 추가/삭제 기능 구현 (#79)
Browse files Browse the repository at this point in the history
* Feat: #63 수행자 목록 UI 작업 & 추가/삭제 기능

* UI: #63 할일 추가 / 상태 추가 버튼 UI 스타일 변경
  • Loading branch information
Seok93 authored Aug 20, 2024
1 parent b1b7a37 commit fe01efe
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 23 deletions.
48 changes: 36 additions & 12 deletions src/components/modal/task/ModalTaskForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import { DateTime } from 'luxon';
import { useForm } from 'react-hook-form';
import { IoSearch } from 'react-icons/io5';
import { IoMdCloseCircle } from 'react-icons/io';

import { TASK_VALIDATION_RULES } from '@constants/formValidationRules';
import ToggleButton from '@components/common/ToggleButton';
import DuplicationCheckInput from '@components/common/DuplicationCheckInput';
import useToast from '@hooks/useToast';
import useAxios from '@hooks/useAxios';
import useTaskQuery from '@hooks/query/useTaskQuery';
import useStatusQuery from '@hooks/query/useStatusQuery';
import { findUserByProject } from '@services/projectService';

import type { SubmitHandler } from 'react-hook-form';
import type { User } from '@/types/UserType';
import type { UserWithRole } from '@/types/UserType';
import type { Project } from '@/types/ProjectType';
import type { Task, TaskForm } from '@/types/TaskType';

Expand All @@ -29,11 +31,12 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
const abortControllerRef = useRef<AbortController | null>(null);
const [hasDeadline, setHasDeadline] = useState(false);
const [keyword, setKeyword] = useState('');
const [workers, setWorkers] = useState<User[]>([]);
const [workers, setWorkers] = useState<UserWithRole[]>([]);

const { statusList } = useStatusQuery(projectId, taskId);
const { taskNameList } = useTaskQuery(projectId);
const { data, loading, clearData, fetchData } = useAxios(findUserByProject);
const { toastInfo } = useToast();

// ToDo: 상태 수정 모달 작성시 기본값 설정 방식 변경할 것
const {
Expand All @@ -49,6 +52,7 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
defaultValues: {
name: '',
content: '',
userId: [],
startDate: DateTime.fromJSDate(new Date()).toFormat('yyyy-LL-dd'),
endDate: DateTime.fromJSDate(new Date()).toFormat('yyyy-LL-dd'),
statusId: statusList[0].statusId,
Expand Down Expand Up @@ -86,24 +90,33 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
const handleSearchClick = () => searchUsers();

const handleSearchKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key.toLocaleLowerCase() === 'enter') {
if (e.key.toLowerCase() === 'enter') {
e.preventDefault();
searchUsers();
}
};

const handleUserClick = (user: User) => {
setWorkers((prev) => [...prev, user]);
const handleUserClick = (user: UserWithRole) => {
const isIncludedUser = workers.find((worker) => worker.userId === user.userId);
if (isIncludedUser) return toastInfo('이미 포함된 수행자입니다');

const newWorkers = [...workers, user];
const workersIdList = newWorkers.map((worker) => worker.userId);
setWorkers(newWorkers);
setValue('userId', workersIdList);
setKeyword('');
clearData();
};

const handleDeleteClick = (user: UserWithRole) => {
const filteredWorker = workers.filter((worker) => worker.userId !== user.userId);
const workersIdList = filteredWorker.map((worker) => worker.userId);
setWorkers(filteredWorker);
setValue('userId', workersIdList);
};

return (
<form
id={formId}
className="mb-20 flex w-4/5 max-w-375 grow flex-col justify-center"
onSubmit={handleSubmit(onSubmit)}
>
<form id={formId} className="mb-20 flex w-4/5 grow flex-col justify-center" onSubmit={handleSubmit(onSubmit)}>
{/* ToDo: 상태 선택 리팩토링 할 것 */}
<div className="flex items-center justify-start gap-4">
{statusList.map((status) => {
Expand Down Expand Up @@ -187,7 +200,7 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
value={keyword}
onChange={handleKeywordChange}
onKeyDown={handleSearchKeyUp}
placeholder="닉네임으로 검색해주세요."
placeholder="닉네임을 검색해주세요."
/>
<button
type="button"
Expand All @@ -198,7 +211,7 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
<IoSearch className="size-15 text-emphasis hover:text-black" />
</button>
{keyword && !loading && (
<ul className="invisible absolute left-0 right-0 rounded-md border-2 bg-white group-focus-within:visible">
<ul className="invisible absolute left-0 right-0 max-h-110 overflow-auto rounded-md border-2 bg-white group-focus-within:visible">
{data && data.length === 0 ? (
<div className="h-20 border px-10 leading-8">&apos;{keyword}&apos; 의 검색 결과가 없습니다.</div>
) : (
Expand All @@ -221,6 +234,17 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
)}
</section>
</label>
<section className="flex w-full flex-wrap items-center gap-4">
{workers.map((user) => (
<div key={user.userId} className="flex items-center space-x-4 rounded-md bg-button px-5">
<div>{user.roleName}</div>
<div>{user.nickname}</div>
<button type="button" aria-label="delete-worker" onClick={() => handleDeleteClick(user)}>
<IoMdCloseCircle className="text-error" />
</button>
</div>
))}
</section>
</div>

<label htmlFor="content" className="mb-20">
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/ModalLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function ModalLayout({ onClose, children }: ModalLayoutProps) {
className="absolute bottom-0 left-0 right-0 top-0 z-20 flex items-center justify-center bg-black/50"
tabIndex={-1}
>
<div className="flex h-4/5 max-h-375 min-w-375 flex-col items-center overflow-auto bg-white p-20">{children}</div>
<div className="flex h-4/5 max-h-375 w-375 flex-col items-center overflow-auto bg-white p-20">{children}</div>
</div>
);
}
4 changes: 2 additions & 2 deletions src/layouts/page/ProjectLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ export default function ProjectLayout() {
</li>
</ul>
<div className="text-main *:ml-10">
<button type="button" onClick={openTaskModal}>
<button type="button" className="outline-none" onClick={openTaskModal}>
+ 할일 추가
</button>
<button type="button" onClick={openStatusModal}>
<button type="button" className="outline-none" onClick={openStatusModal}>
+ 상태 추가
</button>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/mocks/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export const ROLE_DUMMY: Role[] = [
},
{
roleId: 4,
roleName: 'Admin',
roleName: 'ADMIN',
roleType: 'PROJECT',
},
{
Expand All @@ -215,7 +215,7 @@ export const ROLE_DUMMY: Role[] = [
},
{
roleId: 6,
roleName: 'Assignee',
roleName: 'ASSIGNEE',
roleType: 'PROJECT',
},
] as const;
Expand Down
6 changes: 3 additions & 3 deletions src/services/projectService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { authAxios } from '@services/axiosProvider';
import type { AxiosRequestConfig } from 'axios';
import type { User } from '@/types/UserType';
import type { User, UserWithRole } from '@/types/UserType';
import type { Project } from '@/types/ProjectType';

/**
Expand All @@ -11,12 +11,12 @@ import type { Project } from '@/types/ProjectType';
* @param {Project['projectId']} projectId - 프로젝트 아이디
* @param {User['nickname']} nickname - 유저 닉네임
* @param {AxiosRequestConfig} [axiosConfig={}] - axios 요청 옵션 설정 객체
* @returns {Promise<AxiosResponse<User[]>>}
* @returns {Promise<AxiosResponse<UserWithRole[]>>}
*/
export async function findUserByProject(
projectId: Project['projectId'],
nickname: User['nickname'],
axiosConfig: AxiosRequestConfig = {},
) {
return authAxios.get<User[]>(`project/${projectId}/user/search?nickname=${nickname}`, axiosConfig);
return authAxios.get<UserWithRole[]>(`project/${projectId}/user/search?nickname=${nickname}`, axiosConfig);
}
2 changes: 1 addition & 1 deletion src/types/RoleType.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type Role = {
roleId: number;
roleName: 'HEAD' | 'LEADER' | 'MATE' | 'Admin' | 'Assignee';
roleName: 'HEAD' | 'LEADER' | 'MATE' | 'ADMIN' | 'ASSIGNEE';
roleType: 'TEAM' | 'PROJECT';
};
6 changes: 4 additions & 2 deletions src/types/TaskType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ type StatusKeyMapping = {
export type Task = {
taskId: number;
name: string;
order: number;
userId: number;
files: string[];
// content: string;
startDate: string;
endDate: string;
files: string[];
order: number;
};

// ToDo: Task 추가 모달 작업시 같이 정의할 것
export type TaskForm = {
name: string;
content: string;
userId: number[];
startDate: string;
endDate: string;
statusId: number;
Expand Down
4 changes: 4 additions & 0 deletions src/types/UserType.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Role } from '@/types/RoleType';

export type User = {
userId: number;
id: string | null;
Expand All @@ -9,6 +11,8 @@ export type User = {
profileUrl: string | null;
};

export type UserWithRole = User & Role;

export type UserSignUpForm = Omit<User, 'userId' | 'provider'> & {
code: string;
password: string;
Expand Down

0 comments on commit fe01efe

Please sign in to comment.