From c7fde2ea1915f307535be09b515e8bd2503a59c0 Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Sat, 4 Apr 2026 23:31:35 +0900 Subject: [PATCH 01/13] =?UTF-8?q?fix=20:=20=EB=AF=B8=EC=85=98=20=EC=A0=9C?= =?UTF-8?q?=EC=B6=9C=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/mission-card.tsx | 2 +- src/components/card/my-homework-status-card.tsx | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/card/mission-card.tsx b/src/components/card/mission-card.tsx index 665ab95b1..56dbb592a 100644 --- a/src/components/card/mission-card.tsx +++ b/src/components/card/mission-card.tsx @@ -282,7 +282,7 @@ function MissionCardContent({ return (
{deadlineInfo && ( - + {deadlineInfo.text} )} diff --git a/src/components/card/my-homework-status-card.tsx b/src/components/card/my-homework-status-card.tsx index 417556108..eb734517c 100644 --- a/src/components/card/my-homework-status-card.tsx +++ b/src/components/card/my-homework-status-card.tsx @@ -34,8 +34,8 @@ export default function MyHomeworkStatusCard({ }; const isSubmissionOpen = mission?.status === 'IN_PROGRESS'; + const isSubmissionEnded = mission?.status === 'ENDED'; - // 미제출 상태 if (!myHomework || myHomework.homeworkStatus === 'NOT_SUBMITTED') { return (
@@ -44,9 +44,10 @@ export default function MyHomeworkStatusCard({
- {isSubmissionOpen - ? '아직 과제를 제출하지 않았습니다.' - : '제출 기간이 종료되었습니다.'} + {isSubmissionOpen && '아직 과제를 제출하지 않았습니다.'} + + + {isSubmissionEnded && '제출 기간이 종료되었습니다.'} {isSubmissionOpen && }
From a28c53717a76548e9edf94d6e9824c4b8249f5aa Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:35:19 +0900 Subject: [PATCH 02/13] =?UTF-8?q?feat=20:=20=EC=95=84=EC=A7=81=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=9C=20=EC=A0=9C=EC=B6=9C=20=EA=B8=B0=EA=B0=84=EC=9D=B4=20?= =?UTF-8?q?=EC=98=A4=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=B2=98=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/my-homework-status-card.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/card/my-homework-status-card.tsx b/src/components/card/my-homework-status-card.tsx index eb734517c..7f9f11ff4 100644 --- a/src/components/card/my-homework-status-card.tsx +++ b/src/components/card/my-homework-status-card.tsx @@ -34,6 +34,7 @@ export default function MyHomeworkStatusCard({ }; const isSubmissionOpen = mission?.status === 'IN_PROGRESS'; + const isSubmissionNotOpened = mission?.status === 'NOT_STARTED'; const isSubmissionEnded = mission?.status === 'ENDED'; if (!myHomework || myHomework.homeworkStatus === 'NOT_SUBMITTED') { @@ -46,6 +47,9 @@ export default function MyHomeworkStatusCard({ {isSubmissionOpen && '아직 과제를 제출하지 않았습니다.'} + + {isSubmissionNotOpened && '과제 제출 기간이 아닙니다.'} + {isSubmissionEnded && '제출 기간이 종료되었습니다.'} From e9ce265393c8e0581be7a3929af793302f4dcbb4 Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:35:35 +0900 Subject: [PATCH 03/13] =?UTF-8?q?feat=20:=20=EB=AF=B8=EC=85=98=20=ED=8F=89?= =?UTF-8?q?=EA=B0=80=20=EB=AA=A8=EB=8B=AC=20=EB=8B=A4=EC=8B=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/modals/create-evaluation-modal.tsx | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 src/components/common/modals/create-evaluation-modal.tsx diff --git a/src/components/common/modals/create-evaluation-modal.tsx b/src/components/common/modals/create-evaluation-modal.tsx new file mode 100644 index 000000000..0367f1e74 --- /dev/null +++ b/src/components/common/modals/create-evaluation-modal.tsx @@ -0,0 +1,175 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useState } from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; +import { z } from 'zod'; +import Button from '@/components/common/ui/button'; +import FormField from '@/components/common/ui/form/form-field'; +import { TextAreaInput } from '@/components/common/ui/input'; +import { Modal } from '@/components/common/ui/modal'; +import { GroupItems } from '@/components/common/ui/toggle'; +import { + useCreateEvaluation, + useGetMissionEvaluationGrades, +} from '@/hooks/queries/evaluation-api'; +import { useToastStore } from '@/stores/use-toast-store'; + +const CreateEvaluationFormSchema = z.object({ + gradeCode: z.enum([ + 'A_PLUS', + 'A_MINUS', + 'B_PLUS', + 'B_MINUS', + 'C_PLUS', + 'C_MINUS', + 'F', + ]), + comment: z.string().min(1, '정성 코멘트를 입력해주세요.').max(1000), +}); + +type CreateEvaluationFormValues = z.infer; + +interface CreateEvaluationModalProps { + homeworkId: number; +} + +export default function CreateEvaluationModal({ + homeworkId, +}: CreateEvaluationModalProps) { + const [open, setOpen] = useState(false); + + return ( + + + + + + + + + + + 평가하기 + + setOpen(false)} /> + + + setOpen(false)} + /> + + + + ); +} + +interface CreateEvaluationFormProps { + homeworkId: number; + onClose: () => void; +} + +function CreateEvaluationForm({ + homeworkId, + onClose, +}: CreateEvaluationFormProps) { + const methods = useForm({ + resolver: zodResolver(CreateEvaluationFormSchema), + mode: 'onChange', + defaultValues: { + gradeCode: undefined, + comment: '', + }, + }); + + const { handleSubmit, formState } = methods; + + const { data: grades } = useGetMissionEvaluationGrades(); + const { mutate: createEvaluation } = useCreateEvaluation(); + const showToast = useToastStore((state) => state.showToast); + + const onValidSubmit = (values: CreateEvaluationFormValues) => { + createEvaluation( + { + homeworkId, + request: values, + }, + { + onSuccess: () => { + showToast('평가가 성공적으로 제출되었습니다!'); + onClose(); + }, + onError: () => { + showToast('평가 제출에 실패했습니다. 다시 시도해주세요.', 'error'); + }, + }, + ); + }; + + const gradeOptions = grades + ?.sort((a, b) => a.orderNum - b.orderNum) + .map((grade) => ({ + value: grade.code, + label: `${grade.label} (${grade.score === 0 ? '0' : grade.score.toFixed(1)})`, + })); + + return ( + + +
+ + name="gradeCode" + label="평가 점수 선택" + direction="vertical" + required + > + + + + + name="comment" + label="정성 코멘트" + direction="vertical" + required + > + + + +
+ + + + + + + +
+ ); +} From 6fe9f31ac8da2eb13840ccedba5fcb5c1e093425 Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:37:49 +0900 Subject: [PATCH 04/13] =?UTF-8?q?fix=20:=20=EB=A6=AC=EB=8D=94=EC=9D=B4?= =?UTF-8?q?=EB=A9=B4=EC=84=9C=20=EB=A9=98=ED=86=A0=EC=8A=A4=ED=84=B0?= =?UTF-8?q?=EB=94=94=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contents/homework-detail-content.tsx | 66 ++++++++++++++++++- src/components/section/mission-section.tsx | 9 ++- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/components/contents/homework-detail-content.tsx b/src/components/contents/homework-detail-content.tsx index 153665ab9..17b3a988b 100644 --- a/src/components/contents/homework-detail-content.tsx +++ b/src/components/contents/homework-detail-content.tsx @@ -4,7 +4,10 @@ import dynamic from 'next/dynamic'; import { useRouter, useSearchParams } from 'next/navigation'; import { useState } from 'react'; -import type { PeerReviewResponse } from '@/api/openapi/models'; +import type { + EvaluationResponse, + PeerReviewResponse, +} from '@/api/openapi/models'; import Avatar from '@/components/common/ui/avatar'; import Button from '@/components/common/ui/button'; import MoreMenu from '@/components/common/ui/dropdown/more-menu'; @@ -15,7 +18,6 @@ import { useDeletePeerReview, useUpdatePeerReview, } from '@/hooks/queries/peer-review-api'; - import { useUserStore } from '@/stores/useUserStore'; import { formatExternalLink } from '@/utils/format'; import MarkdownContent from '../common/ui/editor/markdown-content'; @@ -35,14 +37,21 @@ const EditHomeworkModal = dynamic( { ssr: false }, ); +const CreateEvaluationModal = dynamic( + () => import('@/components/common/modals/create-evaluation-modal'), + { ssr: false }, +); + interface HomeworkDetailContentProps { missionId: number; homeworkId: number; + showLeaderEvaluation?: boolean; } export default function HomeworkDetailContent({ homeworkId, missionId, + showLeaderEvaluation, }: HomeworkDetailContentProps) { const router = useRouter(); const searchParams = useSearchParams(); @@ -148,6 +157,13 @@ export default function HomeworkDetailContent({ )}
+ {showLeaderEvaluation && ( + + )} + {/* 피어 리뷰 */} ); } + +interface LeaderEvaluationSectionProps { + evaluation?: EvaluationResponse; + homeworkId: number; +} + +function LeaderEvaluationSection({ + evaluation, + homeworkId, +}: LeaderEvaluationSectionProps) { + return ( +
+ 리더 평가 + +
+ {evaluation ? ( +
+
+ + 평가 등급 + + + {evaluation.grade?.gradeLabel ?? '-'} + +
+
+ + 평가 코멘트 + +

+ {evaluation.comment} +

+
+
+ ) : ( + <> + + 아직 평가하지 않은 과제입니다. + + + + )} +
+
+ ); +} diff --git a/src/components/section/mission-section.tsx b/src/components/section/mission-section.tsx index 1914cd7be..e5aaa0e1e 100644 --- a/src/components/section/mission-section.tsx +++ b/src/components/section/mission-section.tsx @@ -6,6 +6,7 @@ import { useRouter, useSearchParams } from 'next/navigation'; import { useRef, useState } from 'react'; import { useAuthReady } from '@/features/auth/model/use-auth'; import { useGetMissions } from '@/hooks/queries/mission-api'; +import { useGroupStudyDetailQuery } from '@/hooks/queries/use-study-query'; import MissionCard from '../card/mission-card'; import PageContainer from '../common/layout/page-container'; import { cn } from '../common/ui/(shadcn)/lib/utils'; @@ -33,7 +34,6 @@ interface MissionSectionProps { groupStudyId: number; isMember?: boolean; isLeader?: boolean; - showMyHomework?: boolean; } const FIRST_WEEK = 1; @@ -42,8 +42,10 @@ export default function MissionSection({ groupStudyId, isMember, isLeader: isLeaderProp, - showMyHomework, }: MissionSectionProps) { + const { data: studyDetail } = useGroupStudyDetailQuery(groupStudyId); + const isPremiumStudy = + studyDetail?.basicInfo.classification === 'MENTOR_STUDY'; const router = useRouter(); const searchParams = useSearchParams(); const [filter, setFilter] = useState('all'); @@ -144,6 +146,7 @@ export default function MissionSection({ ); @@ -164,7 +167,7 @@ export default function MissionSection({ ); From 047c720ae949baad389dfdcf624338186ec8b0e8 Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:08:52 +0900 Subject: [PATCH 05/13] =?UTF-8?q?feat=20:=20mission.status=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/contents/homework-detail-content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/contents/homework-detail-content.tsx b/src/components/contents/homework-detail-content.tsx index 17b3a988b..e00ab6733 100644 --- a/src/components/contents/homework-detail-content.tsx +++ b/src/components/contents/homework-detail-content.tsx @@ -157,7 +157,7 @@ export default function HomeworkDetailContent({ )}
- {showLeaderEvaluation && ( + {showLeaderEvaluation && mission.status === 'EVALUATION_COMPLETED' && ( Date: Tue, 7 Apr 2026 15:41:16 +0900 Subject: [PATCH 06/13] =?UTF-8?q?refactor=20:=20=EC=8A=A4=ED=84=B0?= =?UTF-8?q?=EB=94=94=20=EC=83=81=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?First=20Load=20JS=20=ED=81=AC=EA=B8=B0=20=EA=B0=9C=EC=84=A0=20-?= =?UTF-8?q?=20=EB=A7=88=ED=81=AC=EB=8B=A4=EC=9A=B4=20=EC=97=90=EB=94=94?= =?UTF-8?q?=ED=84=B0=20=EC=A0=81=EC=9A=A9=20=ED=9B=84=20=EC=BB=A4=EC=A1=8C?= =?UTF-8?q?=EB=8D=98=20First=20Load=20JS=20=ED=81=AC=EA=B8=B0=EB=A5=BC=20d?= =?UTF-8?q?ynamic=20import=20=EB=B0=A9=EC=8B=9D=EC=9D=84=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=95=BD=2036%=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/section/group-study-info-section.tsx | 7 ++++++- src/components/section/mission-section.tsx | 12 ++++++++++-- .../section/premium-study-info-section.tsx | 7 ++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/components/section/group-study-info-section.tsx b/src/components/section/group-study-info-section.tsx index e377a6ccb..e354138aa 100644 --- a/src/components/section/group-study-info-section.tsx +++ b/src/components/section/group-study-info-section.tsx @@ -10,7 +10,7 @@ import UserAvatar from '@/components/common/ui/avatar'; import AvatarStack from '@/components/common/ui/avatar-stack'; import type { AvatarStackMember } from '@/components/common/ui/avatar-stack'; import Button from '@/components/common/ui/button'; -import MarkdownContent from '@/components/common/ui/editor/markdown-content'; +// import MarkdownContent from '@/components/common/ui/editor/markdown-content'; import CurriculumSummarySection from '@/components/section/curriculum-summary-section'; import { useApplicantsByStatusQuery } from '@/hooks/queries/use-applicant-query'; @@ -22,6 +22,11 @@ const UserProfileModal = dynamic( { ssr: false }, ); +const MarkdownContent = dynamic( + () => import('@/components/common/ui/editor/markdown-content'), + { ssr: false }, +); + interface StudyInfoSectionProps { study: GroupStudyFullResponseDto; isLeader: boolean; diff --git a/src/components/section/mission-section.tsx b/src/components/section/mission-section.tsx index e5aaa0e1e..5bf44c548 100644 --- a/src/components/section/mission-section.tsx +++ b/src/components/section/mission-section.tsx @@ -10,8 +10,16 @@ import { useGroupStudyDetailQuery } from '@/hooks/queries/use-study-query'; import MissionCard from '../card/mission-card'; import PageContainer from '../common/layout/page-container'; import { cn } from '../common/ui/(shadcn)/lib/utils'; -import HomeworkDetailContent from '../contents/homework-detail-content'; -import MissionDetailContent from '../contents/mission-detail-content'; + +const HomeworkDetailContent = dynamic( + () => import('../contents/homework-detail-content'), + { ssr: false }, +); + +const MissionDetailContent = dynamic( + () => import('../contents/mission-detail-content'), + { ssr: false }, +); const CreateMissionModal = dynamic( () => import('@/components/common/modals/create-mission-modal'), diff --git a/src/components/section/premium-study-info-section.tsx b/src/components/section/premium-study-info-section.tsx index b5bdbf46a..7c9e88d5f 100644 --- a/src/components/section/premium-study-info-section.tsx +++ b/src/components/section/premium-study-info-section.tsx @@ -8,7 +8,7 @@ import UserAvatar from '@/components/common/ui/avatar'; import AvatarStack from '@/components/common/ui/avatar-stack'; import type { AvatarStackMember } from '@/components/common/ui/avatar-stack'; import Button from '@/components/common/ui/button'; -import MarkdownContent from '@/components/common/ui/editor/markdown-content'; +// import MarkdownContent from '@/components/common/ui/editor/markdown-content'; import CurriculumSummarySection from '@/components/section/curriculum-summary-section'; import { useApplicantsByStatusQuery } from '@/hooks/queries/use-applicant-query'; import { useIsLeader } from '@/stores/useLeaderStore'; @@ -23,6 +23,11 @@ const UserProfileModal = dynamic( { ssr: false }, ); +const MarkdownContent = dynamic( + () => import('@/components/common/ui/editor/markdown-content'), + { ssr: false }, +); + function getApplicantsList(pages: { content: T[] }[] | undefined) { if (!pages) return []; From ea7f6acf6b63698ac5e777cef51cfede63180b08 Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:51:41 +0900 Subject: [PATCH 07/13] =?UTF-8?q?delete=20:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/section/group-study-info-section.tsx | 1 - src/components/section/premium-study-info-section.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/components/section/group-study-info-section.tsx b/src/components/section/group-study-info-section.tsx index e354138aa..0b23420a1 100644 --- a/src/components/section/group-study-info-section.tsx +++ b/src/components/section/group-study-info-section.tsx @@ -10,7 +10,6 @@ import UserAvatar from '@/components/common/ui/avatar'; import AvatarStack from '@/components/common/ui/avatar-stack'; import type { AvatarStackMember } from '@/components/common/ui/avatar-stack'; import Button from '@/components/common/ui/button'; -// import MarkdownContent from '@/components/common/ui/editor/markdown-content'; import CurriculumSummarySection from '@/components/section/curriculum-summary-section'; import { useApplicantsByStatusQuery } from '@/hooks/queries/use-applicant-query'; diff --git a/src/components/section/premium-study-info-section.tsx b/src/components/section/premium-study-info-section.tsx index 7c9e88d5f..08a2742ce 100644 --- a/src/components/section/premium-study-info-section.tsx +++ b/src/components/section/premium-study-info-section.tsx @@ -8,7 +8,6 @@ import UserAvatar from '@/components/common/ui/avatar'; import AvatarStack from '@/components/common/ui/avatar-stack'; import type { AvatarStackMember } from '@/components/common/ui/avatar-stack'; import Button from '@/components/common/ui/button'; -// import MarkdownContent from '@/components/common/ui/editor/markdown-content'; import CurriculumSummarySection from '@/components/section/curriculum-summary-section'; import { useApplicantsByStatusQuery } from '@/hooks/queries/use-applicant-query'; import { useIsLeader } from '@/stores/useLeaderStore'; From 6bd9fa0773d99c5994cf893c514dd333cc552714 Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:55:25 +0900 Subject: [PATCH 08/13] =?UTF-8?q?fix=20:=20status=20=EC=88=98=EC=A0=95=20"?= =?UTF-8?q?EVALUATION=5FCOMPLETED"=20=EC=97=90=EC=84=9C=20"ENDED"=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/contents/homework-detail-content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/contents/homework-detail-content.tsx b/src/components/contents/homework-detail-content.tsx index e00ab6733..75953599f 100644 --- a/src/components/contents/homework-detail-content.tsx +++ b/src/components/contents/homework-detail-content.tsx @@ -157,7 +157,7 @@ export default function HomeworkDetailContent({ )} - {showLeaderEvaluation && mission.status === 'EVALUATION_COMPLETED' && ( + {showLeaderEvaluation && mission.status === 'ENDED' && ( Date: Tue, 7 Apr 2026 16:23:11 +0900 Subject: [PATCH 09/13] =?UTF-8?q?fix=20:=20=EC=9B=8C=EB=94=A9=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/my-homework-status-card.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/card/my-homework-status-card.tsx b/src/components/card/my-homework-status-card.tsx index 7f9f11ff4..b066d02d0 100644 --- a/src/components/card/my-homework-status-card.tsx +++ b/src/components/card/my-homework-status-card.tsx @@ -35,7 +35,8 @@ export default function MyHomeworkStatusCard({ const isSubmissionOpen = mission?.status === 'IN_PROGRESS'; const isSubmissionNotOpened = mission?.status === 'NOT_STARTED'; - const isSubmissionEnded = mission?.status === 'ENDED'; + const isSubmissionClosed = + mission?.status === 'ENDED' || mission?.status === 'EVALUATION_COMPLETED'; if (!myHomework || myHomework.homeworkStatus === 'NOT_SUBMITTED') { return ( @@ -48,10 +49,10 @@ export default function MyHomeworkStatusCard({ {isSubmissionOpen && '아직 과제를 제출하지 않았습니다.'} - {isSubmissionNotOpened && '과제 제출 기간이 아닙니다.'} + {isSubmissionNotOpened && '아직 제출 기간이 아닙니다.'} - {isSubmissionEnded && '제출 기간이 종료되었습니다.'} + {isSubmissionClosed && '제출 기간이 종료되었습니다.'} {isSubmissionOpen && } From 95a849b3ab6c4ab9d6653c131348c0fb3d71fe8c Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:49:54 +0900 Subject: [PATCH 10/13] =?UTF-8?q?fix=20:=20=EB=AF=B8=EC=85=98=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EC=A4=91=EC=B2=A9=20=EB=B2=84=ED=8A=BC=20=EC=9C=84?= =?UTF-8?q?=EB=B0=98=20=EB=B0=8F=20Progress=20NaN=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- src/components/card/mission-card.tsx | 40 ++++++++----------- .../common/modals/create-evaluation-modal.tsx | 16 ++------ .../contents/mission-detail-content.tsx | 6 +-- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/src/components/card/mission-card.tsx b/src/components/card/mission-card.tsx index 56dbb592a..a79bff260 100644 --- a/src/components/card/mission-card.tsx +++ b/src/components/card/mission-card.tsx @@ -3,7 +3,7 @@ import dayjs from 'dayjs'; import { Lock } from 'lucide-react'; import dynamic from 'next/dynamic'; -import { ComponentProps, SyntheticEvent } from 'react'; +import type { ComponentProps } from 'react'; import type { MissionListResponse } from '@/api/openapi/models'; import Badge from '@/components/common/ui/badge'; @@ -121,10 +121,6 @@ export default function MissionCard({ ? getDeadlineInfo(mission.endDate) : undefined; - const stopActionAreaPropagation = (event: SyntheticEvent) => { - event.stopPropagation(); - }; - // 비가입자 2주차+ 잠금 카드 if (isLocked) { return ( @@ -158,24 +154,22 @@ export default function MissionCard({ if (isLeader && mission.status === 'NOT_STARTED') { return (
  • - +
    - +
  • ); } diff --git a/src/components/common/modals/create-evaluation-modal.tsx b/src/components/common/modals/create-evaluation-modal.tsx index 0367f1e74..693ea17e3 100644 --- a/src/components/common/modals/create-evaluation-modal.tsx +++ b/src/components/common/modals/create-evaluation-modal.tsx @@ -16,15 +16,7 @@ import { import { useToastStore } from '@/stores/use-toast-store'; const CreateEvaluationFormSchema = z.object({ - gradeCode: z.enum([ - 'A_PLUS', - 'A_MINUS', - 'B_PLUS', - 'B_MINUS', - 'C_PLUS', - 'C_MINUS', - 'F', - ]), + gradeCode: z.string().min(1, '평가 등급을 선택해주세요.'), comment: z.string().min(1, '정성 코멘트를 입력해주세요.').max(1000), }); @@ -109,11 +101,11 @@ function CreateEvaluationForm({ ); }; - const gradeOptions = grades - ?.sort((a, b) => a.orderNum - b.orderNum) + const gradeOptions = (grades ?? []) + .sort((a, b) => (a.orderNum ?? 0) - (b.orderNum ?? 0)) .map((grade) => ({ value: grade.code, - label: `${grade.label} (${grade.score === 0 ? '0' : grade.score.toFixed(1)})`, + label: `${grade.label} (${grade.score === 0 ? '0' : (grade.score?.toFixed(1) ?? '-')})`, })); return ( diff --git a/src/components/contents/mission-detail-content.tsx b/src/components/contents/mission-detail-content.tsx index daa867d00..0593d546c 100644 --- a/src/components/contents/mission-detail-content.tsx +++ b/src/components/contents/mission-detail-content.tsx @@ -40,10 +40,10 @@ export default function MissionDetailContent({ return null; } + const maxCount = mission.maxHomeworkSubmissionCount || 1; + const progressValue = - ((mission.currentHomeworkSubmissionCount ?? 0) / - (mission.maxHomeworkSubmissionCount ?? 1)) * - 100; + ((mission.currentHomeworkSubmissionCount ?? 0) / maxCount) * 100; return (
    From b26e8b51dae6229c62c06390a3b9237d7fcc35d3 Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:34:17 +0900 Subject: [PATCH 11/13] =?UTF-8?q?refactor=20:=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/time.ts | 68 +++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/src/utils/time.ts b/src/utils/time.ts index 41bf67d9a..30300441d 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -121,6 +121,12 @@ export interface CreateDisabledDateMatcherForMissionOptions { editingMissionId?: number; } +const toDateOnly = (date: Date | string): Date => { + const d = new Date(date); + d.setHours(0, 0, 0, 0); + return d; +}; + export const createDisabledDateMatcherForMission = ( options: CreateDisabledDateMatcherForMissionOptions, ) => { @@ -128,53 +134,41 @@ export const createDisabledDateMatcherForMission = ( options; return (date: Date) => { - const normalizedDate = new Date(date); - normalizedDate.setHours(0, 0, 0, 0); + const candidateDate = toDateOnly(date); + const today = toDateOnly(getKoreaDate()); - const today = getKoreaDate(); - today.setHours(0, 0, 0, 0); - - if (normalizedDate <= today) { - return true; - } + const isPastDate = candidateDate < today; + if (isPastDate) return true; + // 스터디 개설일 다음날부터 선택 가능 if (studyStartDate) { - const startDate = new Date(studyStartDate); - startDate.setHours(0, 0, 0, 0); - // 스터디 개설일 다음날부터 선택 가능 - const minDate = addDays(startDate, 1); - if (normalizedDate < minDate) { - return true; - } + const studyPeriodStart = addDays(toDateOnly(studyStartDate), 1); + const isBeforeStudyPeriod = candidateDate < studyPeriodStart; + if (isBeforeStudyPeriod) return true; } if (studyEndDate) { - const endDate = new Date(studyEndDate); - endDate.setHours(0, 0, 0, 0); - if (normalizedDate > endDate) { - return true; - } + const isAfterStudyPeriod = candidateDate > toDateOnly(studyEndDate); + if (isAfterStudyPeriod) return true; } if (existingMissions) { - const targetTime = normalizedDate.getTime(); + const candidateTime = candidateDate.getTime(); for (const mission of existingMissions) { - if (editingMissionId && mission.missionId === editingMissionId) - continue; - - if (mission.startDate && mission.endDate) { - const missionStart = new Date(mission.startDate); - missionStart.setHours(0, 0, 0, 0); - const missionEnd = new Date(mission.endDate); - missionEnd.setHours(0, 0, 0, 0); - - if ( - targetTime >= missionStart.getTime() && - targetTime <= missionEnd.getTime() - ) { - return true; - } - } + if (mission.missionId === editingMissionId) continue; + if (!mission.startDate || !mission.endDate) continue; + + const existingmissionStartDate = toDateOnly( + mission.startDate, + ).getTime(); + + const existingmissionEndDate = toDateOnly(mission.endDate).getTime(); + + if ( + candidateTime >= existingmissionStartDate && + candidateTime <= existingmissionEndDate + ) + return true; } } From 9b11f6155a77fb0b546a4863a5429cc0894a4034 Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:38:41 +0900 Subject: [PATCH 12/13] =?UTF-8?q?fix=20:=20=ED=8F=B4=EB=B0=B1=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/contents/homework-detail-content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/contents/homework-detail-content.tsx b/src/components/contents/homework-detail-content.tsx index 75953599f..a53676ccb 100644 --- a/src/components/contents/homework-detail-content.tsx +++ b/src/components/contents/homework-detail-content.tsx @@ -463,7 +463,7 @@ function LeaderEvaluationSection({ 평가 코멘트

    - {evaluation.comment} + {evaluation.comment?.trim() ? evaluation.comment : '-'}

    From 79a141434cdd6b49d0e82e5479ccbb06e9c8d64e Mon Sep 17 00:00:00 2001 From: Jeong Ha Seung <88266129+HA-SEUNG-JEONG@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:29:04 +0900 Subject: [PATCH 13/13] =?UTF-8?q?fix=20:=20=ED=83=80=EC=9E=85=20=EC=95=88?= =?UTF-8?q?=EC=A0=95=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/section/mission-section.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/section/mission-section.tsx b/src/components/section/mission-section.tsx index 5bf44c548..f9a40607b 100644 --- a/src/components/section/mission-section.tsx +++ b/src/components/section/mission-section.tsx @@ -175,7 +175,7 @@ export default function MissionSection({ );