@@ -9,6 +9,8 @@ import Indicator from "../../pagination/Indicator";
99import { FormListType } from "@/types/response/form" ;
1010import { useFormScrap } from "@/hooks/queries/form/useFormScap" ;
1111import { useRouter } from "next/navigation" ;
12+ import { MdOutlineImage } from "react-icons/md" ;
13+ import { S3_URL } from "@/constants/config" ;
1214
1315/**
1416 * 알바폼 스크랩 리스트 아이템 컴포넌트
@@ -30,6 +32,7 @@ const ScrapListItem = ({
3032 const [ showDropdown , setShowDropdown ] = useState ( false ) ; // 드롭다운 메뉴 표시 상태
3133 const [ currentImageIndex , setCurrentImageIndex ] = useState ( 0 ) ; // 현재 표시 중인 이미지 인덱스
3234 const dropdownRef = useRef < HTMLDivElement > ( null ) ; // 드롭다운 메뉴 참조
35+ const [ imageError , setImageError ] = useState ( false ) ;
3336
3437 // 모집 상태 및 D-day 계산
3538 const recruitmentStatus = getRecruitmentStatus ( recruitmentEndDate ) ;
@@ -80,22 +83,37 @@ const ScrapListItem = ({
8083 } ) ;
8184 } ;
8285
86+ // S3 URL 체크 함수 추가
87+ const isValidS3Url = ( url : string ) => {
88+ return url . startsWith ( S3_URL ) ;
89+ } ;
90+
8391 return (
84- < div className = "relative h-[360px] w-[327px] overflow-hidden rounded-xl border border-grayscale-200 bg-white shadow-md transition-transform duration-300 hover:scale-[1.02] lg:h-[536px] lg: w-[477px ]" >
92+ < div className = "relative h-auto w-[327px] overflow-hidden rounded-xl border border-grayscale-200 bg-white shadow-md transition-transform duration-300 hover:scale-[1.02] lg:w-[372px ]" >
8593 { /* 이미지 슬라이더 영역 */ }
86- < div className = "relative h-[200px] overflow-hidden rounded-t-xl lg:h-[310px]" >
87- { /* 현재 이미지 */ }
88- { imageUrls [ currentImageIndex ] && (
89- < Image
90- src = { imageUrls [ currentImageIndex ] }
91- alt = { `Recruit Image ${ currentImageIndex + 1 } ` }
92- fill
93- className = "object-cover transition-opacity duration-300"
94- />
94+ < div className = "relative h-[200px] overflow-hidden rounded-t-xl lg:h-[240px]" >
95+ { imageUrls [ currentImageIndex ] && ! imageError ? (
96+ isValidS3Url ( imageUrls [ currentImageIndex ] ) ? (
97+ < Image
98+ src = { imageUrls [ currentImageIndex ] }
99+ alt = { `Recruit Image ${ currentImageIndex + 1 } ` }
100+ fill
101+ className = "object-cover transition-opacity duration-300"
102+ onError = { ( ) => setImageError ( true ) }
103+ />
104+ ) : (
105+ < div className = "flex h-full w-full items-center justify-center bg-grayscale-100" >
106+ < MdOutlineImage className = "size-20 text-grayscale-400" />
107+ </ div >
108+ )
109+ ) : (
110+ < div className = "flex h-full w-full items-center justify-center bg-grayscale-100" >
111+ < MdOutlineImage className = "size-20 text-grayscale-400" />
112+ </ div >
95113 ) }
96114
97- { /* 이미지 인디케이터 */ }
98- { imageUrls . length > 1 && (
115+ { /* 이미지 인디케이터 - 유효한 이미지가 2개 이상이고 에러가 없을 때만 표시 */ }
116+ { imageUrls . filter ( ( url ) => isValidS3Url ( url ) ) . length > 1 && ! imageError && (
99117 < div className = "absolute bottom-4 left-1/2 -translate-x-1/2" >
100118 < Indicator
101119 imageCount = { imageUrls . length }
@@ -107,7 +125,7 @@ const ScrapListItem = ({
107125 </ div >
108126
109127 { /* 콘텐츠 영역 */ }
110- < div className = "relative flex h-[140px] flex-col justify-between p-4 lg:h-[226px] lg:p-6 " >
128+ < div className = "relative flex h-[140px] flex-col justify-between p-2 lg:h-[160px] " >
111129 { /* 상단 영역 */ }
112130 < div className = "flex flex-col gap-4" >
113131 { /* 상태 표시 영역 (공개여부, 모집상태, 날짜) */ }
@@ -116,7 +134,7 @@ const ScrapListItem = ({
116134 < div className = "flex items-center justify-between" >
117135 < Chip label = { isPublic ? "공개" : "비공개" } variant = { isPublic ? "positive" : "negative" } />
118136 < Chip label = { recruitmentStatus } variant = { recruitmentStatus === "모집 중" ? "positive" : "negative" } />
119- < span className = "text-xs font-medium text-grayscale-500 md:inline" >
137+ < span className = "text-xs font-medium tracking-tighter text-grayscale-500 md:inline lg:text-sm " >
120138 { formatRecruitDate ( recruitmentStartDate , true ) } ~ { formatRecruitDate ( recruitmentEndDate , true ) }
121139 </ span >
122140 </ div >
@@ -133,7 +151,7 @@ const ScrapListItem = ({
133151 { showDropdown && (
134152 < div className = "absolute right-0 top-8 z-10 w-32 rounded-lg border border-grayscale-200 bg-white py-2 shadow-lg" >
135153 < button
136- className = "w-full px-4 py-2 text-left text-sm hover:bg-primary-orange-100 disabled:opacity-50 "
154+ className = "w-full px-4 py-2 text-left text-sm hover:bg-primary-orange-100"
137155 onClick = { handleFormApplication }
138156 >
139157 지원하기
@@ -151,11 +169,11 @@ const ScrapListItem = ({
151169 </ div >
152170
153171 { /* 제목 */ }
154- < div className = "text-grayscale-900 truncate text-base font-bold lg:text-lg" > { title } </ div >
172+ < div className = "text-grayscale-900 truncate pl-2 text-base font-bold lg:text-lg" > { title } </ div >
155173 </ div >
156174
157- { /* 통계 정보 영역 - mt-auto 제거하고 부모 컨테이너에 justify-between 추가 */ }
158- < div className = "text-grayscale-700 mt-4 flex h-[50px] items-center justify-between rounded-2xl border border-grayscale-100 p-2 text-sm lg:text-base" >
175+ { /* 통계 정보 영역 */ }
176+ < div className = "text-grayscale-700 mt-4 flex h-[50px] items-center justify-between rounded-2xl border border-grayscale-100 text-sm lg:text-base" >
159177 < div className = "flex flex-1 items-center justify-center" >
160178 < span className = "font-medium" > 지원자 { applyCount } 명</ span >
161179 </ div >
0 commit comments