diff --git a/src/api/home/apis.tsx b/src/api/home/apis.tsx index fbfd996..5fc392c 100644 --- a/src/api/home/apis.tsx +++ b/src/api/home/apis.tsx @@ -61,6 +61,7 @@ export const homeApi = { status: item.status, createdAt: item.createdAt, preferredMatchDate: item.preferredMatchDate, + haveTicket: item.haveTicket, })); }, diff --git a/src/pages/Home/components/NewMatchingList.tsx b/src/pages/Home/components/NewMatchingList.tsx index 650f43f..2a052e6 100644 --- a/src/pages/Home/components/NewMatchingList.tsx +++ b/src/pages/Home/components/NewMatchingList.tsx @@ -1,9 +1,17 @@ import { useState, useRef, useEffect } from "react"; -import { Edit3, ChevronRight, CheckCircle2, X } from "lucide-react"; +import { + Edit3, + ChevronRight, + CheckCircle2, + X, + ChevronLeft, +} from "lucide-react"; +import { useNavigate } from "react-router-dom"; import { homeApi } from "../../../api/home/apis"; import { TeamId, getTeamInfo } from "../../../types/Type"; interface MatchingData { + haveTicket: any; postIdx: number; title: string; stadiumIdx: number; @@ -24,6 +32,9 @@ export default function NewMatchingList() { const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const containerRef = useRef(null); + const navigate = useNavigate(); + const [showLeftArrow, setShowLeftArrow] = useState(false); + const [showRightArrow, setShowRightArrow] = useState(false); useEffect(() => { const fetchMatchings = async () => { @@ -36,6 +47,7 @@ export default function NewMatchingList() { })); setMatchings(convertedData); setError(null); + setTimeout(checkScroll, 0); } catch (err) { setError("매칭 목록을 불러오는데 실패했습니다."); console.error("Error fetching matchings:", err); @@ -69,6 +81,53 @@ export default function NewMatchingList() { } }; + const handleViewAll = () => { + navigate("/matching"); + }; + + const handleMatchingClick = (postIdx: number) => { + navigate(`/matching/articles/${postIdx}`); + }; + + const checkScroll = () => { + if (containerRef.current) { + const { scrollLeft, scrollWidth, clientWidth } = containerRef.current; + const hasOverflow = scrollWidth > clientWidth; + setShowLeftArrow(scrollLeft > 0); + setShowRightArrow( + hasOverflow && scrollLeft < scrollWidth - clientWidth - 1, + ); + } + }; + + const handleScroll = (direction: "left" | "right") => { + if (containerRef.current) { + const scrollAmount = 300; // 한 번에 스크롤할 픽셀 양 + const newScrollLeft = + direction === "left" + ? containerRef.current.scrollLeft - scrollAmount + : containerRef.current.scrollLeft + scrollAmount; + + containerRef.current.scrollTo({ + left: newScrollLeft, + behavior: "smooth", + }); + } + }; + + useEffect(() => { + const container = containerRef.current; + if (container) { + container.addEventListener("scroll", checkScroll); + checkScroll(); // 초기 상태 체크 + } + return () => { + if (container) { + container.removeEventListener("scroll", checkScroll); + } + }; + }, []); + return (
@@ -78,90 +137,121 @@ export default function NewMatchingList() { 최신 직관 매칭 글
-

최근에 추가된 직관 매칭 글을 확인해보세요!

- {/* 최신 직관 매칭 글 목록 */} -
- {isLoading ? ( -
- - 로딩 중... - -
- ) : error ? ( -
- - {error} - -
- ) : matchings.length === 0 ? ( -
- - 매칭 목록이 없습니다. - -
- ) : ( - matchings.map(matching => ( -
-
-
- {matching.userNickname} +
+ {showLeftArrow && ( + + )} +
+ {isLoading ? ( +
+ + 로딩 중... + +
+ ) : error ? ( +
+ + {error} + +
+ ) : matchings.length === 0 ? ( +
+ + 매칭 목록이 없습니다. + +
+ ) : ( + matchings.map(matching => ( +
handleMatchingClick(matching.postIdx)} + className="flex h-41 w-73 flex-shrink-0 cursor-pointer flex-col justify-between rounded-xl bg-[var(--surface-1)] p-4 transition-colors hover:bg-blue-100" + > +
+
+ {matching.userNickname} +
+ {getTeamInfo(matching.userCheeringTeamId).name} +
+
+ + 제목 + + + {matching.title} + +
+
+ + 선호 경기일 + + + {matching.preferredMatchDate} + +
+
+ + 티켓 보유여부 + + + {matching.haveTicket ? ( + + ) : ( + + )} +
- {getTeamInfo(matching.userCheeringTeamId).name} -
-
- - 제목 - - - {matching.title} - -
-
- - 선호 경기일 - - - {matching.preferredMatchDate} - -
-
- - 티켓 보유여부 - - - {matching.status === 1 ? ( - - ) : ( - - )} -
-
- )) + )) + )} +
+ {showRightArrow && ( + )}
diff --git a/src/pages/Home/components/NewsBanner.tsx b/src/pages/Home/components/NewsBanner.tsx index 6ea7b51..8644673 100644 --- a/src/pages/Home/components/NewsBanner.tsx +++ b/src/pages/Home/components/NewsBanner.tsx @@ -1,5 +1,6 @@ import { useState, useEffect } from "react"; import { homeApi } from "../../../api/home/apis"; +import { ChevronLeft, ChevronRight } from "lucide-react"; interface NewsItem { title: string; @@ -47,6 +48,14 @@ export default function NewsBanner() { return () => clearInterval(timer); }, [total]); + const goToPrev = () => { + setCurrent(prev => (prev - 1 + total) % total); + }; + + const goToNext = () => { + setCurrent(prev => (prev + 1) % total); + }; + if (isLoading) { return (
@@ -73,37 +82,55 @@ export default function NewsBanner() { return (