diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69dd578..f21b86a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,6 @@ jobs: NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }} NEXT_PUBLIC_OAUTH2=${{ secrets.NEXT_PUBLIC_OAUTH2 }} secrets: | - npmrc=${{ secrets.NPM_TOKEN }} + NPM_TOKEN=${{ secrets.NPM_TOKEN }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/Docker-pinhouse-file b/Docker-pinhouse-file index 14d209f..09e6dbd 100644 --- a/Docker-pinhouse-file +++ b/Docker-pinhouse-file @@ -10,8 +10,9 @@ WORKDIR /pinhouse-fe # 2️⃣ Dependencies # ========================= FROM base AS deps -COPY package.json package-lock.json ./ -RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci +COPY package.json package-lock.json .npmrc ./ +RUN --mount=type=secret,id=NPM_TOKEN \ + NPM_TOKEN="$(cat /run/secrets/NPM_TOKEN)" npm ci # ========================= # 3️⃣ Build diff --git a/app/eligibility/page.tsx b/app/eligibility/page.tsx index b6a01dc..8124988 100644 --- a/app/eligibility/page.tsx +++ b/app/eligibility/page.tsx @@ -1,7 +1,7 @@ "use client"; import { Suspense, useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { EligibilitySection } from "@/src/widgets/eligibilitySection"; import { useEligibilityStore } from "@/src/features/eligibility/model/eligibilityStore"; import { useDiagnosisResultStore } from "@/src/features/eligibility/model/diagnosisResultStore"; @@ -12,6 +12,8 @@ import { Spinner } from "@/src/shared/ui/spinner/default"; export default function EligibilityPage() { const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); const reset = useEligibilityStore(state => state.reset); const setDiagnosisResult = useDiagnosisResultStore(s => s.setResult); const [isModalOpen, setIsModalOpen] = useState(false); @@ -33,7 +35,12 @@ export default function EligibilityPage() { }, { incomeLevel: data.myIncomeLevel } ); - setIsModalOpen(true); + // 주소가 정확히 /eligibility 일 때만 모달 표시 (쿼리 있으면 표시 안 함) + const isEligibilityOnly = + pathname === "/eligibility" && searchParams.toString() === ""; + if (isEligibilityOnly) { + setIsModalOpen(true); + } } else { reset(); } @@ -46,7 +53,7 @@ export default function EligibilityPage() { return () => { mounted = false; }; - }, [reset, setDiagnosisResult]); + }, [pathname, reset, searchParams, setDiagnosisResult]); const handleModalButtonClick = (index: number) => { setIsModalOpen(false); diff --git a/app/eligibility/school/page.tsx b/app/eligibility/school/page.tsx new file mode 100644 index 0000000..c7e4bcb --- /dev/null +++ b/app/eligibility/school/page.tsx @@ -0,0 +1,35 @@ +"use client"; + +import { useSchoolSearch } from "@/src/features/eligibility/hooks/useSchoolSearch"; +import { + SchoolSearchHeader, + SchoolSearchFormSection, + SchoolSearchResultButtonSection, +} from "@/src/features/eligibility/ui/school"; + +export default function SchoolSearchPage() { + const { + keyword, + setKeyword, + options, + hasKeyword, + handleSelect, + handleResultClick, + } = useSchoolSearch(); + + return ( +
+ + + +
+ ); +} diff --git a/app/eligibility/school/result/page.tsx b/app/eligibility/school/result/page.tsx new file mode 100644 index 0000000..653c6fa --- /dev/null +++ b/app/eligibility/school/result/page.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { useSchoolSearchResult } from "@/src/features/eligibility/hooks/useSchoolSearchResult"; +import { + SchoolResultHeader, + SchoolResultContentSection, + SchoolResultTryAgainSection, +} from "@/src/features/eligibility/ui/school"; + +export default function SchoolResultPage() { + const { keyword, message, isEligible, handleTryAgain } = useSchoolSearchResult(); + + return ( +
+ + + +
+ ); +} diff --git a/src/assets/images/eligibility/shcoolSearchEmptyImg.tsx b/src/assets/images/eligibility/shcoolSearchEmptyImg.tsx new file mode 100644 index 0000000..fb8564d --- /dev/null +++ b/src/assets/images/eligibility/shcoolSearchEmptyImg.tsx @@ -0,0 +1,115 @@ +const SchoolSearchEmptyImg = () => { + return ( + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + + + + + + + + + + + + + + +
+ ); +}; + +export default SchoolSearchEmptyImg; diff --git a/src/assets/images/eligibility/shcoolSearchFillImg.tsx b/src/assets/images/eligibility/shcoolSearchFillImg.tsx new file mode 100644 index 0000000..a92352d --- /dev/null +++ b/src/assets/images/eligibility/shcoolSearchFillImg.tsx @@ -0,0 +1,139 @@ +const SchoolSearchFillImg = () => { + return ( + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +export default SchoolSearchFillImg; diff --git a/src/assets/images/svgFile/eligibility/shcoolSearchEmptyImg.svg b/src/assets/images/svgFile/eligibility/shcoolSearchEmptyImg.svg new file mode 100644 index 0000000..b79ad11 --- /dev/null +++ b/src/assets/images/svgFile/eligibility/shcoolSearchEmptyImg.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + +
diff --git a/src/assets/images/svgFile/eligibility/shcoolSearchFillImg.svg b/src/assets/images/svgFile/eligibility/shcoolSearchFillImg.svg new file mode 100644 index 0000000..856c989 --- /dev/null +++ b/src/assets/images/svgFile/eligibility/shcoolSearchFillImg.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/features/eligibility/api/schoolUnivSearchApi.ts b/src/features/eligibility/api/schoolUnivSearchApi.ts new file mode 100644 index 0000000..a4ca625 --- /dev/null +++ b/src/features/eligibility/api/schoolUnivSearchApi.ts @@ -0,0 +1,49 @@ +import { http } from "@/src/shared/api/http"; +import { + UNIV_SEARCH_ENDPOINT, + SCHOOL_SEARCH_ENDPOINT, + UNIV_AVAILABILITY_ENDPOINT, + SCHOOL_AVAILABILITY_ENDPOINT, +} from "@/src/shared/api/endpoints"; +import type { IResponse } from "@/src/shared/types/response"; + +/** /univ/search, /school/search 응답 항목 */ +export interface SchoolUnivSearchItem { + id: string; + name: string; + campusType: string; + collegeType: string; +} + +type SearchResponse = IResponse; + +/** GET /univ/search - 대학교 검색 */ +export async function getUnivSearch(keyword: string): Promise { + return http.get(UNIV_SEARCH_ENDPOINT, { + keyword: keyword.trim() || undefined, + }); +} + +/** GET /school/search - 고등학교 검색 */ +export async function getSchoolSearch(keyword: string): Promise { + return http.get(SCHOOL_SEARCH_ENDPOINT, { + keyword: keyword.trim() || undefined, + }); +} + +/** 가능여부 응답: data에 안내 문구 문자열 */ +type AvailabilityResponse = IResponse; + +/** GET /univ - 대학교 공공임대주택 지원 가능 여부 */ +export async function getUnivAvailability(keyword: string): Promise { + return http.get(UNIV_AVAILABILITY_ENDPOINT, { + keyword, + }); +} + +/** GET /school - 고등학교 공공임대주택 지원 가능 여부 */ +export async function getSchoolAvailability(keyword: string): Promise { + return http.get(SCHOOL_AVAILABILITY_ENDPOINT, { + keyword, + }); +} diff --git a/src/features/eligibility/hooks/useSchoolSearch.ts b/src/features/eligibility/hooks/useSchoolSearch.ts new file mode 100644 index 0000000..6424c39 --- /dev/null +++ b/src/features/eligibility/hooks/useSchoolSearch.ts @@ -0,0 +1,113 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { useRouter } from "next/navigation"; +import { useDebounce } from "@/src/shared/hooks/useDebounce/useDebounce"; +import type { SearchBarOption } from "@/src/shared/ui/searchBar/type"; +import { + getUnivSearch, + getSchoolSearch, + getUnivAvailability, + getSchoolAvailability, +} from "@/src/features/eligibility/api/schoolUnivSearchApi"; +import { useSchoolSearchResultStore } from "@/src/features/eligibility/model/schoolSearchResultStore"; + +function toSearchBarOption(item: { + id: string; + name: string; + collegeType: string; +}): SearchBarOption { + return { + key: item.id, + value: item.name, + description: item.collegeType, + }; +} + +const DEBOUNCE_MS = 300; + +const RESULT_PATH = "/eligibility/school/result"; + +export function useSchoolSearch() { + const router = useRouter(); + const setResult = useSchoolSearchResultStore(s => s.setResult); + const [keyword, setKeywordState] = useState(""); + const [selectedSchoolId, setSelectedSchoolId] = useState(null); + const [options, setOptions] = useState([]); + const debouncedKeyword = useDebounce(keyword, DEBOUNCE_MS); + const hasKeyword = keyword.trim().length > 0; + const skipNextSearchRef = useRef(false); + + const setKeyword = useCallback((value: string) => { + setKeywordState(value); + setSelectedSchoolId(null); + }, []); + + useEffect(() => { + const q = debouncedKeyword.trim(); + if (!q) { + setOptions([]); + return; + } + if (skipNextSearchRef.current) { + skipNextSearchRef.current = false; + setOptions([]); + return; + } + let cancelled = false; + (async () => { + try { + const univRes = await getUnivSearch(q); + if (cancelled) return; + const schoolRes = await getSchoolSearch(q); + if (cancelled) return; + const univList = univRes.data ?? []; + const schoolList = schoolRes.data ?? []; + const merged = [...univList.map(toSearchBarOption), ...schoolList.map(toSearchBarOption)]; + setOptions(merged); + } catch { + if (!cancelled) setOptions([]); + } + })(); + return () => { + cancelled = true; + }; + }, [debouncedKeyword]); + + const handleSelect = (option: SearchBarOption) => { + skipNextSearchRef.current = true; + setKeywordState(option.value); + setSelectedSchoolId(option.key); + setOptions([]); + }; + + const handleResultClick = useCallback(async () => { + if (!hasKeyword) return; + const q = keyword.trim(); + if (!q) return; + const ELIGIBLE_KEYWORD = "가능"; + try { + const [univRes, schoolRes] = await Promise.all([ + getUnivAvailability(q), + getSchoolAvailability(q), + ]); + const univMsg = typeof univRes.data === "string" ? univRes.data : null; + const schoolMsg = typeof schoolRes.data === "string" ? schoolRes.data : null; + const successMessage = [univMsg, schoolMsg].find(m => m?.includes(ELIGIBLE_KEYWORD)) ?? null; + const message = successMessage ?? univMsg ?? schoolMsg ?? null; + setResult(keyword.trim(), message); + router.push(RESULT_PATH); + } catch { + setResult(keyword.trim(), null); + router.push(RESULT_PATH); + } + }, [hasKeyword, keyword, setResult, router]); + + return { + keyword, + setKeyword, + options, + hasKeyword, + hasSelection: selectedSchoolId != null, + handleSelect, + handleResultClick, + }; +} diff --git a/src/features/eligibility/hooks/useSchoolSearchResult.ts b/src/features/eligibility/hooks/useSchoolSearchResult.ts new file mode 100644 index 0000000..f41b1c4 --- /dev/null +++ b/src/features/eligibility/hooks/useSchoolSearchResult.ts @@ -0,0 +1,24 @@ +import { useCallback } from "react"; +import { useRouter } from "next/navigation"; +import { useSchoolSearchResultStore } from "@/src/features/eligibility/model/schoolSearchResultStore"; + +const ELIGIBLE_KEYWORD = "가능"; +const SCHOOL_SEARCH_PATH = "/eligibility/school"; + +export function useSchoolSearchResult() { + const router = useRouter(); + const { keyword, message, clear } = useSchoolSearchResultStore(); + const isEligible = message != null && message.includes(ELIGIBLE_KEYWORD); + + const handleTryAgain = useCallback(() => { + clear(); + router.push(SCHOOL_SEARCH_PATH); + }, [clear, router]); + + return { + keyword, + message, + isEligible, + handleTryAgain, + }; +} diff --git a/src/features/eligibility/model/eligibilityConstants.ts b/src/features/eligibility/model/eligibilityConstants.ts index 002fcc3..5149062 100644 --- a/src/features/eligibility/model/eligibilityConstants.ts +++ b/src/features/eligibility/model/eligibilityConstants.ts @@ -10,3 +10,21 @@ export const ELIGIBILITY_RESULT_BANNER_TITLE = (userName: string) => export const ELIGIBILITY_RESULT_BANNER_SUBTITLE = "입력하신 정보가 맞는지 확인해 보세요"; export const ELIGIBILITY_RESULT_BUTTON = "결과보기"; export const ELIGIBILITY_COMPONENT_RENDERER_PAGE_TITLE = "자격 진단"; + +/** 학교 조회 화면 문구 */ +export const SCHOOL_SEARCH_TITLE = "학교 조회"; +export const SCHOOL_SEARCH_INTRO = + "우리 학교가 공공임대주택 지원자격 대상에 해당하는지 검색해 보세요!"; +export const SCHOOL_SEARCH_LABEL = "학교명을 검색해 주세요 (고등학교/대학교)"; +export const SCHOOL_SEARCH_PLACEHOLDER = "예) 핀하우스 대학교"; +export const SCHOOL_RESULT_BUTTON = "결과보기"; + +/** 학교 조회 결과 화면 문구 */ +export const SCHOOL_RESULT_PAGE_TITLE = "학교 조회 결과"; +export const SCHOOL_RESULT_EMPTY_LINE1 = (keyword: string) => `${keyword}는`; +export const SCHOOL_RESULT_EMPTY_LINE2 = "조회되지 않아요"; +export const SCHOOL_RESULT_EMPTY_HINT1 = "학교 이름을 다시 확인해 주세요."; +export const SCHOOL_RESULT_EMPTY_HINT2 = + "목록에 없는 학교는 공공임대주택 인정 학교가 아닐 수 있어요."; +export const SCHOOL_RESULT_TRY_AGAIN_BUTTON = "다시하기"; +export const SCHOOL_RESULT_FILL_HINT = "지원 가능한 지역 범위는 공고를 확인하세요"; diff --git a/src/features/eligibility/model/eligibilityDecisionTree.ts b/src/features/eligibility/model/eligibilityDecisionTree.ts index f66cebb..3094c55 100644 --- a/src/features/eligibility/model/eligibilityDecisionTree.ts +++ b/src/features/eligibility/model/eligibilityDecisionTree.ts @@ -15,6 +15,7 @@ export type ComponentType = | "priceInput" | "numberInputList" | "infoButton" + | "helpButton" | "datePicker" | "checkbox"; @@ -730,11 +731,13 @@ export const eligibilityDecisionTree: StepConfig[] = [ ], }, { - type: "infoButton", + type: "helpButton", props: { title: "우리 학교가 공공 임대주택에서 인정되는 학교 기준에 해당되는지 궁금하다면?", description: "학점은행제, 사내대학, 원격대학, 재외국민은 제외합니다\n대안학교, 검정고시는 포함하지만 해외 고등학교 졸업은 제외합니다", + action: "path", + path: "/eligibility/school", }, }, ], diff --git a/src/features/eligibility/model/schoolSearchResultStore.ts b/src/features/eligibility/model/schoolSearchResultStore.ts new file mode 100644 index 0000000..cdf8fab --- /dev/null +++ b/src/features/eligibility/model/schoolSearchResultStore.ts @@ -0,0 +1,15 @@ +import { create } from "zustand"; + +export type SchoolSearchResultState = { + keyword: string; + message: string | null; + setResult: (keyword: string, message: string | null) => void; + clear: () => void; +}; + +export const useSchoolSearchResultStore = create(set => ({ + keyword: "", + message: null, + setResult: (keyword, message) => set({ keyword, message }), + clear: () => set({ keyword: "", message: null }), +})); diff --git a/src/features/eligibility/ui/common/eligibilityComponentRenderer.tsx b/src/features/eligibility/ui/common/eligibilityComponentRenderer.tsx index ed5d278..9b89d63 100644 --- a/src/features/eligibility/ui/common/eligibilityComponentRenderer.tsx +++ b/src/features/eligibility/ui/common/eligibilityComponentRenderer.tsx @@ -10,6 +10,7 @@ import { EligibilitySelect } from "./eligibilitySelect"; import { EligibilityPriceInput } from "./eligibilityPriceInput"; import { EligibilityNumberInputList } from "./eligibilityNumberInputList"; import { EligibilityInfoButtonWithSheet } from "./eligibilityInfoButtonWithSheet"; +import { EligibilityHelpButton } from "./eligibilityHelpButton"; import { DatePicker } from "@/src/shared/ui/datePicker/datePicker"; import { Checkbox } from "@/src/shared/lib/headlessUi/checkBox/checkbox"; import { motion, AnimatePresence } from "framer-motion"; @@ -306,6 +307,8 @@ export const EligibilityComponentRenderer = ({ config }: EligibilityComponentRen onClick = () => router.push("/home"); } else if (config.props.action === "back") { onClick = () => router.back(); + } else if (config.props.action === "path") { + onClick = () => router.push(config.props.path); } } @@ -319,6 +322,23 @@ export const EligibilityComponentRenderer = ({ config }: EligibilityComponentRen ); } + case "helpButton": { + let onClick = config.props.onClick; + if (config.props.action === "home") { + onClick = () => router.push("/home"); + } else if (config.props.action === "back") { + onClick = () => router.back(); + } else if (config.props.action === "path") { + onClick = () => router.push(config.props.path); + } + return ( + + ); + } case "datePicker": { const value = config.storeKey ? getStoreValue(config.storeKey) : undefined; const setter = config.storeKey ? getStoreSetter(config.storeKey) : undefined; diff --git a/src/features/eligibility/ui/school/SchoolResultContentSection.tsx b/src/features/eligibility/ui/school/SchoolResultContentSection.tsx new file mode 100644 index 0000000..5f75094 --- /dev/null +++ b/src/features/eligibility/ui/school/SchoolResultContentSection.tsx @@ -0,0 +1,57 @@ +import SchoolSearchFillImg from "@/src/assets/images/eligibility/shcoolSearchFillImg"; +import SchoolSearchEmptyImg from "@/src/assets/images/eligibility/shcoolSearchEmptyImg"; +import { + SCHOOL_RESULT_EMPTY_LINE1, + SCHOOL_RESULT_EMPTY_LINE2, + SCHOOL_RESULT_EMPTY_HINT1, + SCHOOL_RESULT_EMPTY_HINT2, + SCHOOL_RESULT_FILL_HINT, +} from "@/src/features/eligibility/model/eligibilityConstants"; + +type SchoolResultContentSectionProps = { + isEligible: boolean; + message: string | null; + keyword: string; +}; + +export function SchoolResultContentSection({ + isEligible, + message, + keyword, +}: SchoolResultContentSectionProps) { + return ( +
+
+ {isEligible ? : } +
+ +
+ {isEligible && message ? ( + <> +

+ {message} +

+

+ {SCHOOL_RESULT_FILL_HINT} +

+ + ) : ( + <> +

+ {keyword ? SCHOOL_RESULT_EMPTY_LINE1(keyword) : ""} +

+

+ {SCHOOL_RESULT_EMPTY_LINE2} +

+

+ {SCHOOL_RESULT_EMPTY_HINT1} +

+

+ {SCHOOL_RESULT_EMPTY_HINT2} +

+ + )} +
+
+ ); +} diff --git a/src/features/eligibility/ui/school/SchoolResultHeader.tsx b/src/features/eligibility/ui/school/SchoolResultHeader.tsx new file mode 100644 index 0000000..4d7c35c --- /dev/null +++ b/src/features/eligibility/ui/school/SchoolResultHeader.tsx @@ -0,0 +1,12 @@ +import { DefaultHeader } from "@/src/shared/ui/header"; +import { SCHOOL_RESULT_PAGE_TITLE } from "@/src/features/eligibility/model/eligibilityConstants"; + +const BACK_PATH = "/eligibility/school"; + +export function SchoolResultHeader() { + return ( +
+ +
+ ); +} diff --git a/src/features/eligibility/ui/school/SchoolResultTryAgainSection.tsx b/src/features/eligibility/ui/school/SchoolResultTryAgainSection.tsx new file mode 100644 index 0000000..c51e304 --- /dev/null +++ b/src/features/eligibility/ui/school/SchoolResultTryAgainSection.tsx @@ -0,0 +1,24 @@ +import { Button } from "@/src/shared/lib/headlessUi/button/button"; +import { SCHOOL_RESULT_TRY_AGAIN_BUTTON } from "@/src/features/eligibility/model/eligibilityConstants"; + +type SchoolResultTryAgainSectionProps = { + onTryAgain: () => void; +}; + +export function SchoolResultTryAgainSection({ onTryAgain }: SchoolResultTryAgainSectionProps) { + return ( +
+ +
+ ); +} diff --git a/src/features/eligibility/ui/school/SchoolSearchFormSection.tsx b/src/features/eligibility/ui/school/SchoolSearchFormSection.tsx new file mode 100644 index 0000000..4de79e6 --- /dev/null +++ b/src/features/eligibility/ui/school/SchoolSearchFormSection.tsx @@ -0,0 +1,40 @@ +import { SearchBarLabel } from "@/src/shared/ui/searchBarLabel"; +import type { SearchBarOption } from "@/src/shared/ui/searchBar/type"; +import { + SCHOOL_SEARCH_INTRO, + SCHOOL_SEARCH_LABEL, + SCHOOL_SEARCH_PLACEHOLDER, +} from "@/src/features/eligibility/model/eligibilityConstants"; + +type SchoolSearchFormSectionProps = { + keyword: string; + options: SearchBarOption[]; + onKeywordChange: (value: string) => void; + onSelect: (option: SearchBarOption) => void; +}; + +export function SchoolSearchFormSection({ + keyword, + options, + onKeywordChange, + onSelect, +}: SchoolSearchFormSectionProps) { + return ( +
+

+ {SCHOOL_SEARCH_INTRO} +

+
+ onKeywordChange(e.target.value)} + options={options} + onSelect={onSelect} + /> +
+
+ ); +} diff --git a/src/features/eligibility/ui/school/SchoolSearchHeader.tsx b/src/features/eligibility/ui/school/SchoolSearchHeader.tsx new file mode 100644 index 0000000..1bf657e --- /dev/null +++ b/src/features/eligibility/ui/school/SchoolSearchHeader.tsx @@ -0,0 +1,11 @@ +import { DefaultHeader } from "@/src/shared/ui/header"; +import { SCHOOL_SEARCH_TITLE } from "@/src/features/eligibility/model/eligibilityConstants"; + +const BACK_PATH = "/eligibility?step=youngSingle001"; +export function SchoolSearchHeader() { + return ( +
+ +
+ ); +} diff --git a/src/features/eligibility/ui/school/SchoolSearchResultButtonSection.tsx b/src/features/eligibility/ui/school/SchoolSearchResultButtonSection.tsx new file mode 100644 index 0000000..94d9ebb --- /dev/null +++ b/src/features/eligibility/ui/school/SchoolSearchResultButtonSection.tsx @@ -0,0 +1,29 @@ +import { Button } from "@/src/shared/lib/headlessUi/button/button"; +import { SCHOOL_RESULT_BUTTON } from "@/src/features/eligibility/model/eligibilityConstants"; + +type SchoolSearchResultButtonSectionProps = { + hasKeyword: boolean; + onResultClick: () => void; +}; + +export function SchoolSearchResultButtonSection({ + hasKeyword, + onResultClick, +}: SchoolSearchResultButtonSectionProps) { + return ( +
+ +
+ ); +} diff --git a/src/features/eligibility/ui/school/index.ts b/src/features/eligibility/ui/school/index.ts new file mode 100644 index 0000000..6e8ea7b --- /dev/null +++ b/src/features/eligibility/ui/school/index.ts @@ -0,0 +1,6 @@ +export { SchoolSearchHeader } from "./SchoolSearchHeader"; +export { SchoolSearchFormSection } from "./SchoolSearchFormSection"; +export { SchoolSearchResultButtonSection } from "./SchoolSearchResultButtonSection"; +export { SchoolResultHeader } from "./SchoolResultHeader"; +export { SchoolResultContentSection } from "./SchoolResultContentSection"; +export { SchoolResultTryAgainSection } from "./SchoolResultTryAgainSection"; diff --git a/src/features/eligibility/ui/steps/eligibilityStepRenderer.tsx b/src/features/eligibility/ui/steps/eligibilityStepRenderer.tsx index c227897..b040ce3 100644 --- a/src/features/eligibility/ui/steps/eligibilityStepRenderer.tsx +++ b/src/features/eligibility/ui/steps/eligibilityStepRenderer.tsx @@ -39,7 +39,15 @@ export const EligibilityStepRenderer = ({ stepId, className }: EligibilityStepRe )} {/* 컴포넌트 렌더링 */} -
+
diff --git a/src/shared/ui/searchBarLabel/searchBarLabel.tsx b/src/shared/ui/searchBarLabel/searchBarLabel.tsx index 5868ed4..e312c92 100644 --- a/src/shared/ui/searchBarLabel/searchBarLabel.tsx +++ b/src/shared/ui/searchBarLabel/searchBarLabel.tsx @@ -45,7 +45,14 @@ export const SearchBarLabel = ({
- +
) : ( @@ -56,7 +63,14 @@ export const SearchBarLabel = ({
- +
)}