import { useEffect, useRef, useState } from 'react'; import { useQuery, keepPreviousData } from '@tanstack/react-query'; import { motion, AnimatePresence } from 'framer-motion'; import { ExternalLink, ChevronDown, Check, MapPin, Navigation } from 'lucide-react'; import { getSchedule } from '../../../../api/public/schedules'; import { decodeHtmlEntities } from './utils'; import KakaoMap from './KakaoMap'; // 콘서트 섹션 컴포넌트 function ConcertSection({ schedule }) { // 현재 선택된 회차 ID (내부 state로 관리 - URL 변경 없음) const [selectedDateId, setSelectedDateId] = useState(schedule.id); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const dropdownRef = useRef(null); const dropdownListRef = useRef(null); // 표시할 데이터 state (변경된 부분만 업데이트) const [displayData, setDisplayData] = useState({ posterUrl: schedule.images?.[0] || null, title: schedule.title, date: schedule.date, time: schedule.time, locationName: schedule.location_name, locationAddress: schedule.location_address, locationLat: schedule.location_lat, locationLng: schedule.location_lng, description: schedule.description, sourceUrl: schedule.source_url, }); // 선택된 회차 데이터 조회 const { data: selectedSchedule } = useQuery({ queryKey: ['schedule', selectedDateId], queryFn: () => getSchedule(selectedDateId), placeholderData: keepPreviousData, enabled: selectedDateId !== schedule.id, }); // 데이터 비교 후 변경된 부분만 업데이트 useEffect(() => { const newData = selectedDateId === schedule.id ? schedule : selectedSchedule; if (!newData) return; setDisplayData(prev => { const updates = {}; const newPosterUrl = newData.images?.[0] || null; if (prev.posterUrl !== newPosterUrl) updates.posterUrl = newPosterUrl; if (prev.title !== newData.title) updates.title = newData.title; if (prev.date !== newData.date) updates.date = newData.date; if (prev.time !== newData.time) updates.time = newData.time; if (prev.locationName !== newData.location_name) updates.locationName = newData.location_name; if (prev.locationAddress !== newData.location_address) updates.locationAddress = newData.location_address; if (prev.locationLat !== newData.location_lat) updates.locationLat = newData.location_lat; if (prev.locationLng !== newData.location_lng) updates.locationLng = newData.location_lng; if (prev.description !== newData.description) updates.description = newData.description; if (prev.sourceUrl !== newData.source_url) updates.sourceUrl = newData.source_url; if (Object.keys(updates).length > 0) { return { ...prev, ...updates }; } return prev; }); }, [selectedDateId, schedule, selectedSchedule]); // 드롭다운 외부 클릭 감지 useEffect(() => { const handleClickOutside = (event) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { setIsDropdownOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); // 드롭다운 열릴 때 선택된 항목으로 자동 스크롤 useEffect(() => { if (isDropdownOpen && dropdownListRef.current) { const selectedElement = dropdownListRef.current.querySelector('[data-selected="true"]'); if (selectedElement) { // 약간의 지연 후 스크롤 (애니메이션 후) setTimeout(() => { selectedElement.scrollIntoView({ block: 'center', behavior: 'smooth' }); }, 50); } } }, [isDropdownOpen]); const relatedDates = schedule.related_dates || []; const hasMultipleDates = relatedDates.length > 1; const hasLocation = displayData.locationLat && displayData.locationLng; const hasPoster = !!displayData.posterUrl; const hasDescription = !!displayData.description; // 현재 선택된 회차 인덱스 const selectedIndex = relatedDates.findIndex(d => d.id === selectedDateId); const selectedDisplayIndex = selectedIndex >= 0 ? selectedIndex + 1 : 1; // 회차 선택 핸들러 const handleSelectDate = (id) => { setSelectedDateId(id); setIsDropdownOpen(false); }; // 짧은 날짜 포맷팅 (회차 목록용) const formatShortDate = (dateStr, timeStr) => { const date = new Date(dateStr); const dayNames = ['일', '월', '화', '수', '목', '금', '토']; const month = date.getMonth() + 1; const day = date.getDate(); const weekday = dayNames[date.getDay()]; let result = `${month}/${day} (${weekday})`; if (timeStr) { result += ` ${timeStr.slice(0, 5)}`; } return result; }; return (
{/* ========== 히어로 섹션 ========== */} {/* 배경 레이어 - 포스터 확대 + 블러 */}
{hasPoster ? ( <>
) : (
)}
{/* 메인 콘텐츠 */}
{/* 포스터 */} {hasPoster && ( {displayData.title} )} {/* 제목 및 회차 선택 */}
{/* 제목 */} {decodeHtmlEntities(displayData.title)} {/* 회차 선택 드롭다운 */} {hasMultipleDates && ( {isDropdownOpen && ( {/* 드롭다운 헤더 */}
공연 일정 선택
{relatedDates.map((item, index) => { const isSelected = item.id === selectedDateId; return ( ); })}
)}
)}
{/* ========== 장소 정보 카드 ========== */} {displayData.locationName && ( {/* 헤더 */}
{/* 장소 아이콘 */}
{/* 텍스트 */}

{displayData.locationName}

{displayData.locationAddress && (

{displayData.locationAddress}

)}
{/* 길찾기 버튼 - 카카오맵(국내) / 구글맵(해외) */} 길찾기
{/* 지도 - 높이 2배 */}
{hasLocation ? ( ) : (