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
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const PostPageContent = ({ boardCode, postId }: PostPageContentProps) => {
handleBack={() => {
router.push(`/community/${boardCode}`);
}}
icon={post.isOwner && <KebabMenu postId={postId} />}
icon={<KebabMenu postId={postId} />}
/>
<Content post={post} postId={postId} />
<CommentSection comments={post.postFindCommentResponses} postId={postId} refresh={refresh} />
Expand Down
4 changes: 2 additions & 2 deletions src/app/my/_ui/MyProfileContent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ const MyProfileContent = () => {
</div>

{isMentor ? (
<div className="w-full rounded-lg bg-secondary-500 py-2 text-center font-medium text-white">
<div className="w-full cursor-pointer rounded-lg bg-secondary-500 py-2 text-center font-medium text-white">
<Link href={"/my/modify"}>프로필 변경</Link>
</div>
) : (
<div className="mt-4 flex items-center justify-between gap-3">
<div className="w-full rounded-lg bg-secondary-500 py-2 text-center font-medium text-white">
<div className="w-full cursor-pointer rounded-lg bg-secondary-500 py-2 text-center font-medium text-white">
<Link href={"/my/modify"}>프로필 변경</Link>
</div>
<button
Expand Down
15 changes: 9 additions & 6 deletions src/app/university/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ interface SearchBarProps {
initText?: string;
}
// --- 폼 로직을 관리하는 부모 컴포넌트 ---
const SearchBar = ({ initText }: SearchBarProps) => {
const SearchForm = ({ initText }: SearchBarProps) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

1) 컴포넌트명 변경에 따른 정합성 확보: 파일명(SearchBar.tsx) vs. 기본 export(SearchForm).

이름 불일치로 import 경로/자동 리팩토링에서 혼동 여지가 큽니다. 파일명을 SearchForm.tsx로 바꾸거나, 컴포넌트/기본 export를 SearchBar로 되돌리는 두 가지 중 하나로 정리해주세요.

아래 중 “B안”은 코드만으로 해결됩니다.

-const SearchForm = ({ initText }: SearchBarProps) => {
+const SearchBar = ({ initText }: SearchBarProps) => {
   ...
-export default SearchForm;
+export default SearchBar;

레포 내 사용처 점검 스크립트:


🏁 Script executed:

#!/bin/bash
# SearchBar / SearchForm 사용처와 import 확인
rg -nP -C2 '\bSearchBar\b|\\bSearchForm\\b'
# SearchBar.tsx를 기본 import로 참조하는 위치 탐색
rg -nP -C2 'import\s+\w+\s+from\s+["\'][^"\']*SearchBar["\']'

Length of output: 2088


스크립트 내부 정규식 따옴표 오류를 수정하고, SearchBarSearchForm의 사용처를 정확히 재확인하기 위해 아래 스크립트를 실행해 주세요.


🏁 Script executed:

#!/bin/bash
# 1) SearchBar 또는 SearchForm 키워드 사용 위치 전체 확인
rg -nP -C2 '\b(SearchBar|SearchForm)\b' src/app/university/

# 2) SearchBar.tsx를 기본 import로 참조한 구문 확인
rg -nP -C2 "import\s+\w+\s+from\s+['\"][^'\"]*SearchBar['\"]" src/app/university/

Length of output: 3685


컴포넌트명과 파일명 일치화 제안

  1. 파일명 변경 제안
    SearchBar.tsx를 SearchForm.tsx로 변경해 파일명과 기본 export(컴포넌트명)를 동기화합니다.

  2. 컴포넌트명 변경 제안
    src/app/university/SearchBar.tsx 내부 컴포넌트 및 export를 SearchForm에서 SearchBar로 수정해 파일명과 일치시킵니다.

    -const SearchForm = ({ initText }: SearchBarProps) => {
    +const SearchBar = ({ initText }: SearchBarProps) => {-export default SearchForm;
    +export default SearchBar;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const SearchForm = ({ initText }: SearchBarProps) => {
// src/app/university/SearchBar.tsx
const SearchBar = ({ initText }: SearchBarProps) => {
export default SearchBar;
🤖 Prompt for AI Agents
In src/app/university/SearchBar.tsx around line 31, the component name and the
file name are out of sync (file is SearchBar.tsx but the component is declared
as SearchForm); update them to match by either renaming the file to
SearchForm.tsx or renaming the component and its default export to SearchBar. If
you choose renaming the component, change the function/const name and the export
identifier to SearchBar and update any imports across the codebase; if you
choose renaming the file, move/rename SearchBar.tsx to SearchForm.tsx and update
imports accordingly so the file name and exported component name are consistent.

const router = useRouter();

const {
Expand All @@ -50,16 +50,19 @@ const SearchBar = ({ initText }: SearchBarProps) => {
}

const queryString = queryParams.toString();
router.push(`/university/search-results?${queryString}`);
console.log("검색 실행:", queryString);
router.push(`/university/search?${queryString}`);
};

return (
<form onSubmit={handleSubmit(onSubmit)} className="w-full">
<div className="relative w-full">
<div className="relative mb-2">
<input
type="text"
placeholder={"해외 파견 학교를 검색하세요."}
className="w-full rounded-lg border border-gray-200 bg-k-50 p-3 pl-4 pr-10 text-k-600 focus:border-primary"
placeholder={"대학명을 검색해보세요..."}
className={`w-full border-b bg-white p-3 pl-4 pr-10 outline-none transition-colors ${
errors.searchText ? "border-red-500 focus:border-red-500" : "border-gray-200 focus:border-blue-500"
}`}
{...register("searchText")}
/>
<div className="absolute right-3 top-1/2 h-5 w-5 -translate-y-1/2 text-gray-400">
Expand All @@ -71,4 +74,4 @@ const SearchBar = ({ initText }: SearchBarProps) => {
);
};

export default SearchBar;
export default SearchForm;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useSearchParams } from "next/navigation";
import React, { Suspense, useMemo, useState } from "react";

import CloudSpinnerPage from "@/components/ui/CloudSpinnerPage";
import FloatingUpBtn from "@/components/ui/FloatingUpBtn";
import UniversityCards from "@/components/university/UniversityCards";

import RegionFilter from "./RegionFilter";
Expand Down Expand Up @@ -36,7 +37,7 @@ const SearchResultsContent = () => {
// URL에서 전달된 국가 목록을 기본으로 사용
const filteredCountries = countries as CountryCode[];

if (text) {
if (!lang || !countries) {
return {
isTextSearch: true,
searchText: text,
Expand All @@ -54,7 +55,7 @@ const SearchResultsContent = () => {
}
}, [searchParams, selectedRegion]);

const textSearchQuery = useGetUniversitySearchByText(searchText);
const textSearchQuery = useGetUniversitySearchByText(searchText ?? "");
const filterSearchQuery = useGetUniversitySearchByFilter(filterParams);

const { data: serachResult } = isTextSearch ? textSearchQuery : filterSearchQuery;
Expand Down Expand Up @@ -90,6 +91,7 @@ const SearchResultsContent = () => {
<UniversityCards colleges={filteredData} className="mx-5 mt-3" />
</>
)}
<FloatingUpBtn />
</div>
);
};
Expand Down
47 changes: 31 additions & 16 deletions src/app/university/application/ScorePageContent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useRouter } from "next/navigation";
import { useMemo, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";

import ConfirmCancelModal from "@/components/modal/ConfirmCancelModal";
import ButtonTab from "@/components/ui/ButtonTab";
Expand All @@ -20,8 +20,6 @@ import useGetApplicationsList from "@/api/applications/client/useGetApplications

const PREFERENCE_CHOICE: ("1순위" | "2순위" | "3순위")[] = ["1순위", "2순위", "3순위"];

// API 응답 데이터의 타입을 명확하게 정의합니다.
// 실제 ApplicationListResponse 타입을 사용하시는 것이 더 좋습니다.
interface ScoreData {
firstChoice: ScoreSheetType[];
secondChoice: ScoreSheetType[];
Expand All @@ -38,38 +36,47 @@ const ScorePageContent = () => {
const [searchValue, setSearchValue] = useState("");
const [showNeedApply, setShowNeedApply] = useState(false);

// ✨ 1. 초기 데이터를 API 응답 구조와 동일하게 설정합니다.
const initialData: ScoreData = {
firstChoice: [],
secondChoice: [],
thirdChoice: [],
};

// ✨ 2. `data`의 기본값을 위에서 정의한 initialData로 변경합니다.
const { data: scoreResponseData = initialData, isLoading } = useGetApplicationsList();
const {
data: scoreResponseData = initialData,
isError,
isLoading,
} = useGetApplicationsList({
retry: false,
});

const filteredAndSortedData = useMemo(() => {
// 데이터가 없는 경우를 대비한 방어 코드
const firstChoice = scoreResponseData?.firstChoice || [];
const secondChoice = scoreResponseData?.secondChoice || [];
const thirdChoice = scoreResponseData?.thirdChoice || [];
// ✨ 1. 대학 이름(koreanName)을 기준으로 중복을 제거하는 헬퍼 함수
const uniqueByKoreanName = (data: ScoreSheetType[]) => {
// Map을 사용해 koreanName을 키로 하여 중복을 효율적으로 제거합니다.
const universityMap = new Map(data.map((sheet) => [sheet.koreanName, sheet]));
// Map의 값들만 다시 배열로 변환하여 반환합니다.
return Array.from(universityMap.values());
};

// ✨ 2. API 응답 데이터를 받자마자 중복부터 제거합니다.
const firstChoice = uniqueByKoreanName(scoreResponseData?.firstChoice || []);
const secondChoice = uniqueByKoreanName(scoreResponseData?.secondChoice || []);
const thirdChoice = uniqueByKoreanName(scoreResponseData?.thirdChoice || []);

// 원본 데이터를 훼손하지 않기 위해 복사본을 만들어 정렬합니다.
// 3. 중복이 제거된 데이터를 정렬합니다.
const sortedData = {
// ✨ 3. `[...scoreResponseData]`가 아니라 `[...firstChoice]`로 수정합니다.
firstChoice: [...firstChoice].sort((a, b) => b.applicants.length - a.applicants.length),
secondChoice: [...secondChoice].sort((a, b) => b.applicants.length - a.applicants.length),
thirdChoice: [...thirdChoice].sort((a, b) => b.applicants.length - a.applicants.length),
};

// 필터링 로직
// 4. 기존 필터링 로직을 적용합니다.
const applyFilters = (data: ScoreSheetType[]) => {
let result = data;
// 지역 필터
if (regionFilter) {
result = result.filter((sheet) => sheet.region === regionFilter);
}
// 검색어 필터
if (searchValue) {
result = result.filter((sheet) => sheet.koreanName.toLowerCase().includes(searchValue.toLowerCase()));
}
Expand Down Expand Up @@ -116,6 +123,14 @@ const ScorePageContent = () => {
};
const scoreSheets = getScoreSheet();

useEffect(() => {
if (isLoading) return;
if (isError) {
alert("지원 현황을 불러오는 중에 오류가 발생했습니다. 지원 절차를 진행해주세요.");
router.replace("/university/application/apply");
}
}, [isError, isLoading, router]);

if (isLoading) {
return <CloudSpinnerPage />;
}
Expand Down Expand Up @@ -145,7 +160,7 @@ const ScorePageContent = () => {
style={{ padding: "10px 0 10px 18px" }}
/>

<div className="mx-5 mt-2.5 flex w-[calc(100%-44px)] flex-col gap-3 overflow-x-auto">
<div className="mx-auto mt-2.5 flex w-full flex-col gap-3 overflow-x-auto">
{scoreSheets.map((choice) => (
<ScoreSheet key={choice.koreanName} scoreSheet={choice} />
))}
Expand Down
35 changes: 23 additions & 12 deletions src/app/university/application/apply/DoneStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,31 @@ import BlockBtn from "@/components/button/BlockBtn";
const DoneStep = () => {
const router = useRouter();
return (
<div className="mt-52 flex h-full flex-col items-center">
<div className="mt-52 flex h-full flex-col items-center justify-center gap-6">
<Image src="/images/survey-complete-icon.png" width={120} height={120} alt="지원 완료" />
<div className="font-serif text-2xl font-semibold text-k-800">지원 완료</div>
<div className="text-center font-serif text-2xl font-semibold text-k-800">
학교 지원이
<br />
<span className="text-secondary">완료</span>
되었어요!
</div>

<div className="fixed bottom-14 w-full max-w-[600px] bg-white">
<div className="mb-[37px] px-5">
<BlockBtn
onClick={() => {
router.push("/");
}}
>
홈으로
</BlockBtn>
</div>
<div className="mt-10 flex w-full flex-col gap-3 px-5 pt-8">
<BlockBtn
className="bg-primary-900 text-white"
onClick={() => {
router.push("/university/application");
}}
>
지원자 현황 보기
</BlockBtn>
<BlockBtn
onClick={() => {
router.push("/");
}}
>
홈으로
</BlockBtn>
</div>
</div>
);
Expand Down
12 changes: 2 additions & 10 deletions src/app/university/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Metadata } from "next";

import TopDetailNavigation from "@/components/layout/TopDetailNavigation";

import SchoolSearchForm from "./PageContent";
import SearchBar from "./SearchBar";
import SearchResultsContent from "./SearchResultsContent";

export const metadata: Metadata = {
title: "파견 학교 목록",
Expand All @@ -13,14 +12,7 @@ const Page = async () => {
return (
<>
<TopDetailNavigation title="파견학교 검색" />
<main className="flex flex-1 flex-col p-5">
<h2 className="mb-1 text-2xl font-bold">오직 나를 위한</h2>
<h2 className="mb-6 text-2xl font-bold">맞춤 파견 학교 찾기</h2>
<div className="relative mb-4">
<SearchBar />
</div>
<SchoolSearchForm />
</main>
<SearchResultsContent />
</>
);
};
Expand Down
4 changes: 2 additions & 2 deletions src/app/university/score/submit/gpa/GpaSubmitForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ const GpaSubmitForm = () => {
<SubmitResult
title="학점 입력 완료"
description="성적 승인은 최대 3일까지 걸릴 수 있습니다."
buttonText="어학성적 입력하기"
onClick={() => router.push("/university/score/submit/language-test")}
buttonText="홈으로"
onClick={() => router.push("/")}
handleClose={() => setShowResult(false)}
infoRows={infoRows}
/>
Expand Down
20 changes: 0 additions & 20 deletions src/app/university/search-results/page.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Controller, SubmitHandler, useForm } from "react-hook-form";
import clsx from "clsx";
import { z } from "zod";

import CustomDropdown from "./CustomDropdown";
import CustomDropdown from "../CustomDropdown";

// --- 상수, 타입, 아이콘 등 ---
import {
Expand Down Expand Up @@ -62,7 +62,7 @@ const SchoolSearchForm = () => {
});

const queryString = queryParams.toString();
router.push(`/university/search-results?${queryString}`);
router.push(`/university/search?${queryString}`);
};

const availableCountries = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ interface SearchBarProps {
initText?: string;
}
// --- 폼 로직을 관리하는 부모 컴포넌트 ---
const SearchForm = ({ initText }: SearchBarProps) => {
const SearchBar = ({ initText }: SearchBarProps) => {
const router = useRouter();

const {
Expand All @@ -50,19 +50,16 @@ const SearchForm = ({ initText }: SearchBarProps) => {
}

const queryString = queryParams.toString();
console.log("검색 실행:", queryString);
router.push(`/university/search-results?${queryString}`);
router.push(`/university/search?${queryString}`);
};

return (
<form onSubmit={handleSubmit(onSubmit)} className="w-full">
<div className="relative mb-2">
<div className="relative w-full">
<input
type="text"
placeholder={"대학명을 검색해보세요..."}
className={`w-full border-b bg-white p-3 pl-4 pr-10 outline-none transition-colors ${
errors.searchText ? "border-red-500 focus:border-red-500" : "border-gray-200 focus:border-blue-500"
}`}
placeholder={"해외 파견 학교를 검색하세요."}
className="w-full rounded-lg border border-gray-200 bg-k-50 p-3 pl-4 pr-10 text-k-600 focus:border-primary"
{...register("searchText")}
/>
<div className="absolute right-3 top-1/2 h-5 w-5 -translate-y-1/2 text-gray-400">
Expand All @@ -74,4 +71,4 @@ const SearchForm = ({ initText }: SearchBarProps) => {
);
};

export default SearchForm;
export default SearchBar;
28 changes: 28 additions & 0 deletions src/app/university/search/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Metadata } from "next";

import TopDetailNavigation from "@/components/layout/TopDetailNavigation";

import SchoolSearchForm from "./PageContent";
import SearchBar from "./SearchBar";

export const metadata: Metadata = {
title: "파견 학교 목록",
};

const Page = async () => {
return (
<>
<TopDetailNavigation title="파견학교 검색" />
<main className="flex flex-1 flex-col p-5">
<h2 className="mb-1 text-2xl font-bold">오직 나를 위한</h2>
<h2 className="mb-6 text-2xl font-bold">맞춤 파견 학교 찾기</h2>
<div className="relative mb-4">
<SearchBar />
</div>
<SchoolSearchForm />
</main>
</>
);
};

export default Page;
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const ClientModal = () => {
return (
<>
{isFetching ? (
<div aria-live="polite" aria-busy="true" className="fixed bottom-40 left-1/2 z-50 -translate-x-1/2">
<div aria-live="polite" aria-busy="true" className="fixed inset-0 z-50 flex items-center justify-center">
<CloudSpinner />
</div>
) : null}
Expand Down