diff --git a/src/app/(pages)/albaList/components/SearchSection.tsx b/src/app/(pages)/albaList/components/SearchSection.tsx index 494eeea7..d5d991bb 100644 --- a/src/app/(pages)/albaList/components/SearchSection.tsx +++ b/src/app/(pages)/albaList/components/SearchSection.tsx @@ -25,7 +25,7 @@ export default function SearchSection() { return (
-
+
) => setKeyword(e.target.value)} diff --git a/src/app/(pages)/albaList/layout.tsx b/src/app/(pages)/albaList/layout.tsx index 900388a3..21c3a7e1 100644 --- a/src/app/(pages)/albaList/layout.tsx +++ b/src/app/(pages)/albaList/layout.tsx @@ -2,7 +2,7 @@ import React, { Suspense } from "react"; export default function AlbaListLayout({ children }: { children: React.ReactNode }) { return ( -
+
diff --git a/src/app/(pages)/albaList/page.tsx b/src/app/(pages)/albaList/page.tsx index ac3fbf4e..f7c74286 100644 --- a/src/app/(pages)/albaList/page.tsx +++ b/src/app/(pages)/albaList/page.tsx @@ -12,6 +12,7 @@ import SearchSection from "./components/SearchSection"; import { useUser } from "@/hooks/queries/user/me/useUser"; import Link from "next/link"; import { IoAdd } from "react-icons/io5"; +import { userRoles } from "@/constants/userRoles"; const FORMS_PER_PAGE = 10; @@ -20,7 +21,7 @@ export default function AlbaList() { const pathname = usePathname(); const searchParams = useSearchParams(); const { user } = useUser(); - const isOwner = user?.role === "owner"; + const isOwner = user?.role === userRoles.OWNER; // URL 쿼리 파라미터에서 필터 상태와 키워드 가져오기 const isRecruiting = searchParams.get("isRecruiting"); @@ -124,10 +125,10 @@ export default function AlbaList() {
{/* 메인 콘텐츠 영역 */} -
+
{/* 폼 만들기 버튼 - 고정 위치 */} {isOwner && ( -
+
등록된 알바 공고가 없습니다.

) : ( -
-
+
+
{data?.pages.map((page) => ( {page.data.map((form) => ( diff --git a/src/app/components/card/cardList/AlbaListItem.tsx b/src/app/components/card/cardList/AlbaListItem.tsx index 544ace99..2db50c33 100644 --- a/src/app/components/card/cardList/AlbaListItem.tsx +++ b/src/app/components/card/cardList/AlbaListItem.tsx @@ -9,6 +9,8 @@ import useModalStore from "@/store/modalStore"; import Indicator from "../../pagination/Indicator"; import { FormListType } from "@/types/response/form"; import { useFormScrap } from "@/hooks/queries/form/useFormScap"; +import { MdOutlineImage } from "react-icons/md"; +import { S3_URL } from "@/constants/config"; /** * 알바폼 리스트 아이템 컴포넌트 @@ -31,6 +33,7 @@ const AlbaListItem = ({ const [showDropdown, setShowDropdown] = useState(false); // 드롭다운 메뉴 표시 상태 const [currentImageIndex, setCurrentImageIndex] = useState(0); // 현재 이미지 인덱스 const dropdownRef = useRef(null); // 드롭다운 메뉴 참조 + const [imageError, setImageError] = useState(false); // 모집 상태 및 D-day 계산 const recruitmentStatus = getRecruitmentStatus(recruitmentEndDate); @@ -81,22 +84,37 @@ const AlbaListItem = ({ }); }; + // S3 URL 체크 함수 + const isValidS3Url = (url: string) => { + return url.startsWith(S3_URL); + }; + return ( -
+
{/* 이미지 슬라이더 영역 */} -
- {/* 현재 이미지 */} - {imageUrls[currentImageIndex] && ( - {`Recruit +
+ {imageUrls[currentImageIndex] && !imageError ? ( + isValidS3Url(imageUrls[currentImageIndex]) ? ( + {`Recruit setImageError(true)} + /> + ) : ( +
+ +
+ ) + ) : ( +
+ +
)} - {/* 이미지 인디케이터 */} - {imageUrls.length > 1 && ( + {/* 이미지 인디케이터 - 유효한 이미지가 2개 이상이고 에러가 없을 때만 표시 */} + {imageUrls.filter((url) => isValidS3Url(url)).length > 1 && !imageError && (
{/* 콘텐츠 영역 */} -
+
{/* 상단 영역 */}
{/* 상태 표시 영역 (공개여부, 모집상태, 날짜) */} @@ -117,7 +135,7 @@ const AlbaListItem = ({
- + {formatRecruitDate(recruitmentStartDate, true)} ~ {formatRecruitDate(recruitmentEndDate, true)}
@@ -152,11 +170,11 @@ const AlbaListItem = ({
{/* 제목 */} -
{title}
+
{title}
{/* 통계 정보 영역 - mt-auto 제거하고 부모 컨테이너에 justify-between 추가 */} -
+
지원자 {applyCount}명
diff --git a/src/app/components/card/cardList/ScrapListItem.tsx b/src/app/components/card/cardList/ScrapListItem.tsx index 766c0c85..4eea5b64 100644 --- a/src/app/components/card/cardList/ScrapListItem.tsx +++ b/src/app/components/card/cardList/ScrapListItem.tsx @@ -9,6 +9,8 @@ import Indicator from "../../pagination/Indicator"; import { FormListType } from "@/types/response/form"; import { useFormScrap } from "@/hooks/queries/form/useFormScap"; import { useRouter } from "next/navigation"; +import { MdOutlineImage } from "react-icons/md"; +import { S3_URL } from "@/constants/config"; /** * 알바폼 스크랩 리스트 아이템 컴포넌트 @@ -30,6 +32,7 @@ const ScrapListItem = ({ const [showDropdown, setShowDropdown] = useState(false); // 드롭다운 메뉴 표시 상태 const [currentImageIndex, setCurrentImageIndex] = useState(0); // 현재 표시 중인 이미지 인덱스 const dropdownRef = useRef(null); // 드롭다운 메뉴 참조 + const [imageError, setImageError] = useState(false); // 모집 상태 및 D-day 계산 const recruitmentStatus = getRecruitmentStatus(recruitmentEndDate); @@ -80,22 +83,37 @@ const ScrapListItem = ({ }); }; + // S3 URL 체크 함수 추가 + const isValidS3Url = (url: string) => { + return url.startsWith(S3_URL); + }; + return ( -
+
{/* 이미지 슬라이더 영역 */} -
- {/* 현재 이미지 */} - {imageUrls[currentImageIndex] && ( - {`Recruit +
+ {imageUrls[currentImageIndex] && !imageError ? ( + isValidS3Url(imageUrls[currentImageIndex]) ? ( + {`Recruit setImageError(true)} + /> + ) : ( +
+ +
+ ) + ) : ( +
+ +
)} - {/* 이미지 인디케이터 */} - {imageUrls.length > 1 && ( + {/* 이미지 인디케이터 - 유효한 이미지가 2개 이상이고 에러가 없을 때만 표시 */} + {imageUrls.filter((url) => isValidS3Url(url)).length > 1 && !imageError && (
{/* 콘텐츠 영역 */} -
+
{/* 상단 영역 */}
{/* 상태 표시 영역 (공개여부, 모집상태, 날짜) */} @@ -116,7 +134,7 @@ const ScrapListItem = ({
- + {formatRecruitDate(recruitmentStartDate, true)} ~ {formatRecruitDate(recruitmentEndDate, true)}
@@ -133,7 +151,7 @@ const ScrapListItem = ({ {showDropdown && (
{/* 제목 */} -
{title}
+
{title}
- {/* 통계 정보 영역 - mt-auto 제거하고 부모 컨테이너에 justify-between 추가 */} -
+ {/* 통계 정보 영역 */} +
지원자 {applyCount}명
diff --git a/src/app/components/chip/Chip.tsx b/src/app/components/chip/Chip.tsx index 69a5ad19..4c638786 100644 --- a/src/app/components/chip/Chip.tsx +++ b/src/app/components/chip/Chip.tsx @@ -19,12 +19,12 @@ interface ChipProps { const Chip: React.FC = ({ label = "Label", variant, border, icon, textStyle = "" }: ChipProps) => { const wrapperStyle = "rounded flex items-center justify-center min-w-[60px] m-1"; const paddingStyle = icon - ? "px-[10px] py-1 md:px-[14.5px] md:py-1 lg:px-[10px] lg:py-[6px]" - : "px-2 py-1 md:px-[10px] lg:py-[6px] lg:px-3"; + ? "px-[8px] py-1 md:px-[12px] md:py-1 lg:px-[8px] lg:py-[6px]" + : "px-2 py-1 md:px-2 lg:py-[6px] lg:px-2"; const variantStyle = variant === "positive" ? "bg-primary-orange-50 text-primary-orange-300" : "bg-line-100 text-grayscale-200"; const baseTextStyle = - "text-xs leading-[20px] md:leading-[24px] lg:text-base lg:leading-[26px] font-medium tracking-tight"; + "text-xs leading-[18px] md:leading-[20px] lg:text-sm lg:leading-[22px] font-medium tracking-tighter whitespace-nowrap"; const borderStyle = border ? "border border-primary-orange-100" : ""; const iconStyle = "flex items-center justify-center"; diff --git a/src/app/stories/design-system/pages/albaList/page.tsx b/src/app/stories/design-system/pages/albaList/page.tsx index 7c39a915..6ea9a9d3 100644 --- a/src/app/stories/design-system/pages/albaList/page.tsx +++ b/src/app/stories/design-system/pages/albaList/page.tsx @@ -100,9 +100,9 @@ const AlbaList: React.FC = () => {
{/* 메인 콘텐츠 영역 */} -
+
{/* 폼 만들기 버튼 - 고정 위치 */} -
+
= () => {

등록된 알바 공고가 없습니다.

) : ( -
-
+
+
{items.map((form) => (
diff --git a/src/constants/config.ts b/src/constants/config.ts index aa3ffd59..d8b8ccad 100644 --- a/src/constants/config.ts +++ b/src/constants/config.ts @@ -1,3 +1,5 @@ export const API_URL = process.env.NEXT_PUBLIC_API_URL; export const TEAM_NAME = process.env.NEXT_PUBLIC_TEAM_ID; + +export const S3_URL = "https://sprint-fe-project.s3.ap-northeast-2.amazonaws.com"; diff --git a/src/utils/workDayFormatter.ts b/src/utils/workDayFormatter.ts index 0cb60d5a..0c2be47e 100644 --- a/src/utils/workDayFormatter.ts +++ b/src/utils/workDayFormatter.ts @@ -1,6 +1,11 @@ import { workDayOptions } from "@/constants/workDayOptions"; export const formatRecruitDate = (date: Date, isMd: boolean = false) => { + // 유효한 Date 객체인지 확인 + if (!(date instanceof Date) || isNaN(date.getTime())) { + return new Date().toLocaleDateString("ko-KR", { year: "numeric", month: "2-digit", day: "2-digit" }); + } + const year = isMd ? date.getFullYear().toString() : date.getFullYear().toString().slice(2); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0");