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

Refactor: #44 상태 모달 생성/수정 폼 공유를 위한 리팩토링 #46

Merged
merged 4 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/components/modal/ModaFormButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
type ModalFormButtonProps = {
formId: string;
isCreate: boolean;
onClose: () => void;
};

export default function ModaFormButton({ formId, isCreate, onClose }: ModalFormButtonProps) {
return (
<div className="h-20">
<button type="submit" form={formId} className="mr-10 h-full rounded-md bg-main px-10 text-white">
{isCreate ? '등록' : '수정'}
</button>
<button type="button" className="h-full rounded-md bg-button px-10" onClick={onClose}>
닫기
</button>
</div>
);
}
143 changes: 0 additions & 143 deletions src/components/modal/ModalProjectStatus.tsx

This file was deleted.

30 changes: 30 additions & 0 deletions src/components/modal/project-status/CreateModalProjectStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import ModalLayout from '@layouts/ModalLayout';
import ModalPortal from '@components/modal/ModalPortal';
import ModalProjectStatusForm from '@components/modal/project-status/ModalProjectStatusForm';
import ModaFormButton from '@components/modal/ModaFormButton';
import { SubmitHandler } from 'react-hook-form';
import { ProjectStatus, ProjectStatusForm } from '@/types/ProjectStatusType';

type ModalProjectStatusProps = {
onClose: () => void;
projectStatus: ProjectStatus[];
};

export default function CreateModalProjectStatus({ onClose: handleClose, projectStatus }: ModalProjectStatusProps) {
// ToDo: 상태 생성을 위한 네트워크 로직 추가
const handleSubmit: SubmitHandler<ProjectStatusForm> = async (data) => {
console.log('생성 폼 제출');
console.log(data);
handleClose();
};
return (
<ModalPortal>
<ModalLayout onClose={handleClose}>
<div className="flex h-full flex-col items-center justify-center">
<ModalProjectStatusForm formId="createStatusForm" projectStatus={projectStatus} onSubmit={handleSubmit} />
<ModaFormButton formId="createStatusForm" isCreate onClose={handleClose} />
</div>
</ModalLayout>
</ModalPortal>
);
}
99 changes: 99 additions & 0 deletions src/components/modal/project-status/ModalProjectStatusForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { SubmitHandler, useForm } from 'react-hook-form';
import { PROJECT_STATUS_COLORS } from '@constants/projectStatus';
import { STATUS_VALIDATION_RULES } from '@constants/formValidationRules';
import { GiCheckMark } from 'react-icons/gi';
import { RiProhibited2Fill, RiProhibited2Line } from 'react-icons/ri';
import type { ColorInfo, ProjectStatus, ProjectStatusForm } from '@/types/ProjectStatusType';

type ModalProjectStatusFormProps = {
formId: string;
projectStatus: ProjectStatus[];
onSubmit: SubmitHandler<ProjectStatusForm>;
};

// 색상 정보 취득
function getProjectColors(projectStatus: ProjectStatus[]): ColorInfo[] {
const colorMap = new Map();
Object.values(PROJECT_STATUS_COLORS).forEach((color) => {
colorMap.set(color, { color, isUsable: true });
});

projectStatus.forEach(({ color }) => {
if (!colorMap.has(color)) throw Error('[Error] 등록되지 않은 색상입니다.');
colorMap.set(color, { ...colorMap.get(color), isUsable: false });
});

return [...colorMap.values()];
}

