feat: PC 예능 일정 상세 페이지 추가

- VarietySection: 썸네일, 방송사 뱃지, 제목, 날짜/시간, 멤버, 다시보기 버튼
- ScheduleDetail에서 '예능' 카테고리 분기 연결

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-04-05 13:45:14 +09:00
parent c14bd90e89
commit 6feedae267
3 changed files with 99 additions and 2 deletions

View file

@ -5,7 +5,7 @@ import { Calendar, ChevronRight } from 'lucide-react';
import { getSchedule } from '@/api';
//
import { YoutubeSection, XSection, DefaultSection, decodeHtmlEntities } from './sections';
import { YoutubeSection, XSection, VarietySection, DefaultSection, decodeHtmlEntities } from './sections';
import Birthday from './Birthday';
/**
@ -153,6 +153,8 @@ function PCScheduleDetail() {
return <YoutubeSection schedule={schedule} />;
case 'X':
return <XSection schedule={schedule} />;
case '예능':
return <VarietySection schedule={schedule} />;
default:
return <DefaultSection schedule={schedule} />;
}
@ -160,7 +162,8 @@ function PCScheduleDetail() {
const isYoutube = categoryName === '유튜브';
const isX = categoryName === 'X';
const hasCustomLayout = isYoutube || isX;
const isVariety = categoryName === '예능';
const hasCustomLayout = isYoutube || isX || isVariety;
return (
<div className="min-h-[calc(100vh-64px)] bg-gray-50">

View file

@ -0,0 +1,93 @@
import { Calendar, Clock, Tv, ExternalLink } from 'lucide-react';
import { decodeHtmlEntities, formatFullDate, formatTime } from './utils';
/**
* 예능 일정 섹션 컴포넌트
*/
function VarietySection({ schedule }) {
const members = schedule.members || [];
const isFullGroup = members.length === 5;
return (
<div className="space-y-6">
{/* 썸네일 */}
{schedule.thumbnailUrl && (
<div className="rounded-xl overflow-hidden shadow-lg">
<img
src={schedule.thumbnailUrl}
alt={schedule.title}
className="w-full object-cover"
/>
</div>
)}
{/* 정보 카드 */}
<div className="bg-white rounded-xl p-6 shadow-sm">
{/* 방송사 뱃지 + 제목 */}
<div className="flex items-start gap-3 mb-4">
{schedule.broadcaster && (
<span className="flex-shrink-0 inline-flex items-center gap-1.5 px-3 py-1 bg-cyan-50 text-cyan-700 text-sm font-medium rounded-full">
<Tv size={14} />
{schedule.broadcaster}
</span>
)}
</div>
<h1 className="text-2xl font-bold text-gray-900 mb-4">
{decodeHtmlEntities(schedule.title)}
</h1>
{/* 메타 정보 */}
<div className="flex flex-wrap items-center gap-4 text-sm text-gray-500 mb-6">
<div className="flex items-center gap-2">
<Calendar size={16} />
<span>{formatFullDate(schedule.date)}</span>
</div>
{schedule.time && (
<div className="flex items-center gap-2">
<Clock size={16} />
<span>{formatTime(schedule.time)}</span>
</div>
)}
</div>
{/* 멤버 */}
{members.length > 0 && (
<div className="mb-6">
<p className="text-sm text-gray-500 mb-2">출연 멤버</p>
<div className="flex flex-wrap gap-2">
{isFullGroup ? (
<span className="px-3 py-1 bg-primary/10 text-primary text-sm font-medium rounded-full">
프로미스나인
</span>
) : (
members.map((member) => (
<span key={member.id} className="px-3 py-1 bg-primary/10 text-primary text-sm font-medium rounded-full">
{member.name}
</span>
))
)}
</div>
</div>
)}
{/* 다시보기 링크 */}
{schedule.replayUrl && (
<div className="pt-5 border-t border-gray-100">
<a
href={schedule.replayUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-5 py-2.5 bg-cyan-500 hover:bg-cyan-600 text-white rounded-xl font-medium transition-colors"
>
<ExternalLink size={16} />
다시보기
</a>
</div>
)}
</div>
</div>
);
}
export default VarietySection;

View file

@ -1,4 +1,5 @@
export { default as YoutubeSection } from './YoutubeSection';
export { default as XSection } from './XSection';
export { default as VarietySection } from './VarietySection';
export { default as DefaultSection } from './DefaultSection';
export * from './utils';