11"use client" ;
22
3- import { useEffect , useRef , useState } from "react" ;
3+ import React , { forwardRef , useEffect , useRef , useState } from "react" ;
4+ import ReactLinkify from "react-linkify" ;
45
56import clsx from "clsx" ;
67
@@ -11,7 +12,7 @@ import CollegeReviews from "./CollegeReviews";
1112import styles from "./college-bottomsheet.module.css" ;
1213
1314import { Review } from "@/types/review" ;
14- import { University } from "@/types/university" ;
15+ import { LanguageRequirement , University } from "@/types/university" ;
1516
1617import {
1718 deleteUniversityFavoriteApi ,
@@ -28,7 +29,7 @@ interface CollegeBottomSheetProps {
2829}
2930
3031const CollegeBottomSheet = ( { collegeId, convertedKoreanName, reviewList, university } : CollegeBottomSheetProps ) => {
31- const pages : string [ ] = [ "학교정보 " , "어학성적 " , "지원전공 " , "위치 " , "파견후기" ] ;
32+ const pages : string [ ] = [ "어학성적 " , "학교정보 " , "지원정보 " , "지역정보 " , "파견후기" ] ;
3233 const [ activeTab , setActiveTab ] = useState < string > ( "학교정보" ) ;
3334 const sectionRefs = [
3435 useRef < HTMLDivElement > ( null ) ,
@@ -137,162 +138,211 @@ const CollegeBottomSheet = ({ collegeId, convertedKoreanName, reviewList, univer
137138 borderColor = "var(--primary-2, #091F5B)"
138139 />
139140
140- { /* 학교정보 */ }
141- < div className = { styles . scrollOffset } style = { { paddingTop : "123px" } } ref = { sectionRefs [ 0 ] } >
142- < div className = { styles . item } >
143- < div className = { "ml-5 font-serif text-base font-semibold" } > 홈페이지</ div >
144- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
145- < a className = "break-words" href = { university . homepageUrl || "" } target = "_blank" rel = "noreferrer" >
146- { university . homepageUrl || "홈페이지 없음" }
147- </ a >
148- </ div >
149- </ div >
150- < div className = { styles . item } >
151- < div className = { "ml-5 font-serif text-base font-semibold" } > 기숙사</ div >
141+ < LanguageSection
142+ ref = { sectionRefs [ 0 ] }
143+ languageRequirements = { university . languageRequirements || [ ] }
144+ detailsForLanguage = { university . detailsForLanguage }
145+ />
146+ < BasicInfoSection
147+ ref = { sectionRefs [ 1 ] }
148+ homepageUrl = { university . homepageUrl }
149+ region = { university . region }
150+ country = { university . country }
151+ studentCapacity = { university . studentCapacity }
152+ englishName = { university . englishName }
153+ />
154+ < ApplyInfoSection
155+ ref = { sectionRefs [ 2 ] }
156+ semesterAvailableForDispatch = { university . semesterAvailableForDispatch }
157+ semesterRequirement = { university . semesterRequirement }
158+ gpaRequirement = { university . gpaRequirement }
159+ gpaRequirementCriteria = { university . gpaRequirementCriteria }
160+ detailsForAccommodation = { university . detailsForAccommodation }
161+ detailsForMajor = { university . detailsForMajor }
162+ englishCourseUrl = { university . englishCourseUrl }
163+ />
164+ < RegionInfoSection ref = { sectionRefs [ 3 ] } detailsForLocal = { university . detailsForLocal } />
165+ < ReviewSection ref = { sectionRefs [ 4 ] } />
166+ </ div >
167+ </ >
168+ ) ;
169+ } ;
152170
153- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
154- { university . accommodationUrl && (
155- < >
156- < a
157- href = { university . accommodationUrl }
158- target = "_blank"
159- rel = "noreferrer"
160- className = "break-words font-serif text-sm font-normal leading-normal"
161- >
162- { university . accommodationUrl }
163- </ a >
164- < br />
165- </ >
166- ) }
167- < div
168- dangerouslySetInnerHTML = { {
169- __html : university . detailsForAccommodation || "기숙사 추가 정보 없음" ,
170- } }
171- />
172- </ div >
173- </ div >
174- < div className = { styles . item } >
175- < div className = { "ml-5 font-serif text-base font-semibold" } > 파견 대학 위치</ div >
176- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
177- { university . region } { university . country }
178- </ div >
179- </ div >
180- < div className = { styles . item } >
181- < div className = { "ml-5 font-serif text-base font-semibold" } > 지역정보</ div >
182- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
183- < div dangerouslySetInnerHTML = { { __html : university . detailsForLocal || "지역정보 없음" } } />
184- </ div >
171+ export default CollegeBottomSheet ;
172+
173+ const LanguageSection = forwardRef <
174+ HTMLDivElement ,
175+ { languageRequirements : LanguageRequirement [ ] ; detailsForLanguage : string }
176+ > ( function LanguageSection ( { languageRequirements, detailsForLanguage } , ref ) {
177+ return (
178+ < div ref = { ref } className = "mx-5 mt-5 flex flex-col gap-4" >
179+ < div className = "flex gap-2" >
180+ { languageRequirements . map ( ( requirement , index ) => (
181+ < div key = { index } className = "box-border flex h-[30px] flex-col justify-center rounded-sm bg-primary px-2" >
182+ < span className = "text-[11px] font-semibold leading-normal text-k-0" >
183+ { requirement . languageTestType } { requirement . minScore }
184+ </ span >
185185 </ div >
186- </ div >
186+ ) ) }
187+ </ div >
188+ < BackgroundBox subject = "어학세부 요건" content = { detailsForLanguage || "정보 없음" } linkify = { true } />
189+ </ div >
190+ ) ;
191+ } ) ;
187192
188- { /* 어학성적 */ }
189- < div className = { styles . bar } >
190- { university . languageRequirements . map ( ( language ) => (
191- < div key = { language . languageTestType } >
192- { language . languageTestType . replace ( / _ / g, " " ) } { language . minScore }
193- </ div >
194- ) ) }
193+ const BasicInfoSection = forwardRef <
194+ HTMLDivElement ,
195+ {
196+ homepageUrl : string ;
197+ region : string ;
198+ country : string ;
199+ studentCapacity : number ;
200+ englishName : string ;
201+ }
202+ > ( function BasicInfoSection ( { homepageUrl, region, country, studentCapacity, englishName } , ref ) {
203+ return (
204+ < div ref = { ref } className = "flex flex-col gap-3 px-5 pt-8" >
205+ < BorderBox subject = "홈페이지" content = { homepageUrl || "정보 없음" } linkify = { true } />
206+ < div className = "flex gap-2.5" >
207+ < BorderBox className = "flex-1" subject = "파견 대학 위치" content = { region + " " + country || "정보 없음" } />
208+ < BorderBox
209+ className = "flex-1"
210+ subject = "선발인원"
211+ content = { studentCapacity ? `${ studentCapacity } 명` : "정보 없음" }
212+ />
213+ </ div >
214+ < div className = { clsx ( "flex flex-col gap-2.5 rounded-lg bg-k-50 p-2.5" ) } >
215+ < span className = "text-base font-semibold text-k-900" > 파견학교 위치</ span >
216+ < span className = "text-sm font-medium leading-normal text-k-600" >
217+ < GoogleEmbedMap width = "100%" height = "300px" name = { englishName } style = { { border : "0" } } />
218+ </ span >
219+ </ div >
220+ </ div >
221+ ) ;
222+ } ) ;
223+
224+ const ApplyInfoSection = forwardRef <
225+ HTMLDivElement ,
226+ {
227+ semesterAvailableForDispatch : string ;
228+ semesterRequirement : string ;
229+ gpaRequirement : string ;
230+ gpaRequirementCriteria : string ;
231+ detailsForAccommodation : string ;
232+ detailsForMajor : string ;
233+ englishCourseUrl : string ;
234+ }
235+ > ( function ApplyInfoSection (
236+ {
237+ semesterAvailableForDispatch = "정보 없음" ,
238+ semesterRequirement = "정보 없음" ,
239+ gpaRequirement = "정보 없음" ,
240+ gpaRequirementCriteria = "정보 없음" ,
241+ detailsForAccommodation = "정보 없음" ,
242+ detailsForMajor = "정보 없음" ,
243+ englishCourseUrl = "정보 없음" ,
244+ } ,
245+ ref ,
246+ ) {
247+ return (
248+ < div ref = { ref } className = "px-5 pt-8" >
249+ < div className = "flex flex-col gap-4" >
250+ < div className = "flex gap-2.5" >
251+ < BorderBox className = "flex-1" subject = "파견 가능 학기" content = { semesterAvailableForDispatch } />
252+ < BorderBox className = "flex-1" subject = "최저이수학기" content = { semesterRequirement } />
195253 </ div >
196- < div className = { styles . scrollOffsetWithBar } ref = { sectionRefs [ 1 ] } >
197- < div className = { styles . item } >
198- < div className = { "ml-5 font-serif text-base font-semibold" } > 어학 세부요건 </ div >
199- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
200- < div dangerouslySetInnerHTML = { { __html : university . detailsForLanguage || "어학 세부요건 없음" } } />
201- </ div >
202- </ div >
254+ < div className = "flex gap-2.5" >
255+ < BorderBox
256+ className = "min-w-36 flex-1"
257+ subject = "최저성적/기준성적"
258+ content = { gpaRequirement + "/" + gpaRequirementCriteria }
259+ / >
260+ < BorderBox className = "flex-[2]" subject = "기숙사" content = { detailsForAccommodation } / >
203261 </ div >
262+ < BackgroundBox className = "min-h-[150px]" subject = "전공 상세" content = { detailsForMajor } />
263+ < BackgroundBox className = "min-h-[150px]" subject = "영어 강의 리스트" content = { englishCourseUrl } />
264+ </ div >
265+ </ div >
266+ ) ;
267+ } ) ;
204268
205- { /* 지원전공 */ }
206- < div className = { styles . scrollOffset } ref = { sectionRefs [ 2 ] } >
207- < div className = { styles . item } >
208- < div className = { "ml-5 font-serif text-base font-semibold" } > 선발 인원</ div >
209- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
210- { university . studentCapacity || "?" } 명
211- </ div >
212- </ div >
213- < div className = { styles . item } >
214- < div className = { "ml-5 font-serif text-base font-semibold" } > 등록금 납부 유형</ div >
215- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
216- { university . tuitionFeeType || "등록금 납부 유형 정보 없음" }
217- </ div >
218- </ div >
219- < div className = { styles . item } >
220- < div className = { "ml-5 font-serif text-base font-semibold" } > 파견가능학기</ div >
221- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
222- { university . semesterAvailableForDispatch || "파견가능학기 정보 없음" }
223- </ div >
224- </ div >
225- < div className = { styles . item } >
226- < div className = { "ml-5 font-serif text-base font-semibold" } > 최저이수학기</ div >
227- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
228- { university . semesterRequirement || "최저이수학기 정보 없음" }
229- </ div >
230- </ div >
231- < div className = { styles . item } >
232- < div className = { "ml-5 font-serif text-base font-semibold" } > 최저성적 / 기준 성적</ div >
233- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
234- { university . gpaRequirement
235- ? `${ university . gpaRequirement } / ${ university . gpaRequirementCriteria } `
236- : "없음" }
237- </ div >
238- </ div >
239- < div className = { styles . item } >
240- < div className = { "ml-5 font-serif text-base font-semibold" } > 전공 상세</ div >
241- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
242- < div dangerouslySetInnerHTML = { { __html : university . detailsForMajor || "전공 상세 정보 없음" } } />
243- </ div >
244- </ div >
245-
246- < div className = { styles . item } >
247- < div className = { "ml-5 font-serif text-base font-semibold" } > 영어강의 리스트</ div >
269+ const RegionInfoSection = forwardRef <
270+ HTMLDivElement ,
271+ {
272+ detailsForLocal : string ;
273+ }
274+ > ( function RegionInfoSection ( { detailsForLocal = "지역 정보가 없습니다." } , ref ) {
275+ return (
276+ < div ref = { ref } className = "flex flex-col gap-4 px-5 pt-8" >
277+ { /* <div>사진이 여기 들어갑니다</div> */ }
278+ < BackgroundBox className = "min-h-[150px]" subject = "지역 정보" content = { detailsForLocal } />
279+ </ div >
280+ ) ;
281+ } ) ;
248282
249- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
250- { university . englishCourseUrl && (
251- < >
252- < a
253- href = { university . englishCourseUrl }
254- target = "_blank"
255- rel = "noreferrer"
256- className = "break-words font-serif text-sm font-normal leading-normal"
257- >
258- { university . englishCourseUrl }
259- </ a >
260- < br />
261- </ >
262- ) }
263- < div
264- dangerouslySetInnerHTML = { { __html : university . detailsForEnglishCourse || "영어강의 추가 정보 없음" } }
265- />
266- </ div >
267- </ div >
268- < div className = { styles . item } >
269- < div className = { "ml-5 font-serif text-base font-semibold" } > 비고</ div >
270- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
271- < div dangerouslySetInnerHTML = { { __html : university . details || "비고 없음" } } />
272- </ div >
273- </ div >
274- </ div >
283+ const ReviewSection = forwardRef < HTMLDivElement , { } > ( function ReviewSection ( { } , ref ) {
284+ return (
285+ < div ref = { ref } className = "px-5 pt-8" >
286+ < span className = "text-base font-semibold text-k-900" > 생생한 후기</ span >
287+ < div className = "h-40" > </ div >
288+ </ div >
289+ ) ;
290+ } ) ;
275291
276- { /* 위치 */ }
277- < div className = { styles . scrollOffset } ref = { sectionRefs [ 3 ] } >
278- < div className = { styles . item } >
279- < div className = { styles . title } > 파견학교 위치</ div >
280- < div className = "mx-5 mt-2.5 font-serif text-sm font-normal leading-normal" >
281- < GoogleEmbedMap width = "100%" height = "204" style = { { border : 0 } } name = { university . englishName } />
282- </ div >
283- </ div >
292+ const BorderBox = ( {
293+ subject,
294+ content,
295+ linkify = false ,
296+ className,
297+ } : {
298+ subject : string ;
299+ content : string ;
300+ linkify ?: boolean ;
301+ className ?: string ;
302+ } ) => {
303+ if ( linkify ) {
304+ return (
305+ < ReactLinkify >
306+ < div className = { clsx ( "flex flex-col gap-2.5 rounded-lg border border-secondary p-2.5" , className ) } >
307+ < span className = "text-base font-semibold text-k-900" > { subject } </ span >
308+ < span className = "text-sm font-medium leading-normal text-k-600" > { content } </ span >
284309 </ div >
310+ </ ReactLinkify >
311+ ) ;
312+ }
313+ return (
314+ < div className = { clsx ( "flex flex-col gap-2.5 rounded-lg border border-secondary p-2.5" , className ) } >
315+ < span className = "text-base font-semibold text-k-900" > { subject } </ span >
316+ < span className = "text-sm font-medium leading-normal text-k-600" > { content } </ span >
317+ </ div >
318+ ) ;
319+ } ;
285320
286- { /* 파견후기 */ }
287- < div className = { styles . scrollOffset } ref = { sectionRefs [ 4 ] } >
288- < div className = { styles . item } style = { { marginBottom : "30px" } } >
289- < div className = { styles . title } > 생생한 후기</ div >
290- < CollegeReviews style = { { marginTop : "10px" } } reviewList = { reviewList } />
291- </ div >
321+ const BackgroundBox = ( {
322+ subject,
323+ content,
324+ linkify = true ,
325+ className,
326+ } : {
327+ subject : string ;
328+ content : string ;
329+ linkify ?: boolean ;
330+ className ?: string ;
331+ } ) => {
332+ if ( linkify ) {
333+ return (
334+ < ReactLinkify >
335+ < div className = { clsx ( "flex flex-col gap-2.5 rounded-lg bg-k-50 p-2.5" , className ) } >
336+ < span className = "text-base font-semibold text-k-900" > { subject } </ span >
337+ < span className = "text-sm font-medium leading-normal text-k-600" > { content } </ span >
292338 </ div >
293- </ div >
294- </ >
339+ </ ReactLinkify >
340+ ) ;
341+ }
342+ return (
343+ < div className = { clsx ( "flex flex-col gap-2.5 rounded-lg bg-k-50 p-2.5" , className ) } >
344+ < span className = "text-base font-semibold text-k-900" > { subject } </ span >
345+ < span className = "text-sm font-medium leading-normal text-k-600" > { content } </ span >
346+ </ div >
295347 ) ;
296348} ;
297-
298- export default CollegeBottomSheet ;
0 commit comments