import { useParams, Link } from 'react-router-dom'; import { useQuery, keepPreviousData } from '@tanstack/react-query'; import { useEffect, useRef, useState } from 'react'; import { motion } from 'framer-motion'; import { Clock, Calendar, ExternalLink, ChevronRight, Link2, MapPin, Navigation } from 'lucide-react'; import { getSchedule, getXProfile } from '../../../api/public/schedules'; // 카카오맵 SDK 키 const KAKAO_MAP_KEY = import.meta.env.VITE_KAKAO_JS_KEY; // 카테고리 ID 상수 const CATEGORY_ID = { YOUTUBE: 2, X: 3, ALBUM: 4, FANSIGN: 5, CONCERT: 6, TICKET: 7, }; // HTML 엔티티 디코딩 함수 const decodeHtmlEntities = (text) => { if (!text) return ''; const textarea = document.createElement('textarea'); textarea.innerHTML = text; return textarea.value; }; // 유튜브 비디오 ID 추출 const extractYoutubeVideoId = (url) => { if (!url) return null; const shortMatch = url.match(/youtu\.be\/([a-zA-Z0-9_-]{11})/); if (shortMatch) return shortMatch[1]; const watchMatch = url.match(/youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})/); if (watchMatch) return watchMatch[1]; const shortsMatch = url.match(/youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/); if (shortsMatch) return shortsMatch[1]; return null; }; // 날짜 포맷팅 const formatFullDate = (dateStr) => { if (!dateStr) return ''; const date = new Date(dateStr); const dayNames = ['일', '월', '화', '수', '목', '금', '토']; return `${date.getFullYear()}. ${date.getMonth() + 1}. ${date.getDate()}. (${dayNames[date.getDay()]})`; }; // 시간 포맷팅 const formatTime = (timeStr) => { if (!timeStr) return null; return timeStr.slice(0, 5); }; // X용 날짜/시간 포맷팅 (오후 2:30 · 2026년 1월 15일) const formatXDateTime = (dateStr, timeStr) => { if (!dateStr) return ''; const date = new Date(dateStr); const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); let result = `${year}년 ${month}월 ${day}일`; if (timeStr) { const [hours, minutes] = timeStr.split(':').map(Number); const period = hours < 12 ? '오전' : '오후'; const hour12 = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours; result = `${period} ${hour12}:${String(minutes).padStart(2, '0')} · ${result}`; } return result; }; // 영상 정보 컴포넌트 (공통) function VideoInfo({ schedule, isShorts }) { return (
{/* 제목 */}

{decodeHtmlEntities(schedule.title)}

{/* 메타 정보 */}
{/* 날짜 */}
{formatFullDate(schedule.date)}
{/* 시간 */} {schedule.time && ( <>
{formatTime(schedule.time)}
)} {/* 채널명 */} {schedule.source_name && ( <>
{schedule.source_name}
)}
{/* 유튜브에서 보기 버튼 */}
YouTube에서 보기
); } // 유튜브 섹션 컴포넌트 function YoutubeSection({ schedule }) { const videoId = extractYoutubeVideoId(schedule.source_url); const isShorts = schedule.source_url?.includes('/shorts/'); if (!videoId) return null; // 숏츠: 가로 레이아웃 (영상 + 정보) if (isShorts) { return (
{/* 영상 임베드 */}