Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/web/src/apis/Scores/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const ScoresQueryKeys = {

// ====== Types ======
export interface UseMyGpaScoreResponse {
homeUniversityName: string;
gpaScoreStatusResponseList: GpaScore[];
}

Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/apis/Scores/getGpaList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { ScoresQueryKeys, scoresApi } from "./api";
/**
* @description 내 학점 점수 조회 훅
*/
const useGetMyGpaScore = () => {
const useGetMyGpaScore = ({ enabled = true }: { enabled?: boolean } = {}) => {
return useQuery({
queryKey: [ScoresQueryKeys.myGpaScore],
queryFn: scoresApi.getMyGpaScore,
enabled,
staleTime: Infinity,
select: (data) => data.data.gpaScoreStatusResponseList,
select: (data) => data.data,
});
};

Expand Down
19 changes: 19 additions & 0 deletions apps/web/src/app/my/school-email/_lib/returnTo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const SCHOOL_EMAIL_RETURN_PATHS = {
applicationApply: "/university/application/apply",
score: "/university/score",
gpaSubmit: "/university/score/submit/gpa",
} as const;

export type SchoolEmailReturnTo = keyof typeof SCHOOL_EMAIL_RETURN_PATHS;

export const getSchoolEmailReturnPath = (returnTo: string | null | undefined) => {
if (!returnTo || !(returnTo in SCHOOL_EMAIL_RETURN_PATHS)) {
return "/";
}

return SCHOOL_EMAIL_RETURN_PATHS[returnTo as SchoolEmailReturnTo];
};

export const getSchoolEmailVerificationPath = (returnTo: SchoolEmailReturnTo) => {
return `/my/school-email?returnTo=${returnTo}`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Input } from "@/components/ui/Inputa";
import { Label } from "@/components/ui/Label";
import { Progress } from "@/components/ui/Progress";
import { IconExpRed } from "@/public/svgs/ui";
import { getSchoolEmailReturnPath } from "../../_lib/returnTo";

const VERIFICATION_LIMIT_SECONDS = 300;
const CODE_LENGTH = 6;
Expand Down Expand Up @@ -43,7 +44,7 @@ const SchoolEmailVerificationContent = () => {
const isCodeInputValid = code.length === CODE_LENGTH;
const isTimerExpired = remainingSeconds <= 0;
const progressValue = step === "email" ? 0 : step === "code" ? 50 : 100;
const shouldReturnToApply = searchParams?.get("returnTo") === "applicationApply";
const returnPath = getSchoolEmailReturnPath(searchParams?.get("returnTo"));

useEffect(() => {
if (step !== "code" || remainingSeconds <= 0) return;
Expand Down Expand Up @@ -134,8 +135,8 @@ const SchoolEmailVerificationContent = () => {

<div className="fixed bottom-14 left-0 right-0 w-full bg-white">
<div className="mx-auto mb-[37px] w-full max-w-app px-5">
<BlockBtn onClick={() => router.replace(shouldReturnToApply ? "/university/application/apply" : "/")}>
{shouldReturnToApply ? "지원하기로 돌아가기" : "홈으로"}
<BlockBtn onClick={() => router.replace(returnPath)}>
{returnPath === "/" ? "홈으로" : "이전 화면으로 돌아가기"}
</BlockBtn>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useEffect, useMemo, useState } from "react";
import { usePostSubmitApplication } from "@/apis/applications";
import { useGetMyGpaScore, useGetMyLanguageTestScore } from "@/apis/Scores";
import { useUniversitySearch } from "@/apis/universities";
import { getSchoolEmailVerificationPath } from "@/app/my/school-email/_lib/returnTo";
import TopDetailNavigation from "@/components/layout/TopDetailNavigation";
import ProgressBar from "@/components/ui/ProgressBar";
import { DEFAULT_MAX_CHOICE_COUNT, getHomeUniversityById } from "@/constants/university";
Expand Down Expand Up @@ -36,7 +37,9 @@ const ApplyPageContent = () => {
);

const { data: universityList = [] } = useUniversitySearch("", undefined, universitySearchOptions);
const { data: gpaScoreList = [] } = useGetMyGpaScore();
const shouldFetchGpaScore = isAuthInitialized && isAuthenticated && homeUniversityId !== null;
// TODO: 서버의 모학교 미인증 에러 코드가 확정되면 GPA 조회 실패 시 학교 인증으로 보내는 fallback을 추가한다.
const { data: gpaScoreData } = useGetMyGpaScore({ enabled: shouldFetchGpaScore });
const { data: languageTestScoreList = [] } = useGetMyLanguageTestScore();
const { mutate: postSubmitApplication } = usePostSubmitApplication({
onSuccess: () => {
Expand All @@ -53,7 +56,7 @@ const ApplyPageContent = () => {
return;
}

router.replace("/my/school-email?returnTo=applicationApply");
router.replace(getSchoolEmailVerificationPath("applicationApply"));
}, [homeUniversityId, isAuthInitialized, isAuthenticated, router]);

// 다음 스텝으로 넘어가기
Expand Down Expand Up @@ -95,6 +98,8 @@ const ApplyPageContent = () => {
});
};

const gpaScoreList = gpaScoreData?.gpaScoreStatusResponseList ?? [];
const homeUniversityName = gpaScoreData?.homeUniversityName;
const isDataExist = gpaScoreList.length === 0 || languageTestScoreList.length === 0;
const hasSelectedUniversity = curUniversityList.some((universityId) => universityId > 0);
const progressStep = step === 3 && hasSelectedUniversity ? APPLY_PROGRESS_TOTAL_STEPS : step + 1;
Expand Down Expand Up @@ -123,9 +128,10 @@ const ApplyPageContent = () => {
onNext={goNextStep}
/>
)}
{step === 2 && (
{step === 2 && homeUniversityName && (
<GpaStep
gpaScoreList={gpaScoreList}
homeUniversityName={homeUniversityName}
curGpaScore={curGpaScore}
setCurGpaScore={setCurGpaScore}
onNext={goNextStep}
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/app/university/application/apply/GpaStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import ApplicationSectionTitle from "../_components/ApplicationSectionTitle";

type GpaStepProps = {
gpaScoreList: GpaScore[];
homeUniversityName: string;
curGpaScore: number | null;
setCurGpaScore: (id: number) => void;
onNext: () => void;
};

const GpaStep = ({ gpaScoreList, curGpaScore, setCurGpaScore, onNext }: GpaStepProps) => {
const GpaStep = ({ gpaScoreList, homeUniversityName, curGpaScore, setCurGpaScore, onNext }: GpaStepProps) => {
const [isModalOpen, setIsModalOpen] = useState(false);

const handleNext = () => {
Expand Down Expand Up @@ -50,7 +51,7 @@ const GpaStep = ({ gpaScoreList, curGpaScore, setCurGpaScore, onNext }: GpaStepP
className="transition-transform hover:scale-[1.01] active:scale-[0.97]"
>
<ScoreCard
name="인하대학교" // TODO: 학교명 API에서 받아오기
name={homeUniversityName}
score={`${score.gpaResponse.gpa.toFixed(2)}/${score.gpaResponse.gpaCriteria}`}
status={score.verifyStatus}
// date={new Date(score.issueDate).toISOString()}
Expand Down
31 changes: 28 additions & 3 deletions apps/web/src/app/university/score/ScoreScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"use client";

import { useRouter } from "next/navigation";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useGetMyGpaScore, useGetMyLanguageTestScore } from "@/apis/Scores";
import { getSchoolEmailVerificationPath } from "@/app/my/school-email/_lib/returnTo";
import BlockBtn from "@/components/button/BlockBtn";
import Tab from "@/components/ui/Tab";
import { showIconToast } from "@/lib/toast/showIconToast";
import useAuthStore from "@/lib/zustand/useAuthStore";
import { IconSolidConnectionSmallLogo } from "@/public/svgs/my";
import { formatLanguageTestScore, languageTestMapping, ScoreSubmitStatus } from "@/types/score";
import ScoreCard from "./ScoreCard";
Expand All @@ -15,11 +17,25 @@ type ScoreTab = (typeof SCORE_TAB_CHOICES)[number];

const ScoreScreen = () => {
const router = useRouter();
const homeUniversityId = useAuthStore((state) => state.homeUniversityId);
const isAuthInitialized = useAuthStore((state) => state.isInitialized);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const [curTab, setCurTab] = useState<ScoreTab>("공인어학");
const { data: gpaScoreList = [] } = useGetMyGpaScore();
const shouldFetchGpaScore = isAuthInitialized && isAuthenticated && homeUniversityId !== null;
// TODO: 서버의 모학교 미인증 에러 코드가 확정되면 GPA 조회 실패 시 학교 인증으로 보내는 fallback을 추가한다.
const { data: gpaScoreData } = useGetMyGpaScore({ enabled: shouldFetchGpaScore });
const { data: languageTestScoreList = [] } = useGetMyLanguageTestScore();
const gpaScoreList = gpaScoreData?.gpaScoreStatusResponseList ?? [];
const isEmptyCurrentTab = curTab === "공인어학" ? languageTestScoreList.length === 0 : gpaScoreList.length === 0;

useEffect(() => {
if (!isAuthInitialized || !isAuthenticated || homeUniversityId !== null) {
return;
}

router.replace(getSchoolEmailVerificationPath("score"));
}, [homeUniversityId, isAuthInitialized, isAuthenticated, router]);

const handleScoreClick = (status: ScoreSubmitStatus, rejectedReason?: string | null) => {
if (status === ScoreSubmitStatus.REJECTED) {
showIconToast("logo", rejectedReason ?? "승인이 거절되었습니다.");
Expand All @@ -30,6 +46,14 @@ const ScoreScreen = () => {
}
};

if (!isAuthInitialized) {
return null;
}

if (isAuthenticated && homeUniversityId === null) {
return null;
}

return (
<div className="h-full">
<div className="mx-5 mb-40">
Expand Down Expand Up @@ -68,6 +92,7 @@ const ScoreScreen = () => {
))}

{curTab === "학점" &&
gpaScoreData &&
gpaScoreList.map((score) => (
<button
key={score.id}
Expand All @@ -76,7 +101,7 @@ const ScoreScreen = () => {
onClick={() => handleScoreClick(score.verifyStatus, score.rejectedReason)}
>
<ScoreCard
name="인하대학교" // TODO: 학교명 API에서 받아오기
name={gpaScoreData.homeUniversityName}
score={`${score.gpaResponse.gpa.toFixed(2)}/${score.gpaResponse.gpaCriteria}`}
status={score.verifyStatus}
// date={new Date(score.issueDate).toISOString()}
Expand Down
19 changes: 18 additions & 1 deletion apps/web/src/app/university/score/submit/gpa/GpaSubmitForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ import { zodResolver } from "@hookform/resolvers/zod";
import clsx from "clsx";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { Suspense, useState } from "react";
import { Suspense, useEffect, useState } from "react";
import { Controller, type SubmitHandler, useForm } from "react-hook-form";
import { usePostGpaScore } from "@/apis/Scores";
import { getSchoolEmailVerificationPath } from "@/app/my/school-email/_lib/returnTo";
import SubmitLinkTab from "@/components/score/SubmitLinkTab";
import SubmitResult, { type InfoRowProps } from "@/components/score/SubmitResult";
import CustomDropdown from "@/components/search/CustomDropdown";
import CloudSpinnerPage from "@/components/ui/CloudSpinnerPage";
import useAuthStore from "@/lib/zustand/useAuthStore";
import { type GpaFormData, gpaSchema } from "./_lib/schema";

const GpaSubmitForm = () => {
const router = useRouter();
const homeUniversityId = useAuthStore((state) => state.homeUniversityId);
const isAuthInitialized = useAuthStore((state) => state.isInitialized);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const [showResult, setShowResult] = useState(false);
const [submittedData, setSubmittedData] = useState<GpaFormData | null>(null);
const { mutateAsync: postGpaScore } = usePostGpaScore();
Expand All @@ -33,6 +38,18 @@ const GpaSubmitForm = () => {
});
const selectedFile = watch("file");

useEffect(() => {
if (!isAuthInitialized || !isAuthenticated || homeUniversityId !== null) {
return;
}

router.replace(getSchoolEmailVerificationPath("gpaSubmit"));
}, [homeUniversityId, isAuthInitialized, isAuthenticated, router]);

if (isAuthInitialized && isAuthenticated && homeUniversityId === null) {
return null;
}

// 3. 폼 제출 핸들러
const onSubmit: SubmitHandler<GpaFormData> = async (data) => {
await postGpaScore({
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/types/score.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export interface GpaScore {
}

export interface MyGpaScoreResponse {
homeUniversityName: string;
gpaScoreStatusResponseList: GpaScore[];
}

Expand Down
Loading