Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/(pages)/albaList/components/SearchSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function SearchSection() {
return (
<form onSubmit={handleSubmit} className="w-full">
<div className="mx-auto flex items-center justify-between gap-4">
<div className="w-[270px] md:w-[500px] lg:w-[700px] xl:w-[900px] 2xl:w-[1100px]">
<div className="w-[270px] md:w-[500px] lg:w-[700px] xl:w-[900px]">
<SearchInput
value={keyword}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setKeyword(e.target.value)}
Expand Down
2 changes: 1 addition & 1 deletion src/app/(pages)/albaList/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { Suspense } from "react";

export default function AlbaListLayout({ children }: { children: React.ReactNode }) {
return (
<div className="mx-auto max-w-screen-2xl px-4 py-8">
<div className="mx-auto max-w-screen-xl px-4 py-8">
<Suspense
fallback={
<div className="flex h-[calc(100vh-200px)] items-center justify-center">
Expand Down
11 changes: 6 additions & 5 deletions src/app/(pages)/albaList/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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");
Expand Down Expand Up @@ -124,10 +125,10 @@ export default function AlbaList() {
</div>

{/* 메인 콘텐츠 영역 */}
<div className="w-full pt-[224px]">
<div className="w-full pt-[132px]">
{/* 폼 만들기 버튼 - 고정 위치 */}
{isOwner && (
<div className="fixed bottom-[28%] right-8 z-[9999] translate-y-1/2 md:right-12 lg:right-16 xl:right-20">
<div className="fixed bottom-[50%] right-4 z-[9999] translate-y-1/2">
<Link
href="/addForm"
className="flex items-center gap-2 rounded-lg bg-[#FFB800] px-4 py-3 text-base font-semibold text-white shadow-lg transition-all hover:bg-[#FFA800] md:px-6 md:text-lg"
Expand All @@ -143,8 +144,8 @@ export default function AlbaList() {
<p className="text-grayscale-500">등록된 알바 공고가 없습니다.</p>
</div>
) : (
<div className="mx-auto mt-4 w-full max-w-screen-2xl px-4 md:px-6 lg:px-8">
<div className="flex flex-wrap items-center justify-center gap-6">
<div className="mx-auto mt-4 w-full max-w-screen-xl px-3">
<div className="flex flex-wrap justify-start gap-6">
{data?.pages.map((page) => (
<React.Fragment key={page.nextCursor}>
{page.data.map((form) => (
Expand Down
50 changes: 34 additions & 16 deletions src/app/components/card/cardList/AlbaListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/**
* 알바폼 리스트 아이템 컴포넌트
Expand All @@ -31,6 +33,7 @@ const AlbaListItem = ({
const [showDropdown, setShowDropdown] = useState(false); // 드롭다운 메뉴 표시 상태
const [currentImageIndex, setCurrentImageIndex] = useState(0); // 현재 이미지 인덱스
const dropdownRef = useRef<HTMLDivElement>(null); // 드롭다운 메뉴 참조
const [imageError, setImageError] = useState(false);

// 모집 상태 및 D-day 계산
const recruitmentStatus = getRecruitmentStatus(recruitmentEndDate);
Expand Down Expand Up @@ -81,22 +84,37 @@ const AlbaListItem = ({
});
};

// S3 URL 체크 함수
const isValidS3Url = (url: string) => {
return url.startsWith(S3_URL);
};

return (
<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]">
<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]">
{/* 이미지 슬라이더 영역 */}
<div className="relative h-[200px] overflow-hidden rounded-t-xl lg:h-[310px]">
{/* 현재 이미지 */}
{imageUrls[currentImageIndex] && (
<Image
src={imageUrls[currentImageIndex]}
alt={`Recruit Image ${currentImageIndex + 1}`}
fill
className="object-cover transition-opacity duration-300"
/>
<div className="relative h-[200px] overflow-hidden rounded-t-xl lg:h-[240px]">
{imageUrls[currentImageIndex] && !imageError ? (
isValidS3Url(imageUrls[currentImageIndex]) ? (
<Image
src={imageUrls[currentImageIndex]}
alt={`Recruit Image ${currentImageIndex + 1}`}
fill
className="object-cover transition-opacity duration-300"
onError={() => setImageError(true)}
/>
) : (
<div className="flex h-full w-full items-center justify-center bg-grayscale-100">
<MdOutlineImage className="size-20 text-grayscale-400" />
</div>
)
) : (
<div className="flex h-full w-full items-center justify-center bg-grayscale-100">
<MdOutlineImage className="size-20 text-grayscale-400" />
</div>
)}

{/* 이미지 인디케이터 */}
{imageUrls.length > 1 && (
{/* 이미지 인디케이터 - 유효한 이미지가 2개 이상이고 에러가 없을 때만 표시 */}
{imageUrls.filter((url) => isValidS3Url(url)).length > 1 && !imageError && (
Comment on lines +116 to +117
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

<div className="absolute bottom-4 left-1/2 -translate-x-1/2">
<Indicator
imageCount={imageUrls.length}
Expand All @@ -108,7 +126,7 @@ const AlbaListItem = ({
</div>

{/* 콘텐츠 영역 */}
<div className="relative flex h-[140px] flex-col justify-between p-4 lg:h-[226px] lg:p-6">
<div className="relative flex h-[140px] flex-col justify-between p-2 lg:h-[160px]">
{/* 상단 영역 */}
<div className="flex flex-col gap-4">
{/* 상태 표시 영역 (공개여부, 모집상태, 날짜) */}
Expand All @@ -117,7 +135,7 @@ const AlbaListItem = ({
<div className="flex items-center justify-between">
<Chip label={isPublic ? "공개" : "비공개"} variant={isPublic ? "positive" : "negative"} />
<Chip label={recruitmentStatus} variant={recruitmentStatus === "모집 중" ? "positive" : "negative"} />
<span className="text-xs font-medium text-grayscale-500 md:inline">
<span className="text-xs font-medium tracking-tighter text-grayscale-500 md:inline lg:text-sm">
{formatRecruitDate(recruitmentStartDate, true)} ~ {formatRecruitDate(recruitmentEndDate, true)}
</span>
</div>
Expand Down Expand Up @@ -152,11 +170,11 @@ const AlbaListItem = ({
</div>

{/* 제목 */}
<div className="text-grayscale-900 truncate text-base font-bold lg:text-lg">{title}</div>
<div className="text-grayscale-900 truncate pl-2 text-base font-bold lg:text-lg">{title}</div>
</div>

{/* 통계 정보 영역 - mt-auto 제거하고 부모 컨테이너에 justify-between 추가 */}
<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">
<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">
<div className="flex flex-1 items-center justify-center">
<span className="font-medium">지원자 {applyCount}명</span>
</div>
Expand Down
54 changes: 36 additions & 18 deletions src/app/components/card/cardList/ScrapListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/**
* 알바폼 스크랩 리스트 아이템 컴포넌트
Expand All @@ -30,6 +32,7 @@ const ScrapListItem = ({
const [showDropdown, setShowDropdown] = useState(false); // 드롭다운 메뉴 표시 상태
const [currentImageIndex, setCurrentImageIndex] = useState(0); // 현재 표시 중인 이미지 인덱스
const dropdownRef = useRef<HTMLDivElement>(null); // 드롭다운 메뉴 참조
const [imageError, setImageError] = useState(false);

// 모집 상태 및 D-day 계산
const recruitmentStatus = getRecruitmentStatus(recruitmentEndDate);
Expand Down Expand Up @@ -80,22 +83,37 @@ const ScrapListItem = ({
});
};

// S3 URL 체크 함수 추가
const isValidS3Url = (url: string) => {
return url.startsWith(S3_URL);
};

return (
<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]">
<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]">
{/* 이미지 슬라이더 영역 */}
<div className="relative h-[200px] overflow-hidden rounded-t-xl lg:h-[310px]">
{/* 현재 이미지 */}
{imageUrls[currentImageIndex] && (
<Image
src={imageUrls[currentImageIndex]}
alt={`Recruit Image ${currentImageIndex + 1}`}
fill
className="object-cover transition-opacity duration-300"
/>
<div className="relative h-[200px] overflow-hidden rounded-t-xl lg:h-[240px]">
{imageUrls[currentImageIndex] && !imageError ? (
isValidS3Url(imageUrls[currentImageIndex]) ? (
<Image
src={imageUrls[currentImageIndex]}
alt={`Recruit Image ${currentImageIndex + 1}`}
fill
className="object-cover transition-opacity duration-300"
onError={() => setImageError(true)}
/>
) : (
<div className="flex h-full w-full items-center justify-center bg-grayscale-100">
<MdOutlineImage className="size-20 text-grayscale-400" />
</div>
)
) : (
<div className="flex h-full w-full items-center justify-center bg-grayscale-100">
<MdOutlineImage className="size-20 text-grayscale-400" />
</div>
)}

{/* 이미지 인디케이터 */}
{imageUrls.length > 1 && (
{/* 이미지 인디케이터 - 유효한 이미지가 2개 이상이고 에러가 없을 때만 표시 */}
{imageUrls.filter((url) => isValidS3Url(url)).length > 1 && !imageError && (
<div className="absolute bottom-4 left-1/2 -translate-x-1/2">
<Indicator
imageCount={imageUrls.length}
Expand All @@ -107,7 +125,7 @@ const ScrapListItem = ({
</div>

{/* 콘텐츠 영역 */}
<div className="relative flex h-[140px] flex-col justify-between p-4 lg:h-[226px] lg:p-6">
<div className="relative flex h-[140px] flex-col justify-between p-2 lg:h-[160px]">
{/* 상단 영역 */}
<div className="flex flex-col gap-4">
{/* 상태 표시 영역 (공개여부, 모집상태, 날짜) */}
Expand All @@ -116,7 +134,7 @@ const ScrapListItem = ({
<div className="flex items-center justify-between">
<Chip label={isPublic ? "공개" : "비공개"} variant={isPublic ? "positive" : "negative"} />
<Chip label={recruitmentStatus} variant={recruitmentStatus === "모집 중" ? "positive" : "negative"} />
<span className="text-xs font-medium text-grayscale-500 md:inline">
<span className="text-xs font-medium tracking-tighter text-grayscale-500 md:inline lg:text-sm">
{formatRecruitDate(recruitmentStartDate, true)} ~ {formatRecruitDate(recruitmentEndDate, true)}
</span>
</div>
Expand All @@ -133,7 +151,7 @@ const ScrapListItem = ({
{showDropdown && (
<div className="absolute right-0 top-8 z-10 w-32 rounded-lg border border-grayscale-200 bg-white py-2 shadow-lg">
<button
className="w-full px-4 py-2 text-left text-sm hover:bg-primary-orange-100 disabled:opacity-50"
className="w-full px-4 py-2 text-left text-sm hover:bg-primary-orange-100"
onClick={handleFormApplication}
>
지원하기
Expand All @@ -151,11 +169,11 @@ const ScrapListItem = ({
</div>

{/* 제목 */}
<div className="text-grayscale-900 truncate text-base font-bold lg:text-lg">{title}</div>
<div className="text-grayscale-900 truncate pl-2 text-base font-bold lg:text-lg">{title}</div>
</div>

{/* 통계 정보 영역 - mt-auto 제거하고 부모 컨테이너에 justify-between 추가 */}
<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">
{/* 통계 정보 영역 */}
<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">
<div className="flex flex-1 items-center justify-center">
<span className="font-medium">지원자 {applyCount}명</span>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/app/components/chip/Chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ interface ChipProps {
const Chip: React.FC<ChipProps> = ({ 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";

Expand Down
8 changes: 4 additions & 4 deletions src/app/stories/design-system/pages/albaList/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ const AlbaList: React.FC<AlbaListProps> = () => {
</div>

{/* 메인 콘텐츠 영역 */}
<div className="w-full pt-[224px]">
<div className="w-full pt-[132px]">
{/* 폼 만들기 버튼 - 고정 위치 */}
<div className="fixed bottom-[28%] right-8 z-[9999] translate-y-1/2 md:right-12 lg:right-16 xl:right-20">
<div className="fixed bottom-[50%] right-4 z-[9999] translate-y-1/2">
<Link
href="/addForm"
className="flex items-center gap-2 rounded-lg bg-[#FFB800] px-4 py-3 text-base font-semibold text-white shadow-lg transition-all hover:bg-[#FFA800] md:px-6 md:text-lg"
Expand All @@ -117,8 +117,8 @@ const AlbaList: React.FC<AlbaListProps> = () => {
<p className="text-grayscale-500">등록된 알바 공고가 없습니다.</p>
</div>
) : (
<div className="mx-auto mt-4 w-full max-w-screen-2xl px-4 md:px-6 lg:px-8">
<div className="flex flex-wrap items-center justify-center gap-6">
<div className="mx-auto mt-4 w-full max-w-screen-xl px-3">
<div className="flex flex-wrap justify-start gap-6">
{items.map((form) => (
<div key={form.id}>
<AlbaListItem {...form} />
Expand Down
2 changes: 2 additions & 0 deletions src/constants/config.ts
Original file line number Diff line number Diff line change
@@ -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";
5 changes: 5 additions & 0 deletions src/utils/workDayFormatter.ts
Original file line number Diff line number Diff line change
@@ -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" });
}

Comment on lines +4 to +8
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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");
Expand Down
Loading