Skip to content

Commit 6c53f67

Browse files
authored
feat: [SC-71]학교 정보 상세 페이지 디자인 수정 (#185)
* refactor: CollegeDetail tailwind 전환 * feat: CollegeBottomSheet 디자인 변경중 * feat: CollegeBottomSheet BorderBox, BackgroundBox 추가 * feat: CollegeBottomSheet linkify 추가 * feat: CollegeBottomSheet 정보 연결
1 parent 681a72f commit 6c53f67

File tree

3 files changed

+201
-181
lines changed

3 files changed

+201
-181
lines changed

src/app/university/[id]/CollegeBottomSheet.tsx

Lines changed: 199 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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

56
import clsx from "clsx";
67

@@ -11,7 +12,7 @@ import CollegeReviews from "./CollegeReviews";
1112
import styles from "./college-bottomsheet.module.css";
1213

1314
import { Review } from "@/types/review";
14-
import { University } from "@/types/university";
15+
import { LanguageRequirement, University } from "@/types/university";
1516

1617
import {
1718
deleteUniversityFavoriteApi,
@@ -28,7 +29,7 @@ interface CollegeBottomSheetProps {
2829
}
2930

3031
const 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

Comments
 (0)