-
Notifications
You must be signed in to change notification settings - Fork 4
feat: 대학교 선택 온보딩 페이지 및 대학별 파견학교 목록 추가 #392
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
apps/web/src/app/university/list/[homeUniversityName]/SearchResultsContent.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| "use client"; | ||
|
|
||
| import { useSearchParams } from "next/navigation"; | ||
| import React, { Suspense, useMemo, useState } from "react"; | ||
| // 필요한 타입과 훅 import | ||
| import { | ||
| type UniversitySearchFilterParams, | ||
| useGetUniversitySearchByFilter, | ||
| useUniversitySearch, | ||
| } from "@/apis/universities"; | ||
| import CloudSpinnerPage from "@/components/ui/CloudSpinnerPage"; | ||
| import FloatingUpBtn from "@/components/ui/FloatingUpBtn"; | ||
| import UniversityCards from "@/components/university/UniversityCards"; | ||
| import { type CountryCode, type HomeUniversityName, type LanguageTestType, RegionEnumExtend } from "@/types/university"; | ||
| import RegionFilter from "../../RegionFilter"; | ||
| import SearchBar from "../../SearchBar"; | ||
|
|
||
| interface SearchResultsContentInnerProps { | ||
| homeUniversityName: HomeUniversityName; | ||
| } | ||
|
|
||
| // --- URL 파라미터를 읽고 데이터를 처리하는 메인 컨텐츠 --- | ||
| const SearchResultsContentInner = ({ homeUniversityName }: SearchResultsContentInnerProps) => { | ||
| const searchParams = useSearchParams(); | ||
|
|
||
| // 지역 상태 관리 | ||
| const [selectedRegion, setSelectedRegion] = useState<RegionEnumExtend>(RegionEnumExtend.ALL); | ||
| // 지역 변경 핸들러 | ||
| const handleRegionChange = (region: RegionEnumExtend) => { | ||
| setSelectedRegion(region); | ||
| }; | ||
|
|
||
| const { isTextSearch, searchText, filterParams } = useMemo(() => { | ||
| const text = searchParams.get("searchText"); | ||
| const lang = searchParams.get("languageTestType"); | ||
| const countries = searchParams.getAll("countryCode"); | ||
|
|
||
| // URL에서 전달된 국가 목록을 기본으로 사용 | ||
| const filteredCountries = countries as CountryCode[]; | ||
|
|
||
| if (!lang || !countries) { | ||
| return { | ||
| isTextSearch: true, | ||
| searchText: text, | ||
| filterParams: {} as UniversitySearchFilterParams, | ||
| }; | ||
| } | ||
| return { | ||
| isTextSearch: false, | ||
| searchText: "", | ||
| filterParams: { | ||
| languageTestType: (lang as LanguageTestType) || undefined, | ||
| countryCode: filteredCountries.length > 0 ? filteredCountries : undefined, | ||
| }, | ||
| }; | ||
| }, [searchParams]); | ||
|
|
||
| const textSearchQuery = useUniversitySearch(searchText ?? "", homeUniversityName); | ||
| const filterSearchQuery = useGetUniversitySearchByFilter(filterParams, homeUniversityName); | ||
|
|
||
| const { data: searchResult } = isTextSearch ? textSearchQuery : filterSearchQuery; | ||
|
|
||
| // homeUniversityName과 지역 필터링된 데이터 | ||
| const filteredData = useMemo(() => { | ||
| if (!searchResult) return searchResult; | ||
|
|
||
| let filtered = searchResult; | ||
|
|
||
| // 지역 필터링 | ||
| if (selectedRegion !== RegionEnumExtend.ALL) { | ||
| filtered = filtered.filter((university) => university.region === selectedRegion); | ||
| } | ||
|
|
||
| return filtered; | ||
| }, [searchResult, selectedRegion]); | ||
|
|
||
| // 초기 URL에서 지역 파라미터 읽기 | ||
| React.useEffect(() => { | ||
| const region = searchParams.get("region"); | ||
| if (region && Object.values(RegionEnumExtend).includes(region as RegionEnumExtend)) { | ||
| setSelectedRegion(region as RegionEnumExtend); | ||
| } | ||
| }, [searchParams]); | ||
|
|
||
| return ( | ||
| <div className="pt-4"> | ||
| <SearchBar initText={searchText || undefined} /> | ||
|
|
||
| {/* 지역 필터 */} | ||
| <RegionFilter selectedRegion={selectedRegion} onRegionChange={handleRegionChange} /> | ||
|
|
||
| {/* 결과 표시 */} | ||
| {!filteredData || filteredData.length === 0 ? ( | ||
| <div className="p-5 text-center text-gray-500">검색 결과가 없습니다.</div> | ||
| ) : ( | ||
| <UniversityCards colleges={filteredData} className="mt-3" /> | ||
| )} | ||
| <FloatingUpBtn /> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| interface SearchResultsContentProps { | ||
| homeUniversityName: HomeUniversityName; | ||
| } | ||
|
|
||
| export default function SearchResultsContent({ homeUniversityName }: SearchResultsContentProps) { | ||
| return ( | ||
| <Suspense fallback={<CloudSpinnerPage />}> | ||
| <SearchResultsContentInner homeUniversityName={homeUniversityName} /> | ||
| </Suspense> | ||
| ); | ||
| } | ||
59 changes: 59 additions & 0 deletions
59
apps/web/src/app/university/list/[homeUniversityName]/page.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import type { Metadata } from "next"; | ||
| import { notFound } from "next/navigation"; | ||
|
|
||
| import TopDetailNavigation from "@/components/layout/TopDetailNavigation"; | ||
| import { HOME_UNIVERSITIES, HOME_UNIVERSITY_SLUG, type HomeUniversityName } from "@/types/university"; | ||
|
|
||
| import SearchResultsContent from "./SearchResultsContent"; | ||
|
|
||
| // ISR: 정적 페이지 생성 | ||
| export const revalidate = false; | ||
|
|
||
| // 정적 경로 생성 (ISR) | ||
| export async function generateStaticParams() { | ||
| return HOME_UNIVERSITIES.map((university) => ({ | ||
| homeUniversityName: university.slug, | ||
| })); | ||
| } | ||
|
|
||
| type PageProps = { | ||
| params: Promise<{ homeUniversityName: string }>; | ||
| }; | ||
|
|
||
| export async function generateMetadata({ params }: PageProps): Promise<Metadata> { | ||
| const { homeUniversityName } = await params; | ||
|
|
||
| const universityName = HOME_UNIVERSITY_SLUG[homeUniversityName]; | ||
|
|
||
| if (!universityName) { | ||
| return { | ||
| title: "파견 학교 목록", | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| title: `${universityName} 파견 학교 목록 | 솔리드커넥션`, | ||
| description: `${universityName}에서 파견 가능한 교환학생 대학교 목록입니다. 지역별, 어학 요건별로 검색하고 관심있는 대학을 찾아보세요.`, | ||
| }; | ||
| } | ||
|
|
||
| const UniversityListPage = async ({ params }: PageProps) => { | ||
| const { homeUniversityName } = await params; | ||
|
|
||
| const universityName = HOME_UNIVERSITY_SLUG[homeUniversityName] as HomeUniversityName | undefined; | ||
|
|
||
| if (!universityName) { | ||
| notFound(); | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <TopDetailNavigation title={`${universityName} 파견학교`} /> | ||
| <div className="mt-14 w-full px-5"> | ||
| <SearchResultsContent homeUniversityName={universityName} /> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default UniversityListPage; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,61 @@ | ||
| import type { Metadata } from "next"; | ||
| import Image from "next/image"; | ||
| import Link from "next/link"; | ||
|
|
||
| import TopDetailNavigation from "@/components/layout/TopDetailNavigation"; | ||
|
|
||
| import SearchResultsContent from "./SearchResultsContent"; | ||
| import CheveronRightFilled from "@/components/ui/icon/ChevronRightFilled"; | ||
| import { HOME_UNIVERSITIES } from "@/types/university"; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: "파견 학교 목록", | ||
| title: "파견 학교 목록 | 대학교 선택", | ||
| description: | ||
| "교환학생 파견 대학을 선택하세요. 인하대학교, 인천대학교, 성신여자대학교의 교환학생 프로그램 정보를 확인할 수 있습니다.", | ||
| }; | ||
|
|
||
| const Page = async () => { | ||
| // ISR: 정적 페이지 생성 | ||
| export const revalidate = false; | ||
|
|
||
| const UniversityOnboardingPage = () => { | ||
| return ( | ||
| <> | ||
| <TopDetailNavigation title="파견학교 검색" /> | ||
| <div className="w-full px-5"> | ||
| <SearchResultsContent /> | ||
| <TopDetailNavigation title="대학교 선택" /> | ||
| <div className="mt-14 w-full px-5 py-6"> | ||
| <h1 className="mb-2 text-k-800 typo-bold-1">파견 대학교를 선택해주세요</h1> | ||
| <p className="mb-6 text-k-500 typo-medium-4"> | ||
| 소속 대학교를 선택하면 해당 대학교의 교환학생 파견 정보를 확인할 수 있습니다. | ||
| </p> | ||
|
|
||
| <div className="flex flex-col gap-2.5"> | ||
| {HOME_UNIVERSITIES.map((university) => ( | ||
| <Link key={university.slug} href={`/university/list/${university.slug}`} className="block"> | ||
| <div className="relative h-[91px] w-full overflow-hidden rounded-lg border border-solid border-k-100 hover:-translate-y-0.5 hover:shadow-md hover:shadow-black/10"> | ||
| <div className="flex justify-between px-5 py-3.5"> | ||
| <div className="flex gap-[23.5px]"> | ||
| <div className="flex flex-shrink-0 items-center"> | ||
| <Image | ||
| src={university.imageUrl} | ||
| alt={`${university.name} 이미지`} | ||
| width={56} | ||
| height={56} | ||
| className="h-14 w-14 rounded-full object-cover" | ||
| /> | ||
| </div> | ||
| <div className="flex flex-col justify-center"> | ||
| <h2 className="text-k-700 typo-bold-4">{university.name}</h2> | ||
| <p className="text-k-500 typo-medium-4">{university.description}</p> | ||
| </div> | ||
| </div> | ||
| <div className="flex items-center"> | ||
| <CheveronRightFilled color="black" opacity="0.54" /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </Link> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default Page; | ||
| export default UniversityOnboardingPage; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1. 배열 falsy 체크 버그
countries는searchParams.getAll()의 반환값으로 항상 배열입니다. 빈 배열[]도 JavaScript에서 truthy이므로!countries조건은 항상false입니다.현재 로직에서는
lang만 없으면 텍스트 검색 모드로 전환되는데, 의도한 동작인지 확인이 필요합니다.🐛 수정 제안
🤖 Prompt for AI Agents