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: # 63 할일 등록 모달 토대 UI 작성 & 일정명, 날짜 설정 기능 추가 #67

Merged
merged 10 commits into from
Aug 7, 2024
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"formulahendry.auto-rename-tag",
"pkief.material-icon-theme",
"equinusocio.vsc-material-theme",
"wix.vscode-import-cost"
"wix.vscode-import-cost",
"streetsidesoftware.code-spell-checker"
]
}
6 changes: 6 additions & 0 deletions src/assets/calendar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions src/components/common/DuplicationCheckInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { UseFormRegisterReturn } from 'react-hook-form';
import { GiCheckMark } from 'react-icons/gi';
import { RiProhibited2Line } from 'react-icons/ri';

type DuplicationCheckInputProp = {
id: string;
label?: string;
value: string;
placeholder?: string;
errors?: string;
register?: UseFormRegisterReturn;
};

export default function DuplicationCheckInput({
id,
label,
value,
placeholder,
errors,
register,
}: DuplicationCheckInputProp) {
return (
<label htmlFor={id}>
{label && <h3 className="text-large">{label}</h3>}
<div className="relative">
<input
type="text"
id={id}
className="h-25 w-full min-w-200 rounded-md border border-input pl-10 pr-25 text-regular placeholder:text-xs"
placeholder={placeholder}
{...register}
/>
{value && (
<div className="absolute right-10 top-1/2 -translate-y-1/2">
{errors ? (
<RiProhibited2Line className="size-10 text-error" />
) : (
<GiCheckMark className="size-10 text-main" />
)}
</div>
)}
</div>
<div className={`my-5 h-10 text-xs text-error ${errors ? 'visible' : 'invisible'}`}>{errors}</div>
</label>
);
}
26 changes: 26 additions & 0 deletions src/components/common/ToggleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
type ToggleButtonProps = {
id: string;
checked: boolean;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
};

