From bc579f748e1560e30b0235b123e346c6c5a31ab1 Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 25 Jun 2024 16:02:45 +0900 Subject: [PATCH 01/39] =?UTF-8?q?refactor:=20=EA=B3=BC=EC=A0=9C=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=BD=94=EB=93=9C=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MissionDetail/MissionDetail.module.css | 17 +++++ .../MissionDetail/MissionDetail.tsx | 12 ++-- .../MyApplicationFormItem.tsx | 28 ++++---- .../MyApplicationItem.module.css | 40 +++++++---- .../MyMissionItem/MyMissionItem.tsx | 68 +++++++++++-------- .../RecruitmentDetail/RecruitmentDetail.tsx | 4 +- 6 files changed, 112 insertions(+), 57 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.module.css b/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.module.css index eaba80eb8..e787a4743 100644 --- a/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.module.css +++ b/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.module.css @@ -6,6 +6,16 @@ word-break: break-all; } +.detail-status-container { + display: flex; + justify-content: space-between; +} + +.detail-status-container ul { + display: flex; + gap: 0.25rem; +} + .guide-container { display: flex; gap: 0.25rem; @@ -13,3 +23,10 @@ color: var(--gray-600); font-size: 0.815rem; } + +@media screen and (max-width: 800px) { + .title-container { + flex-direction: column; + gap: 0.875rem; + } +} diff --git a/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.tsx b/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.tsx index 7340edfe5..17b7296c8 100644 --- a/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.tsx +++ b/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.tsx @@ -1,3 +1,4 @@ +import { PropsWithChildren } from "react"; import { Mission } from "../../../../types/domains/recruitments"; import { MY_MISSION_TOOLTIP_MESSAGE } from "../../../constants/messages"; import Tooltip from "../../@common/Tooltip/Tooltip"; @@ -5,14 +6,17 @@ import CommitHash from "../CommitHash/CommitHash"; import JudgmentResultText from "../JudgmentResult/JudgmentResult"; import styles from "./MissionDetail.module.css"; -type MissionDetailProps = { +type MissionDetailProps = PropsWithChildren<{ judgment: Mission["judgment"]; -}; +}>; -const MissionDetail = ({ judgment }: MissionDetailProps) => { +const MissionDetail = ({ judgment, children }: MissionDetailProps) => { return (
- +
+ +
{children}
+

테스트 코드 실행이 끝나기까지 3 ~ 5분이 걸릴 수 있습니다

diff --git a/frontend/src/components/MyApplicationItem/MyApplicationFormItem/MyApplicationFormItem.tsx b/frontend/src/components/MyApplicationItem/MyApplicationFormItem/MyApplicationFormItem.tsx index aacfa52f0..14c24ca15 100644 --- a/frontend/src/components/MyApplicationItem/MyApplicationFormItem/MyApplicationFormItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyApplicationFormItem/MyApplicationFormItem.tsx @@ -68,19 +68,23 @@ const MyApplicationFormItem = ({ recruitment, submitted }: MyApplicationFormItem return (
-
- - {recruitment.title} - +
+
+ + {recruitment.title} + -
- { - routeToApplicationForm(recruitment); - }} - label={applyButtonLabel} - /> +
    +
  • + { + routeToApplicationForm(recruitment); + }} + label={applyButtonLabel} + /> +
  • +
diff --git a/frontend/src/components/MyApplicationItem/MyApplicationItem.module.css b/frontend/src/components/MyApplicationItem/MyApplicationItem.module.css index 8c93f16ad..a6aac5ef9 100644 --- a/frontend/src/components/MyApplicationItem/MyApplicationItem.module.css +++ b/frontend/src/components/MyApplicationItem/MyApplicationItem.module.css @@ -1,3 +1,7 @@ +.divider { + border: 0.5px solid var(--gray-300); +} + .content-box { position: relative; display: flex; @@ -11,29 +15,41 @@ border: 1px solid var(--gray-300); } -.text-container { +.content-wrapper { display: flex; flex-direction: column; gap: 0.875rem; width: 100%; } -.auto-judgment-detail-contour { - border: 0.5px solid var(--gray-300); +.title-container { + display: flex; + justify-content: space-between; +} + +.title-text { + flex: 1; } -.button-container { - position: absolute; - top: 1.25rem; - right: 1.5rem; +.title-button-list { display: flex; - gap: 1rem; +} + +.title-button-list li:not(:last-child) { + margin-right: 0.4rem; } @media screen and (max-width: 800px) { - .button-container { - position: static; - gap: 0.4rem; - margin-left: auto; + .title-container { + flex-direction: column; + gap: 0.875rem; + } + + .title-button-list > li { + flex: 1; + } + + .title-button-list > li > button { + width: 100%; } } diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index 833d07d66..f0bcd5ca8 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -72,35 +72,49 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { return (
-
- - {missionItem.title} - - -
+
+
+ + {missionItem.title} + +
    +
  • + +
  • +
+
- +
-
- - - -
+ +
    +
  • + {/* + 새로고침버튼... 상태에 따라 null을 반환할 수 있음... + 이걸 버튼에서 판별해야 하나? + */} + +
  • +
  • + +
  • +
+
); diff --git a/frontend/src/components/MyApplicationItem/RecruitmentDetail/RecruitmentDetail.tsx b/frontend/src/components/MyApplicationItem/RecruitmentDetail/RecruitmentDetail.tsx index 73f87475a..9bf1d721a 100644 --- a/frontend/src/components/MyApplicationItem/RecruitmentDetail/RecruitmentDetail.tsx +++ b/frontend/src/components/MyApplicationItem/RecruitmentDetail/RecruitmentDetail.tsx @@ -9,7 +9,7 @@ type RecruitmentDetailProps = { const RecruitmentDetail = ({ children, startDate, endDate }: RecruitmentDetailProps) => { return ( - <> +

{children}

@@ -19,7 +19,7 @@ const RecruitmentDetail = ({ children, startDate, endDate }: RecruitmentDetailPr {startDate} ~ {endDate}

- +
); }; From 86676d02965dea2491a4e37e3e66ed98517edf2c Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 2 Jul 2024 09:50:21 +0900 Subject: [PATCH 02/39] =?UTF-8?q?refactor:=20=EA=B3=BC=EC=A0=9C=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=88?= =?UTF-8?q?=EB=A1=9C=EA=B3=A0=EC=B9=A8=20=EB=B2=84=ED=8A=BC=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyApplicationButtons/RefreshButton.tsx | 45 ++++------------- .../MyMissionItem/MyMissionItem.tsx | 25 ++++++---- .../MyMissionItem/useRefresh.ts | 50 +++++++++++++++++++ 3 files changed, 73 insertions(+), 47 deletions(-) create mode 100644 frontend/src/components/MyApplicationItem/MyMissionItem/useRefresh.ts diff --git a/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx b/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx index 79a5f3f16..27685f3e0 100644 --- a/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx +++ b/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx @@ -2,12 +2,10 @@ import { AxiosError } from "axios"; import classNames from "classnames"; import { Mission, Recruitment } from "../../../../types/domains/recruitments"; import { fetchMyMissionJudgment } from "../../../api"; -import { JUDGMENT_STATUS } from "../../../constants/judgment"; -import { MISSION_STATUS } from "../../../constants/recruitment"; import useTokenContext from "../../../hooks/useTokenContext"; import Button, { BUTTON_VARIANT } from "../../@common/Button/Button"; -import { isJudgmentTimedOut } from "./../../../utils/validation/judgmentTime"; import styles from "./ApplicationButtons.module.css"; +import useRefresh from "../MyMissionItem/useRefresh"; type RefreshButtonProps = { recruitmentId?: Recruitment["id"]; @@ -18,40 +16,19 @@ type RefreshButtonProps = { const RefreshButton = ({ recruitmentId, missionItem, setMission }: RefreshButtonProps) => { const { token } = useTokenContext(); const missionId = missionItem.id; - const status = missionItem.judgment?.status; - if (status !== JUDGMENT_STATUS.STARTED || missionItem.status !== MISSION_STATUS.SUBMITTING) { - return null; - } - - if (missionId === undefined || recruitmentId === undefined) { - return null; - } - - if (isJudgmentTimedOut(missionItem.judgment)) { - return null; - } - - const handleRefreshMission = async ({ - missionId, - recruitmentId, - token, - }: { - missionId: string; - recruitmentId: string; - token: string; - }) => { + const { fetchRefreshedResultData } = useRefresh({ recruitmentId, missionItem }); + const requestRefresh = async () => { try { - const response = await fetchMyMissionJudgment({ - recruitmentId: Number(recruitmentId), - missionId: Number(missionId), + const result = await fetchRefreshedResultData({ + missionId: String(missionId), + recruitmentId: String(recruitmentId), token, }); - setMission({ ...missionItem, judgment: response.data }); - alert("새로고침 되었습니다"); + setMission(result); } catch (error) { - alert((error as AxiosError).response?.data.message); + error instanceof Error && alert(error.message); } }; @@ -62,11 +39,7 @@ const RefreshButton = ({ recruitmentId, missionItem, setMission }: RefreshButton variant={BUTTON_VARIANT.CONTAINED} cancel={false} onClick={() => { - handleRefreshMission({ - missionId: String(missionId), - recruitmentId: String(recruitmentId), - token, - }); + requestRefresh(); }} > 새로고침 diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index f0bcd5ca8..f77de0659 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -13,6 +13,7 @@ import RefreshButton from "../MyApplicationButtons/RefreshButton"; import styles from "../MyApplicationItem.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; import { PATH } from "./../../../constants/path"; +import useRefresh from "./useRefresh"; type MyMissionItemProps = { mission: Mission; @@ -33,6 +34,10 @@ const missionLabel = (submitted: boolean, missionStatus: Mission["status"]) => { const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { const navigate = useNavigate(); const [missionItem, setMissionItem] = useState({ ...mission }); + const { refreshAvailable } = useRefresh({ + recruitmentId: Number(recruitmentId), + missionItem, + }); const applyButtonLabel = missionLabel(mission.submitted, mission.status); @@ -95,17 +100,15 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => {
    -
  • - {/* - 새로고침버튼... 상태에 따라 null을 반환할 수 있음... - 이걸 버튼에서 판별해야 하나? - */} - -
  • + {(refreshAvailable || true) && ( +
  • + +
  • + )}
  • { + const isValidMissionId = missionItem.id && recruitmentId; + + const refreshAvailable = + isValidMissionId && + missionItem.judgment?.status === JUDGMENT_STATUS.STARTED && + missionItem.status !== MISSION_STATUS.SUBMITTING && + !isJudgmentTimedOut(missionItem.judgment); + + const fetchRefreshedResultData = async ({ + missionId, + recruitmentId, + token, + }: { + missionId: string; + recruitmentId: string; + token: string; + }) => { + try { + const response = await fetchMyMissionJudgment({ + recruitmentId: Number(recruitmentId), + missionId: Number(missionId), + token, + }); + + return { ...missionItem, judgment: response.data }; + } catch (error) { + throw new Error((error as AxiosError).response?.data.message); + } + }; + + return { + refreshAvailable, + fetchRefreshedResultData, + }; +}; + +export default useRefresh; From 4c7e9109d0d94e44e421929126f81fd0533853a8 Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 2 Jul 2024 10:18:12 +0900 Subject: [PATCH 03/39] =?UTF-8?q?refactor:=20=EC=83=88=EB=A1=9C=EA=B3=A0?= =?UTF-8?q?=EC=B9=A8=20=EB=B2=84=ED=8A=BC=20=EC=97=AD=ED=95=A0=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyApplicationButtons/RefreshButton.tsx | 33 ++----------------- .../MyMissionItem/MyMissionItem.tsx | 27 +++++++++++---- 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx b/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx index 27685f3e0..0e2028c04 100644 --- a/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx +++ b/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx @@ -1,46 +1,19 @@ -import { AxiosError } from "axios"; import classNames from "classnames"; -import { Mission, Recruitment } from "../../../../types/domains/recruitments"; -import { fetchMyMissionJudgment } from "../../../api"; -import useTokenContext from "../../../hooks/useTokenContext"; import Button, { BUTTON_VARIANT } from "../../@common/Button/Button"; import styles from "./ApplicationButtons.module.css"; -import useRefresh from "../MyMissionItem/useRefresh"; type RefreshButtonProps = { - recruitmentId?: Recruitment["id"]; - missionItem: Mission; - setMission: React.Dispatch>; + onClick?: () => void; }; -const RefreshButton = ({ recruitmentId, missionItem, setMission }: RefreshButtonProps) => { - const { token } = useTokenContext(); - const missionId = missionItem.id; - - const { fetchRefreshedResultData } = useRefresh({ recruitmentId, missionItem }); - const requestRefresh = async () => { - try { - const result = await fetchRefreshedResultData({ - missionId: String(missionId), - recruitmentId: String(recruitmentId), - token, - }); - - setMission(result); - } catch (error) { - error instanceof Error && alert(error.message); - } - }; - +const RefreshButton = ({ onClick }: RefreshButtonProps) => { return ( diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index f77de0659..5d02f024a 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -14,6 +14,7 @@ import styles from "../MyApplicationItem.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; import { PATH } from "./../../../constants/path"; import useRefresh from "./useRefresh"; +import useTokenContext from "../../../hooks/useTokenContext"; type MyMissionItemProps = { mission: Mission; @@ -32,9 +33,11 @@ const missionLabel = (submitted: boolean, missionStatus: Mission["status"]) => { }; const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { + const { token } = useTokenContext(); const navigate = useNavigate(); + const [missionItem, setMissionItem] = useState({ ...mission }); - const { refreshAvailable } = useRefresh({ + const { refreshAvailable, fetchRefreshedResultData } = useRefresh({ recruitmentId: Number(recruitmentId), missionItem, }); @@ -75,6 +78,20 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { ); }; + const requestRefresh = async () => { + try { + const result = await fetchRefreshedResultData({ + missionId: String(missionItem.id), + recruitmentId: String(recruitmentId), + token, + }); + + setMissionItem(result); + } catch (error) { + error instanceof Error && alert(error.message); + } + }; + return (
    @@ -100,13 +117,9 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => {
      - {(refreshAvailable || true) && ( + {refreshAvailable && (
    • - +
    • )}
    • From 5480ba6cf5e2cb1c447e11d0fc03a3a7e2f80899 Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 2 Jul 2024 10:57:39 +0900 Subject: [PATCH 04/39] =?UTF-8?q?refactor:=20JudgmentButton=20=ED=9B=85=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyApplicationButtons/JudgmentButton.tsx | 59 ++++--------------- .../MyMissionItem/useMissionJudgement.ts | 53 +++++++++++++++++ 2 files changed, 64 insertions(+), 48 deletions(-) create mode 100644 frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts diff --git a/frontend/src/components/MyApplicationItem/MyApplicationButtons/JudgmentButton.tsx b/frontend/src/components/MyApplicationItem/MyApplicationButtons/JudgmentButton.tsx index 9e15bdb36..d4251ca4e 100644 --- a/frontend/src/components/MyApplicationItem/MyApplicationButtons/JudgmentButton.tsx +++ b/frontend/src/components/MyApplicationItem/MyApplicationButtons/JudgmentButton.tsx @@ -1,13 +1,8 @@ -import { AxiosError } from "axios"; import classNames from "classnames"; import { Mission, Recruitment } from "../../../../types/domains/recruitments"; -import { postMyMissionJudgment } from "../../../api/recruitments"; -import { JUDGMENT_STATUS } from "../../../constants/judgment"; -import { MISSION_STATUS } from "../../../constants/recruitment"; -import useTokenContext from "../../../hooks/useTokenContext"; import Button, { BUTTON_VARIANT } from "../../@common/Button/Button"; -import { isJudgmentTimedOut } from "./../../../utils/validation/judgmentTime"; import styles from "./ApplicationButtons.module.css"; +import useMissionJudgement from "../MyMissionItem/useMissionJudgement"; type JudgmentButtonProps = { recruitmentId: Recruitment["id"]; @@ -16,37 +11,10 @@ type JudgmentButtonProps = { }; const JudgmentButton = ({ missionItem, recruitmentId, setMission }: JudgmentButtonProps) => { - const { token } = useTokenContext(); - const missionStatus = missionItem.status; - const judgment = missionItem.judgment; - - const handleJudgeError = async (error: AxiosError, missionId: string) => { - if (!error) return; - - const errorMessage = error.response?.data.message; - alert(errorMessage); - }; - - const handleJudgeMission = async ({ - missionId, + const { handleJudgeMission, isJudgmentAvailable } = useMissionJudgement({ + missionItem, recruitmentId, - token, - }: { - missionId: string; - recruitmentId: string; - token: string; - }) => { - try { - const response = await postMyMissionJudgment({ - recruitmentId: Number(recruitmentId), - missionId: Number(missionId), - token, - }); - setMission({ ...missionItem, judgment: response.data }); - } catch (error) { - handleJudgeError(error as AxiosError, missionId); - } - }; + }); return ( diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index 5d02f024a..ecccd454f 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -14,7 +14,7 @@ import styles from "../MyApplicationItem.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; import { PATH } from "./../../../constants/path"; import useRefresh from "./useRefresh"; -import useTokenContext from "../../../hooks/useTokenContext"; +import useMissionJudgement from "./useMissionJudgement"; type MyMissionItemProps = { mission: Mission; @@ -33,13 +33,16 @@ const missionLabel = (submitted: boolean, missionStatus: Mission["status"]) => { }; const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { - const { token } = useTokenContext(); const navigate = useNavigate(); const [missionItem, setMissionItem] = useState({ ...mission }); const { refreshAvailable, fetchRefreshedResultData } = useRefresh({ + missionItem, recruitmentId: Number(recruitmentId), + }); + const { fetchJudgmentMissionResult, isJudgmentAvailable } = useMissionJudgement({ missionItem, + recruitmentId: Number(recruitmentId), }); const applyButtonLabel = missionLabel(mission.submitted, mission.status); @@ -80,11 +83,16 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { const requestRefresh = async () => { try { - const result = await fetchRefreshedResultData({ - missionId: String(missionItem.id), - recruitmentId: String(recruitmentId), - token, - }); + const result = await fetchRefreshedResultData(); + setMissionItem(result); + } catch (error) { + error instanceof Error && alert(error.message); + } + }; + + const requestMissionJudgment = async () => { + try { + const result = await fetchJudgmentMissionResult(); setMissionItem(result); } catch (error) { @@ -123,11 +131,7 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => {
    • )}
    • - +
    diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts index 3f84b51a3..70caff46e 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts @@ -21,14 +21,7 @@ const useMissionJudgement = ({ missionItem, recruitmentId }: MissionJudgementPro status !== MISSION_STATUS.ENDED && status !== MISSION_STATUS.UNSUBMITTABLE; - const handleJudgeError = async (error: AxiosError) => { - if (!error) return; - - const errorMessage = error.response?.data.message; - alert(errorMessage); - }; - - const handleJudgeMission = async () => { + const fetchJudgmentMissionResult = async () => { try { const response = await postMyMissionJudgment({ recruitmentId: Number(recruitmentId), @@ -38,15 +31,14 @@ const useMissionJudgement = ({ missionItem, recruitmentId }: MissionJudgementPro return { ...missionItem, judgment: response.data }; } catch (error) { - handleJudgeError(error as AxiosError); + throw new Error((error as AxiosError).response?.data.message); } }; return { isJudgmentAvailable, - handleJudgeError, - handleJudgeMission, + fetchJudgmentMissionResult, }; }; diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useRefresh.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/useRefresh.ts index 4f8d2bae3..f2ec5ca98 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/useRefresh.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/useRefresh.ts @@ -4,6 +4,7 @@ import { fetchMyMissionJudgment } from "../../../api"; import { JUDGMENT_STATUS } from "../../../constants/judgment"; import { MISSION_STATUS } from "../../../constants/recruitment"; import { isJudgmentTimedOut } from "../../../utils/validation/judgmentTime"; +import useTokenContext from "../../../hooks/useTokenContext"; type Props = { recruitmentId?: Recruitment["id"]; @@ -11,6 +12,7 @@ type Props = { }; const useRefresh = ({ recruitmentId, missionItem }: Props) => { + const { token } = useTokenContext(); const isValidMissionId = missionItem.id && recruitmentId; const refreshAvailable = @@ -19,19 +21,11 @@ const useRefresh = ({ recruitmentId, missionItem }: Props) => { missionItem.status !== MISSION_STATUS.SUBMITTING && !isJudgmentTimedOut(missionItem.judgment); - const fetchRefreshedResultData = async ({ - missionId, - recruitmentId, - token, - }: { - missionId: string; - recruitmentId: string; - token: string; - }) => { + const fetchRefreshedResultData = async () => { try { const response = await fetchMyMissionJudgment({ + missionId: Number(missionItem.id), recruitmentId: Number(recruitmentId), - missionId: Number(missionId), token, }); From 6f417f9783bf21c21d6ca3ed6b89e127a83ec3b6 Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 2 Jul 2024 11:29:22 +0900 Subject: [PATCH 06/39] =?UTF-8?q?refactor:=20MyMissionItem=20=ED=9B=85=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyMissionItem/MyMissionItem.tsx | 99 +++------------- .../MyMissionItem/useMission.ts | 112 ++++++++++++++++++ 2 files changed, 126 insertions(+), 85 deletions(-) create mode 100644 frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index ecccd454f..ec0c09958 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -1,104 +1,33 @@ import classNames from "classnames"; -import { useEffect, useMemo, useState } from "react"; -import { generatePath } from "react-router"; -import { useNavigate } from "react-router-dom"; import { Mission } from "../../../../types/domains/recruitments"; -import { PARAM } from "../../../constants/path"; import { BUTTON_LABEL, MISSION_STATUS } from "../../../constants/recruitment"; -import { formatDateTime } from "../../../utils/format/date"; import MissionDetail from "../MissionDetail/MissionDetail"; import ApplyButton from "../MyApplicationButtons/ApplyButton"; import JudgmentButton from "../MyApplicationButtons/JudgmentButton"; import RefreshButton from "../MyApplicationButtons/RefreshButton"; import styles from "../MyApplicationItem.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; -import { PATH } from "./../../../constants/path"; -import useRefresh from "./useRefresh"; -import useMissionJudgement from "./useMissionJudgement"; +import useMission from "./useMission"; type MyMissionItemProps = { mission: Mission; recruitmentId: string; }; -const missionLabel = (submitted: boolean, missionStatus: Mission["status"]) => { - const labelMap = { - SUBMITTABLE: BUTTON_LABEL.BEFORE_SUBMIT, - SUBMITTING: submitted ? BUTTON_LABEL.EDIT : BUTTON_LABEL.SUBMIT, - UNSUBMITTABLE: BUTTON_LABEL.UNSUBMITTABLE, - ENDED: submitted ? BUTTON_LABEL.COMPLETE : BUTTON_LABEL.UNSUBMITTED, - } as const; - - return labelMap[missionStatus]; -}; - const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { - const navigate = useNavigate(); - - const [missionItem, setMissionItem] = useState({ ...mission }); - const { refreshAvailable, fetchRefreshedResultData } = useRefresh({ - missionItem, - recruitmentId: Number(recruitmentId), - }); - const { fetchJudgmentMissionResult, isJudgmentAvailable } = useMissionJudgement({ - missionItem, - recruitmentId: Number(recruitmentId), - }); - - const applyButtonLabel = missionLabel(mission.submitted, mission.status); - - useEffect(() => { - setMissionItem(mission); - }, [mission]); - - const formattedStartDateTime = useMemo( - () => (missionItem.startDateTime ? formatDateTime(new Date(missionItem.startDateTime)) : ""), - [missionItem.startDateTime] - ); - - const formattedEndDateTime = useMemo( - () => (missionItem.endDateTime ? formatDateTime(new Date(missionItem.endDateTime)) : ""), - [missionItem.endDateTime] - ); - - const routeToAssignmentSubmit = - ({ recruitmentId, mission }: { recruitmentId: string; mission: Mission }) => - () => { - const isSubmitted = mission.submitted; - - navigate( - { - pathname: generatePath(PATH.ASSIGNMENT, { - status: isSubmitted ? PARAM.ASSIGNMENT_STATUS.EDIT : PARAM.ASSIGNMENT_STATUS.NEW, - }), - }, - { - state: { - recruitmentId, - currentMission: mission, - }, - } - ); - }; - - const requestRefresh = async () => { - try { - const result = await fetchRefreshedResultData(); - setMissionItem(result); - } catch (error) { - error instanceof Error && alert(error.message); - } - }; - - const requestMissionJudgment = async () => { - try { - const result = await fetchJudgmentMissionResult(); - - setMissionItem(result); - } catch (error) { - error instanceof Error && alert(error.message); - } - }; + const { + getter: { + missionItem, + applyButtonLabel, + formattedStartDateTime, + formattedEndDateTime, + isJudgmentAvailable, + refreshAvailable, + }, + routeToAssignmentSubmit, + requestRefresh, + requestMissionJudgment, + } = useMission({ mission, recruitmentId }); return (
    diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts new file mode 100644 index 000000000..e4005d247 --- /dev/null +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts @@ -0,0 +1,112 @@ +import { useEffect, useMemo, useState } from "react"; +import { formatDateTime } from "../../../utils/format/date"; +import useMissionJudgement from "./useMissionJudgement"; +import { generatePath, useNavigate } from "react-router-dom"; +import useRefresh from "./useRefresh"; +import { Mission } from "../../../../types/domains/recruitments"; +import { PARAM, PATH } from "../../../constants/path"; +import { BUTTON_LABEL } from "../../../constants/recruitment"; + +type MissionProps = { + mission: Mission; + recruitmentId: string; +}; + +const missionLabel = (submitted: boolean, missionStatus: Mission["status"]) => { + const labelMap = { + SUBMITTABLE: BUTTON_LABEL.BEFORE_SUBMIT, + SUBMITTING: submitted ? BUTTON_LABEL.EDIT : BUTTON_LABEL.SUBMIT, + UNSUBMITTABLE: BUTTON_LABEL.UNSUBMITTABLE, + ENDED: submitted ? BUTTON_LABEL.COMPLETE : BUTTON_LABEL.UNSUBMITTED, + } as const; + + return labelMap[missionStatus]; +}; + +const useMission = ({ mission, recruitmentId }: MissionProps) => { + const navigate = useNavigate(); + + const [missionItem, setMissionItem] = useState({ ...mission }); + const { refreshAvailable, fetchRefreshedResultData } = useRefresh({ + missionItem, + recruitmentId: Number(recruitmentId), + }); + const { fetchJudgmentMissionResult, isJudgmentAvailable } = useMissionJudgement({ + missionItem, + recruitmentId: Number(recruitmentId), + }); + + const applyButtonLabel = missionLabel(mission.submitted, mission.status); + + useEffect(() => { + setMissionItem(mission); + }, [mission]); + + const formattedStartDateTime = useMemo( + () => (missionItem.startDateTime ? formatDateTime(new Date(missionItem.startDateTime)) : ""), + [missionItem.startDateTime] + ); + + const formattedEndDateTime = useMemo( + () => (missionItem.endDateTime ? formatDateTime(new Date(missionItem.endDateTime)) : ""), + [missionItem.endDateTime] + ); + + const routeToAssignmentSubmit = + ({ recruitmentId, mission }: { recruitmentId: string; mission: Mission }) => + () => { + const isSubmitted = mission.submitted; + + navigate( + { + pathname: generatePath(PATH.ASSIGNMENT, { + status: isSubmitted ? PARAM.ASSIGNMENT_STATUS.EDIT : PARAM.ASSIGNMENT_STATUS.NEW, + }), + }, + { + state: { + recruitmentId, + currentMission: mission, + }, + } + ); + }; + + const requestRefresh = async () => { + try { + const result = await fetchRefreshedResultData(); + setMissionItem(result); + } catch (error) { + error instanceof Error && alert(error.message); + } + }; + + const requestMissionJudgment = async () => { + try { + const result = await fetchJudgmentMissionResult(); + + setMissionItem(result); + } catch (error) { + error instanceof Error && alert(error.message); + } + }; + + return { + getter: { + missionItem, + applyButtonLabel, + formattedStartDateTime, + formattedEndDateTime, + isJudgmentAvailable, + refreshAvailable, + }, + setter: { + setMissionItem, + }, + routeToAssignmentSubmit, + requestRefresh, + requestMissionJudgment, + }; +}; + +export default useMission; From d218f8631cdceb4601fefbbe740c56d9a7bd61bf Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 2 Jul 2024 11:52:13 +0900 Subject: [PATCH 07/39] =?UTF-8?q?refactor:=20Apply,=20Refresh,=20Judgment?= =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC(Button)=20=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/@common/Button/Button.tsx | 3 ++ .../MyApplicationButtons/ApplyButton.tsx | 26 ----------------- .../MyApplicationButtons/JudgmentButton.tsx | 25 ---------------- .../MyApplicationButtons/RefreshButton.tsx | 23 --------------- .../MyApplicationFormItem.tsx | 11 +++---- .../ApplicationButtonStyles.module.css} | 0 .../MyMissionItem/MyMissionItem.tsx | 29 ++++++++++++------- 7 files changed, 28 insertions(+), 89 deletions(-) delete mode 100644 frontend/src/components/MyApplicationItem/MyApplicationButtons/ApplyButton.tsx delete mode 100644 frontend/src/components/MyApplicationItem/MyApplicationButtons/JudgmentButton.tsx delete mode 100644 frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx rename frontend/src/components/MyApplicationItem/{MyApplicationButtons/ApplicationButtons.module.css => MyMissionItem/ApplicationButtonStyles.module.css} (100%) diff --git a/frontend/src/components/@common/Button/Button.tsx b/frontend/src/components/@common/Button/Button.tsx index 38038da73..50157dcbb 100644 --- a/frontend/src/components/@common/Button/Button.tsx +++ b/frontend/src/components/@common/Button/Button.tsx @@ -9,12 +9,14 @@ export const BUTTON_VARIANT = { export type ButtonProps = React.ButtonHTMLAttributes & { variant?: typeof BUTTON_VARIANT[keyof typeof BUTTON_VARIANT]; cancel?: boolean; + disabled?: boolean; }; const Button = ({ className, variant = BUTTON_VARIANT.CONTAINED, cancel = false, + disabled = false, children, ...props }: ButtonProps) => { @@ -23,6 +25,7 @@ const Button = ({ className={classNames(className, styles[variant], styles.button, { [styles.cancel]: cancel, })} + disabled={disabled} {...props} > {children} diff --git a/frontend/src/components/MyApplicationItem/MyApplicationButtons/ApplyButton.tsx b/frontend/src/components/MyApplicationItem/MyApplicationButtons/ApplyButton.tsx deleted file mode 100644 index 889b305ee..000000000 --- a/frontend/src/components/MyApplicationItem/MyApplicationButtons/ApplyButton.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import classNames from "classnames"; -import Button, { BUTTON_VARIANT } from "../../@common/Button/Button"; -import styles from "./ApplicationButtons.module.css"; - -type ApplyButtonProps = { - readonly label: string; - isButtonDisabled: boolean; - onClick: () => void; -}; - -const ApplyButton = ({ label, isButtonDisabled, onClick }: ApplyButtonProps) => { - return ( - - ); -}; - -export default ApplyButton; diff --git a/frontend/src/components/MyApplicationItem/MyApplicationButtons/JudgmentButton.tsx b/frontend/src/components/MyApplicationItem/MyApplicationButtons/JudgmentButton.tsx deleted file mode 100644 index e50986908..000000000 --- a/frontend/src/components/MyApplicationItem/MyApplicationButtons/JudgmentButton.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import classNames from "classnames"; -import Button, { BUTTON_VARIANT } from "../../@common/Button/Button"; -import styles from "./ApplicationButtons.module.css"; - -type JudgmentButtonProps = { - disabled?: boolean; - onClick?: () => void; -}; - -const JudgmentButton = ({ disabled, onClick }: JudgmentButtonProps) => { - return ( - - ); -}; - -export default JudgmentButton; diff --git a/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx b/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx deleted file mode 100644 index 0e2028c04..000000000 --- a/frontend/src/components/MyApplicationItem/MyApplicationButtons/RefreshButton.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import classNames from "classnames"; -import Button, { BUTTON_VARIANT } from "../../@common/Button/Button"; -import styles from "./ApplicationButtons.module.css"; - -type RefreshButtonProps = { - onClick?: () => void; -}; - -const RefreshButton = ({ onClick }: RefreshButtonProps) => { - return ( - - ); -}; - -export default RefreshButton; diff --git a/frontend/src/components/MyApplicationItem/MyApplicationFormItem/MyApplicationFormItem.tsx b/frontend/src/components/MyApplicationItem/MyApplicationFormItem/MyApplicationFormItem.tsx index 14c24ca15..bf42587c1 100644 --- a/frontend/src/components/MyApplicationItem/MyApplicationFormItem/MyApplicationFormItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyApplicationFormItem/MyApplicationFormItem.tsx @@ -6,10 +6,10 @@ import { Recruitment, RecruitmentStatus } from "../../../../types/domains/recrui import { PARAM, PATH } from "../../../constants/path"; import { formatDateTime } from "../../../utils/format/date"; import { generateQuery } from "../../../utils/route/query"; -import ApplyButton from "../MyApplicationButtons/ApplyButton"; import styles from "../MyApplicationItem.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; import { BUTTON_LABEL, RECRUITMENT_STATUS } from "./../../../constants/recruitment"; +import Button from "../../@common/Button/Button"; type MyApplicationFormItemProps = { recruitment: Recruitment; @@ -76,13 +76,14 @@ const MyApplicationFormItem = ({ recruitment, submitted }: MyApplicationFormItem
    • - { routeToApplicationForm(recruitment); }} - label={applyButtonLabel} - /> + > + {applyButtonLabel} +
    diff --git a/frontend/src/components/MyApplicationItem/MyApplicationButtons/ApplicationButtons.module.css b/frontend/src/components/MyApplicationItem/MyMissionItem/ApplicationButtonStyles.module.css similarity index 100% rename from frontend/src/components/MyApplicationItem/MyApplicationButtons/ApplicationButtons.module.css rename to frontend/src/components/MyApplicationItem/MyMissionItem/ApplicationButtonStyles.module.css diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index ec0c09958..f0427d66a 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -1,13 +1,12 @@ import classNames from "classnames"; import { Mission } from "../../../../types/domains/recruitments"; -import { BUTTON_LABEL, MISSION_STATUS } from "../../../constants/recruitment"; +import { MISSION_STATUS } from "../../../constants/recruitment"; import MissionDetail from "../MissionDetail/MissionDetail"; -import ApplyButton from "../MyApplicationButtons/ApplyButton"; -import JudgmentButton from "../MyApplicationButtons/JudgmentButton"; -import RefreshButton from "../MyApplicationButtons/RefreshButton"; import styles from "../MyApplicationItem.module.css"; +import buttonStyles from "./ApplicationButtonStyles.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; import useMission from "./useMission"; +import Button from "../../@common/Button/Button"; type MyMissionItemProps = { mission: Mission; @@ -38,14 +37,16 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => {
    • - + > + {applyButtonLabel} +
    @@ -56,11 +57,19 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => {
      {refreshAvailable && (
    • - +
    • )}
    • - +
    From 604377fbef517e60d3f0b01c1800a91d9edbb7fd Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 2 Jul 2024 13:34:37 +0900 Subject: [PATCH 08/39] =?UTF-8?q?refactor:=20=EC=A1=B0=EA=B1=B4=EB=AC=B8?= =?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 --- .../MyApplicationItem/MyMissionItem/useMissionJudgement.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts index 70caff46e..da1a6c8f3 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgement.ts @@ -1,5 +1,5 @@ import { AxiosError } from "axios"; -import { Mission, Recruitment } from "../../../../types/domains/recruitments"; +import { Mission, MissionStatus, Recruitment } from "../../../../types/domains/recruitments"; import { postMyMissionJudgment } from "../../../api"; import useTokenContext from "../../../hooks/useTokenContext"; import { isJudgmentTimedOut } from "../../../utils/validation/judgmentTime"; @@ -18,8 +18,7 @@ const useMissionJudgement = ({ missionItem, recruitmentId }: MissionJudgementPro const isJudgmentAvailable = submitted && (judgment?.status !== JUDGMENT_STATUS.STARTED || isJudgmentTimedOut(judgment)) && - status !== MISSION_STATUS.ENDED && - status !== MISSION_STATUS.UNSUBMITTABLE; + !([MISSION_STATUS.ENDED, MISSION_STATUS.UNSUBMITTABLE] as MissionStatus[]).includes(status); const fetchJudgmentMissionResult = async () => { try { From b1ed5fbd4f3bd66d02d7cc2bd39c266d759e55be Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 2 Jul 2024 13:43:45 +0900 Subject: [PATCH 09/39] =?UTF-8?q?refactor:=20=EC=83=88=EB=A1=9C=EA=B3=A0?= =?UTF-8?q?=EC=B9=A8=20=EA=B0=80=EB=8A=A5=EC=97=AC=EB=B6=80=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyApplicationItem/MyMissionItem/MyMissionItem.tsx | 6 ++---- .../MyApplicationItem/MyMissionItem/useMission.ts | 4 ++-- .../MyApplicationItem/MyMissionItem/useRefresh.ts | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index f0427d66a..079401812 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -21,7 +21,7 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { formattedStartDateTime, formattedEndDateTime, isJudgmentAvailable, - refreshAvailable, + isRefreshAvailable, }, routeToAssignmentSubmit, requestRefresh, @@ -50,12 +50,10 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => {
-
-
    - {refreshAvailable && ( + {isRefreshAvailable && (
  • +
  • + )} +
  • + +
  • +
diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index 079401812..c77bb72e9 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -15,17 +15,8 @@ type MyMissionItemProps = { const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { const { - getter: { - missionItem, - applyButtonLabel, - formattedStartDateTime, - formattedEndDateTime, - isJudgmentAvailable, - isRefreshAvailable, - }, + getter: { missionItem, applyButtonLabel, formattedStartDateTime, formattedEndDateTime }, routeToAssignmentSubmit, - requestRefresh, - requestMissionJudgment, } = useMission({ mission, recruitmentId }); return ( @@ -51,26 +42,11 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => {

- -
    - {isRefreshAvailable && ( -
  • - -
  • - )} -
  • - -
  • -
-
+
); From 24437bcf07d75efb0ce6bcc6537b448affff46da Mon Sep 17 00:00:00 2001 From: Cron Date: Thu, 11 Jul 2024 10:37:03 +0900 Subject: [PATCH 29/39] =?UTF-8?q?refactor:=20=ED=95=98=EC=9C=84=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B3=84=EC=B8=B5=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20(=EC=A2=85=EC=86=8D=20=EA=B4=80=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MissionDetail/MissionDetail.module.css | 0 .../MissionDetail/MissionDetail.tsx | 16 ++++++++-------- .../MyMissionItem/MyMissionItem.tsx | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) rename frontend/src/components/MyApplicationItem/{ => MyMissionItem}/MissionDetail/MissionDetail.module.css (100%) rename frontend/src/components/MyApplicationItem/{ => MyMissionItem}/MissionDetail/MissionDetail.tsx (74%) diff --git a/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.module.css b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.module.css similarity index 100% rename from frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.module.css rename to frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.module.css diff --git a/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx similarity index 74% rename from frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.tsx rename to frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx index d3d97c12d..272afe5df 100644 --- a/frontend/src/components/MyApplicationItem/MissionDetail/MissionDetail.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx @@ -1,11 +1,11 @@ -import { Mission } from "../../../../types/domains/recruitments"; -import { MY_MISSION_TOOLTIP_MESSAGE } from "../../../constants/messages"; -import Button from "../../@common/Button/Button"; -import CommitHash from "../CommitHash/CommitHash"; -import JudgmentResultText from "../JudgmentResult/JudgmentResult"; -import Tooltip from "../../@common/Tooltip/Tooltip"; -import useMission from "../MyMissionItem/useMission"; -import buttonStyles from "../MyMissionItem/ApplicationButtonStyles.module.css"; +import { Mission } from "../../../../../types/domains/recruitments"; +import { MY_MISSION_TOOLTIP_MESSAGE } from "../../../../constants/messages"; +import Button from "../../../@common/Button/Button"; +import CommitHash from "../../CommitHash/CommitHash"; +import JudgmentResultText from "../../JudgmentResult/JudgmentResult"; +import Tooltip from "../../../@common/Tooltip/Tooltip"; +import useMission from "../useMission"; +import buttonStyles from "../ApplicationButtonStyles.module.css"; import styles from "./MissionDetail.module.css"; type MissionDetailProps = { diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index c77bb72e9..281d6b264 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -1,7 +1,7 @@ import classNames from "classnames"; import { Mission } from "../../../../types/domains/recruitments"; import { MISSION_STATUS } from "../../../constants/recruitment"; -import MissionDetail from "../MissionDetail/MissionDetail"; +import MissionDetail from "./MissionDetail/MissionDetail"; import styles from "../MyApplicationItem.module.css"; import buttonStyles from "./ApplicationButtonStyles.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; From 0a010495a18d400e9abffaafe23b321cdcc056cb Mon Sep 17 00:00:00 2001 From: Cron Date: Mon, 15 Jul 2024 09:19:06 +0900 Subject: [PATCH 30/39] =?UTF-8?q?refactor:=20MyApplicationItem=20module=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=9D=BC=EA=B4=80=EC=84=B1=20?= =?UTF-8?q?=EB=A7=9E=EC=B6=94=EA=B8=B0=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyApplicationItem/MyApplicationItem.module.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyApplicationItem.module.css b/frontend/src/components/MyApplicationItem/MyApplicationItem.module.css index a6aac5ef9..e5b1ce70a 100644 --- a/frontend/src/components/MyApplicationItem/MyApplicationItem.module.css +++ b/frontend/src/components/MyApplicationItem/MyApplicationItem.module.css @@ -31,11 +31,11 @@ flex: 1; } -.title-button-list { +ul.title-button-list { display: flex; } -.title-button-list li:not(:last-child) { +ul.title-button-list > li:not(:last-child) { margin-right: 0.4rem; } @@ -45,11 +45,11 @@ gap: 0.875rem; } - .title-button-list > li { + ul.title-button-list > li { flex: 1; } - .title-button-list > li > button { + ul.title-button-list > li > button { width: 100%; } } From f1517c3e4a14eb184de3eb841b88bf7829e6f808 Mon Sep 17 00:00:00 2001 From: Cron Date: Mon, 15 Jul 2024 09:30:41 +0900 Subject: [PATCH 31/39] =?UTF-8?q?refactor:=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EA=B0=92=20Judgment=20=EC=B6=94=EB=A1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/recruitments.ts | 5 ++++- frontend/src/mock/dummy.ts | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/api/recruitments.ts b/frontend/src/api/recruitments.ts index 2ddc2a364..1a0b6ae7f 100644 --- a/frontend/src/api/recruitments.ts +++ b/frontend/src/api/recruitments.ts @@ -2,6 +2,7 @@ import axios from "axios"; import { Assignment, AssignmentData, + Judgment, Mission, Recruitment, RecruitmentItem, @@ -24,6 +25,8 @@ export type FetchMyMissionJudgmentRequest = RequestWithToken<{ missionId: number; }>; +export type FetchMyMissionJudgmentResponseData = Judgment; + export type FetchMyMissionsResponseData = Mission[]; export type FetchAssignmentRequest = RequestWithToken<{ @@ -73,7 +76,7 @@ export const fetchMyMissionJudgment = ({ missionId, token, }: FetchMyMissionJudgmentRequest) => - axios.get( + axios.get( `/api/recruitments/${recruitmentId}/missions/${missionId}/judgments/judge-example`, headers({ token }) ); diff --git a/frontend/src/mock/dummy.ts b/frontend/src/mock/dummy.ts index 731cf91ae..8ad613cc5 100644 --- a/frontend/src/mock/dummy.ts +++ b/frontend/src/mock/dummy.ts @@ -1,4 +1,5 @@ import { ISO8601DateString } from "../../types/domains/common"; +import { Judgment } from "../../types/domains/recruitments"; import { JUDGMENT_STATUS } from "./../constants/judgment"; import { MISSION_STATUS, RECRUITMENT_STATUS } from "./../constants/recruitment"; @@ -544,7 +545,7 @@ export const missionsDummy = { ], }; -export const judgmentDummy = { +export const judgmentDummy: Judgment = { pullRequestUrl: "https://github.com/woowacourse/jwp-dashboard-http/pull/298", commitHash: "642951e1324eaf66914bd53df339d94cad5667e3", status: JUDGMENT_STATUS.SUCCEEDED, From bc9d6d7db51328d863b7625a6f935e957b08cc1f Mon Sep 17 00:00:00 2001 From: Cron Date: Mon, 15 Jul 2024 13:26:11 +0900 Subject: [PATCH 32/39] =?UTF-8?q?refactor:=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=83=81=EC=88=98=ED=99=94=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MissionDetail/MissionDetail.tsx | 5 ++-- .../__tests__/MyMissionItem.test.tsx | 28 ++++++++----------- frontend/src/constants/recruitment.ts | 3 ++ 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx index 272afe5df..2752df74c 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx @@ -7,6 +7,7 @@ import Tooltip from "../../../@common/Tooltip/Tooltip"; import useMission from "../useMission"; import buttonStyles from "../ApplicationButtonStyles.module.css"; import styles from "./MissionDetail.module.css"; +import { BUTTON_LABEL } from "../../../../constants/recruitment"; type MissionDetailProps = { mission: Mission; @@ -29,7 +30,7 @@ const MissionDetail = ({ mission, recruitmentId, judgment }: MissionDetailProps) {isRefreshAvailable && (
  • )} @@ -39,7 +40,7 @@ const MissionDetail = ({ mission, recruitmentId, judgment }: MissionDetailProps) disabled={!isJudgmentAvailable} onClick={requestMissionJudgment} > - 예제 테스트 실행 + {BUTTON_LABEL.JUDGMENT} diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx index 583b4a628..8137cdb67 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx @@ -3,16 +3,10 @@ import { MemoryRouter } from "react-router-dom"; import MyMissionItem from "../MyMissionItem"; import * as useMissionModule from "../useMission"; import { createMockMission } from "./testMissionMockUtils"; -import { MISSION_STATUS } from "../../../../constants/recruitment"; +import { BUTTON_LABEL, MISSION_STATUS } from "../../../../constants/recruitment"; jest.mock("../useMission"); -const LABELS = { - APPLY: "과제 제출", - REFRESH: "새로고침", - JUDGMENT: "예제 테스트 실행", -}; - describe("MyMissionItem 통합 테스트", () => { const mockRecruitmentId = "123"; const mockMission = createMockMission(); @@ -20,7 +14,7 @@ describe("MyMissionItem 통합 테스트", () => { const mockUseMission = { getter: { missionItem: mockMission, - applyButtonLabel: LABELS.APPLY, + applyButtonLabel: BUTTON_LABEL.APPLY, formattedStartDateTime: "2023-01-01 00:00", formattedEndDateTime: "2023-12-31 23:59", isJudgmentAvailable: true, @@ -44,9 +38,9 @@ describe("MyMissionItem 통합 테스트", () => { ); expect(screen.getByText(mockMission.title)).toBeInTheDocument(); - expect(screen.getByText(LABELS.APPLY)).toBeInTheDocument(); - expect(screen.getByText(LABELS.REFRESH)).toBeInTheDocument(); - expect(screen.getByText(LABELS.JUDGMENT)).toBeInTheDocument(); + expect(screen.getByText(BUTTON_LABEL.APPLY)).toBeInTheDocument(); + expect(screen.getByText(BUTTON_LABEL.REFRESH)).toBeInTheDocument(); + expect(screen.getByText(BUTTON_LABEL.JUDGMENT)).toBeInTheDocument(); }); it("미션 상태가 SUBMITTING이 아닐 때 과제 제출 버튼이 비활성화되어야 한다", () => { @@ -62,7 +56,7 @@ describe("MyMissionItem 통합 테스트", () => { ); - expect(screen.getByText(LABELS.APPLY)).toBeDisabled(); + expect(screen.getByText(BUTTON_LABEL.APPLY)).toBeDisabled(); }); it("judgment가 없을 때 새로고침 버튼이 보이지 않아야 한다", () => { @@ -82,7 +76,7 @@ describe("MyMissionItem 통합 테스트", () => { ); - expect(screen.queryByText(LABELS.REFRESH)).not.toBeInTheDocument(); + expect(screen.queryByText(BUTTON_LABEL.REFRESH)).not.toBeInTheDocument(); }); it("isJudgmentAvailable이 false일 때 예제 테스트 실행 버튼이 비활성화되어야 한다", () => { @@ -97,7 +91,7 @@ describe("MyMissionItem 통합 테스트", () => { ); - expect(screen.getByText(LABELS.JUDGMENT)).toBeDisabled(); + expect(screen.getByText(BUTTON_LABEL.JUDGMENT)).toBeDisabled(); }); }); @@ -109,7 +103,7 @@ describe("MyMissionItem 통합 테스트", () => { ); - fireEvent.click(screen.getByText(LABELS.APPLY)); + fireEvent.click(screen.getByText(BUTTON_LABEL.APPLY)); expect(mockUseMission.routeToAssignmentSubmit).toHaveBeenCalled(); }); @@ -120,7 +114,7 @@ describe("MyMissionItem 통합 테스트", () => { ); - fireEvent.click(screen.getByText(LABELS.REFRESH)); + fireEvent.click(screen.getByText(BUTTON_LABEL.REFRESH)); await waitFor(() => { expect(mockUseMission.requestRefresh).toHaveBeenCalled(); }); @@ -133,7 +127,7 @@ describe("MyMissionItem 통합 테스트", () => { ); - fireEvent.click(screen.getByText(LABELS.JUDGMENT)); + fireEvent.click(screen.getByText(BUTTON_LABEL.JUDGMENT)); await waitFor(() => { expect(mockUseMission.requestMissionJudgment).toHaveBeenCalled(); }); diff --git a/frontend/src/constants/recruitment.ts b/frontend/src/constants/recruitment.ts index 4ab19316e..ea742fc62 100644 --- a/frontend/src/constants/recruitment.ts +++ b/frontend/src/constants/recruitment.ts @@ -19,4 +19,7 @@ export const BUTTON_LABEL = { UNSUBMITTABLE: "제출불가", COMPLETE: "제출완료", UNSUBMITTED: "미제출", + APPLY: "과제 제출", + REFRESH: "새로고침", + JUDGMENT: "예제 테스트 실행", } as const; From 20924fda8adff78ac8da72d725ec13e35cbb0cc1 Mon Sep 17 00:00:00 2001 From: Cron Date: Mon, 15 Jul 2024 13:27:26 +0900 Subject: [PATCH 33/39] =?UTF-8?q?refactor:=20=ED=8C=90=EC=A0=95=20->=20?= =?UTF-8?q?=EC=B1=84=EC=A0=90=20=EC=9A=A9=EC=96=B4=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyMissionItem/__tests__/useJudgment.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useJudgment.test.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useJudgment.test.ts index c80e0aa94..d22c0b2c3 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useJudgment.test.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useJudgment.test.ts @@ -65,7 +65,7 @@ describe("useMissionJudgment 훅 테스트", () => { }); }); - describe("미션 상태에 따른 판정 가능 여부", () => { + describe("미션 상태에 따른 채점 요청 가능 여부", () => { it("미션 상태가 제출 기간 이후 시점(ENDED)일 때 false를 반환해야 한다", () => { expectJudgmentAvailability({ status: MISSION_STATUS.ENDED }, false); }); @@ -77,7 +77,7 @@ describe("useMissionJudgment 훅 테스트", () => { }); describe("fetchJudgmentMissionResult 테스트", () => { - it("판정 결과를 성공적으로 가져와야 한다", async () => { + it("채점 결과를 성공적으로 가져와야 한다", async () => { const SAMPLE_PASS_COUNT = 8; const SAMPLE_TOTAL_COUNT = 10; @@ -113,8 +113,8 @@ describe("useMissionJudgment 훅 테스트", () => { }); }); - it("판정 결과 가져오기 실패 시 에러를 던져야 한다", async () => { - const errorMessage = "판정 결과를 가져오는 데 실패했습니다."; + it("채점 결과 가져오기 실패 시 에러를 던져야 한다", async () => { + const errorMessage = "채점 결과를 가져오는 데 실패했습니다."; const mockError = { response: { data: { message: errorMessage } } }; (postMyMissionJudgment as jest.Mock).mockRejectedValue(mockError); From 04bcc215b623c10ac55eeb609dd26e0fe1de12ae Mon Sep 17 00:00:00 2001 From: Cron Date: Mon, 15 Jul 2024 13:39:22 +0900 Subject: [PATCH 34/39] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EB=AA=85=EC=84=B8=20=EB=AC=B8=EA=B5=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/useMission.test.ts | 16 +++++++------- .../__tests__/useRefresh.test.ts | 22 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts index a44a718b5..efa6c4a76 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts @@ -81,7 +81,7 @@ describe("useMission", () => { describe("라우팅 테스트", () => { const mockMission = createMissionItem(); - it("미션 과제제출물 미제출 시, 'new' 상태로 과제 제출 페이지로 이동해야 한다", () => { + it("미션 과제 제출물 미제출 시, 'new' 상태로 과제 제출 페이지로 이동해야 한다", () => { const mockNavigate = jest.fn(); (useNavigate as jest.Mock).mockReturnValue(mockNavigate); @@ -110,7 +110,7 @@ describe("useMission", () => { ); }); - it("미션 과제제출물 제출 시, 'edit' 상태로 과제 제출 페이지로 이동해야 한다", () => { + it("미션 과제 제출물 제출 시, 'edit' 상태로 과제 제출 페이지로 이동해야 한다", () => { const mockNavigate = jest.fn(); (useNavigate as jest.Mock).mockReturnValue(mockNavigate); @@ -141,10 +141,10 @@ describe("useMission", () => { }); }); describe("데이터 갱신 테스트", () => { - describe("requestRefresh 함수 테스트", () => { + describe("채점 결과 새로고침 함수 테스트", () => { const mockMission = createMissionItem(); - it("fetchRefreshedResultData 성공 시 missionItem 상태를 갱신해야 한다", async () => { + it("채점 결과 새로고침 요청에 성공하면, missionItem 상태를 갱신해야 한다", async () => { const { result } = renderHook(() => useMission({ mission: mockMission, recruitmentId: mockRecruitmentId }) ); @@ -156,7 +156,7 @@ describe("useMission", () => { expect(result.current.getter.missionItem.title).toBe("Refreshed Mission"); }); - it("fetchRefreshedResultData 실패 시 에러 메시지를 보여줘야 한다.", async () => { + it("채점 결과 새로고침 요청에 실패하면, 에러 메시지를 보여줘야 한다.", async () => { const errorMessage = "Refresh failed"; (useRefresh as jest.Mock).mockReturnValue({ isRefreshAvailable: true, @@ -177,10 +177,10 @@ describe("useMission", () => { }); }); - describe("requestMissionJudgment 함수 테스트", () => { + describe("채점 요청 함수 테스트", () => { const mockMission = createMissionItem(); - it("fetchJudgmentMissionResult 성공 시 missionItem 상태를 갱신해야 한다", async () => { + it("채점 요청 성공 시 missionItem 상태를 갱신해야 한다", async () => { const { result } = renderHook(() => useMission({ mission: mockMission, recruitmentId: mockRecruitmentId }) ); @@ -192,7 +192,7 @@ describe("useMission", () => { expect(result.current.getter.missionItem.title).toBe("Judged Mission"); }); - it("fetchJudgmentMissionResult 실패 시 에러 메시지를 보여줘야 한다.", async () => { + it("채점 요청 실패 시 에러 메시지를 보여줘야 한다.", async () => { const errorMessage = "Judgment failed"; (useMissionJudgment as jest.Mock).mockReturnValue({ isJudgmentAvailable: true, diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useRefresh.test.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useRefresh.test.ts index fb8bf6c16..6c7811313 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useRefresh.test.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useRefresh.test.ts @@ -26,8 +26,8 @@ describe("useRefresh", () => { (isJudgmentTimedOut as jest.Mock).mockReturnValue(false); }); - describe("isRefreshAvailable 테스트", () => { - describe("false를 반환하는 경우", () => { + describe("채점 결과 새로고침 가능 여부 테스트", () => { + describe("새로고침이 불가능한 경우", () => { it("recruitmentId가 undefined일 때", () => { const { result } = renderHook(() => useRefresh({ recruitmentId: undefined, missionItem: createMissionItem() }) @@ -36,7 +36,7 @@ describe("useRefresh", () => { expect(result.current.isRefreshAvailable).toBe(false); }); - it("missionItem.id가 없을 때", () => { + it("missionItem의 id 값이 없을 때", () => { const { result } = renderHook(() => useRefresh({ recruitmentId: mockRecruitmentId, @@ -47,7 +47,7 @@ describe("useRefresh", () => { expect(result.current.isRefreshAvailable).toBe(false); }); - it("missionItem.judgment가 null일 때", () => { + it("missionItem의 채점 정보가 null일 때", () => { const { result } = renderHook(() => useRefresh({ recruitmentId: mockRecruitmentId, @@ -58,7 +58,7 @@ describe("useRefresh", () => { expect(result.current.isRefreshAvailable).toBe(false); }); - it("judgment.status가 STARTED가 아닐 때", () => { + it("채점 상태가 시작된 상태가 아닐 때", () => { const { result } = renderHook(() => useRefresh({ recruitmentId: mockRecruitmentId, @@ -72,7 +72,7 @@ describe("useRefresh", () => { expect(result.current.isRefreshAvailable).toBe(false); }); - it("missionItem.status가 SUBMITTING이 아닐 때", () => { + it("미션 상태가 제출 기간 중이 아닐 때", () => { const { result } = renderHook(() => useRefresh({ recruitmentId: mockRecruitmentId, @@ -83,7 +83,7 @@ describe("useRefresh", () => { expect(result.current.isRefreshAvailable).toBe(false); }); - it("isJudgmentTimedOut이 true를 반환할 때", () => { + it("채점 시 타임아웃이 발생했을 때", () => { (isJudgmentTimedOut as jest.Mock).mockReturnValue(true); const { result } = renderHook(() => @@ -94,7 +94,7 @@ describe("useRefresh", () => { }); }); - describe("true를 반환하는 경우", () => { + describe("새로고침이 가능한 경우", () => { it("모든 조건이 충족될 때 true를 반환해야 한다", () => { const { result } = renderHook(() => useRefresh({ recruitmentId: mockRecruitmentId, missionItem: createMissionItem() }) @@ -105,8 +105,8 @@ describe("useRefresh", () => { }); }); - describe("fetchRefreshedResultData 테스트", () => { - it("refreshedResultData를 성공적으로 가져와야 한다", async () => { + describe("채점 새로고침 테스트", () => { + it("채점 새로고침이 성공하면, 관련된 데이터를 성공적으로 가져와야 한다", async () => { const SAMPLE_PASS_COUNT = 8; const SAMPLE_TOTAL_COUNT = 10; const mockResponse = { @@ -143,7 +143,7 @@ describe("useRefresh", () => { }); }); - it("refreshedResultData 가져오기 실패 시 에러 메시지를 보여줘야 한다", async () => { + it("채점 새로고침이 실패하면, 에러 메시지를 보여줘야 한다", async () => { const errorMessage = "데이터를 가져오는 데 실패했습니다."; const mockError = { response: { data: { message: errorMessage } } }; (fetchMyMissionJudgment as jest.Mock).mockRejectedValue(mockError); From b4277b4fe4c15a09308fa614df3d95f05aeb072a Mon Sep 17 00:00:00 2001 From: Cron Date: Mon, 15 Jul 2024 14:38:08 +0900 Subject: [PATCH 35/39] =?UTF-8?q?refactor:=20useMission=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EB=B6=84=EB=A6=AC=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MissionDetail/MissionDetail.tsx | 36 +++++++++++- .../MyMissionItem/MyMissionItem.tsx | 25 +++++++- .../MyMissionItem/useMission.ts | 58 ------------------- 3 files changed, 57 insertions(+), 62 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx index 2752df74c..7a96bf86d 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx @@ -8,6 +8,8 @@ import useMission from "../useMission"; import buttonStyles from "../ApplicationButtonStyles.module.css"; import styles from "./MissionDetail.module.css"; import { BUTTON_LABEL } from "../../../../constants/recruitment"; +import useRefresh from "../useRefresh"; +import useMissionJudgment from "../useMissionJudgment"; type MissionDetailProps = { mission: Mission; @@ -17,11 +19,39 @@ type MissionDetailProps = { const MissionDetail = ({ mission, recruitmentId, judgment }: MissionDetailProps) => { const { - getter: { isJudgmentAvailable, isRefreshAvailable }, - requestRefresh, - requestMissionJudgment, + getter: { missionItem }, + setter: { setMissionItem }, } = useMission({ mission, recruitmentId }); + const { isJudgmentAvailable, fetchJudgmentMissionResult } = useMissionJudgment({ + missionItem, + recruitmentId: Number(recruitmentId), + }); + + const { isRefreshAvailable, fetchRefreshedResultData } = useRefresh({ + missionItem, + recruitmentId: Number(recruitmentId), + }); + + const requestRefresh = async () => { + try { + const result = await fetchRefreshedResultData(); + setMissionItem(result); + } catch (error) { + error instanceof Error && alert(error.message); + } + }; + + const requestMissionJudgment = async () => { + try { + const result = await fetchJudgmentMissionResult(); + + setMissionItem(result); + } catch (error) { + error instanceof Error && alert(error.message); + } + }; + return (
    diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index 281d6b264..1d8b30537 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -7,6 +7,8 @@ import buttonStyles from "./ApplicationButtonStyles.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; import useMission from "./useMission"; import Button from "../../@common/Button/Button"; +import { generatePath, useNavigate } from "react-router-dom"; +import { PARAM, PATH } from "../../../constants/path"; type MyMissionItemProps = { mission: Mission; @@ -14,11 +16,32 @@ type MyMissionItemProps = { }; const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { + const navigate = useNavigate(); + const { getter: { missionItem, applyButtonLabel, formattedStartDateTime, formattedEndDateTime }, - routeToAssignmentSubmit, } = useMission({ mission, recruitmentId }); + const routeToAssignmentSubmit = + ({ recruitmentId, mission }: { recruitmentId: string; mission: Mission }) => + () => { + const isSubmitted = mission.submitted; + + navigate( + { + pathname: generatePath(PATH.ASSIGNMENT, { + status: isSubmitted ? PARAM.ASSIGNMENT_STATUS.EDIT : PARAM.ASSIGNMENT_STATUS.NEW, + }), + }, + { + state: { + recruitmentId, + currentMission: mission, + }, + } + ); + }; + return (
    diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts index 25e57ca20..f47775e6d 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts @@ -1,10 +1,6 @@ import { useEffect, useMemo, useState } from "react"; import { formatDateTime } from "../../../utils/format/date"; -import useMissionJudgment from "./useMissionJudgment"; -import { generatePath, useNavigate } from "react-router-dom"; -import useRefresh from "./useRefresh"; import { Mission } from "../../../../types/domains/recruitments"; -import { PARAM, PATH } from "../../../constants/path"; import { BUTTON_LABEL } from "../../../constants/recruitment"; type MissionProps = { @@ -24,17 +20,7 @@ const missionLabel = (submitted: boolean, missionStatus: Mission["status"]) => { }; const useMission = ({ mission, recruitmentId }: MissionProps) => { - const navigate = useNavigate(); - const [missionItem, setMissionItem] = useState({ ...mission }); - const { isRefreshAvailable, fetchRefreshedResultData } = useRefresh({ - missionItem, - recruitmentId: Number(recruitmentId), - }); - const { fetchJudgmentMissionResult, isJudgmentAvailable } = useMissionJudgment({ - missionItem, - recruitmentId: Number(recruitmentId), - }); const applyButtonLabel = missionLabel(mission.submitted, mission.status); @@ -52,60 +38,16 @@ const useMission = ({ mission, recruitmentId }: MissionProps) => { [missionItem.endDateTime] ); - const routeToAssignmentSubmit = - ({ recruitmentId, mission }: { recruitmentId: string; mission: Mission }) => - () => { - const isSubmitted = mission.submitted; - - navigate( - { - pathname: generatePath(PATH.ASSIGNMENT, { - status: isSubmitted ? PARAM.ASSIGNMENT_STATUS.EDIT : PARAM.ASSIGNMENT_STATUS.NEW, - }), - }, - { - state: { - recruitmentId, - currentMission: mission, - }, - } - ); - }; - - const requestRefresh = async () => { - try { - const result = await fetchRefreshedResultData(); - setMissionItem(result); - } catch (error) { - error instanceof Error && alert(error.message); - } - }; - - const requestMissionJudgment = async () => { - try { - const result = await fetchJudgmentMissionResult(); - - setMissionItem(result); - } catch (error) { - error instanceof Error && alert(error.message); - } - }; - return { getter: { missionItem, applyButtonLabel, formattedStartDateTime, formattedEndDateTime, - isJudgmentAvailable, - isRefreshAvailable, }, setter: { setMissionItem, }, - routeToAssignmentSubmit, - requestRefresh, - requestMissionJudgment, }; }; From f84fe3aa5e8c00e13abee84172636dbdd9499011 Mon Sep 17 00:00:00 2001 From: Cron Date: Mon, 15 Jul 2024 14:38:23 +0900 Subject: [PATCH 36/39] =?UTF-8?q?test:=20useMission=20=ED=9B=85=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EB=B6=84=EB=A6=AC=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=20=ED=9B=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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 --- .../__tests__/useMission.test.ts | 164 +----------------- 1 file changed, 2 insertions(+), 162 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts index efa6c4a76..a4e6db6a5 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts @@ -1,20 +1,14 @@ -import { renderHook, act } from "@testing-library/react"; +import { renderHook } from "@testing-library/react"; import useMission from "../useMission"; -import useRefresh from "../useRefresh"; -import useMissionJudgment from "../useMissionJudgment"; -import { generatePath, useNavigate } from "react-router-dom"; -import { PARAM, PATH } from "../../../../constants/path"; import { BUTTON_LABEL } from "../../../../constants/recruitment"; import { createMockMission } from "./testMissionMockUtils"; -jest.mock("../useRefresh"); -jest.mock("../useMissionJudgment"); jest.mock("react-router-dom", () => ({ generatePath: jest.fn(), useNavigate: jest.fn(), })); -describe("useMission", () => { +describe("useMission 훅 테스트", () => { const mockRecruitmentId = "123"; function createMissionItem(overrides = {}) { @@ -25,23 +19,6 @@ describe("useMission", () => { }); } - beforeEach(() => { - (useRefresh as jest.Mock).mockReturnValue({ - isRefreshAvailable: true, - fetchRefreshedResultData: jest - .fn() - .mockResolvedValue({ ...createMissionItem(), title: "Refreshed Mission" }), - }); - (useMissionJudgment as jest.Mock).mockReturnValue({ - isJudgmentAvailable: true, - fetchJudgmentMissionResult: jest - .fn() - .mockResolvedValue({ ...createMissionItem(), title: "Judged Mission" }), - }); - (useNavigate as jest.Mock).mockReturnValue(jest.fn()); - (generatePath as jest.Mock).mockImplementation((path) => path); - }); - describe("초기 상태 및 계산된 값을 점검", () => { const mockMission = createMissionItem(); @@ -56,8 +33,6 @@ describe("useMission", () => { applyButtonLabel: BUTTON_LABEL.SUBMIT, formattedStartDateTime: "2023-01-01 00:00", formattedEndDateTime: "2023-12-31 23:59", - isJudgmentAvailable: true, - isRefreshAvailable: true, }) ); }); @@ -78,139 +53,4 @@ describe("useMission", () => { expect(result.current.getter.missionItem).toEqual(updatedMission); }); }); - - describe("라우팅 테스트", () => { - const mockMission = createMissionItem(); - it("미션 과제 제출물 미제출 시, 'new' 상태로 과제 제출 페이지로 이동해야 한다", () => { - const mockNavigate = jest.fn(); - (useNavigate as jest.Mock).mockReturnValue(mockNavigate); - - const { result } = renderHook(() => - useMission({ mission: mockMission, recruitmentId: mockRecruitmentId }) - ); - - act(() => { - result.current.routeToAssignmentSubmit({ - recruitmentId: mockRecruitmentId, - mission: mockMission, - })(); - }); - - expect(generatePath).toHaveBeenCalledWith(PATH.ASSIGNMENT, { - status: PARAM.ASSIGNMENT_STATUS.NEW, - }); - expect(mockNavigate).toHaveBeenCalledWith( - { pathname: PATH.ASSIGNMENT }, - { - state: { - recruitmentId: mockRecruitmentId, - currentMission: mockMission, - }, - } - ); - }); - - it("미션 과제 제출물 제출 시, 'edit' 상태로 과제 제출 페이지로 이동해야 한다", () => { - const mockNavigate = jest.fn(); - (useNavigate as jest.Mock).mockReturnValue(mockNavigate); - - const submittedMission = { ...mockMission, submitted: true }; - const { result } = renderHook(() => - useMission({ mission: submittedMission, recruitmentId: mockRecruitmentId }) - ); - - act(() => { - result.current.routeToAssignmentSubmit({ - recruitmentId: mockRecruitmentId, - mission: submittedMission, - })(); - }); - - expect(generatePath).toHaveBeenCalledWith(PATH.ASSIGNMENT, { - status: PARAM.ASSIGNMENT_STATUS.EDIT, - }); - expect(mockNavigate).toHaveBeenCalledWith( - { pathname: PATH.ASSIGNMENT }, - { - state: { - recruitmentId: mockRecruitmentId, - currentMission: submittedMission, - }, - } - ); - }); - }); - describe("데이터 갱신 테스트", () => { - describe("채점 결과 새로고침 함수 테스트", () => { - const mockMission = createMissionItem(); - - it("채점 결과 새로고침 요청에 성공하면, missionItem 상태를 갱신해야 한다", async () => { - const { result } = renderHook(() => - useMission({ mission: mockMission, recruitmentId: mockRecruitmentId }) - ); - - await act(async () => { - await result.current.requestRefresh(); - }); - - expect(result.current.getter.missionItem.title).toBe("Refreshed Mission"); - }); - - it("채점 결과 새로고침 요청에 실패하면, 에러 메시지를 보여줘야 한다.", async () => { - const errorMessage = "Refresh failed"; - (useRefresh as jest.Mock).mockReturnValue({ - isRefreshAvailable: true, - fetchRefreshedResultData: jest.fn().mockRejectedValue(new Error(errorMessage)), - }); - const alertMock = jest.spyOn(window, "alert").mockImplementation(() => {}); - - const { result } = renderHook(() => - useMission({ mission: mockMission, recruitmentId: mockRecruitmentId }) - ); - - await act(async () => { - await result.current.requestRefresh(); - }); - - expect(alertMock).toHaveBeenCalledWith(errorMessage); - alertMock.mockRestore(); - }); - }); - - describe("채점 요청 함수 테스트", () => { - const mockMission = createMissionItem(); - - it("채점 요청 성공 시 missionItem 상태를 갱신해야 한다", async () => { - const { result } = renderHook(() => - useMission({ mission: mockMission, recruitmentId: mockRecruitmentId }) - ); - - await act(async () => { - await result.current.requestMissionJudgment(); - }); - - expect(result.current.getter.missionItem.title).toBe("Judged Mission"); - }); - - it("채점 요청 실패 시 에러 메시지를 보여줘야 한다.", async () => { - const errorMessage = "Judgment failed"; - (useMissionJudgment as jest.Mock).mockReturnValue({ - isJudgmentAvailable: true, - fetchJudgmentMissionResult: jest.fn().mockRejectedValue(new Error(errorMessage)), - }); - const alertMock = jest.spyOn(window, "alert").mockImplementation(() => {}); - - const { result } = renderHook(() => - useMission({ mission: mockMission, recruitmentId: mockRecruitmentId }) - ); - - await act(async () => { - await result.current.requestMissionJudgment(); - }); - - expect(alertMock).toHaveBeenCalledWith(errorMessage); - alertMock.mockRestore(); - }); - }); - }); }); From 5e4685351a930266765f122079bd55e6434a9c30 Mon Sep 17 00:00:00 2001 From: Cron Date: Tue, 16 Jul 2024 10:08:25 +0900 Subject: [PATCH 37/39] =?UTF-8?q?refactor:=20MyMissionItem=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/MyMissionItem.test.tsx | 197 +++++++++++------- 1 file changed, 118 insertions(+), 79 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx index 8137cdb67..5e78cb3e2 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx @@ -1,136 +1,175 @@ -import { render, screen, fireEvent, waitFor } from "@testing-library/react"; -import { MemoryRouter } from "react-router-dom"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { generatePath, MemoryRouter } from "react-router-dom"; +import "@testing-library/jest-dom"; import MyMissionItem from "../MyMissionItem"; -import * as useMissionModule from "../useMission"; +import useMission from "../useMission"; +import useRefresh from "../useRefresh"; +import useMissionJudgment from "../useMissionJudgment"; import { createMockMission } from "./testMissionMockUtils"; -import { BUTTON_LABEL, MISSION_STATUS } from "../../../../constants/recruitment"; +import { PARAM, PATH } from "../../../../constants/path"; +import { Mission } from "../../../../../types/domains/recruitments"; +import { BUTTON_LABEL } from "../../../../constants/recruitment"; jest.mock("../useMission"); +jest.mock("../useRefresh"); +jest.mock("../useMissionJudgment"); -describe("MyMissionItem 통합 테스트", () => { - const mockRecruitmentId = "123"; - const mockMission = createMockMission(); - - const mockUseMission = { - getter: { - missionItem: mockMission, - applyButtonLabel: BUTTON_LABEL.APPLY, - formattedStartDateTime: "2023-01-01 00:00", - formattedEndDateTime: "2023-12-31 23:59", - isJudgmentAvailable: true, - isRefreshAvailable: true, - }, - routeToAssignmentSubmit: jest.fn(), - requestRefresh: jest.fn(), - requestMissionJudgment: jest.fn(), - }; +const mockNavigate = jest.fn(); +jest.mock("react-router-dom", () => ({ + ...jest.requireActual("react-router-dom"), + useNavigate: () => mockNavigate, +})); + +describe("MyMissionItem 컴포넌트 테스트", () => { + const RECRUITMENT_ID = "123"; + const MISSION_ITEM = createMockMission(); beforeEach(() => { - (useMissionModule.default as jest.Mock).mockReturnValue(mockUseMission); + jest.clearAllMocks(); + (useMission as jest.Mock).mockReturnValue({ + getter: { + missionItem: MISSION_ITEM, + applyButtonLabel: BUTTON_LABEL.SUBMIT, + formattedStartDateTime: "2023-01-01 00:00", + formattedEndDateTime: "2023-01-31 23:59", + }, + setter: { + setMissionItem: jest.fn(), + }, + }); + (useRefresh as jest.Mock).mockReturnValue({ requestRefresh: jest.fn() }); + (useMissionJudgment as jest.Mock).mockReturnValue({ + isJudgmentAvailable: false, + judgment: null, + requestMissionJudgment: jest.fn(), + }); }); describe("렌더링 테스트", () => { it("컴포넌트가 올바르게 렌더링되어야 한다", () => { render( - + ); - expect(screen.getByText(mockMission.title)).toBeInTheDocument(); - expect(screen.getByText(BUTTON_LABEL.APPLY)).toBeInTheDocument(); - expect(screen.getByText(BUTTON_LABEL.REFRESH)).toBeInTheDocument(); - expect(screen.getByText(BUTTON_LABEL.JUDGMENT)).toBeInTheDocument(); + expect(screen.getByText(MISSION_ITEM.title)).toBeInTheDocument(); }); - it("미션 상태가 SUBMITTING이 아닐 때 과제 제출 버튼이 비활성화되어야 한다", () => { - const notSubmittingMission = createMockMission({ status: MISSION_STATUS.ENDED }); - (useMissionModule.default as jest.Mock).mockReturnValue({ - ...mockUseMission, - getter: { ...mockUseMission.getter, missionItem: notSubmittingMission }, + it("미션 상태가 제출 중이 아닐 때 과제 제출 버튼이 비활성화되어야 한다", () => { + (useMission as jest.Mock).mockReturnValue({ + getter: { + missionItem: { ...MISSION_ITEM, status: "NOT_SUBMITTING" }, + applyButtonLabel: BUTTON_LABEL.SUBMIT, + formattedStartDateTime: "2023-01-01 00:00", + formattedEndDateTime: "2023-01-31 23:59", + }, + setter: { + setMissionItem: jest.fn(), + }, }); render( - + ); - expect(screen.getByText(BUTTON_LABEL.APPLY)).toBeDisabled(); + const submitButton = screen.getByText(BUTTON_LABEL.SUBMIT); + expect(submitButton).toBeDisabled(); }); - it("judgment가 없을 때 새로고침 버튼이 보이지 않아야 한다", () => { - const missionWithoutJudgment = createMockMission({ judgment: null }); - (useMissionModule.default as jest.Mock).mockReturnValue({ - ...mockUseMission, - getter: { - ...mockUseMission.getter, - missionItem: missionWithoutJudgment, - isRefreshAvailable: false, - }, - }); - + it("예제 테스트 실행 중이 아닐 때는 새로고침 버튼이 보이지 않아야 한다", () => { render( - + ); expect(screen.queryByText(BUTTON_LABEL.REFRESH)).not.toBeInTheDocument(); }); - it("isJudgmentAvailable이 false일 때 예제 테스트 실행 버튼이 비활성화되어야 한다", () => { - (useMissionModule.default as jest.Mock).mockReturnValue({ - ...mockUseMission, - getter: { ...mockUseMission.getter, isJudgmentAvailable: false }, - }); - + it("예제 테스트를 할 수 없는 상태일 때, 예제 테스트 실행 버튼이 비활성화되어야 한다", () => { render( - + ); - expect(screen.getByText(BUTTON_LABEL.JUDGMENT)).toBeDisabled(); + const runExampleTestButton = screen.getByText(BUTTON_LABEL.JUDGMENT); + expect(runExampleTestButton).toBeDisabled(); }); }); - describe("기능 테스트", () => { - it("과제 제출 버튼 클릭 시 routeToAssignmentSubmit 함수가 호출되어야 한다", () => { - render( - - - - ); + describe("라우팅 테스트", () => { + it("미션 과제 제출물 미제출 시, 'new' 상태로 과제 제출 페이지로 이동해야 한다", () => { + const mockMission: Mission = { ...MISSION_ITEM, submitted: false, status: "SUBMITTING" }; + (useMission as jest.Mock).mockReturnValue({ + getter: { + missionItem: mockMission, + applyButtonLabel: BUTTON_LABEL.SUBMIT, + formattedStartDateTime: "2023-01-01 00:00", + formattedEndDateTime: "2023-01-31 23:59", + }, + setter: { + setMissionItem: jest.fn(), + }, + }); - fireEvent.click(screen.getByText(BUTTON_LABEL.APPLY)); - expect(mockUseMission.routeToAssignmentSubmit).toHaveBeenCalled(); - }); + render(); - it("새로고침 버튼 클릭 시 requestRefresh 함수가 호출되어야 한다", async () => { - render( - - - + const submitButton = screen.getByText(BUTTON_LABEL.SUBMIT); + expect(submitButton).toBeEnabled(); + fireEvent.click(submitButton); + + expect(mockNavigate).toHaveBeenCalledWith( + { + pathname: generatePath(PATH.ASSIGNMENT, { status: PARAM.ASSIGNMENT_STATUS.NEW }), + }, + { + state: { + recruitmentId: RECRUITMENT_ID, + currentMission: mockMission, + }, + } ); + }); - fireEvent.click(screen.getByText(BUTTON_LABEL.REFRESH)); - await waitFor(() => { - expect(mockUseMission.requestRefresh).toHaveBeenCalled(); + it("미션 과제 제출물 제출 시, 'edit' 상태로 과제 제출 페이지로 이동해야 한다", () => { + const mockMission: Mission = { ...MISSION_ITEM, submitted: true, status: "SUBMITTING" }; + (useMission as jest.Mock).mockReturnValue({ + getter: { + missionItem: mockMission, + applyButtonLabel: BUTTON_LABEL.EDIT, + formattedStartDateTime: "2023-01-01 00:00", + formattedEndDateTime: "2023-01-31 23:59", + }, + setter: { + setMissionItem: jest.fn(), + }, }); - }); - it("예제 테스트 실행 버튼 클릭 시 requestMissionJudgment 함수가 호출되어야 한다", async () => { render( - + ); - fireEvent.click(screen.getByText(BUTTON_LABEL.JUDGMENT)); - await waitFor(() => { - expect(mockUseMission.requestMissionJudgment).toHaveBeenCalled(); - }); + const submitButton = screen.getByText(BUTTON_LABEL.EDIT); + expect(submitButton).toBeEnabled(); + fireEvent.click(submitButton); + + expect(mockNavigate).toHaveBeenCalledWith( + { + pathname: generatePath(PATH.ASSIGNMENT, { status: PARAM.ASSIGNMENT_STATUS.EDIT }), + }, + { + state: { + recruitmentId: RECRUITMENT_ID, + currentMission: mockMission, + }, + } + ); }); }); }); From c7213d29cedc03260644b6a0ad4691fd256cc56e Mon Sep 17 00:00:00 2001 From: Cron Date: Wed, 24 Jul 2024 09:37:51 +0900 Subject: [PATCH 38/39] =?UTF-8?q?refactor:=20useMission=20=ED=9B=85=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyMissionItem/MyMissionItem.tsx | 5 ++--- .../MyApplicationItem/MyMissionItem/useMission.ts | 15 ++++++--------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index 1d8b30537..8933bf713 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -18,9 +18,8 @@ type MyMissionItemProps = { const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { const navigate = useNavigate(); - const { - getter: { missionItem, applyButtonLabel, formattedStartDateTime, formattedEndDateTime }, - } = useMission({ mission, recruitmentId }); + const { missionItem, applyButtonLabel, formattedStartDateTime, formattedEndDateTime } = + useMission({ mission, recruitmentId }); const routeToAssignmentSubmit = ({ recruitmentId, mission }: { recruitmentId: string; mission: Mission }) => diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts index f47775e6d..f88344d82 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts @@ -39,15 +39,12 @@ const useMission = ({ mission, recruitmentId }: MissionProps) => { ); return { - getter: { - missionItem, - applyButtonLabel, - formattedStartDateTime, - formattedEndDateTime, - }, - setter: { - setMissionItem, - }, + missionItem, + applyButtonLabel, + formattedStartDateTime, + formattedEndDateTime, + + setMissionItem, }; }; From acd968248b2abee71e4c2308c1ef8fa81d62ea96 Mon Sep 17 00:00:00 2001 From: Cron Date: Wed, 24 Jul 2024 09:42:11 +0900 Subject: [PATCH 39/39] =?UTF-8?q?rename:=20hook=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyMissionItem/MissionDetail/MissionDetail.tsx | 11 ++++------- .../MyMissionItem/MyMissionItem.tsx | 2 +- .../MyMissionItem/__tests__/MyMissionItem.test.tsx | 6 +++--- .../MyMissionItem/__tests__/useJudgment.test.ts | 2 +- .../MyMissionItem/__tests__/useMission.test.ts | 2 +- .../MyMissionItem/__tests__/useRefresh.test.ts | 2 +- .../MyMissionItem => hooks}/useMission.ts | 6 +++--- .../MyMissionItem => hooks}/useMissionJudgment.ts | 12 ++++++------ .../MyMissionItem => hooks}/useRefresh.ts | 12 ++++++------ 9 files changed, 26 insertions(+), 29 deletions(-) rename frontend/src/{components/MyApplicationItem/MyMissionItem => hooks}/useMission.ts (87%) rename frontend/src/{components/MyApplicationItem/MyMissionItem => hooks}/useMissionJudgment.ts (74%) rename frontend/src/{components/MyApplicationItem/MyMissionItem => hooks}/useRefresh.ts (74%) diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx index 7a96bf86d..433317544 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MissionDetail/MissionDetail.tsx @@ -4,12 +4,12 @@ import Button from "../../../@common/Button/Button"; import CommitHash from "../../CommitHash/CommitHash"; import JudgmentResultText from "../../JudgmentResult/JudgmentResult"; import Tooltip from "../../../@common/Tooltip/Tooltip"; -import useMission from "../useMission"; +import useMission from "../../../../hooks/useMission"; import buttonStyles from "../ApplicationButtonStyles.module.css"; import styles from "./MissionDetail.module.css"; import { BUTTON_LABEL } from "../../../../constants/recruitment"; -import useRefresh from "../useRefresh"; -import useMissionJudgment from "../useMissionJudgment"; +import useRefresh from "../../../../hooks/useRefresh"; +import useMissionJudgment from "../../../../hooks/useMissionJudgment"; type MissionDetailProps = { mission: Mission; @@ -18,10 +18,7 @@ type MissionDetailProps = { }; const MissionDetail = ({ mission, recruitmentId, judgment }: MissionDetailProps) => { - const { - getter: { missionItem }, - setter: { setMissionItem }, - } = useMission({ mission, recruitmentId }); + const { missionItem, setMissionItem } = useMission({ mission, recruitmentId }); const { isJudgmentAvailable, fetchJudgmentMissionResult } = useMissionJudgment({ missionItem, diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index 8933bf713..4d7d334ee 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -5,7 +5,7 @@ import MissionDetail from "./MissionDetail/MissionDetail"; import styles from "../MyApplicationItem.module.css"; import buttonStyles from "./ApplicationButtonStyles.module.css"; import RecruitmentDetail from "../RecruitmentDetail/RecruitmentDetail"; -import useMission from "./useMission"; +import useMission from "../../../hooks/useMission"; import Button from "../../@common/Button/Button"; import { generatePath, useNavigate } from "react-router-dom"; import { PARAM, PATH } from "../../../constants/path"; diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx index 5e78cb3e2..68a6ebfbe 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/MyMissionItem.test.tsx @@ -2,9 +2,9 @@ import { render, screen, fireEvent } from "@testing-library/react"; import { generatePath, MemoryRouter } from "react-router-dom"; import "@testing-library/jest-dom"; import MyMissionItem from "../MyMissionItem"; -import useMission from "../useMission"; -import useRefresh from "../useRefresh"; -import useMissionJudgment from "../useMissionJudgment"; +import useMission from "../../../../hooks/useMission"; +import useRefresh from "../../../../hooks/useRefresh"; +import useMissionJudgment from "../../../../hooks/useMissionJudgment"; import { createMockMission } from "./testMissionMockUtils"; import { PARAM, PATH } from "../../../../constants/path"; import { Mission } from "../../../../../types/domains/recruitments"; diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useJudgment.test.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useJudgment.test.ts index d22c0b2c3..2e98220c7 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useJudgment.test.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useJudgment.test.ts @@ -1,5 +1,5 @@ import { renderHook, act } from "@testing-library/react"; -import useMissionJudgment from "../useMissionJudgment"; +import useMissionJudgment from "../../../../hooks/useMissionJudgment"; import { postMyMissionJudgment } from "../../../../api"; import useTokenContext from "../../../../hooks/useTokenContext"; import { isJudgmentTimedOut } from "../../../../utils/validation/judgmentTime"; diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts index a4e6db6a5..52e639d0f 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useMission.test.ts @@ -1,5 +1,5 @@ import { renderHook } from "@testing-library/react"; -import useMission from "../useMission"; +import useMission from "../../../../hooks/useMission"; import { BUTTON_LABEL } from "../../../../constants/recruitment"; import { createMockMission } from "./testMissionMockUtils"; diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useRefresh.test.ts b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useRefresh.test.ts index 6c7811313..81354e92b 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useRefresh.test.ts +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/__tests__/useRefresh.test.ts @@ -1,5 +1,5 @@ import { renderHook, act } from "@testing-library/react"; -import useRefresh from "../useRefresh"; +import useRefresh from "../../../../hooks/useRefresh"; import { JUDGMENT_STATUS } from "../../../../constants/judgment"; import { MISSION_STATUS } from "../../../../constants/recruitment"; import { isJudgmentTimedOut } from "../../../../utils/validation/judgmentTime"; diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts b/frontend/src/hooks/useMission.ts similarity index 87% rename from frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts rename to frontend/src/hooks/useMission.ts index f88344d82..80a336d28 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/useMission.ts +++ b/frontend/src/hooks/useMission.ts @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from "react"; -import { formatDateTime } from "../../../utils/format/date"; -import { Mission } from "../../../../types/domains/recruitments"; -import { BUTTON_LABEL } from "../../../constants/recruitment"; +import { formatDateTime } from "../utils/format/date"; +import { Mission } from "../../types/domains/recruitments"; +import { BUTTON_LABEL } from "../constants/recruitment"; type MissionProps = { mission: Mission; diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgment.ts b/frontend/src/hooks/useMissionJudgment.ts similarity index 74% rename from frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgment.ts rename to frontend/src/hooks/useMissionJudgment.ts index 761762483..e09a0b4b6 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/useMissionJudgment.ts +++ b/frontend/src/hooks/useMissionJudgment.ts @@ -1,10 +1,10 @@ import { AxiosError } from "axios"; -import { Mission, MissionStatus, Recruitment } from "../../../../types/domains/recruitments"; -import { postMyMissionJudgment } from "../../../api"; -import useTokenContext from "../../../hooks/useTokenContext"; -import { isJudgmentTimedOut } from "../../../utils/validation/judgmentTime"; -import { MISSION_STATUS } from "../../../constants/recruitment"; -import { JUDGMENT_STATUS } from "../../../constants/judgment"; +import { Mission, MissionStatus, Recruitment } from "../../types/domains/recruitments"; +import { postMyMissionJudgment } from "../api"; +import useTokenContext from "./useTokenContext"; +import { isJudgmentTimedOut } from "../utils/validation/judgmentTime"; +import { MISSION_STATUS } from "../constants/recruitment"; +import { JUDGMENT_STATUS } from "../constants/judgment"; type MissionJudgementProps = { missionItem: Mission; diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/useRefresh.ts b/frontend/src/hooks/useRefresh.ts similarity index 74% rename from frontend/src/components/MyApplicationItem/MyMissionItem/useRefresh.ts rename to frontend/src/hooks/useRefresh.ts index 5f572af8e..135eecc89 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/useRefresh.ts +++ b/frontend/src/hooks/useRefresh.ts @@ -1,10 +1,10 @@ import { AxiosError } from "axios"; -import { Mission, Recruitment } from "../../../../types/domains/recruitments"; -import { fetchMyMissionJudgment } from "../../../api"; -import { JUDGMENT_STATUS } from "../../../constants/judgment"; -import { MISSION_STATUS } from "../../../constants/recruitment"; -import { isJudgmentTimedOut } from "../../../utils/validation/judgmentTime"; -import useTokenContext from "../../../hooks/useTokenContext"; +import { Mission, Recruitment } from "../../types/domains/recruitments"; +import { fetchMyMissionJudgment } from "../api"; +import { JUDGMENT_STATUS } from "../constants/judgment"; +import { MISSION_STATUS } from "../constants/recruitment"; +import { isJudgmentTimedOut } from "../utils/validation/judgmentTime"; +import useTokenContext from "./useTokenContext"; type Props = { recruitmentId?: Recruitment["id"];