From 97d61482801b2c49497fce7bd9d3ef82cdbcb9f0 Mon Sep 17 00:00:00 2001 From: caadiq Date: Thu, 22 Jan 2026 13:19:36 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=95=A8=EB=B2=94=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=BB=A8=EC=85=89=20=ED=8F=AC?= =?UTF-8?q?=ED=86=A0=20=EB=9D=BC=EC=9D=B4=ED=8A=B8=EB=B0=95=EC=8A=A4=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PC/Mobile 앨범 상세 페이지 컨셉 포토 라이트박스에서 좌우 네비게이션 지원 - 현재 보이는 미리보기 사진 내에서만 네비게이션 (PC 4장, Mobile 6장) - 컨셉 이름과 멤버 정보 표시 (AlbumGallery와 동일한 UX) - lightbox state에 photos 배열 추가하여 메타데이터 관리 Co-Authored-By: Claude Opus 4.5 --- .../src/pages/album/mobile/AlbumDetail.jsx | 75 ++++++++++++++++--- .../src/pages/album/pc/AlbumDetail.jsx | 64 ++++++++++++++-- 2 files changed, 122 insertions(+), 17 deletions(-) diff --git a/frontend-temp/src/pages/album/mobile/AlbumDetail.jsx b/frontend-temp/src/pages/album/mobile/AlbumDetail.jsx index 854987e..d7a2ce7 100644 --- a/frontend-temp/src/pages/album/mobile/AlbumDetail.jsx +++ b/frontend-temp/src/pages/album/mobile/AlbumDetail.jsx @@ -27,7 +27,7 @@ import { LightboxIndicator } from '@/components/common'; function MobileAlbumDetail() { const { name } = useParams(); const navigate = useNavigate(); - const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, showNav: true, teasers: null }); + const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, showNav: true, teasers: null, photos: null }); const [showAllTracks, setShowAllTracks] = useState(false); const [showDescriptionModal, setShowDescriptionModal] = useState(false); const swiperRef = useRef(null); @@ -47,6 +47,7 @@ function MobileAlbumDetail() { index, showNav: options.showNav !== false, teasers: options.teasers, + photos: options.photos || null, }); window.history.pushState({ lightbox: true }, ''); }, []); @@ -123,7 +124,24 @@ function MobileAlbumDetail() { ); } - const allPhotos = album.conceptPhotos ? Object.values(album.conceptPhotos).flat() : []; + // 컨셉 정보를 포함한 사진 배열 생성 + const allPhotosWithInfo = []; + if (album.conceptPhotos) { + Object.entries(album.conceptPhotos).forEach(([concept, conceptPhotos]) => { + conceptPhotos.forEach((p) => + allPhotosWithInfo.push({ + ...p, + originalUrl: p.original_url, + mediumUrl: p.medium_url, + thumbUrl: p.thumb_url, + title: concept, + members: p.members || '', + }) + ); + }); + } + const previewCount = 6; + const previewPhotos = allPhotosWithInfo.slice(0, previewCount); const displayTracks = showAllTracks ? album.tracks : album.tracks?.slice(0, 5); return ( @@ -276,7 +294,7 @@ function MobileAlbumDetail() { )} {/* 컨셉 포토 */} - {allPhotos.length > 0 && ( + {previewPhotos.length > 0 && (

컨셉 포토

- {allPhotos.slice(0, 6).map((photo, idx) => ( + {previewPhotos.map((photo, idx) => (
openLightbox([photo.original_url], 0, { showNav: false })} + onClick={() => + openLightbox( + previewPhotos.map((p) => p.originalUrl), + idx, + { showNav: true, photos: previewPhotos } + ) + } className="aspect-square bg-gray-100 rounded-xl overflow-hidden shadow-sm" > {`컨셉 navigate(`/album/${name}/gallery`)} className="w-full mt-3 py-3 text-sm text-primary font-medium bg-primary/5 rounded-xl flex items-center justify-center gap-1" > - 전체 {allPhotos.length}장 보기 + 전체 {allPhotosWithInfo.length}장 보기 @@ -403,11 +427,11 @@ function MobileAlbumDetail() { > {lightbox.images.map((url, index) => ( -
+
{lightbox.teasers?.[index]?.media_type === 'video' ? (
))} diff --git a/frontend-temp/src/pages/album/pc/AlbumDetail.jsx b/frontend-temp/src/pages/album/pc/AlbumDetail.jsx index 6570f80..15028cf 100644 --- a/frontend-temp/src/pages/album/pc/AlbumDetail.jsx +++ b/frontend-temp/src/pages/album/pc/AlbumDetail.jsx @@ -23,7 +23,7 @@ import { LightboxIndicator } from '@/components/common'; function PCAlbumDetail() { const { name } = useParams(); const navigate = useNavigate(); - const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, teasers: null }); + const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, teasers: null, photos: null }); const [slideDirection, setSlideDirection] = useState(0); const [imageLoaded, setImageLoaded] = useState(false); const [preloadedImages] = useState(() => new Set()); @@ -60,7 +60,7 @@ function PCAlbumDetail() { // 라이트박스 열기 const openLightbox = useCallback((images, index, options = {}) => { - setLightbox({ open: true, images, index, teasers: options.teasers }); + setLightbox({ open: true, images, index, teasers: options.teasers, photos: options.photos || null }); window.history.pushState({ lightbox: true }, ''); }, []); @@ -387,9 +387,22 @@ function PCAlbumDetail() { className="mt-10" > {(() => { - const allPhotos = Object.values(album.conceptPhotos).flat(); - const previewPhotos = allPhotos.slice(0, 4); - const totalCount = allPhotos.length; + // 컨셉 정보를 포함한 사진 배열 생성 + const allPhotosWithInfo = []; + Object.entries(album.conceptPhotos).forEach(([concept, conceptPhotos]) => { + conceptPhotos.forEach((p) => + allPhotosWithInfo.push({ + ...p, + originalUrl: p.original_url, + mediumUrl: p.medium_url, + title: concept, + members: p.members || '', + }) + ); + }); + const previewCount = 4; + const previewPhotos = allPhotosWithInfo.slice(0, previewCount); + const totalCount = allPhotosWithInfo.length; return ( <> @@ -406,11 +419,17 @@ function PCAlbumDetail() { {previewPhotos.map((photo, idx) => (
openLightbox([photo.original_url], 0)} + onClick={() => + openLightbox( + previewPhotos.map((p) => p.originalUrl), + idx, + { photos: previewPhotos } + ) + } className="aspect-square bg-gray-200 rounded-xl overflow-hidden cursor-pointer transition-all duration-300 ease-out hover:scale-[1.03] hover:shadow-xl hover:z-10" > {`컨셉 )} + {/* 컨셉 포토 정보 (멤버 + 컨셉) */} + {imageLoaded && lightbox.photos && (() => { + const photo = lightbox.photos[lightbox.index]; + const title = photo?.title; + const hasValidTitle = title && title.trim() && title !== 'Default'; + const members = photo?.members; + const hasMembers = members && String(members).trim(); + + if (!hasValidTitle && !hasMembers) return null; + + return ( +
+ {hasValidTitle && ( + + {title} + + )} + {hasMembers && ( +
+ {String(members) + .split(',') + .map((member, idx) => ( + + {member.trim()} + + ))} +
+ )} +
+ ); + })()}
{/* 다음 버튼 */}