diff --git a/app/routes/brand-detail/brand-detail-content.tsx b/app/routes/brand-detail/brand-detail-content.tsx index 78788a6..5d8ac15 100644 --- a/app/routes/brand-detail/brand-detail-content.tsx +++ b/app/routes/brand-detail/brand-detail-content.tsx @@ -235,7 +235,8 @@ export default function BrandDetailContent({ data }: Props) { (p) => { const fallback = (p.productImageUrls ?? []).find(Boolean) ?? - (p.thumbnailImageUrl ?? ""); + p.thumbnailImageUrl ?? + ""; return { productId: p.productId, @@ -343,12 +344,11 @@ export default function BrandDetailContent({ data }: Props) { ? domainParam : "beauty"; - const brandIdNum = - validBrandId - ? brandId - : Number.isFinite(Number(data.id)) && Number(data.id) > 0 - ? Number(data.id) - : null; + const brandIdNum = validBrandId + ? brandId + : Number.isFinite(Number(data.id)) && Number(data.id) > 0 + ? Number(data.id) + : null; if (!brandIdNum) return; @@ -489,14 +489,19 @@ export default function BrandDetailContent({ data }: Props) { const showTitle = showSectionTitle; return ( -
+
{showTitle ? (
{sec.title}
) : null} -
+
{(sec.groups ?? []).map((g, gi) => (
-
캠페인 내역
- - {histories.length === 0 ? ( -
-
-
-
-
- 진행한 캠페인이 없어요 -
-
-
-
-
- ) : ( - <> -
- {pageItems.map((h) => ( - - ))} -
- -
- {page > GROUP_SIZE && ( - - )} - - - -
- {displayPages.map((p) => { - const disabledPage = p > totalPages && !hasNext; - const active = p === page; - - return ( - - ); - })} -
- - - - -
- - )} -
+
캠페인 내역
+ + {histories.length === 0 ? ( +
+
+
+
+
+ 진행한 캠페인이 없어요 +
+
+
+
+
+ ) : ( + <> +
+ {pageItems.map((h) => ( + + ))} +
+ +
+ {page > GROUP_SIZE && ( + + )} + + + +
+ {displayPages.map((p) => { + const disabledPage = p > totalPages && !hasNext; + const active = p === page; + + return ( + + ); + })} +
+ + + +
+ + )} +
diff --git a/app/routes/brand-detail/components/OngoingCampaignSection.tsx b/app/routes/brand-detail/components/OngoingCampaignSection.tsx index e5bcaf7..70856b4 100644 --- a/app/routes/brand-detail/components/OngoingCampaignSection.tsx +++ b/app/routes/brand-detail/components/OngoingCampaignSection.tsx @@ -20,7 +20,7 @@ export default function OngoingCampaignSection({ const isEmpty = campaigns.length === 0; return ( -
+
진행 중인 다른 캠페인
@@ -28,26 +28,34 @@ export default function OngoingCampaignSection({ ) : null}
{isEmpty ? ( -
-
-
-
+
+
+
+
진행 중인 다른 캠페인이 없어요
) : ( -
+
{campaigns.map((c) => (
void; + onClick?: () => void; }; export default function ProductMiniCard({ item, onClick }: Props) { return (
diff --git a/app/routes/brand-detail/components/SponsorableProductSection.tsx b/app/routes/brand-detail/components/SponsorableProductSection.tsx index 2502e1b..9b2db77 100644 --- a/app/routes/brand-detail/components/SponsorableProductSection.tsx +++ b/app/routes/brand-detail/components/SponsorableProductSection.tsx @@ -41,10 +41,10 @@ export default function SponsorableProductSection({ {isEmpty ? (
-
-
-
-
+
+
+
+
협찬 가능한 제품이 없어요
@@ -53,7 +53,7 @@ export default function SponsorableProductSection({
) : (
-
+
{products.map((p, idx) => ( @@ -85,7 +99,10 @@ function formatItems(productName: string, items: SponsorAvailableItem[]) { .join(" / "); } -function formatSubtitleNoQty(productName: string, items: SponsorAvailableItem[]) { +function formatSubtitleNoQty( + productName: string, + items: SponsorAvailableItem[], +) { const name = (productName ?? "").toString(); return (items ?? []) @@ -152,6 +169,43 @@ export default function SponsorableDetailContent() { const [loading, setLoading] = useState(false); const [errorText, setErrorText] = useState(null); + const [current, setCurrent] = useState(0); + const timerRef = useRef | null>(null); + + const banners: BannerItem[] = useMemo(() => { + const urls = (data?.productImageUrls ?? []) + .map((v) => (v ?? "").toString().trim()) + .filter(Boolean); + + return urls.map((src, idx) => ({ + src, + alt: `${data?.productName ?? "제품"} 배너 ${idx + 1}`, + })); + }, [data?.productImageUrls, data?.productName]); + + const start = useCallback(() => { + if (timerRef.current) return; + if (banners.length <= 1) return; + + timerRef.current = setInterval(() => { + setCurrent((prev) => (prev + 1) % banners.length); + }, INTERVAL); + }, [banners.length]); + + const stop = useCallback(() => { + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + } + }, []); + + useEffect(() => { + setCurrent(0); + stop(); + start(); + return stop; + }, [banners.length, start, stop]); + useEffect(() => { if (!layout) return; layout.setHideHeader(true); @@ -200,8 +254,6 @@ export default function SponsorableDetailContent() { }; }, [brandId, productId, canFetch]); - const heroUrl = state.heroImageUrl || data?.productImageUrls?.[0] || ""; - const itemsText = useMemo(() => { if (!data) return ""; const items = data.sponsorInfo?.items ?? []; @@ -227,7 +279,10 @@ export default function SponsorableDetailContent() {
- navigate(-1)} /> + navigate(-1)} + />
@@ -242,20 +297,47 @@ export default function SponsorableDetailContent() { {!loading && !errorText && data && (
- {heroUrl ? ( + {banners.length ? (
- {data.productName} -
- - - +
+ {banners.map((banner, i) => ( +
+ {banner.alt} +
+ ))}
+ + {banners.length > 1 && ( +
+ {banners.map((_, i) => ( +
+ )}
) : (
@@ -278,6 +360,7 @@ export default function SponsorableDetailContent() { ) : null}
+
@@ -315,7 +398,6 @@ export default function SponsorableDetailContent() {
- {/* ✅ 버튼 영역: 컨텐츠와 분리 (피그마: px-6 pt-14 pb-24) */} {showButton && (