export default function ToggleButton({ id, checked, onChange: handleChange }: ToggleButtonProps) {
return (
<label htmlFor={id} className="relative inline-block h-10 w-20">
<input
id={id}
type="checkbox"
className="peer h-0 w-0 border-main opacity-0"
checked={checked}
onChange={handleChange}
/>
{/* prettier-ignore */}
<span className="
absolute bottom-0 left-0 right-0 top-0 cursor-pointer rounded-full bg-disable transition duration-300
before:content-[''] before:absolute before:left-2 before:top-1/2 before:-translate-y-1/2 before:size-7
before:rounded-full before:bg-white before:transition before:duration-300
peer-checked:bg-main peer-checked:before:translate-x-9
"/>
</label>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ type ModalFormButtonProps = {
onClose: () => void;
};

export default function ModaFormButton({ formId, isCreate, onClose }: ModalFormButtonProps) {
export default function ModalFormButton({ formId, isCreate, onClose }: ModalFormButtonProps) {
return (
<div className="h-20">
<div className="min-h-20">
<button type="submit" form={formId} className="mr-10 h-full rounded-md bg-main px-10 text-white">
{isCreate ? '등록' : '수정'}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SubmitHandler } from 'react-hook-form';
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 ModalFormButton from '@components/modal/ModalFormButton';
import { ProjectStatusForm } from '@/types/ProjectStatusType';

type CreateModalProjectStatusProps = {
Expand All @@ -19,10 +19,8 @@ export default function CreateModalProjectStatus({ onClose: handleClose }: Creat
return (
<ModalPortal>
<ModalLayout onClose={handleClose}>
<div className="flex h-full flex-col items-center justify-center">
<ModalProjectStatusForm formId="createStatusForm" onSubmit={handleSubmit} />
<ModaFormButton formId="createStatusForm" isCreate onClose={handleClose} />
</div>
<ModalProjectStatusForm formId="createStatusForm" onSubmit={handleSubmit} />
<ModalFormButton formId="createStatusForm" isCreate onClose={handleClose} />
</ModalLayout>
</ModalPortal>
);
Expand Down
34 changes: 10 additions & 24 deletions src/components/modal/project-status/ModalProjectStatusForm.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SubmitHandler, useForm } from 'react-hook-form';
import { STATUS_VALIDATION_RULES } from '@constants/formValidationRules';
import useProjectStatusQuery from '@hooks/query/useProjectStatusQuery';
import { GiCheckMark } from 'react-icons/gi';
import { RiProhibited2Fill, RiProhibited2Line } from 'react-icons/ri';
import DuplicationCheckInput from '@components/common/DuplicationCheckInput';
import { RiProhibited2Fill } from 'react-icons/ri';
import type { ProjectStatus, ProjectStatusForm } from '@/types/ProjectStatusType';

type ModalProjectStatusFormProps = {
Expand All @@ -27,28 +27,14 @@ export default function ModalProjectStatusForm({ formId, statusId, onSubmit }: M

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>
<DuplicationCheckInput
id="name"
label="상태명"
value={statusName}
placeholder="상태명을 입력하세요."
errors={errors.name?.message}
register={register('name', STATUS_VALIDATION_RULES.STATUS_NAME(nameList))}
/>
<h3 className="text-large">색상</h3>
<section className="grid grid-cols-8 gap-4">
{usableColorList.map(({ color, isUsable }, index) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SubmitHandler } from 'react-hook-form';
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 ModalFormButton from '@components/modal/ModalFormButton';
import { ProjectStatus, ProjectStatusForm } from '@/types/ProjectStatusType';

type UpdateModalProjectStatusProps = {
Expand All @@ -21,10 +21,8 @@ export default function UpdateModalProjectStatus({ statusId, onClose: handleClos
return (
<ModalPortal>
<ModalLayout onClose={handleClose}>
<div className="flex h-full flex-col items-center justify-center">
<ModalProjectStatusForm formId="updateStatusForm" statusId={statusId} onSubmit={handleSubmit} />
<ModaFormButton formId="updateStatusForm" isCreate={false} onClose={handleClose} />
</div>
<ModalProjectStatusForm formId="updateStatusForm" statusId={statusId} onSubmit={handleSubmit} />
<ModalFormButton formId="updateStatusForm" isCreate={false} onClose={handleClose} />
</ModalLayout>
</ModalPortal>
);
Expand Down
13 changes: 6 additions & 7 deletions src/components/modal/task/CreateModalTask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { SubmitHandler } from 'react-hook-form';
import ModalLayout from '@layouts/ModalLayout';
import ModalPortal from '@components/modal/ModalPortal';
import ModalTaskForm from '@components/modal/task/ModalTaskForm';
import ModaFormButton from '@components/modal/ModaFormButton';
import ModalFormButton from '@components/modal/ModalFormButton';
import { TaskForm } from '@/types/TaskType';
import { Project } from '@/types/ProjectType';

type CreateModalTaskProps = {
project: Project;
onClose: () => void;
};

export default function CreateModalTask({ onClose: handleClose }: CreateModalTaskProps) {
export default function CreateModalTask({ project, onClose: handleClose }: CreateModalTaskProps) {
// ToDo: 상태 생성을 위한 네트워크 로직 추가
const handleSubmit: SubmitHandler<TaskForm> = async (data) => {
console.log('생성 폼 제출');
Expand All @@ -19,11 +21,8 @@ export default function CreateModalTask({ onClose: handleClose }: CreateModalTas
return (
<ModalPortal>
<ModalLayout onClose={handleClose}>
<div className="flex h-full flex-col items-center justify-center">
{/* ToDo: Task 생성 모달 작성시 수정할 것 */}
<ModalTaskForm formId="updateTaskForm" onSubmit={handleSubmit} />
<ModaFormButton formId="updateTaskForm" isCreate={false} onClose={handleClose} />
</div>
<ModalTaskForm formId="createTaskForm" project={project} onSubmit={handleSubmit} />
<ModalFormButton formId="createTaskForm" isCreate onClose={handleClose} />
</ModalLayout>
</ModalPortal>
);
Expand Down
112 changes: 105 additions & 7 deletions src/components/modal/task/ModalTaskForm.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,119 @@
import { useState } from 'react';
import { DateTime } from 'luxon';
import { IoSearch } from 'react-icons/io5';
import { SubmitHandler, useForm } from 'react-hook-form';
import { TASK_VALIDATION_RULES } from '@constants/formValidationRules';
import ToggleButton from '@components/common/ToggleButton';
import DuplicationCheckInput from '@components/common/DuplicationCheckInput';
import useTaskQuery from '@hooks/query/useTaskQuery';
import { Project } from '@/types/ProjectType';
import { Task, TaskForm } from '@/types/TaskType';

type ModalTaskFromProps = {
type ModalTaskFormProps = {
formId: string;
project: Project;
taskId?: Task['taskId'];
onSubmit: SubmitHandler<TaskForm>;
};

// ToDo: Task 모달 작성시 Task Form 만들기
export default function ModalTaskForm({ formId, taskId, onSubmit }: ModalTaskFromProps) {
const { register, handleSubmit } = useForm<TaskForm>({
export default function ModalTaskForm({ formId, project, taskId, onSubmit }: ModalTaskFormProps) {
const [hasDeadline, setHasDeadline] = useState(false);
const { taskNameList } = useTaskQuery(project.projectId);
const {
register,
watch,
setValue,
getValues,
clearErrors,
handleSubmit,
formState: { errors },
} = useForm<TaskForm>({
mode: 'onChange',
defaultValues: {},
defaultValues: {
name: '',
content: '',
startDate: DateTime.fromJSDate(new Date()).toFormat('yyyy-LL-dd'),
endDate: DateTime.fromJSDate(new Date()).toFormat('yyyy-LL-dd'),
},
});

const handleDeadlineToggle = () => {
setValue('endDate', getValues('startDate'));
clearErrors('endDate');
setHasDeadline((prev) => !prev);
};

return (
<form id={formId} className="mb-10 flex grow flex-col justify-center" onSubmit={handleSubmit(onSubmit)}>
<div>{taskId}</div>
<form
id={formId}
className="mb-20 flex w-4/5 max-w-375 grow flex-col justify-center"
onSubmit={handleSubmit(onSubmit)}
>
<DuplicationCheckInput
id="name"
label="일정"
value={watch('name')}
placeholder="일정명을 입력해주세요."
errors={errors.name?.message}
register={register('name', TASK_VALIDATION_RULES.TASK_NAME(taskNameList))}
/>

<div className="flex items-center justify-center gap-10">
<label htmlFor="startDate" className="w-1/2">
<h3 className="text-large">시작일</h3>
<input
type="date"
id="startDate"
{...register('startDate', TASK_VALIDATION_RULES.START_DATE(project.startDate, project.endDate))}
/>
<div className={`my-5 h-10 grow text-xs text-error ${errors.startDate ? 'visible' : 'invisible'}`}>
{errors.startDate?.message}
</div>
</label>
<label htmlFor="endDate" className="w-1/2">
<h3 className="flex items-center text-large">
<span className="mr-2">종료일</span>
<ToggleButton id="deadline" checked={hasDeadline} onChange={handleDeadlineToggle} />
</h3>
<input
type="date"
id="endDate"
className={`${hasDeadline ? '' : '!bg-disable'}`}
disabled={!hasDeadline}
{...register(
'endDate',
TASK_VALIDATION_RULES.END_DATE(hasDeadline, project.startDate, project.endDate, watch('startDate')),
)}
/>
<div className={`my-5 h-10 grow text-xs text-error ${errors.endDate ? 'visible' : 'invisible'}`}>
{errors.endDate?.message}
</div>
</label>
</div>

<label htmlFor="user" className="mb-20 flex items-center gap-5">
<h3 className="text-large">수행자</h3>
<div className="relative grow">
<input
type="text"
id="user"
className="h-25 w-full rounded-md border border-input pl-10 pr-25 text-regular placeholder:text-xs"
/>
<div className="absolute right-5 top-1/2 -translate-y-1/2">
<IoSearch className="size-15 text-emphasis" />
</div>
</div>
</label>

<label htmlFor="content" className="mb-20">
<h3 className="text-large">내용</h3>
<textarea name="content" id="content" className="w-full border" rows={5} />
</label>

<label htmlFor="files">
<h3 className="text-large">첨부파일</h3>
<input type="file" id="files" />
</label>
</form>
);
}
14 changes: 7 additions & 7 deletions src/components/modal/task/UpdateModalTask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import { SubmitHandler } from 'react-hook-form';
import ModalLayout from '@layouts/ModalLayout';
import ModalPortal from '@components/modal/ModalPortal';
import ModalTaskForm from '@components/modal/task/ModalTaskForm';
import ModaFormButton from '@components/modal/ModaFormButton';
import ModalFormButton from '@components/modal/ModalFormButton';
import { Task, TaskForm } from '@/types/TaskType';
import { Project } from '@/types/ProjectType';

type UpdateModalTaskProps = {
project: Project;
taskId: Task['taskId'];
onClose: () => void;
};

export default function UpdateModalTask({ taskId, onClose: handleClose }: UpdateModalTaskProps) {
export default function UpdateModalTask({ project, taskId, onClose: handleClose }: UpdateModalTaskProps) {
// ToDo: 상태 생성을 위한 네트워크 로직 추가
const handleSubmit: SubmitHandler<TaskForm> = async (data) => {
console.log('생성 폼 제출');
Expand All @@ -20,11 +22,9 @@ export default function UpdateModalTask({ taskId, onClose: handleClose }: Update
return (
<ModalPortal>
<ModalLayout onClose={handleClose}>
<div className="flex h-full flex-col items-center justify-center">
{/* ToDo: Task 수정 모달 작성시 수정할 것 */}
<ModalTaskForm formId="updateTaskForm" taskId={taskId} onSubmit={handleSubmit} />
<ModaFormButton formId="updateTaskForm" isCreate={false} onClose={handleClose} />
</div>
{/* ToDo: Task 수정 모달 작성시 수정할 것 */}
<ModalTaskForm formId="updateTaskForm" taskId={taskId} project={project} onSubmit={handleSubmit} />
<ModalFormButton formId="updateTaskForm" isCreate={false} onClose={handleClose} />
</ModalLayout>
</ModalPortal>
);
Expand Down
Loading