feat: PC 예능 일정 상세 페이지 추가
- VarietySection: 썸네일, 방송사 뱃지, 제목, 날짜/시간, 멤버, 다시보기 버튼 - ScheduleDetail에서 '예능' 카테고리 분기 연결 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c14bd90e89
commit
6feedae267
3 changed files with 99 additions and 2 deletions
|
|
@ -5,7 +5,7 @@ import { Calendar, ChevronRight } from 'lucide-react';
|
||||||
import { getSchedule } from '@/api';
|
import { getSchedule } from '@/api';
|
||||||
|
|
||||||
// 섹션 컴포넌트들
|
// 섹션 컴포넌트들
|
||||||
import { YoutubeSection, XSection, DefaultSection, decodeHtmlEntities } from './sections';
|
import { YoutubeSection, XSection, VarietySection, DefaultSection, decodeHtmlEntities } from './sections';
|
||||||
import Birthday from './Birthday';
|
import Birthday from './Birthday';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -153,6 +153,8 @@ function PCScheduleDetail() {
|
||||||
return <YoutubeSection schedule={schedule} />;
|
return <YoutubeSection schedule={schedule} />;
|
||||||
case 'X':
|
case 'X':
|
||||||
return <XSection schedule={schedule} />;
|
return <XSection schedule={schedule} />;
|
||||||
|
case '예능':
|
||||||
|
return <VarietySection schedule={schedule} />;
|
||||||
default:
|
default:
|
||||||
return <DefaultSection schedule={schedule} />;
|
return <DefaultSection schedule={schedule} />;
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +162,8 @@ function PCScheduleDetail() {
|
||||||
|
|
||||||
const isYoutube = categoryName === '유튜브';
|
const isYoutube = categoryName === '유튜브';
|
||||||
const isX = categoryName === 'X';
|
const isX = categoryName === 'X';
|
||||||
const hasCustomLayout = isYoutube || isX;
|
const isVariety = categoryName === '예능';
|
||||||
|
const hasCustomLayout = isYoutube || isX || isVariety;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-[calc(100vh-64px)] bg-gray-50">
|
<div className="min-h-[calc(100vh-64px)] bg-gray-50">
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export { default as YoutubeSection } from './YoutubeSection';
|
export { default as YoutubeSection } from './YoutubeSection';
|
||||||
export { default as XSection } from './XSection';
|
export { default as XSection } from './XSection';
|
||||||
|
export { default as VarietySection } from './VarietySection';
|
||||||
export { default as DefaultSection } from './DefaultSection';
|
export { default as DefaultSection } from './DefaultSection';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue