수정된 파일 (5개):
- pages/mobile/public/Album.jsx
- fetch('/api/albums') → getAlbums()
- pages/mobile/public/Members.jsx
- fetch('/api/members') → getMembers()
- pages/mobile/public/AlbumDetail.jsx
- fetch('/api/albums') → getAlbums()
- fetch('/api/albums/{id}/tracks') → getAlbumTracks()
- pages/mobile/public/AlbumGallery.jsx
- fetch('/api/albums') → getAlbums()
- fetch('/api/albums/{id}/photos') → getAlbumPhotos()
- pages/mobile/public/Schedule.jsx
- fetch('/api/schedules?year=...') → getSchedules()
- fetch('/api/schedules/categories') → getCategories()
- fetch('/api/schedules?search=...') → searchSchedules()
Home.jsx는 이미 API 모듈 사용 중이므로 제외
141 lines
6 KiB
JavaScript
141 lines
6 KiB
JavaScript
import { motion } from 'framer-motion';
|
|
import { useState, useEffect } from 'react';
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
import { ArrowLeft, Play } from 'lucide-react';
|
|
import { getAlbums, getAlbumTracks } from '../../../api/public/albums';
|
|
|
|
// 모바일 앨범 상세 페이지
|
|
function MobileAlbumDetail() {
|
|
const { name } = useParams();
|
|
const navigate = useNavigate();
|
|
const [album, setAlbum] = useState(null);
|
|
const [tracks, setTracks] = useState([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
// 앨범 정보 로드
|
|
getAlbums()
|
|
.then(data => {
|
|
const found = data.find(a => a.folder_name === name);
|
|
if (found) {
|
|
setAlbum(found);
|
|
// 트랙 정보 로드
|
|
getAlbumTracks(found.id)
|
|
.then(setTracks)
|
|
.catch(console.error);
|
|
}
|
|
setLoading(false);
|
|
})
|
|
.catch(console.error);
|
|
}, [name]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-64">
|
|
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!album) {
|
|
return (
|
|
<div className="text-center py-12">
|
|
<p className="text-gray-500">앨범을 찾을 수 없습니다</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="pb-6">
|
|
{/* 헤더 */}
|
|
<div className="sticky top-14 z-40 bg-white/80 backdrop-blur-sm px-4 py-3 flex items-center gap-3 border-b">
|
|
<button onClick={() => navigate(-1)} className="p-1">
|
|
<ArrowLeft size={24} />
|
|
</button>
|
|
<span className="font-semibold truncate">{album.title}</span>
|
|
</div>
|
|
|
|
{/* 앨범 정보 */}
|
|
<div className="px-4 py-6">
|
|
<div className="flex gap-4">
|
|
<div className="w-32 h-32 rounded-2xl overflow-hidden bg-gray-200 shadow-lg flex-shrink-0">
|
|
{album.cover_medium_url && (
|
|
<img
|
|
src={album.cover_medium_url}
|
|
alt={album.title}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className="flex-1">
|
|
<h1 className="text-xl font-bold">{album.title}</h1>
|
|
<p className="text-gray-500 text-sm mt-1">{album.album_type}</p>
|
|
<p className="text-gray-400 text-sm">{album.release_date}</p>
|
|
|
|
<button
|
|
onClick={() => navigate(`/album/${name}/gallery`)}
|
|
className="mt-4 px-4 py-2 bg-primary text-white rounded-full text-sm font-medium"
|
|
>
|
|
갤러리 보기
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 트랙 리스트 */}
|
|
{tracks.length > 0 && (
|
|
<div className="px-4">
|
|
<h2 className="text-lg font-bold mb-3">수록곡</h2>
|
|
<div className="bg-white rounded-2xl overflow-hidden shadow-sm">
|
|
{tracks.map((track, index) => (
|
|
<motion.div
|
|
key={track.id}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ delay: index * 0.05 }}
|
|
className="flex items-center gap-3 p-4 border-b border-gray-100 last:border-none"
|
|
>
|
|
<span className="w-6 text-center text-gray-400 text-sm">
|
|
{track.track_number}
|
|
</span>
|
|
<div className="flex-1 min-w-0">
|
|
<p className={`font-medium text-sm truncate ${track.is_title_track ? 'text-primary' : ''}`}>
|
|
{track.title}
|
|
{track.is_title_track && (
|
|
<span className="ml-2 text-xs bg-primary/10 text-primary px-2 py-0.5 rounded-full">
|
|
타이틀
|
|
</span>
|
|
)}
|
|
</p>
|
|
<p className="text-xs text-gray-400 truncate">{track.duration}</p>
|
|
</div>
|
|
{track.music_video_url && (
|
|
<a
|
|
href={track.music_video_url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="p-2 text-red-500"
|
|
>
|
|
<Play size={18} fill="currentColor" />
|
|
</a>
|
|
)}
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 설명 */}
|
|
{album.description && (
|
|
<div className="px-4 mt-6">
|
|
<h2 className="text-lg font-bold mb-3">소개</h2>
|
|
<p className="text-gray-600 text-sm leading-relaxed bg-white p-4 rounded-2xl">
|
|
{album.description}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default MobileAlbumDetail;
|