export default function ModalProjectStatusForm({ formId, projectStatus, onSubmit }: ModalProjectStatusFormProps) {
const {
register,
watch,
handleSubmit,
formState: { errors },
} = useForm<ProjectStatusForm>({
mode: 'onChange',
defaultValues: {
name: '',
color: '',
},
});
const statusName = watch('name');
const selectedColor = watch('color');

const colorList = getProjectColors(projectStatus);
const nameList = projectStatus.map((status) => status.name);
const colorNameList = projectStatus.map((status) => status.color);

return (
<form id={formId} className="mb-10 flex grow flex-col justify-center" onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="name" className="mb-10">
<h3 className="text-large">상태명</h3>
<div className="relative">
<input
type="text"
id="name"
className="h-25 w-200 rounded-md border border-input pl-10 pr-25 text-regular placeholder:text-xs"
placeholder="상태명을 입력하세요."
{...register('name', STATUS_VALIDATION_RULES.STATUS_NAME(nameList))}
/>
{statusName && (
<div className="absolute right-10 top-1/2 -translate-y-1/2">
{errors.name ? (
<RiProhibited2Line className="size-10 text-error" />
) : (
<GiCheckMark className="size-10 text-main" />
)}
</div>
)}
</div>
{errors.name && <div className="mt-5 text-xs text-error">{errors.name.message}</div>}
</label>

<h3 className="text-large">색상</h3>
<section className="grid grid-cols-8 gap-4">
{colorList.map(({ color, isUsable }, index) => (
<div className="group relative m-auto" key={index}>
<label
htmlFor={color}
style={{ backgroundColor: color }}
className={`realative inline-block size-20 cursor-pointer rounded-full ${selectedColor === color ? 'border-4 border-selected' : ''}`}
>
<input
type="radio"
id={color}
value={color}
className="hidden"
disabled={!isUsable}
{...register('color', STATUS_VALIDATION_RULES.COLOR(colorNameList))}
/>
{!isUsable && <RiProhibited2Fill className="size-20 text-white" />}
</label>
</div>
))}
</section>
{errors.color && <div className="mt-5 text-xs text-error">{errors.color.message}</div>}
</form>
);
}
10 changes: 10 additions & 0 deletions src/constants/projectStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const PROJECT_STATUS_COLORS = Object.freeze({
RED: '#c83c00',
YELLOW: '#dab700',
GREEN: '#237700',
BLUE: '#00c2ff',
ORANGE: '#ff7a00',
PURPLE: '#db00ff',
PINK: '#ff0099',
YELLO_GREEN: '#8fff00',
});
22 changes: 3 additions & 19 deletions src/layouts/page/ProjectLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { NavLink, Outlet, useParams } from 'react-router-dom';
import ListSidebar from '@components/sidebar/ListSidebar';
import ListProject from '@components/sidebar/ListProject';

import ModalPortal from '@components/common/ModalPortal';
import ModalLayout from '@layouts/ModalLayout';
import ModalProjectStatus from '@components/modal/ModalProjectStatus';
import CreateModalProjectStatus from '@components/modal/project-status/CreateModalProjectStatus';
import { TASK_DUMMY } from '@mocks/mockData';

const dummy = {
teamName: '김찌와 소주',
Expand All @@ -25,17 +24,6 @@ const dummy = {
],
};

const dummyColor = {
proejctId: 1,
state: [
{ statusId: 1, name: 'To Do', color: '#c83c00' },
{ statusId: 2, name: 'In Progress', color: '#dab700' },
{ statusId: 3, name: 'Done', color: '#237700' },
{ statusId: 4, name: 'Ready', color: '#2dd4bf' },
{ statusId: 4, name: '보류', color: '#2dd4bf' },
],
};

export default function ProjectLayout() {
const { projectId } = useParams();
const [showStateModal, setShowStateModal] = useState(false);
Expand Down Expand Up @@ -82,11 +70,7 @@ export default function ProjectLayout() {
</section>
</section>
{showStateModal && (
<ModalPortal>
<ModalLayout onClose={() => setShowStateModal(false)}>
<ModalProjectStatus onClose={() => setShowStateModal(false)} projectStatus={dummyColor.state} />
</ModalLayout>
</ModalPortal>
<CreateModalProjectStatus onClose={() => setShowStateModal(false)} projectStatus={TASK_DUMMY} />
)}
</>
);
Expand Down
4 changes: 2 additions & 2 deletions src/mocks/mockData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { TaskStatus } from '@/types/ProjectStatusType';
import type { ProjectStatus } from '@/types/ProjectStatusType';
import type { TaskWithStatus } from '@/types/TaskType';

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

export const STATUS_DUMMY: TaskStatus[] = [
export const STATUS_DUMMY: ProjectStatus[] = [
{
statusId: 1,
name: 'To Do',
Expand Down
1 change: 0 additions & 1 deletion src/types/ProjectStatusType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@ export type ProjectStatusForm = {

export type ColorInfo = {
color: string;
isDefault: boolean;
isUsable: boolean;
};