diff --git a/apps/web/src/apis/mentor/api.ts b/apps/web/src/apis/mentor/api.ts index 88910738..872a7f2c 100644 --- a/apps/web/src/apis/mentor/api.ts +++ b/apps/web/src/apis/mentor/api.ts @@ -5,6 +5,7 @@ import { MentoringApprovalStatus, type MentoringItem, } from "@/types/mentor"; +import type { CountryCode } from "@/types/university"; import { axiosInstance } from "@/utils/axiosInstance"; // QueryKeys for mentor domain @@ -75,10 +76,11 @@ export interface PostApplyMentoringResponse { } export interface PostMentorApplicationRequest { - interestedCountries: string[]; - country: string; - universityName: string; - studyStatus: "STUDYING" | "PLANNING" | "COMPLETED"; + preparationStatus: "AFTER_EXCHANGE"; + universitySelectType: "CATALOG"; + country: CountryCode; + universityId: number; + term: string; verificationFile: File; } @@ -127,17 +129,18 @@ export const mentorApi = { postMentorApplication: async (body: PostMentorApplicationRequest): Promise => { const formData = new FormData(); const applicationData = { - interestedCountries: body.interestedCountries, + preparationStatus: body.preparationStatus, + universitySelectType: body.universitySelectType, country: body.country, - universityName: body.universityName, - studyStatus: body.studyStatus, + universityId: body.universityId, + term: body.term, }; formData.append( "mentorApplicationRequest", new Blob([JSON.stringify(applicationData)], { type: "application/json" }), ); formData.append("file", body.verificationFile); - const res = await axiosInstance.post("/mentor/verification", formData, { + const res = await axiosInstance.post("/mentees/mentor-applications", formData, { headers: { "Content-Type": "multipart/form-data" }, }); return res.data; diff --git a/apps/web/src/app/my/_ui/MyProfileContent/index.tsx b/apps/web/src/app/my/_ui/MyProfileContent/index.tsx index ff6ce465..b248eb38 100644 --- a/apps/web/src/app/my/_ui/MyProfileContent/index.tsx +++ b/apps/web/src/app/my/_ui/MyProfileContent/index.tsx @@ -6,7 +6,6 @@ import { useDeleteUserAccount, usePostLogout } from "@/apis/Auth"; import { type MyInfoResponse, useGetMyInfo } from "@/apis/MyPage"; import LinkedTextWithIcon from "@/components/ui/LinkedTextWithIcon"; import ProfileWithBadge from "@/components/ui/ProfileWithBadge"; -import { infoToastOptions } from "@/lib/toast/options"; import useAuthStore from "@/lib/zustand/useAuthStore"; import { IconLikeFill } from "@/public/svgs/mentor"; import { @@ -85,17 +84,12 @@ const MyProfileContent = () => {
프로필 변경
- {/* - - */} - + )} diff --git a/apps/web/src/app/my/apply-mentor/_components/CompletionScreen/index.tsx b/apps/web/src/app/my/apply-mentor/_components/CompletionScreen/index.tsx index 51e04f67..9edd8c29 100644 --- a/apps/web/src/app/my/apply-mentor/_components/CompletionScreen/index.tsx +++ b/apps/web/src/app/my/apply-mentor/_components/CompletionScreen/index.tsx @@ -20,25 +20,22 @@ const CompletionScreen = () => { {/* 타이틀 */} -

증명서 첨부 완료

+

멘토 전환 신청 완료

{/* 설명 */}

- 승인은 최대 7일이 소요되며 + 관리자 검토가 완료되면
- 마이페이지에서 확인할 수 있습니다. + 멘토 회원으로 전환돼요.

{/* 버튼들 */}
router.push("/")} + onClick={() => router.push("/my")} className="hover:bg-primary-50 border border-primary bg-white text-primary" > - 홈으로 이동하기 - - router.push("/mentor/modify")} className="bg-primary text-white"> - 멘토 프로필 작성하기 + 마이페이지로 이동하기
diff --git a/apps/web/src/app/my/apply-mentor/_components/InterestCountriesScreen/index.tsx b/apps/web/src/app/my/apply-mentor/_components/InterestCountriesScreen/index.tsx index f2d55d1e..a95800df 100644 --- a/apps/web/src/app/my/apply-mentor/_components/InterestCountriesScreen/index.tsx +++ b/apps/web/src/app/my/apply-mentor/_components/InterestCountriesScreen/index.tsx @@ -6,49 +6,49 @@ import { useFormContext } from "react-hook-form"; import BlockBtn from "@/components/button/BlockBtn"; import { mentorRegionList } from "@/constants/regions"; -import type { MentorApplicationFormData } from "../../_lib/schema"; +import { COUNTRY_CODE_MAP } from "@/constants/university"; +import type { CountryCode } from "@/types/university"; +import type { MentorApplicationFormInputData } from "../../_lib/schema"; type InterestCountriesScreenProps = { onNext: () => void; }; +const countryCodeByName = Object.fromEntries( + Object.entries(COUNTRY_CODE_MAP).map(([code, countryName]) => [countryName, code]), +) as Record; + const InterestCountriesScreen = ({ onNext }: InterestCountriesScreenProps) => { const { watch, setValue, trigger, formState: { errors }, - } = useFormContext(); + } = useFormContext(); const [selectedRegion, setSelectedRegion] = useState("미주권"); - const selectedCountries = watch("interestedCountries") || []; + const selectedCountryCode = watch("country"); + + const selectedCountryName = selectedCountryCode ? COUNTRY_CODE_MAP[selectedCountryCode] : ""; const handleNext = async () => { - const isValid = await trigger("interestedCountries"); + const isValid = await trigger("country"); if (isValid) { onNext(); } }; - const removeCountry = (country: string) => { - setValue( - "interestedCountries", - selectedCountries.filter((c) => c !== country), - ); - }; + const selectCountry = (country: string) => { + const countryCode = countryCodeByName[country]; + if (!countryCode) return; - const toggleCountry = (country: string) => { - if (selectedCountries.includes(country)) { - setValue( - "interestedCountries", - selectedCountries.filter((c) => c !== country), - ); - } else { - setValue("interestedCountries", [...selectedCountries, country]); - } + setValue("country", countryCode, { shouldValidate: true }); + setValue("universityId", 0); + setValue("term", ""); }; const currentRegion = mentorRegionList.find((r) => r.name === selectedRegion); + const currentCountries = currentRegion?.countries.filter((country) => countryCodeByName[country]) ?? []; return (
@@ -58,31 +58,30 @@ const InterestCountriesScreen = ({ onNext }: InterestCountriesScreenProps) => { 나의 수학 국가
- 선택해주세요 + 선택해주세요.
- {/* Selected Countries Tags */} - {selectedCountries.length > 0 && ( + {/* Selected Country Tag */} + {selectedCountryName && (
- {selectedCountries.map((country) => ( - - ))} +
)} {/* Error Message */} - {errors.interestedCountries && ( -

{errors.interestedCountries.message}

- )} + {errors.country &&

{errors.country.message}

} {/* Region Tabs - Large Icon Buttons */}
@@ -114,14 +113,14 @@ const InterestCountriesScreen = ({ onNext }: InterestCountriesScreenProps) => { {/* Country Buttons - Only show current region's countries */}
- {currentRegion?.countries.map((country) => ( + {currentCountries.map((country) => (
{/* 증명서 첨부 */} @@ -215,10 +206,10 @@ const UniversityScreen = ({ onNext }: UniversityScreenProps) => {
- 다음 + {isSubmitting ? "신청 중..." : "신청하기"}
diff --git a/apps/web/src/app/my/apply-mentor/_lib/schema.ts b/apps/web/src/app/my/apply-mentor/_lib/schema.ts index b4db65d6..1a2b1eb6 100644 --- a/apps/web/src/app/my/apply-mentor/_lib/schema.ts +++ b/apps/web/src/app/my/apply-mentor/_lib/schema.ts @@ -1,4 +1,7 @@ import { z } from "zod"; +import { CountryCode } from "@/types/university"; + +const countryCodeValues = Object.values(CountryCode) as [CountryCode, ...CountryCode[]]; const verificationFileSchema = z .union([z.instanceof(File), z.null(), z.undefined()]) @@ -18,35 +21,38 @@ const verificationFileSchema = z .transform((file) => file!); export const mentorApplicationSchema = z.object({ - // Step 1: 관심 국가 - interestedCountries: z.array(z.string()).min(1, "관심 국가를 하나 이상 선택해주세요."), - - // Step 2: 수학 학교 - country: z.string().min(1, "국가를 선택해주세요."), - universityName: z.string().min(1, "학교를 선택해주세요."), - verificationFile: verificationFileSchema, + // Step 1: 멘토 신청 가능 상태 + preparationStatus: z.enum(["AFTER_EXCHANGE"], { + message: "수학 완료 상태만 멘토 전환을 신청할 수 있습니다.", + }), - // Step 3: 준비 단계 - studyStatus: z.enum(["PLANNING", "STUDYING", "COMPLETED"], { - message: "준비 단계를 선택해주세요.", + // Step 2: 수학 국가 + country: z.enum(countryCodeValues, { + message: "국가를 선택해주세요.", }), + + // Step 3: 수학 학교 및 증명서 + universityId: z.number().int().positive("학교를 선택해주세요."), + term: z.string().min(1, "파견 학기 정보를 확인할 수 없습니다."), + verificationFile: verificationFileSchema, }); -export type MentorApplicationFormData = z.infer; +export type MentorApplicationFormInputData = z.input; +export type MentorApplicationFormData = z.output; // 단계별 부분 스키마 export const step1Schema = mentorApplicationSchema.pick({ - interestedCountries: true, + preparationStatus: true, }); export const step2Schema = mentorApplicationSchema.pick({ country: true, - universityName: true, - verificationFile: true, }); export const step3Schema = mentorApplicationSchema.pick({ - studyStatus: true, + universityId: true, + term: true, + verificationFile: true, }); export type Step1FormData = z.infer; diff --git a/apps/web/src/app/my/apply-mentor/page.tsx b/apps/web/src/app/my/apply-mentor/page.tsx index 514f1a77..13b07041 100644 --- a/apps/web/src/app/my/apply-mentor/page.tsx +++ b/apps/web/src/app/my/apply-mentor/page.tsx @@ -22,24 +22,23 @@ const ApplyMentorPage = () => { const router = useRouter(); const [step, setStep] = useState(1); - const methods = useForm({ + const methods = useForm({ resolver: zodResolver(mentorApplicationSchema), defaultValues: { - interestedCountries: [], - country: "", - universityName: "", + universityId: 0, + term: "", verificationFile: undefined, - studyStatus: undefined, }, mode: "onChange", }); - const { mutate: postMentorApplication } = usePostMentorApplication(); + const { mutate: postMentorApplication, isPending } = usePostMentorApplication(); const goNextStep = () => setStep((prev) => prev + 1); const goPrevStep = () => { if (step === 1) { router.back(); + return; } setStep((prev) => Math.max(1, prev - 1)); }; @@ -49,10 +48,11 @@ const ApplyMentorPage = () => { const onSubmit = methods.handleSubmit((data: FormOutputValues) => { postMentorApplication( { - interestedCountries: data.interestedCountries, + preparationStatus: data.preparationStatus, + universitySelectType: "CATALOG", country: data.country, - universityName: data.universityName, - studyStatus: data.studyStatus, + universityId: data.universityId, + term: data.term, verificationFile: data.verificationFile, }, { @@ -73,7 +73,7 @@ const ApplyMentorPage = () => { {/* Top Navigation */} @@ -85,7 +85,7 @@ const ApplyMentorPage = () => { {/* Step Content */} {step === 1 && } {step === 2 && } - {step === 3 && } + {step === 3 && } );